Power Automate Expression Syntax: The Complete Cheat Sheet (2026)
Power Automate expressions reference: the WDL function-call rules plus 90+ copy-paste formulas for strings, dates, arrays, objects, loops, nulls and errors.

Power Automate expression syntax is a function-call language — every expression looks like functionName(argument1, argument2), never an inline operator like + or -. It runs on Workflow Definition Language (WDL), the same engine behind Azure Logic Apps (Microsoft Learn, 2026). You drop it into a flow's action inputs to transform, compare, or compute values while the flow runs. This Power Automate expressions reference covers the syntax rules first, then every function with a worked example. The parser is strict and unforgiving: single quotes only, no infix math operators, two distinct forms that aren't interchangeable. Get any one of those three rules wrong and the flow won't even save.
Need to pull just the file extension off an uploaded document? Build a subject line from three different fields? Fall back to a default when SharePoint hands you a null? That's what expressions are for. You write them in the expression editor tab of any action input field: click the field, select "Expression", type your function, click OK. The result replaces the field value at run time.
Two forms exist, and mixing them up is the single most common syntax mistake. Inline — @{functionName(args)} — embeds the result inside a string, so Hello @{triggerOutputs()?['body/Name']} produces Hello Rizwan. Pure expression — just functionName(args) with no @{} wrapper — returns the raw value and is what you type into the expression editor tab. Wrap a pure expression in @{} there and you won't get a warning — you'll get a string that starts with a literal @.
Key Takeaways
- Power Automate expression syntax is a strict, WDL-based function-call language —concat(),if(),coalesce(), and more — shared with Azure Logic Apps (Microsoft Learn, 2026).
- Use@{...}only for inline string interpolation; use barefunctionName()in the expression editor tab. Mixing the two forms up is the #1 syntax mistake.
- The?operator makes any property access null-safe:body('Get_item')?['value']never throws on a missing field.
- coalesce() is the fastest fix for null values from SharePoint, Dataverse, or HTTP responses.- All 90+ functions in this cheat sheet are copy-paste ready with live examples, including the workflow-reference functions (variables(),parameters(),triggerBody()) that most cheat sheets skip.
---
Power Automate Expression Syntax Basics
Power Automate expressions follow a strict parser. Get these rules wrong and you'll see InvalidTemplate before the flow even runs.
Syntax rules at a glance
Five rules cover almost every InvalidTemplate error you'll hit. Bookmark this table before you touch the expression editor:
| Rule | Right | Wrong |
|---|---|---|
| Two forms aren't interchangeable | concat('a','b') in the Expression tab | @{concat('a','b')} in the Expression tab |
| Strings use single quotes only | equals(toLower(x), 'done') | equals(toLower(x), "done") |
| No infix math operators | add(sub(10,2), 1) | 10 - 2 + 1 |
| Null-safe property access | body('Get_item')?['value'] | body('Get_item')['value'] |
| Escape a literal quote by doubling it | 'O''Brien' | 'O\'Brien' |
Treat this table as the TL;DR — check it first when a flow won't save. The "Two forms," "Quote rules," and "Null safety" sections below walk through the first, second, and fourth rows with real SharePoint/Graph field references instead of placeholder
x and 'a'/'b' values. The rest of the cheat sheet is the full function reference.Power Automate expression syntax vs. Excel and Power Fx
If you've written Excel formulas or Power Apps' Power Fx, the biggest adjustment isn't the function names — it's that WDL has no infix operators at all. =A1+B1 in Excel and TextInput1.Text & " rows" in Power Fx both use inline symbols; Power Automate has neither. Everything, including basic addition, goes through a function call: add(10, 5) not 10 + 5. String concatenation is concat('a', 'b'), not 'a' & 'b' or 'a' + 'b'.
The second gap is quoting. Power Fx and most JSON tooling accept double quotes for strings; WDL expressions only accept single quotes. Paste a Power Fx formula straight into the expression editor and you'll get InvalidTemplate on the double quotes before anything else even runs. Retype the string literals with single quotes and the same logical formula usually works once you swap the operators for function calls.
The two forms
# Pure expression (use in the Expression tab):
concat('Hello', ' ', triggerOutputs()?['body/Name'])
# Inline interpolation (embed inside a text field):
Hello @{triggerOutputs()?['body/Name']}, your order is @{outputs('Get_order')?['body/Status']}Never wrap a pure expression in @{...} in the expression editor — the parser will double-evaluate it and produce a string that starts with a literal @.
Quote rules
Single quotes delimit string literals. Double quotes are not valid inside expressions and will break the parser:
# Correct
replace(triggerOutputs()?['body/Title'], 'Draft', 'Final')
# Broken — double quotes cause InvalidTemplate
replace(triggerOutputs()?["body/Title"], "Draft", "Final")To include a literal single quote in a string, escape it by doubling: 'O''Brien'.
Null safety with ?
The ? operator on a property access returns null instead of throwing when the property doesn't exist. Use it on every dynamic content reference:
# Throws if 'Manager' is null in SharePoint People column
triggerOutputs()?['body/Manager/Email']
# Safe — returns null instead of crashing
triggerOutputs()?['body/Manager']?['Email']Chain as many ? as you need. Combine with coalesce() to substitute a default when null comes through.
Operator precedence
Power Automate expressions don't support infix math operators (+, -, *, /). Use functions: add(), sub(), mul(), div(), mod(). Parentheses control evaluation order inside function arguments exactly as you'd expect.
How to test any expression
Drop a Compose action into your flow, paste the expression in its input, and run the flow. The Compose output shows exactly what the expression returns — invaluable before wiring a complex expression into a Send Email or Condition action.
---
Power Automate Expressions Reference: Function Index
Every function covered below, alphabetically. Click a category header to jump to the full table with syntax and examples.
Looking for a searchable, filterable list you can copy from rather than a guide to read? Use the Power Automate expressions reference tool — same functions, with search, category filters and one-click copy.
| Function | Category | Returns |
|---|---|---|
action | Workflow | Metadata for the currently running action |
add | Math | Sum of two numbers |
addDays | Date/Time | Timestamp ± N days |
addHours | Date/Time | Timestamp ± N hours |
addMinutes | Date/Time | Timestamp ± N minutes |
addProperty | Object | Object with a new property added (errors if it already exists) |
addSeconds | Date/Time | Timestamp ± N seconds |
addToTime | Date/Time | Timestamp plus N units (Second…Year) |
and | Logic | true if all conditions true |
array | Conversion | Value wrapped in a single-element array |
base64 | Conversion | Base64-encoded string |
base64ToString | Conversion | Decoded UTF-8 string |
body | Workflow | The body of a named action's output |
bool | Conversion | Boolean true/false |
chunk | Array | Array split into sub-arrays |
coalesce | Logic | First non-null value |
concat | String | Joined strings |
contains | String / Array | true if item found |
convertTimeZone | Date/Time | Timestamp in target time zone |
createArray | Array | New array |
dateDifference | Date/Time | Difference as duration |
dayOfMonth | Date/Time | Day number (1–31) |
dayOfWeek | Date/Time | Day number (0=Sunday) |
dayOfYear | Date/Time | Day number within the year (1–366) |
decimal | Conversion | Decimal-precision number |
decodeBase64 | Conversion | Decoded string |
decodeUriComponent | Conversion | URL-decoded string |
div | Math | Quotient (integer) |
empty | Logic | true if null/empty |
endsWith | String | true if ends with |
equals | Logic | true if values equal |
first | Array | First element |
float | Conversion | Floating-point number |
formatDateTime | Date/Time | Formatted date string |
formatNumber | String | Formatted number string |
getFutureTime | Date/Time | Timestamp N units from now |
getPastTime | Date/Time | Timestamp N units ago |
greater | Logic | true if value1 > value2 |
greaterOrEquals | Logic | true if value1 >= value2 |
guid | Utility | New GUID string |
if | Logic | Conditional value |
indexOf | String | Position of substring |
int | Conversion | Integer |
intersection | Array | Common items |
isFloat | Logic | true if a string parses as a floating-point number |
isInt | Logic | true if a string parses as an integer |
item | Workflow | Current item inside the enclosing Apply to each |
items | Workflow | Current item of a named Apply to each loop |
join | Array | Array joined to string |
json | Conversion | Parsed JSON object |
last | Array | Last element |
lastIndexOf | String | Position of the last occurrence of a substring |
length | String / Array | Count |
less | Logic | true if value1 < value2 |
lessOrEquals | Logic | true if value1 <= value2 |
max | Math | Highest of a set of numbers or an array |
min | Math | Lowest of a set of numbers or an array |
mod | Math | Remainder |
mul | Math | Product |
not | Logic | Negated boolean |
nthIndexOf | String | Nth occurrence position |
or | Logic | true if any condition true |
outputs | Workflow | Full output (status, headers, body) of a named action |
parameters | Workflow | Value of a defined flow parameter |
parseDateTime | Date/Time | ISO timestamp parsed from a locale-formatted string |
rand | Math | Random integer in a range |
range | Array | Array of sequential integers |
removeProperty | Object | Object with a property removed |
replace | String | String with substitutions |
result | Workflow | Results of the first-level actions inside a Scope, For each or Until |
reverse | Array | Reversed array |
setProperty | Object | Object with a property set (added if missing) |
skip | Array | Array minus first N |
slice | String | Substring by start and end index (negative indexes allowed) |
sort | Array | Sorted array |
split | String | Array from split |
startOfDay | Date/Time | Timestamp at 00:00:00 of its day |
startOfHour | Date/Time | Timestamp at minute zero of its hour |
startOfMonth | Date/Time | Timestamp at 00:00:00 on the 1st of its month |
startsWith | String | true if starts with |
string | Conversion | String representation |
sub | Math | Difference |
substring | String | Substring by index |
subtractFromTime | Date/Time | Timestamp minus N units (Second…Year) |
take | Array | First N elements |
ticks | Date/Time | Tick count (100-ns units) |
toLower | String | Lowercase string |
toUpper | String | Uppercase string |
triggerBody | Workflow | The trigger's output body |
triggerOutputs | Workflow | Full output of the trigger (?['body/Field'] to reach a field) |
trim | String | Whitespace-trimmed string |
union | Array | Merged array (deduped) |
uriComponent | Conversion | URL-encoded string |
uriHost | Conversion | Host name of a URL |
uriPath | Conversion | Path of a URL |
utcNow | Date/Time | Current UTC timestamp |
variables | Workflow | Value of a flow variable |
workflow | Workflow | Metadata about the running flow |
xml | Conversion | XML object |
xpath | Conversion | Nodes or values matching an XPath expression |
All functions are documented at Microsoft Learn's Workflow Definition Language Functions Reference (linked at the top of this post and again at the end).
---
String Functions
These 14 string functions cover concatenation, search, replacement, padding, and formatting. These are the functions you'll use in nearly every flow that handles user data, file names, or email content — all documented in the Microsoft Learn WDL reference above.
| Function | Syntax | Example | Output |
|---|---|---|---|
| concat | concat(text1, text2, ...) | concat('Hello', ' ', 'World') | Hello World |
| substring | substring(text, startIndex, length) | substring('PowerAutomate', 5, 8) | Automate |
| replace | replace(text, oldText, newText) | replace('Hello World', 'World', 'Flow') | Hello Flow |
| split | split(text, delimiter) | split('a;b;c', ';') | ["a","b","c"] |
| toLower | toLower(text) | toLower('HELLO') | hello |
| toUpper | toUpper(text) | toUpper('hello') | HELLO |
| trim | trim(text) | trim(' hello ') | hello |
| indexOf | indexOf(text, searchText) | indexOf('Hello World', 'World') | 6 |
| nthIndexOf | nthIndexOf(text, searchText, n) | nthIndexOf('a.b.c.d', '.', 2) | 3 |
| length | length(text) | length('Power') | 5 |
| startsWith | startsWith(text, searchText) | startsWith('PowerAutomate', 'Power') | true |
| endsWith | endsWith(text, searchText) | endsWith('report.pdf', '.pdf') | true |
| contains | contains(text, searchText) | contains('Hello World', 'World') | true |
| formatNumber | formatNumber(number, format) | formatNumber(1234.5, 'C2') | $1,234.50 |
String tips
Use concat() instead of the @{...} inline syntax when combining more than two dynamic values — it's easier to debug. When checking user input, always wrap in toLower() or toUpper() first so comparisons are case-insensitive. Use nthIndexOf() to locate the second or third occurrence of a delimiter (e.g., finding the second dot in a version string like 1.20.3).
Power Automate cloud flows don't have a native padLeft() expression — use formatNumber() instead for zero-padded integers: formatNumber(triggerOutputs()?['body/ID'], '000000') turns 42 into 000042. For arbitrary string padding, the workaround is substring(concat('000000', string(triggerOutputs()?['body/ID'])), sub(length(concat('000000', string(triggerOutputs()?['body/ID']))), 6)).
---
Date and Time Functions
Power Automate stores every timestamp internally as UTC. These 13 functions handle formatting, arithmetic, and time-zone conversion — the three tasks you'll need for every flow that touches dates in emails, SharePoint columns, or approval deadlines. The formatDateTime() format strings follow the .NET DateTimeFormat conventions (Microsoft Learn, 2026).
| Function | Syntax | Example | Output |
|---|---|---|---|
| utcNow | utcNow(format?) | utcNow('yyyy-MM-dd') | 2026-07-10 |
| addDays | addDays(timestamp, days, format?) | addDays(utcNow(), 7, 'yyyy-MM-dd') | 7 days from now |
| addHours | addHours(timestamp, hours, format?) | addHours(utcNow(), -3) | 3 hours ago |
| addMinutes | addMinutes(timestamp, minutes) | addMinutes(utcNow(), 30) | 30 min from now |
| addSeconds | addSeconds(timestamp, seconds) | addSeconds(utcNow(), 90) | 90 seconds from now |
| formatDateTime | formatDateTime(timestamp, format) | formatDateTime(utcNow(), 'MM/dd/yyyy') | 07/10/2026 |
| convertTimeZone | convertTimeZone(timestamp, source, dest, format?) | convertTimeZone(utcNow(), 'UTC', 'Eastern Standard Time', 'hh:mm tt') | 08:30 AM |
| getPastTime | getPastTime(interval, timeUnit, format?) | getPastTime(7, 'Day', 'yyyy-MM-dd') | 7 days ago |
| getFutureTime | getFutureTime(interval, timeUnit, format?) | getFutureTime(1, 'Month', 'yyyy-MM-dd') | 1 month from now |
| dayOfWeek | dayOfWeek(timestamp) | dayOfWeek('2026-07-10') | 5 (Friday) |
| dayOfMonth | dayOfMonth(timestamp) | dayOfMonth('2026-07-10') | 10 |
| ticks | ticks(timestamp) | ticks('2026-01-01') | Tick count |
| dateDifference | dateDifference(startDate, endDate) | dateDifference('2026-01-01', '2026-07-10') | 190.00:00:00 |
Date tips
formatDateTime() uses .NET custom date format strings. Common patterns: yyyy-MM-dd for ISO, MM/dd/yyyy for US, dd/MM/yyyy for EU, dddd, MMMM d, yyyy for display. Always convert to the user's time zone with convertTimeZone() before including dates in emails — Power Automate stores every timestamp as UTC internally.
getPastTime and getFutureTime are cleaner than addDays(utcNow(), -7) when you want a fixed offset from now. Valid timeUnit values: Second, Minute, Hour, Day, Week, Month, Year.
Use ticks() to compare two dates numerically — subtract them, then divide by 864000000000 to get days:
div(sub(ticks(outputs('DueDate')), ticks(utcNow())), 864000000000)---
Collection and Array Functions
Power Automate has 14 collection and array functions. They work on arrays returned by "List rows", "Get items", and HTTP responses — anywhere a multi-value result comes back and needs filtering, slicing, or merging before the next action. The PnP community maintains real-world examples of these patterns in SharePoint flows at PnP Samples if you need more context beyond this reference.
| Function | Syntax | Example | Output |
|---|---|---|---|
| first | first(collection) | first(createArray('a','b','c')) | a |
| last | last(collection) | last(createArray('a','b','c')) | c |
| length | length(collection) | length(createArray('a','b','c')) | 3 |
| contains | contains(collection, value) | contains(createArray('a','b'), 'b') | true |
| join | join(collection, delimiter) | join(createArray('a','b','c'), '; ') | a; b; c |
| split | split(text, delimiter) | split('a,b,c', ',') | ["a","b","c"] |
| union | union(collection1, collection2) | union(createArray(1,2), createArray(2,3)) | [1,2,3] |
| intersection | intersection(col1, col2) | intersection(createArray(1,2,3), createArray(2,3,4)) | [2,3] |
| skip | skip(collection, count) | skip(createArray('a','b','c'), 1) | ["b","c"] |
| take | take(collection, count) | take(createArray('a','b','c'), 2) | ["a","b"] |
| createArray | createArray(item1, item2, ...) | createArray(1, 2, 3) | [1,2,3] |
| reverse | reverse(collection) | reverse(createArray(1,2,3)) | [3,2,1] |
| sort | sort(collection, sortBy?) | sort(createArray(3,1,2)) | [1,2,3] |
| chunk | chunk(collection, size) | chunk(createArray(1,2,3,4,5), 2) | [[1,2],[3,4],[5]] |
Array tips
Use empty() to check if an array has items before looping. Combine skip() and take() for pagination. union() also merges two objects (not just arrays), which is useful for building dynamic JSON payloads.
chunk() is useful when you need to process a large SharePoint list in batches — split the ID array with chunk(), then loop over each sub-array. sort() works on arrays of strings or numbers; for complex objects, sort inside a Select + Apply to Each combination. reverse() is handy when you want the most recent item from a sorted list without reversing the sort order in a query.
---
Logical Functions
| Function | Syntax | Example | Output |
|---|---|---|---|
| if | if(expression, valueIfTrue, valueIfFalse) | if(equals(1,1), 'yes', 'no') | yes |
| equals | equals(value1, value2) | equals(toLower('Hello'), 'hello') | true |
| and | and(expr1, expr2) | and(greater(5,3), less(5,10)) | true |
| or | or(expr1, expr2) | or(equals(1,2), equals(1,1)) | true |
| not | not(expression) | not(equals(1,2)) | true |
| greater | greater(value1, value2) | greater(10, 5) | true |
| greaterOrEquals | greaterOrEquals(value1, value2) | greaterOrEquals(5, 5) | true |
| less | less(value1, value2) | less(3, 5) | true |
| lessOrEquals | lessOrEquals(value1, value2) | lessOrEquals(5, 5) | true |
| empty | empty(value) | empty('') | true |
| coalesce | coalesce(value1, value2, ...) | coalesce(null, null, 'default') | default |
Logic tips
Don't nest if() more than two levels deep — it becomes unreadable and hard to debug. Use a Condition action instead for branching. coalesce() is the go-to fix for null values from SharePoint People columns, Dataverse lookups, and HTTP responses — it returns the first non-null value. You can use it to provide fallbacks for optional fields without a separate null check:
coalesce(triggerOutputs()?['body/Manager/Email'], triggerOutputs()?['body/Owner/Email'], 'admin@contoso.com')---
Math Functions
Power Automate has no infix operators — there is no +, -, *, or / syntax. Every arithmetic operation requires an explicit function call. This surprises developers coming from Excel formulas or Power Fx, but the WDL parser simply doesn't support operator notation (Microsoft Learn, 2026).
| Function | Syntax | Example | Output |
|---|---|---|---|
| add | add(num1, num2) | add(10, 5) | 15 |
| sub | sub(num1, num2) | sub(10, 5) | 5 |
| mul | mul(num1, num2) | mul(4, 3) | 12 |
| div | div(num1, num2) | div(10, 3) | 3 (integer) |
| mod | mod(num1, num2) | mod(10, 3) | 1 |
Math tips
div() performs integer division — div(10, 3) returns 3, not 3.333. To get a float result, convert one operand first: div(float(10), 3). Chain functions for compound calculations: add(mul(3, 4), mod(10, 3)) gives 13. For percentage calculations, use mul(div(float(part), float(total)), 100) — the float() conversions are required or the intermediate div() will truncate.
---
Conversion Functions
Type mismatches are the most common cause of InvalidTemplate and ExpressionEvaluationFailed errors in production flows. These 13 functions handle every type conversion you'll need — between strings, numbers, booleans, JSON, XML, and encoded formats. When in doubt, call string() before passing a value to a string function or int() before arithmetic.
| Function | Syntax | Example | Output |
|---|---|---|---|
| array | array(value) | array('abc') | ["abc"] |
| decimal | decimal(value) | string(decimal('1.2345678912312131')) | "1.2345678912312131" |
| int | int(value) | int('42') | 42 |
| float | float(value) | float('3.14') | 3.14 |
| string | string(value) | string(42) | "42" |
| bool | bool(value) | bool(1) | true |
| json | json(value) | json('{"name":"test"}') | JSON object |
| xml | xml(value) | xml('<root><item>1</item></root>') | XML object |
| base64 | base64(value) | base64('Hello') | SGVsbG8= |
| base64ToString | base64ToString(value) | base64ToString('SGVsbG8=') | Hello |
| decodeBase64 | decodeBase64(value) | decodeBase64('SGVsbG8=') | Hello |
| uriComponent | uriComponent(value) | uriComponent('hello world') | hello%20world |
| decodeUriComponent | decodeUriComponent(value) | decodeUriComponent('hello%20world') | hello world |
Conversion tips
Use json() when an HTTP response body comes back as a string instead of a parsed object — this happens with some older REST APIs and SharePoint REST endpoints that return Content-Type: text/plain. Use xml() for SharePoint SOAP service responses. base64() and base64ToString() are commonly needed when working with attachment content in connectors like Outlook or OneDrive. uriComponent() is essential when you're building query string parameters dynamically — without it, spaces and special characters break the URL silently.
---
Workflow and Reference Functions
Most cheat sheets stop at string, date, array, and math functions and skip the ones that read the flow's own state — variables, parameters, trigger data, run metadata. These 8 are what you reach for once a flow gets past "hello world" (Microsoft Learn, 2026).
| Function | Syntax | Example | Output |
|---|---|---|---|
| variables | variables(name) | variables('RetryCount') | Current value of the flow variable |
| parameters | parameters(name) | parameters('SiteUrl') | Value of a defined flow parameter |
| triggerBody | triggerBody() | triggerBody()?['Title'] | The trigger's output body — shorthand for triggerOutputs()?['body'] |
| action | action() | action()?['startTime'] | Metadata for the currently running action |
| workflow | workflow() | workflow()?['run']?['name'] | Metadata about the flow itself — name, run ID, tags |
| guid | guid() | guid() | A new GUID, e.g. 3f2504e0-4f89-11d3-9a0c-0305e82c3301 |
| rand | rand(min, max) | rand(1, 100) | Random integer between min (inclusive) and max (exclusive) |
| range | range(start, count) | range(1, 5) | [1,2,3,4,5] |
Workflow function tips
variables() only works after an Initialize variable action has run earlier in the flow — reference it before that action runs and you'll get a null reference error, not a helpful message. triggerBody() is the one to reach for instead of the longer triggerOutputs()?['body'] when you just need the trigger's payload with no headers or status code. guid() is handy for generating idempotency keys before an HTTP POST, so retries don't create duplicate records downstream. range() combined with an Apply to each loop is the standard way to run an action a fixed number of times when there's no collection to iterate over.
---
Dates, Objects, Loops and URIs: The Functions Most Cheat Sheets Skip
The tables above cover the functions you use every day. These 25 are the ones you go looking for when a flow needs something specific. Think of a "first of the month" report date, a JSON payload with one field changed, the current item of a named loop, or the host name out of a URL. Every one is in the Microsoft Learn WDL functions reference (2026); the examples below are the documented ones, with 2026 dates.
More date and time functions
| Function | Syntax | Example | Output |
|---|---|---|---|
| startOfDay | startOfDay(timestamp, format?) | startOfDay('2026-03-15T13:30:30Z') | 2026-03-15T00:00:00.0000000Z |
| startOfHour | startOfHour(timestamp, format?) | startOfHour('2026-03-15T13:30:30Z') | 2026-03-15T13:00:00.0000000Z |
| startOfMonth | startOfMonth(timestamp, format?) | startOfMonth('2026-03-15T13:30:30Z', 'yyyy-MM-dd') | 2026-03-01 |
| addToTime | addToTime(timestamp, interval, timeUnit, format?) | addToTime('2026-01-01T00:00:00Z', 1, 'Day') | 2026-01-02T00:00:00.0000000Z |
| subtractFromTime | subtractFromTime(timestamp, interval, timeUnit, format?) | subtractFromTime('2026-01-02T00:00:00Z', 1, 'Day') | 2026-01-01T00:00:00.0000000Z |
| dayOfYear | dayOfYear(timestamp) | dayOfYear('2026-03-15T13:27:36Z') | 74 |
| parseDateTime | parseDateTime(text, locale?, format?) | parseDateTime('20/10/2026', 'fr-fr') | 2026-10-20T00:00:00.0000000 |
addToTime() and subtractFromTime() take the same timeUnit values as getFutureTime() — Second, Minute, Hour, Day, Week, Month, Year — but start from a timestamp you supply instead of from now. That makes them the right call for "due date = created date + 2 weeks":addToTime(triggerOutputs()?['body/Created'], 2, 'Week', 'yyyy-MM-dd')startOfMonth() is the clean way to build a month-to-date filter for a "Get items" query. parseDateTime() is the only safe way to read a dd/MM/yyyy string typed into a form: formatDateTime() will read 03/04/2026 as March 4 unless you parse it with the right locale first.
More string, math and type-check functions
| Function | Syntax | Example | Output |
|---|---|---|---|
| lastIndexOf | lastIndexOf(text, searchText) | lastIndexOf('hello world hello world', 'world') | 18 |
| slice | slice(text, startIndex, endIndex?) | slice('Hello World', 2) | llo World |
| min | min(num1, num2, ...) or min(array) | min(createArray(1, 2, 3)) | 1 |
| max | max(num1, num2, ...) or max(array) | max(1, 2, 3) | 3 |
| isInt | isInt(text) | isInt('10') | true |
| isFloat | isFloat(text, locale?) | isFloat('10.000,00', 'de-DE') | true |
slice() accepts negative indexes, so slice(variables('FileName'), -4) returns the last four characters — a shorter way to get a .pdf extension than the substring() + length() arithmetic. isInt() and isFloat() are the guard to put in a Condition before calling int() or float() on form input; without them a stray space turns into an ExpressionEvaluationFailed at runtime. min() and max() take an array directly, so they pair with a Select action that pulls one numeric column out of "Get items".Object functions
| Function | Syntax | Example | Output |
|---|---|---|---|
| setProperty | setProperty(object, property, value) | setProperty(json('{"a":1}'), 'a', 2) | {"a":2} |
| addProperty | addProperty(object, property, value) | addProperty(json('{"a":1}'), 'b', 2) | {"a":1,"b":2} |
| removeProperty | removeProperty(object, property) | removeProperty(json('{"a":1,"b":2}'), 'b') | {"a":1} |
addProperty() throws at runtime if the property already exists; setProperty() adds it when missing and overwrites it when present, so it is the safer default. To change a nested field, nest the calls — setProperty(obj, 'parent', setProperty(obj['parent'], 'child', value)) — because a bare inner call returns only the child object. These three are how you patch an HTTP request body without a Parse JSON + Compose round trip.Loop and action functions
| Function | Syntax | Example | Returns |
|---|---|---|---|
| item | item() | item()?['Title'] | The current item of the enclosing Apply to each |
| items | items(loopName) | items('Apply_to_each')?['Title'] | The current item of the named loop — use in nested loops |
| outputs | outputs(actionName) | outputs('Get_user')?['body'] | The action's full output: status code, headers and body |
| body | body(actionName) | body('Get_user')?['displayName'] | Just the body — shorthand for outputs('...')?['body'] |
| triggerOutputs | triggerOutputs() | triggerOutputs()?['body/Title'] | The trigger's full output; triggerBody() is the body-only shorthand |
| result | result(scopeName) | result('Scope_Try') | An array with status, inputs and outputs of each first-level action in the scope |
Inside two nested Apply to each loops,
item() always means the innermost loop; reach the outer item with items('Outer_loop_name'). result() is the basis of the try/catch pattern: put the risky actions in a Scope, set a second Scope to run only on failure, and read result('Scope_Try') there to log which action failed and why.URI and XML functions
| Function | Syntax | Example | Output |
|---|---|---|---|
| uriHost | uriHost(uri) | uriHost('https://www.localhost.com:8080') | www.localhost.com |
| uriPath | uriPath(uri) | uriPath('https://www.contoso.com/catalog/shownew.htm?date=today') | /catalog/shownew.htm |
| xpath | xpath(xml, xpathExpression) | xpath(xml(body('HTTP')), '/order/items/item/text()') | Array of matching nodes or values |
uriHost() and uriPath() save the split() gymnastics when a SharePoint hyperlink column or an HTTP trigger hands you a full URL. xpath() runs on the .NET XPath library, so stick to XPath 1.0 syntax; wrap the string in xml() first or the function fails with a type error.---
Common Production Patterns
These are the expressions you'll copy-paste most often in real flows — tested patterns for the date formatting, null handling, HTTP parsing, and subject-line building scenarios that come up in almost every approval or notification flow.
Format a date for emails
When SharePoint returns an ISO timestamp and you need a clean display date:
formatDateTime(triggerOutputs()?['body/Created'], 'MMMM d, yyyy')Output: July 10, 2026. For the US short format: formatDateTime(..., 'M/d/yyyy').
Get file extension from a filename
last(split(triggerOutputs()?['body/{FilenameWithExtension}'], '.'))Splits report-final.pdf on . and returns pdf. Works for any delimiter-based extraction.
Null-safe field access with fallback
coalesce(triggerOutputs()?['body/Manager/Email'], 'no-manager@contoso.com')For numeric fields that might be null:
coalesce(triggerOutputs()?['body/Amount'], 0)Build a dynamic email subject
concat('[', triggerOutputs()?['body/Department'], '] ', triggerOutputs()?['body/RequestType'], ' - ', triggerOutputs()?['body/Title'])Output: [Finance] Purchase Order - Office Supplies Q2
For end-to-end approval flows using this pattern, see the Power Automate document approval workflow guide.
Calculate days between two dates
div(sub(ticks(outputs('EndDate')), ticks(outputs('StartDate'))), 864000000000)Each tick is 100 nanoseconds; 864000000000 ticks = 1 day. For business-days counting (excluding weekends), store holidays in a SharePoint list and subtract them in a loop.
Parse a JSON response from an HTTP action
json(body('HTTP_Request'))?['results']Safe nested access:
coalesce(json(body('HTTP_Request'))?['data']?['value'], 'Not found')For calling external APIs with OAuth from Power Automate flows, see the custom connectors guide.
Build styled HTML for email output
Expressions can build HTML strings inline:
concat('<b>', triggerOutputs()?['body/Title'], '</b><br/>', formatDateTime(triggerOutputs()?['body/DueDate'], 'MMMM d, yyyy'))For applying CSS to HTML tables generated by flows, see Power Automate HTML table styling with CSS.
Practical note: the expression editor in Power Automate (and the underlying WDL engine) shares its function library with Azure Logic Apps. If you find a function in the Logic Apps reference that isn't in the Power Automate UI docs, try it anyway — it usually works. Microsoft keeps the two runtimes in sync but the UI docs sometimes lag by months.
---
Quick Reference: Expression Editor Shortcuts
A few things that trip up new users every time:
- Accessing dynamic content in expressions: Use
triggerOutputs(),outputs('ActionName'), orbody('ActionName')to reference values — never paste raw dynamic content tokens into the expression editor.
- Optional chaining: The
?operator (e.g.,body('Get_item')?['value']) prevents errors when a property doesn't exist.
- Nested quotes: Use single quotes inside expressions. Double quotes break the parser.
- Testing expressions: Use a Compose action — the output shows exactly what the expression returns before you wire it into something consequential.
- Type mismatches: If
equals()returnsfalsewhen you expecttrue, one side is probably a string and the other is an integer. Useint(),float(), orstring()to normalize.
To look a function up quickly rather than read it in context, use the Power Automate expressions reference tool. It lists every function with its syntax and a worked example, filterable by category and copyable in one click. Nothing evaluates expressions outside Power Automate itself — a Compose action remains the only way to see what one actually returns. The same expression patterns apply in Power Apps canvas formulas, too — see the Power Apps canvas app + SharePoint guide for side-by-side examples.
---
Common Expression Syntax Errors and Fixes
These are the errors Power Automate throws most often when expression syntax is wrong — with the exact message and what to change.
InvalidTemplate on save
Full error:
InvalidTemplate. Unable to process template language expressions in action '...' inputs at line '...' and column '...': '...'. Please see https://aka.ms/logicexpressions for usage details.Causes and fixes:
| Root cause | Fix |
|---|---|
| Double quotes inside expression | Replace "value" with 'value' |
| Unmatched parentheses | Count opening vs closing parens — add a missing ) |
@{...} wrapper inside the expression editor | Remove the @{ and } — they're for inline interpolation only |
| Referencing an action name with spaces | Wrap in quotes: outputs('My Action') not outputs(My Action) |
ExpressionEvaluationFailed at runtime
Full error:
The execution of template language expression '...' failed: 'The template language function '...' expects its parameter to be a string...Type mismatch. SharePoint list columns return different types depending on the field type. A Number column returns an integer; pass it to a string function and you'll hit this. Fix with string(triggerOutputs()?['body/Count']).
NullValueInArgumentError at runtime
The execution of template language expression 'first(triggerOutputs()?['body/value'])' failed: '...array was null or empty'.The array was empty. Wrap with an empty() guard:
if(empty(triggerOutputs()?['body/value']), 'No items', first(triggerOutputs()?['body/value'])?['Title'])ActionInputsNotFound for dynamic content
This isn't an expression syntax error — it means the action you're referencing in outputs('ActionName') doesn't exist or has been renamed. Check the internal name of the action (visible in the "..." menu of the action → rename) and update the expression.
div() returns 0 unexpectedly
div() is integer division. div(5, 10) returns 0 not 0.5. Convert to float first:
div(float(5), float(10))From real flows: the most common syntax error I see in shared flow templates is action names with special characters —Get itembecomesGet_iteminternally butGet_item_2if it was renamed. Always use the Dynamic Content pane to grab the exact internal name rather than guessing the string to put insideoutputs().
---
FAQ
What are Power Automate expressions used for?
They handle anything a static field can't:
- Pulling a file extension off an uploaded document
- Building a subject line from three different fields
- Converting a UTC timestamp to the reader's time zone
- Falling back to a default when a SharePoint column comes back empty
Anywhere a flow needs to transform, compare, or compute a value on the fly instead of using a fixed input, you reach for an expression. That means concat(), if(), formatDateTime(), or one of the other 90-odd functions in this cheat sheet. Not sure whether you need one? Ask: "does this value change based on what the trigger sends in?" If yes, it's an expression, not a hardcoded string.
What is Power Automate expression syntax?
Power Automate expression syntax is the function-call grammar of the Workflow Definition Language (WDL), the same engine Azure Logic Apps runs on (Microsoft Learn, 2026). Every expression takes the form functionName(argument1, argument2, ...) — there's no infix math, no double-quoted strings, and no bare property access without the null-safe ? operator. You type it into the Expression tab of any action field, or wrap it in @{...} to interpolate it inside a text field.
What is the difference between concat() and inline @{...} syntax?
concat() is a function you call in the expression editor. @{...} is an interpolation syntax you use directly in text fields (not the expression tab) to embed an expression result mid-string. They produce the same output but live in different places. Use concat() when you need to combine more than two values — it's easier to debug. Use @{...} for simple inline substitutions in text fields like email subjects.
Can you nest expressions inside other expressions?
Yes — this is how most real expressions work. if(empty(triggerOutputs()?['body/Email']), 'N/A', toLower(triggerOutputs()?['body/Email'])) is a nested expression combining if, empty, and toLower. Watch your parentheses — one missing ) causes InvalidTemplate with a line/column pointer that's often off by one.
How do you handle time zones in Power Automate?
All timestamps in Power Automate are stored as UTC internally. Use convertTimeZone(utcNow(), 'UTC', 'Eastern Standard Time', 'hh:mm tt') before displaying dates to users. The full list of supported time zone names is in the Microsoft Learn reference linked at the top of this post — use the Windows time zone name format (e.g., Eastern Standard Time, not America/New_York).
Why does equals() return false when both values look the same?
Almost always a type mismatch or a case difference. SharePoint columns return typed values — a Yes/No column returns a boolean, not the string "true". Use string() or int() to normalize before comparing, and toLower() when comparing user-entered text. Use a Compose action to inspect both sides individually before debugging the equals() call.
---
What's Next
Bookmark this page — it's built to be the one Power Automate expressions reference you keep open in a tab while you write flows, not something you read once and forget.
- Power Automate Document Approval Workflow — end-to-end flow using expressions for dynamic email subjects and conditional routing
- Power Automate HTML Table Styling with CSS — applying expressions to build formatted HTML in flow emails
- Power Platform Custom Connectors REST API Guide — building connectors that consume the JSON outputs your expressions parse
- Microsoft Learn — WDL Functions Reference — the canonical function reference with full parameter specs