LowCodeStacks™
Sign in

Power Automate expressions cheat sheet: functions with examples

Power Automate expressions list by job: text, arrays, logic, conversion, dates and step references, each with syntax and an example, plus null-safe ? and the traps.

Power AutomateQuick reference13 min read

Checked against Microsoft Learn · 11 sourcesPosted by the Maker DeskHow we write guides

This cheat sheet lists the Power Automate expressions makers use most, grouped by job: text, arrays, logic, conversion, dates and reading earlier steps. Each entry has the syntax, one line on what it does, and an example with its result. The same functions run in Azure Logic Apps, because both use Microsoft's Workflow Definition Language.

Four common expression mistakes and their fixes. First, reading a field that isn't there: triggerBody() followed by Region in square brackets fails the run with Unable to process template language expressions; the fix is coalesce of triggerBody() question mark Region with Unassigned as the fallback, which returns Unassigned. Second, matching text: contains of the subject and urgent returns false for a subject in capitals, because contains is case-sensitive; the fix is to wrap the subject in toLower first. Third, a percentage: div of 75 by 200 returns 0, because dividing two whole numbers gives a whole number; the fix is div of float 75 by float 200, which returns 0.375. Fourth, a time in an email: utcNow() gives the time in UTC, not the reader's; the fix is convertFromUtc of utcNow() to GMT Standard Time with the format dd/MM/yyyy HH:mm.
Four expressions that go wrong, what happens, and the version that works.

Where expressions go

  • New designer: select the field, then select fx (Insert expression) next to the lightning bolt (Insert dynamic content). Type the function, use the Dynamic content tab inside the editor to drop in values from earlier steps, then select Add. The editor suggests functions and their parameters as you type.
  • Classic designer: select Add dynamic content, then the Expression tab, type the function, and select OK.
  • Type the function without @. Microsoft's own steps type formatDateTime(...) straight into the editor; the designer handles the rest.
  • Text in single quotes: 'Approved'. Numbers and arrays need none: length('Hello'), not length("Hello").

Behind the designer, the flow's definition marks an expression with @. That changes what the result is:

In the definitionWhat you getExample
@<expression> as the whole valueThe function's own type: a number, array, object or Boolean@length(body('Get_items')?['value']) returns 12
@{<expression>} inside textAlways text, joined into the surrounding wordsOrder @{triggerBody()?['Id']} received
No @ at allThe characters exactly as typedutcNow() comes back as the text utcNow()
@@ at the startA literal @@@ gives @

You also write the @ yourself in a few places, such as a trigger condition: @equals(triggerBody()?['Status'], 'Approved').

Text functions

Positions start at 0.

FunctionSyntaxWhat it doesExample → result
concatconcat('<text1>', '<text2>', …)Joins two or more stringsconcat('INV-', '0042') → INV-0042
substringsubstring('<text>', <start>, <length>?)Characters from a start position, for a length (not an end position)substring('Invoice-2026-0042', 8, 4) → 2026
sliceslice('<text>', <start>, <end>?)Characters from a start position up to, not including, an end; negative counts from the endslice('Hello World', -2) → ld
replacereplace('<text>', '<old>', '<new>')Swaps a substring for new text; case-sensitive; returns the original if there's no matchreplace('2026/03/18', '/', '-') → 2026-03-18
splitsplit('<text>', '<delimiter>')Breaks text into an arraysplit('a_b_c', '_') → ["a","b","c"]
toLower / toUppertoLower('<text>')Changes casetoLower('Hello World') → hello world
trimtrim('<text>')Removes spaces at the start and endtrim(' Hello ') → Hello
indexOfindexOf('<text>', '<search>')Position of the first match, not case-sensitive; -1 if noneindexOf('hello world', 'world') → 6
startsWith / endsWithstartsWith('<text>', '<search>')true or false; not case-sensitiveendsWith('report.PDF', '.pdf') → true
text
Last 4 characters:     substring(variables('Code'), sub(length(variables('Code')), 4), 4)
Second part of a name: split(triggerBody()?['FullName'], ' ')[1]
Domain of an email:    last(split(triggerBody()?['Email'], '@'))
New line in text:      concat('Line 1', decodeUriComponent('%0A'), 'Line 2')
  • substring fails if it runs past the end of the text, unlike similar functions in other languages. Check the length first, or use slice, which stops quietly at the end.
  • split keeps spaces: split('a, b, c', ',') gives "a", " b" and " c". Trim each item afterwards.
  • \n isn't a line break in an expression. Use decodeUriComponent('%0A'), or <br> in HTML.

Collection functions

FunctionSyntaxWhat it doesExample → result
lengthlength(<array or text>)Number of items, or characterslength(body('Get_items')?['value']) → the number of items
first / lastfirst(<array or text>)First or last item (or character)first(createArray(0, 1, 2)) → 0
unionunion(<array1>, <array2>, …)Every item from all the arrays, without duplicatesunion(createArray(1, 2, 3), createArray(1, 2, 10)) → [1,2,3,10]
containscontains(<collection>, '<value>')Text contains a substring, array contains a value, or object has a key; case-sensitivecontains(createArray('North', 'East'), 'East') → true
emptyempty(<collection>)true if the text, array or object is emptyempty('') → true
joinjoin(<array>, '<delimiter>')One string from every item of an arrayjoin(createArray('a', 'b', 'c'), '; ') → a; b; c
  • first of an empty array returns null, not an error. Check with empty() before you use the result.
  • contains on an array of objects compares whole objects, not one field. Filter the array with Filter array and check the length of the result instead.
  • join needs plain values. For an array of records, pull out one field with a Select action first.

Logical functions

FunctionSyntaxWhat it doesExample → result
ifif(<test>, <if true>, <if false>)One of two valuesif(equals(1, 1), 'yes', 'no') → yes
equalsequals(<value1>, <value2>)true if they're equivalentequals(triggerBody()?['Status'], 'Approved')
and / orand(<test1>, <test2>, …)All true / at least one trueand(greater(1, 10), equals(0, 0)) → false
notnot(<test>)The oppositenot(contains('200 Success', 'Fail')) → true
greatergreater(<value>, <compareTo>)true if the first is bigger; equal gives falsegreater(10, 10) → false
less, greaterOrEquals, lessOrEqualsSame shape as greaterThe other comparisons, for numbers and textlessOrEquals(10, 10) → true

There are no operators such as >, = or && in these expressions: every comparison is a function, and conditions nest, as in or(and(A, B), C). When a comparison meets a null, it treats it as empty text (''). The function reference lets and and or take more than two tests; Microsoft's conditions tutorial says they take two. Nesting pairs, as in and(A, and(B, C)), fits both descriptions.

Conversion functions

FunctionSyntaxWhat it doesExample → result
intint('<text>')Text to a whole numberint('10') → 10
floatfloat('<text>', '<locale>'?)Text to a decimal number, optionally read in a localefloat('10.000,333', 'de-DE') → 10000.333
stringstring(<value>)Any value to text; null becomes ''string(10) → "10"
jsonjson('<text>')JSON text (or XML) to an object or arrayjson('{"name":"Alice"}')?['name'] → Alice
base64base64('<text>')Encodes text as base64base64('hello') → aGVsbG8=
base64ToStringbase64ToString('<base64>')Decodes base64 back to textbase64ToString('aGVsbG8=') → hello
formatNumberformatNumber(<number>, '<format>', '<locale>'?)A number as formatted textformatNumber(1234567890, '#,##0.00', 'en-US') → 1,234,567,890.00
  • int('3.5') fails; it doesn't round. Use int(float(...)) when the text might have decimals.
  • Don't wrap an action's output in json() when it's already an object; most connector outputs are. That causes an error. Use json() on text only. Dynamic content missing covers when to parse and when to read values directly.
  • You rarely need the base64 functions. The platform does most base64 encoding and decoding for you, and Microsoft warns that adding these functions by hand can make them vanish from the designer, leaving just their inputs.

Dates and times

FunctionSyntaxWhat it doesExample → result
utcNowutcNow('<format>'?)The current date and time, in UTCutcNow('yyyy-MM-dd') → 2026-10-22
addDaysaddDays('<timestamp>', <days>, '<format>'?)Adds (or, with a negative number, subtracts) daysaddDays('2018-03-15T00:00:00Z', -5) → 2018-03-10T00:00:00.0000000Z
formatDateTimeformatDateTime('<timestamp>', '<format>'?, '<locale>'?)A timestamp as text in your formatformatDateTime('2026-03-18T14:30:00Z', 'dd MMM yyyy') → 18 Mar 2026
convertFromUtcconvertFromUtc('<timestamp>', '<zone>', '<format>'?)UTC to a named time zoneconvertFromUtc(utcNow(), 'GMT Standard Time', 'HH:mm')
convertTimeZoneconvertTimeZone('<timestamp>', '<from zone>', '<to zone>', '<format>'?)Any zone to any zoneconvertTimeZone('2018-01-01T08:00:00.0000000Z', 'UTC', 'Pacific Standard Time') → 2018-01-01T00:00:00.0000000
ticksticks('<timestamp>')The timestamp as a count of 100-nanosecond ticks since 1 January 0001ticks('2026-03-18')
text
Days between two dates: div(sub(ticks(variables('End')), ticks(variables('Start'))), 864000000000)
Is A after B?           greater(ticks(variables('A')), ticks(variables('B')))
Today in London:        convertFromUtc(utcNow(), 'GMT Standard Time', 'dd/MM/yyyy')
  • Format codes are case-sensitive: MM is the month and mm is minutes; dd is the day.
  • Time zone names come from Microsoft's list of Windows default time zones, such as GMT Standard Time or AUS Eastern Standard Time. Microsoft notes you may have to drop punctuation from a name.
  • Compare dates with ticks, not as text: text only sorts correctly in the yyyy-MM-dd shape.
  • One day is 864,000,000,000 ticks. Microsoft's expression cookbook says there's no built-in date difference function and uses this pattern, but the function reference does list dateDifference(), which returns a timespan as text, such as 1268.00:00:00. Use ticks when you need a number.

For the time zone fixes in detail, see Power Automate dates and time zones.

Referencing earlier steps

FunctionSyntaxWhat it returnsExample
triggerBodytriggerBody()The trigger's bodytriggerBody()?['Title']
triggerOutputstriggerOutputs()The trigger's whole output: headers and bodytriggerOutputs()?['headers']
bodybody('<action name>')An action's bodybody('Get_user')?['mail']
outputsoutputs('<action name>')An action's whole output: status code, headers and bodyoutputs('Compose')
itemsitems('<loop name>')The current item of a named Apply to each loopitems('Apply_to_each')?['Email']
itemitem()The current item in a repeating action, such as Apply to each or Filter arrayitem()?['Status']
variablesvariables('<name>')A variable's valuevariables('Total')
parametersparameters('<name>')A parameter defined in the flow's definitionparameters('customerName')
resultresult('<scope name>')The inputs, outputs and status of each top-level action in a scope or loopresult('Try')
workflowworkflow()Details of the flow and the current runworkflow()?['tags']?['flowDisplayName']
  • Action names use underscores for spaces: the action Get user is body('Get_user').
  • Path-style keys work too. The designer sometimes writes outputs('Get_secret')?['body/value'] for a field inside the body.
  • Environment variables in a solution appear in the Dynamic content list; pick them from there rather than typing a reference.
  • result('Try') is how a Catch scope finds the failed action; child flows that fail well uses it to send the parent a proper error report, and workflow() to link to the failed run.

Null-safe access and coalesce

A field that's missing or null stops the run with Unable to process template language expressions. The question mark operator prevents that.

You writeWhen the field is missingUse it for
triggerBody()['Region']The run failsNever, for optional fields
triggerBody()?['Region']Returns nullAny field that might be absent
body('HTTP')?['data']?['items']?[0]?['name']Returns null at the first missing levelDeep paths
coalesce(triggerBody()?['Region'], 'Unassigned')Returns UnassignedA default value
  • Put ? before every level of a path, not only the first.
  • coalesce returns the first value that isn't null, and an empty string isn't null. For a field that can arrive as '', test it: if(empty(triggerBody()?['Region']), 'Unassigned', triggerBody()?['Region']).
  • empty() is true for '' and null, but errors on a number, such as empty(0).

Traps

Warning

Typed straight into the box, a function doesn't run. Microsoft's steps add every expression through fx (or the Expression tab), and in the definition only a value marked with @ is evaluated. Anything else comes back as the characters you typed, so an email reads utcNow() instead of a date. Delete the text and add it through fx.

Don't

  • Don't write Order @triggerBody()?['Id'] received in the definition. Inside text, an expression needs braces, Order @{triggerBody()?['Id']} received, or it may not be read as an expression at all.
  • Don't trust contains or replace to ignore case. Both are case-sensitive; indexOf isn't. Wrap both sides in toLower.
  • Don't divide two whole numbers and expect a fraction: div(75, 200) is 0 and div(11, 5) is 2. Use div(float(75), float(200)).
  • Don't format utcNow() and send it to people. It's UTC; convert it first.
  • Don't use toUpper on one side of a comparison only. Normalise both sides.

Do

  • Use ?['field'] for every optional field, and coalesce for a default.
  • Give actions meaningful names, so body('Get_manager') says what it reads.
  • Test an awkward expression in a Compose action first, and read its output in the run history.

Sources

Was this helpful?

Comments

No comments yet. Share what worked for you.

Sign in to comment. Comments show under a display name you choose, never your email.