127 functions9 categories200+ examples
Why learn these functions?
Every expression you write in Power Automate is evaluated by the Workflow Definition Language (WDL). WDL is the underlying JSON-based language that defines how both Power Automate flows and Azure Logic Apps workflows actually run. When you click “Expression” in the designer and type a formula, you are writing WDL.
Understanding WDL matters for three reasons. First, the visual designer hides the language from you, which means you can only build what the designer makes visible. Once you understand the expression engine underneath, you can build things the designer cannot represent as buttons and dropdowns. Second, WDL expressions are significantly more efficient than the equivalent sequences of Condition steps and Compose actions — they execute in a single operation, cost fewer API calls, and make your flow logic readable rather than a maze of branches. Third, the same expressions work identically in Azure Logic Apps, which means your knowledge transfers directly to enterprise integration scenarios.
Part 1 — String Functions
concat
concat(text1: string, text2: string, ...) → string
The only way to join strings in Power Automate — there is no + operator for text. You pass any number of string arguments and get back a single combined string.
concat + formatDateTime — timestamped filename
Build a unique filename that includes the current date. Every time the flow runs, the filename is different — no overwrite risk.
concat + trim + toLower — safe deduplicated key
Before storing a customer name as a lookup key, normalize it. Without this, “Contoso” and ” contoso ” are treated as different entries.
concat('INV_', customerCode, '_', formatDateTime(utcNow(),'yyyyMMdd'), '.pdf')— returns INV_ACME_20250915.pdfconcat('[', formatDateTime(utcNow(),'yyyy-MM-dd HH:mm'), '] Contract signed')— returns [2025-09-15 14:22] Contract signed
string(variables('Amount')) before concatenating.concat('Report_', formatDateTime(utcNow(), 'yyyyMMdd'), '.pdf')
// Result: 'Report_20250915.pdf'trim
trim(text: string) → string
Strips leading and trailing whitespace. Simple, but essential. Forms, SharePoint fields, and email subjects regularly carry invisible whitespace that silently breaks string comparisons and Dataverse lookups.
trim + toLower — normalize before comparison
Form fields and SharePoint columns often carry invisible whitespace. Always trim before toLower when building keys or doing comparisons.
trim(' Contoso Ltd ')— returns Contoso Ltdtrim(' john.doe@contoso.com ')— returns john.doe@contoso.com
trim removes outer whitespace only. Use replace(text,' ','') to strip all spaces.equals(trim(toLower(triggerBody()?['Email'])), 'john@contoso.com')
// Matches ' JOHN@CONTOSO.COM ' correctlytoLower
toLower(text: string) → string
toUpper(text: string) → string
Converts a string to all-lowercase or all-uppercase. Use toLower before any string comparison, before storing email addresses, before deduplication checks. Use toUpper for internal codes and identifiers.
toLower + contains — case-insensitive keyword check
Without normalization, “URGENT” and “urgent” are different strings. Wrap both sides in toLower to catch any casing.
toLower + equals — safe status comparison
Status fields typed by users are unreliable. Normalize before comparing.
toLower('JOHN.DOE@CONTOSO.COM')— returns john.doe@contoso.comtoLower('ACTIVE')— returns active
contains(toLower(triggerBody()?['Subject']), 'urgent')
// Matches 'URGENT', 'Urgent', 'urgent'replace
replace(text: string, oldText: string, newText: string) → string
Replaces every occurrence of a substring with another string. Essential for sanitizing data before storage — removing spaces from phone numbers, replacing slashes in file paths, cleaning up codes before they go into another system.
replace + replace — chain two cleanups
Each replace() returns a new string you can pipe directly into the next call. Clean in two passes without intermediate variables.
replace('0742 123 456', ' ', '')— returns 0742123456replace('Q3/2025 Report', '/', '-')— returns Q3-2025 Report
replace is case-sensitive. Use toLower on a copy before replacing if case-insensitive matching is needed.replace(replace('GB29 NWBK-6016 1234 5678', ' ', ''), '-', '')
// Result: 'GB29NWBK60161234567'split
split(text: string, delimiter: string) → array
Breaks a string into an array of substrings wherever the delimiter appears. The counterpart of join. Whenever a field contains multiple values packed into one string — tags, categories, email lists, CSV rows — this is how you turn it into something you can actually loop through or count.
split + last — extract filename from path
Split on the separator and take the last element. Works regardless of how deep the path is.
split + length — validate tag count
After splitting a comma-separated field, count the items to validate that exactly the right number of values was provided.
split('Power Apps,Dataverse,Canvas', ',')— returns [“Power Apps”,”Dataverse”,”Canvas”]split('docs/reports/q3.pdf', '/')— returns [“docs”,”reports”,”q3.pdf”]
split('Apple,Orange,', ',') -> ["Apple","Orange",""]. Always trim first.last(split('docs/reports/2025/q3.pdf', '/'))
// Result: 'q3.pdf'substring
substring(text: string, startIndex: integer, length: integer) → string
Both functions extract a portion of a string. substring uses start position + character count. slice uses start + end position and — crucially — supports negative indices to count from the end. For anything variable-length, prefer slice.
indexOf + substring — extract before delimiter
Find the position of a character you do not know in advance, then cut everything before it.
substring('REF-2025-INV-00123', 4, 4)— returns 2025substring('john@contoso.com', 0, 4)— returns john
substring('john@contoso.com', 0, indexOf('john@contoso.com', '@'))
// Result: 'john'slice
slice(text: string, startIndex: integer, endIndex?: integer) → string
Both functions extract a portion of a string. substring uses start position + character count. slice uses start + end position and — crucially — supports negative indices to count from the end. For anything variable-length, prefer slice.
slice + toLower — normalize file extension
Extract the last 4 characters (the extension) and lowercase it for a safe comparison.
slice('report.pdf', -4)— returns .pdfslice('REF-2025-INV-00123', -5)— returns 00123
slice over substring for variable-length strings. substring throws if start+len exceeds string length.toLower(slice('Report_Q3.PDF', -4))
// Result: '.pdf' — safe for comparisonindexOf
indexOf(text: string, searchText: string) → integer
Returns the zero-based position of a substring. indexOf finds the first occurrence, lastIndexOf finds the last, nthIndexOf finds the Nth (1-based). All return -1 if not found.
indexOf + substring — extract username from email
Find where the @ is, then take everything before it.
indexOf('john@contoso.com', '@')— returns 4indexOf('REF-2025-INV-00123', '-')— returns 3
substring('john.doe@contoso.com', 0, indexOf('john.doe@contoso.com', '@'))
// Result: 'john.doe'lastIndexOf
lastIndexOf(text: string, searchText: string) → integer
Returns the zero-based position of a substring. indexOf finds the first occurrence, lastIndexOf finds the last, nthIndexOf finds the Nth (1-based). All return -1 if not found.
lastIndexOf('report.v2.pdf', '.')— returns 9lastIndexOf('/sites/Sales/Lists', '/')— returns 13
nthIndexOf
nthIndexOf(text: string, searchText: string, occurrence: integer) → integer
Returns the zero-based position of a substring. indexOf finds the first occurrence, lastIndexOf finds the last, nthIndexOf finds the Nth (1-based). All return -1 if not found.
These are the tools you use when you need to extract a dynamic portion of a string — when the content length varies and position-based extraction would be too fragile.
nthIndexOf('EU/Sales/ACME/123', '/', 2)— returns 8nthIndexOf('a.b.c.d', '.', 3)— returns 5
-1 before using the result in substring. Passing -1 causes a runtime error.startsWith
startsWith(text: string, searchText: string) → boolean
Returns true if a string begins or ends with a given substring. Cleaner and more readable than the equivalent indexOf check for simple prefix/suffix validation.
startsWith('admin.ops@contoso.com', 'admin.')— returns truestartsWith('INV-2025-00123', 'INV-')— returns true
endsWith
endsWith(text: string, searchText: string) → boolean
Returns true if a string begins or ends with a given substring. Cleaner and more readable than the equivalent indexOf check for simple prefix/suffix validation.
endsWith(toLower(fileName), '.pdf')— returns true / falseendsWith('contoso.sharepoint.com', '.sharepoint.com')— returns true
endsWith('Report.PDF', '.pdf') returns false. Always toLower() both sides first.length
length(collection: string | array) → integer
Returns the number of characters in a string, or the number of items in an array. Used for validation, loop control, and conditional logic based on size.
length('Hello')— returns 5length(['Alpha','Beta','Gamma'])— returns 3
length(null) throws. Use empty() to check existence first. empty() = existence check, length() = size check.formatNumber
formatNumber(number: number, format: string, locale?: string) → string
Converts a raw number into a display-ready string using .NET format specifiers. Raw numbers from expressions look terrible in emails and notifications — this is how you fix that.
formatNumber(1250.75, 'N2', 'en-GB')— returns 1,250.75formatNumber(0.875, 'P1', 'en-US')— returns 87.5%
float() first.guid
guid(format?: string) → string
Generates a globally unique identifier. Every call produces a different value. Use it for correlation IDs in logging, temporary filenames, deduplication keys, and any scenario where you need a guaranteed-unique identifier at runtime.
Watch out: multiple guid() calls inside the same expression evaluation produce the same value. If you need two distinct GUIDs, use two separate Compose actions.
| Parameter | Type | Required | Description |
|---|---|---|---|
`format` | string | no | “D” (default, with hyphens), “N” (no hyphens), “B” (braces), “P” (parentheses). |
guid()— returns a7f3c2d1-8b4e-4f1a-92c3-d6e1b5f0c2a8guid('N')— returns a7f3c2d18b4e4f1a92c3d6e1b5f0c2a8
chunk
chunk(collection: string | array, length: integer) → array
Splits a string or array into equal-length pieces. The last piece may be shorter if the total does not divide evenly. Essential for batch processing — when an API accepts only 50 records at a time, chunk handles the splitting automatically.
chunk + Apply to each — batch API processing
Split a large array into fixed-size batches, then process each batch in a loop. Keeps you under API rate limits.
chunk(range(1,7), 3)— returns [[1,2,3],[4,5,6],[7]]chunk(allRecordIDs, 50)— returns [[id1..id50],[id51..id100],…]
chunk('Hello World', 5) -> ['Hello',' Worl','d']. Use split for word-aware splitting.// chunk source:
chunk(body('Get_all_records')?['value'], 50)
// Each Apply to each iteration:
// item() = ['record1','record2',...,'record50']
// Send as one batch to the APIPart 2 — Number Functions
add
add(n1: number, n2: number) → number
The four basic arithmetic operations. There is no +, -, *, / operator syntax in Power Automate expressions — you must use these functions.
add(2, 1)— returns 3 — counter incrementedadd(100, 42)— returns 142
sub
sub(n1: number, n2: number) → number
The four basic arithmetic operations. There is no +, -, *, / operator syntax in Power Automate expressions — you must use these functions.
sub(10000, 3500)— returns 6500sub(10000, 3500)— returns 6500
mul
mul(n1: number, n2: number) → number
The four basic arithmetic operations. There is no +, -, *, / operator syntax in Power Automate expressions — you must use these functions.
mul(int('5'), float('49.99'))— returns 249.95mul(1.08, 250.00)— returns 270.0 — total with 8% tax
div
div(n1: number, n2: number) → number
The four basic arithmetic operations. There is no +, -, *, / operator syntax in Power Automate expressions — you must use these functions.
div(float('249.95'), float('5'))— returns 49.99 — unit costdiv(float(7), 2)— returns 3.5
div(7, 2) = 3 (integer division truncates). Use div(float(7), 2) = 3.5 whenever decimal precision matters.mod
mod(dividend: integer, divisor: integer) → integer
Returns the remainder after division. Used for round-robin routing, even/odd checks, and any logic that cycles through a fixed set of options based on position.
mod — round-robin queue assignment
Distribute incoming records evenly across N queues based on the record ID.
mod(7, 3)— returns 1 — routes to Queue Bequals(mod(4, 2), 0)— returns true — 4 is even
mod(n, 0) throws divide-by-zero. Validate divisor when it comes from user input.// 3 queues: 0, 1, 2
mod(int(triggerBody()?['ID']), 3)
// ID 7 → 1 (Queue B)
// ID 9 → 0 (Queue A)
// ID 11 → 2 (Queue C)max
max(number1, number2, ...) → number
min(number1, number2, ...) → number
Return the highest or lowest value from a set of numbers. Also accept a single array argument. The key use case is the guardrail pattern: clamping a calculated value within a safe range without needing a condition step.
max(min(115, 100), 0)— returns 100 — clamped to maximummax(3, 17, 8)— returns 17
max([1,2,3], 4) throws a type error.rand
rand(minValue: integer, maxValue: integer) → integer
Returns a random integer. Used for load balancing, random assignment across queues, and adding jitter to retry delays so multiple flows do not all retry simultaneously.
rand + addSeconds — jittered retry delay
When multiple flows retry simultaneously, they hammer the target API together. Add a random delay so they spread out.
rand(1, 11)— returns 1 through 10 (inclusive)rand(0, 4)— returns 0, 1, 2, or 3
addSeconds(utcNow(), rand(5, 30))
// Each retry instance picks a different delay
// Prevents thundering herd on the target systemrange
range(startIndex: integer, count: integer) → array
Creates an array of consecutive integers starting at startIndex for count elements. Used to generate index sequences and create fixed-count iteration without needing a real data source.
Watch out: range generates integers only, always ascending, always consecutive. No floats, no descending, no gaps.
range + Apply to each — 7-day window
Generate a sequence of day offsets and compute each date inside the loop. No pre-built date array needed.
range(1, 5)— returns [1, 2, 3, 4, 5]range(0, 7)— returns [0, 1, 2, 3, 4, 5, 6]
// Apply to each source:
range(0, 7)
// Inside loop — item() = 0, 1, 2 ... 6:
addDays(utcNow(), item(), 'yyyy-MM-dd')
// Produces: today, tomorrow, day+2 ... day+6Part 3 — Data Structures: Collections, Arrays, Objects
coalesce
coalesce(value1: any, value2: any, ...) → any
Returns the first non-null value. Accepts any number of arguments and evaluates them left to right. It is the single most underused function in Power Automate — it replaces entire chains of null-check condition steps.
Watch out: coalesce handles null only. Empty string '' is treated as a valid non-null value. coalesce('', 'fallback') returns '', not 'fallback'. For empty strings, use if(empty(...)).
coalesce — three-level fallback
Try PreferredName first, fall back to FirstName, then to a hardcoded default. One expression, no Condition steps.
coalesce + replace — null-safe string cleanup
replace() throws on null. Coalesce first to guarantee a string before cleaning.
| Parameter | Type | Required | Description |
|---|---|---|---|
`value1, value2, ...` | any | yes | Two or more values. Evaluated left to right — the first non-null one is returned. |
coalesce(null, 'Vincenzo', 'Unknown')— returns Vincenzo” — null skipped, first non-null returnedcoalesce(null, 'N/A')— returns N/A” — phone field was missing, fallback used
coalesce(triggerBody()?['PreferredName'], triggerBody()?['FirstName'], 'there')
// null, null, 'there' → 'there'
// null, 'Vincenzo', 'there' → 'Vincenzo'contains
contains(collection: array | string | object, value: any) → boolean
Returns true if an array contains the value, a string contains the substring, or an object contains the key. The same function does three different things depending on the input type.
| Parameter | Type | Required | Description |
|---|---|---|---|
`collection` | array, string, or object | yes | Array: checks membership. String: checks substring. Object: checks if key exists. |
`value` | any | yes | The item, substring, or key to look for. |
contains(createArray('Draft','Active','Done'), 'Active')— returns truecontains(toLower('URGENT: Contract Review'), 'urgent')— returns true
toLower to both sides before checking.empty
empty(collection: string | array | object | null) → boolean
Returns true if a string, array, or object is empty or null. The safest pre-check before calling first(), last(), or indexing into a collection.
Important distinction: empty(0) returns false — zero is a value. empty(false) returns false — false is a value. It checks for structural emptiness, not falsy-ness.
empty + if — safe first/last access
first() and last() throw on empty arrays. Always guard before calling them.
| Parameter | Type | Required | Description |
|---|---|---|---|
`collection` | string, array, object, or null | yes | Returns true for null, empty string ”, empty array [], and empty object {}. |
empty('')— returns true — empty stringempty(createArray())— returns true — empty array
if(empty(body('Get_rows')?['value']), 'No records', first(body('Get_rows')?['value'])?['Title'])
// Returns 'No records' instead of throwingfirst
first(collection: array | string) → any
last(collection: array | string) → any
Returns the first or last item in a collection. Cleaner than indexing with [0] or computing the last index manually. Works on both arrays and strings (returns first/last character on strings).
sort + reverse + first — get maximum value
Sort ascending, flip to descending, take the first item. Clean and readable without scanning manually.
| Parameter | Type | Required | Description |
|---|---|---|---|
`collection` | array or string | yes | Throws a runtime error if empty. Always guard with empty() first. |
first(body('GetApprovals')?['value'])?['ApprovedBy']— returns first approver namefirst(split('Power Apps,Dataverse,Canvas', ','))— returns Power Apps
first([]) and last([]) both throw. Always guard: if(empty(arr), null, first(arr))first(reverse(sort(body('Get_items')?['value'], 'Budget')))?['Title']
// Returns the project with the highest Budgetjoin
join(collection: array, delimiter: string) → string
Combines all array items into a single string with a delimiter between each element. The counterpart of split. Used for building display strings from arrays, constructing CSV rows, and assembling dynamic content for emails and notifications.
join — build a semicolon-separated recipient list
Collect approver emails in an array variable inside a loop, then join once at the end. Cleaner than string concatenation inside the loop.
| Parameter | Type | Required | Description |
|---|---|---|---|
`collection` | array of strings | yes | All elements must be strings. Use a Select action to convert numbers first. |
`delimiter` | string | yes | Placed between each element. e.g. “, ” or “;” |
join(createArray('Power Apps','Dataverse','Canvas'), ', ')— returns Power Apps, Dataverse, Canvasjoin(createArray('alice@co.com','bob@co.com'), ';')— returns alice@co.com;bob@co.com
join(variables('approverEmails'), ';')
// ['alice@co.com','bob@co.com'] → 'alice@co.com;bob@co.com'union
union(collection1: array, collection2: array, ...) → array
merges multiple arrays and removes duplicates. intersection returns only the items that appear in every specified array. Both work on objects too.
union — merge two tag lists without duplicates
When two sources each provide a list of tags, union merges them and automatically removes any tag that appears in both.
union(createArray('Alice','Bob'), createArray('Bob','Carol'))— returns [“Alice”,”Bob”,”Carol”]union(createArray('Alice','Bob'), createArray('Bob','Carol'))— returns [“Alice”,”Bob”,”Carol”]
union(
createArray('Power Apps','Dataverse'),
createArray('Dataverse','Canvas')
)
// Result: ['Power Apps','Dataverse','Canvas']intersection
intersection(collection1: array, collection2: array, ...) → array
merges multiple arrays and removes duplicates. intersection returns only the items that appear in every specified array. Both work on objects too.
intersection — find users in multiple groups
Only return users who are members of every specified group simultaneously.
intersection(createArray('Alice','Bob','Carol'), createArray('Bob','Carol','Dave'))— returns [“Bob”,”Carol”]intersection(createArray('A','B','C'), createArray('B','C','D'))— returns [“B”,”C”]
intersection(
variables('betaTesters'),
variables('euRegionUsers')
)
// Only users who appear in both listsskip
skip(collection: array, count: integer) → array
take(collection: array, count: integer) → array
removes the first N items and returns the rest. take returns only the first N items. Together they give you manual pagination — extract any page from a sorted array without a loop.
Both are boundary-safe: skip with a count larger than the array returns an empty array (not an error), and take with a larger count returns all items.
skip + take — manual pagination
Page 2 of 10 items per page: skip the first 10, then take the next 10.
skip(createArray(1,2,3,4,5), 2)— returns [3,4,5] — first 2 removedtake(skip(createArray(1,2,3,4,5,6,7,8,9,10), 3), 3)— returns [4,5,6] — page 2 of size 3
take(skip(body('Get_all')?['value'], 10), 10)
// Items 11-20 from the full arraysort
sort(collection: array, sortBy?: string) → array
reverse(collection: array) → array
orders a collection in ascending order, optionally by a named property when the array contains objects. reverse flips the order. Combine them for descending sort.
sort + reverse + first — top record
Sort ascending by a property, flip it, take the first item — the record with the highest value.
sort(createArray(3,1,4,1,5,9,2), null)— returns [1,1,2,3,4,5,9]sort(createArray(3,1,4,1,5,9,2), null)— returns [1,1,2,3,4,5,9]
first(reverse(sort(body('Get_projects')?['value'], 'Budget')))
// Returns the project with the highest Budget fieldcreateArray
createArray(value1: any, value2: any, ...) → array
builds an array from multiple individual values. array wraps a single value in an array. Used to build inline option sets, construct static lists for contains() checks, and wrap scalar values for actions that expect an array input.
createArray + contains — inline allowlist check
Check membership against a fixed set without a separate variable or Compose action.
createArray('Active','Pending','On Hold')— returns [“Active”,”Pending”,”On Hold”]contains(createArray('Draft','Active'), variables('status'))— returns true / false
contains(createArray('Active','Pending','On Hold'), triggerBody()?['Status'])
// true if Status is one of the three allowed valuesarray
array(value: any) → array
builds an array from multiple individual values. array wraps a single value in an array. Used to build inline option sets, construct static lists for contains() checks, and wrap scalar values for actions that expect an array input.
array(triggerBody()?['ManagerEmail'])— returns [“manager@contoso.com”]array('single value')— returns [“single value”]
array('a','b') fails. Use createArray('a','b') for multiple values.item
item() → any
items('loopName': string) → any
returns the current element inside an Apply to each loop — always the innermost loop. items('loopName') returns the current element of a named loop, which is essential when you have nested loops and need to reference the outer loop’s current item from inside the inner loop.
item()?['cr_title']— returns Website Redesign” — Title field of the current Dataverse recorditem()?['Status']— returns Active” — Status field of the current record
item() = innermost loop always. Use items('OuterLoopName') to reach the parent loop’s item.Part 4 — Logical and Conditional Functions
if
if(expression: boolean, valueIfTrue: any, valueIfFalse: any) → any
The inline ternary operator. Evaluates a boolean and returns one of two values. Half the time a Condition step with two branches that differ by a single value should just be an if() inside a Compose action — one step, zero branches, same result.
if + and — compound approval routing
Replace two nested Condition steps with a single expression.
| Parameter | Type | Required | Description |
|---|---|---|---|
`expression` | boolean | yes | The condition to evaluate. Must resolve to true or false. |
`valueIfTrue` | any | yes | Returned when expression is true. |
`valueIfFalse` | any | yes | Returned when expression is false. |
if(greater(15000, 10000), 'Senior Approval', 'Standard')— returns Senior Approvalif(empty(''), 'No notes', '')— returns No notes” — empty string returns fallback
if(
and(greater(float(triggerBody()?['Amount']), 10000), equals(triggerBody()?['Region'], 'EU')),
'Senior EU Approval',
'Standard Approval'
)and
and(expression1: boolean, expression2: boolean, ...) → boolean
Boolean logic operators. and returns true only if all conditions are true. or returns true if at least one condition is true. not inverts a boolean.
and(equals('Active','Active'), equals('EU','EU'))— returns true — both conditions metand(greater(5000, 1000), less(5000, 50000))— returns true — 5000 is in range
or
or(expression1: boolean, expression2: boolean, ...) → boolean
Boolean logic operators. and returns true only if all conditions are true. or returns true if at least one condition is true. not inverts a boolean.
or(equals('High','High'), equals('High','Critical'))— returns true — first condition matchesor(greater(45, 30), greater(8000, 50000))— returns true — 45 days open
not
not(expression: boolean) → boolean
Boolean logic operators. and returns true only if all conditions are true. or returns true if at least one condition is true. not inverts a boolean.
not + contains — exclusion filter
Express “status is not in this list” cleanly, without chaining multiple ne comparisons.
not(contains(createArray('Done','Archived'), 'Active'))— returns true — Active is not in the listnot(empty('Project review scheduled for Q4'))— returns true — Notes has content
not(contains(createArray('Done','Archived','Cancelled'), triggerBody()?['Status']))
// true for 'Active', 'Pending' — false for 'Done'equals
equals(object1: any, object2: any) → boolean
Standard comparison operators. Every conditional check in your flows uses one of these.
equals('Active', 'Active')— returns trueequals(int('0'), 0)— returns true
greater
greater(value, compareTo) → boolean
Standard comparison operators. Every conditional check in your flows uses one of these.
greater(15000, 10000)— returns truegreater(ticks('2025-09-20T00:00:00Z'), ticks('2025-09-15T00:00:00Z'))— returns true — 20 Sep is after 15 Sep
greaterOrEquals
greaterOrEquals(value, compareTo) → boolean
Standard comparison operators. Every conditional check in your flows uses one of these.
greaterOrEquals(float('7500'), 5000.0)— returns true — approval requiredgreaterOrEquals(length('Project kick-off'), 10)— returns true
less
less(value, compareTo) → boolean
Standard comparison operators. Every conditional check in your flows uses one of these.
less(2, 3)— returns true — still within retry limitless(float('0.25'), 0.5)— returns true — discount is 25%
lessOrEquals
lessOrEquals(value, compareTo) → boolean
Standard comparison operators. Every conditional check in your flows uses one of these.
Common trap 1: equals is type-sensitive. equals(1, '1') returns false. Align types with string(), int(), or float() before comparing.
Common trap 2: never compare dates as raw strings. greater('15/09/2025','10/09/2025') fails lexicographically. Always use ticks() for reliable date comparison.
lessOrEquals(5, 7)— returns true — 5 days leftlessOrEquals(45, 60)— returns true — score is failing
isInt
isInt(value: string) → boolean
isFloat(value: string, locale?: string) → boolean
Returns true if a string can be parsed as an integer or floating-point number. Used for input validation before conversion — prevents int() and float() from crashing on non-numeric input.
isInt('42')— returns trueif(isInt('42'), int('42'), 0)— returns 42 — valid integer
isInt returns false for ‘3.0’. Use isFloat for general numeric validation.Part 5 — Date and Time Functions
utcNow
utcNow(format?: string) → string
Returns the current date and time in UTC. The baseline for all date-based logic in Power Automate. Start here and convert to local time only when you need to display a timestamp to a user.
utcNow + formatDateTime — date in specific format
Get today in exactly the format a system or API expects.
utcNow + addDays — deadline date
Calculate a due date 30 days from now, already formatted for storage or display.
| Parameter | Type | Required | Description |
|---|---|---|---|
`format` | string | no | .NET date format string. Omit to get the full ISO 8601 timestamp: “2025-09-15T08:30:00.0000000Z”. |
utcNow()— returns 2025-09-15T08:30:00.0000000ZutcNow('yyyy-MM-dd')— returns 2025-09-15
formatDateTime(utcNow(), 'yyyy-MM-dd')
// '2025-09-15' — ISO date for OData filteraddDays
addDays(timestamp, days, format?) → string
Add or subtract time units from a timestamp. Negative values go backward. addToTime handles any unit in a single call using a string unit parameter.
addDays — due date in email body
Calculate and format a deadline in one expression — no separate Compose needed.
addDays(utcNow(), 30, 'yyyy-MM-dd')— returns 2025-10-15″ — 30-day deadlineaddDays('2025-09-30', -7, 'yyyy-MM-dd')— returns 2025-09-23
addDays(utcNow(), 30, 'dd MMMM yyyy')
// '15 October 2025' — ready for email bodyaddHours
addHours(timestamp, hours, format?) → string
Add or subtract time units from a timestamp. Negative values go backward. addToTime handles any unit in a single call using a string unit parameter.
addHours(triggerBody()?['MeetingTime'], -2)— returns 2-hour reminder before meetingaddHours(utcNow(), 24)— returns timestamp 24 hours from now
addMinutes
addMinutes(timestamp, minutes, format?) → string
Add or subtract time units from a timestamp. Negative values go backward. addToTime handles any unit in a single call using a string unit parameter.
addMinutes(utcNow(), 30)— returns 30 minutes from nowaddMinutes(appointmentTime, -15)— returns 15-minute warning
addSeconds
Add or subtract time units from a timestamp. Negative values go backward. addToTime handles any unit in a single call using a string unit parameter.
addSeconds(utcNow(), 90)— returns 90 seconds from nowaddSeconds(utcNow(), rand(5,30))— returns jittered retry timestamp
addMonths
addMonths(timestamp, months, format?) → string
Add or subtract time units from a timestamp. Negative values go backward. addToTime handles any unit in a single call using a string unit parameter.
addMonths(utcNow(), 3)— returns 3 months from todayaddMonths('2025-01-15', 12, 'yyyy-MM-dd')— returns 2026-01-15
addToTime
addToTime(timestamp, interval, timeUnit, format?) → string
Add or subtract time units from a timestamp. Negative values go backward. addToTime handles any unit in a single call using a string unit parameter.
addToTime(utcNow(), 1, 'Week', 'yyyy-MM-dd')— returns 2025-09-22″ — one week from todayaddToTime(utcNow(), 6, 'Month')— returns 6 months from now
convertTimeZone
convertTimeZone(timestamp, sourceTimeZone, destinationTimeZone, format?) → string
Power Automate runs internally on UTC. These functions convert between UTC and local time zones. convertTimeZone converts between any two zones. convertFromUtc is shorthand when the source is always UTC. convertToUtc normalizes local timestamps to UTC for storage.
convertTimeZone(utcNow(), 'UTC', 'W. Europe Standard Time', 'HH:mm')— returns 10:30″ — Italian local timeconvertTimeZone(utcNow(), 'UTC', 'Eastern Standard Time', 'yyyy-MM-dd HH:mm')— returns US Eastern datetime
convertFromUtc
convertFromUtc(timestamp, destinationTimeZone, format?) → string
Power Automate runs internally on UTC. These functions convert between UTC and local time zones. convertTimeZone converts between any two zones. convertFromUtc is shorthand when the source is always UTC. convertToUtc normalizes local timestamps to UTC for storage.
convertFromUtc(utcNow(), 'W. Europe Standard Time', 'dd/MM/yyyy HH:mm')— returns 15/09/2025 10:30convertFromUtc('2025-09-15T08:00:00Z', 'India Standard Time', 'HH:mm')— returns 13:30″ — IST is UTC+5:30
convertToUtc
convertToUtc(timestamp, sourceTimeZone, format?) → string
Power Automate runs internally on UTC. These functions convert between UTC and local time zones. convertTimeZone converts between any two zones. convertFromUtc is shorthand when the source is always UTC. convertToUtc normalizes local timestamps to UTC for storage.
convertToUtc('15/09/2025 10:30', 'W. Europe Standard Time')— returns 2025-09-15T08:30:00ZconvertToUtc('2025-09-15 08:00', 'Eastern Standard Time', 'yyyy-MM-ddTHH:mm:ssZ')— returns 2025-09-15T12:00:00Z
formatDateTime
formatDateTime(timestamp: string, format: string, locale?: string) → string
Formats any timestamp into a string using .NET date format specifiers. Use this whenever you need a specific output format — for APIs, for display in emails, for filenames.
convertFromUtc + formatDateTime — display local time
Convert UTC to local time zone, then format for display in an email or notification.
formatDateTime(utcNow(), 'yyyy-MM-dd')— returns 2025-09-15formatDateTime(triggerBody()?['CreatedAt'], 'dd/MM/yyyy HH:mm')— returns 15/09/2025 08:30
formatDateTime(
convertFromUtc(utcNow(), 'W. Europe Standard Time'),
'dd/MM/yyyy HH:mm'
)
// '15/09/2025 10:30' — Italian local timeticks
ticks(timestamp: string) → integer
Converts a timestamp to the number of 100-nanosecond intervals since January 1, 0001. The result is a large integer that you can subtract to calculate exact durations. This is the only reliable way to compare two dates or measure elapsed time in an expression.
ticks + sub + div — elapsed time in minutes
Subtract two tick values to get the raw duration, then divide to convert to the unit you need.
ticks + greater — overdue check
The only reliable way to compare two dates. String comparison fails silently on locale-formatted dates.
| Parameter | Type | Required | Description |
|---|---|---|---|
`timestamp` | string | yes | An ISO 8601 timestamp string. e.g. “2025-09-15T08:30:00Z” |
greater(ticks('2025-09-20T00:00:00Z'), ticks('2025-09-15T00:00:00Z'))— returns true — 20 Sep is after 15 Sepdiv(sub(ticks('2025-09-15T09:30:00Z'), ticks('2025-09-15T08:00:00Z')), 600000000)— returns 90 — 9:30 minus 8:00 = 90 minutes
div(
sub(ticks(utcNow()), ticks(variables('startTime'))),
600000000
)
// 600000000 ticks = 1 minute
// Result: elapsed minutes as integerstartOfDay
startOfDay(timestamp, format?) → string
Truncates a timestamp to the start of the day, month, or hour — zeroing out the smaller time components. The primary use case is building date-range query boundaries: to get all records created today, you need startOfDay(utcNow()) as the lower bound.
startOfDay + convertTimeZone — local midnight
For a user in a specific time zone, get their local midnight as a UTC value — the correct lower bound for a “today” query.
startOfDay(utcNow())— returns 2025-09-15T00:00:00Z” — today at midnightstartOfDay(utcNow(), 'yyyy-MM-dd')— returns 2025-09-15″ — for OData filter
convertToUtc(
startOfDay(convertFromUtc(utcNow(), 'W. Europe Standard Time')),
'W. Europe Standard Time'
)
// Returns UTC equivalent of 00:00 Italian timestartOfMonth
startOfMonth(timestamp, format?) → string
Truncates a timestamp to the start of the day, month, or hour — zeroing out the smaller time components. The primary use case is building date-range query boundaries: to get all records created today, you need startOfDay(utcNow()) as the lower bound.
startOfMonth(utcNow(), 'yyyy-MM-dd')— returns 2025-09-01″ — first of monthstartOfMonth(addMonths('2025-09-15', -1), 'yyyy-MM-dd')— returns 2025-08-01
startOfHour
startOfHour(timestamp, format?) → string
Truncates a timestamp to the start of the day, month, or hour — zeroing out the smaller time components. The primary use case is building date-range query boundaries: to get all records created today, you need startOfDay(utcNow()) as the lower bound.
startOfHour(utcNow())— returns 2025-09-15T08:00:00Z” — top of hourstartOfHour(utcNow(), 'HH:mm')— returns 08:00
dayOfWeek
dayOfWeek(ts) → 0(Sun) to 6(Sat)
Extract a specific numeric component from a timestamp. Used for scheduling logic: skip weekends, run only in Q4, generate year-based filenames.
dayOfWeek(utcNow())— returns 1 for Monday, 0 for Sundayand(greaterOrEquals(dayOfWeek(utcNow()),1), lessOrEquals(dayOfWeek(utcNow()),5))— returns true Mon-Fri
dayOfMonth
dayOfMonth(ts) → 1-31
Extract a specific numeric component from a timestamp. Used for scheduling logic: skip weekends, run only in Q4, generate year-based filenames.
dayOfMonth(utcNow())— returns 15 — current dayequals(dayOfMonth(utcNow()), 1)— returns true on 1st of month
dayOfYear
dayOfYear(ts) → 1-366
Extract a specific numeric component from a timestamp. Used for scheduling logic: skip weekends, run only in Q4, generate year-based filenames.
dayOfYear(utcNow())— returns 258 — day number in yeargreater(dayOfYear(utcNow()), 180)— returns true in second half of year
month
month(ts) → 1-12
Extract a specific numeric component from a timestamp. Used for scheduling logic: skip weekends, run only in Q4, generate year-based filenames.
month(utcNow())— returns 9 — current month numbergreaterOrEquals(month(utcNow()), 10)— returns true in Q4
year
year(ts) → 4-digit year
Extract a specific numeric component from a timestamp. Used for scheduling logic: skip weekends, run only in Q4, generate year-based filenames.
year(utcNow())— returns 2025concat('Report_', string(year(utcNow())), '.pdf')— returns Report_2025.pdf
parseDateTime
parseDateTime(timestamp, locale?, format?) → string
converts a non-ISO date string into a standard ISO 8601 timestamp that all other date functions can consume. dateDifference returns the duration between two timestamps as a formatted string. subtractFromTime, getPastTime, and getFutureTime are readable shortcuts for common relative-time calculations.
parseDateTime('15/09/2025', 'en-GB', 'dd/MM/yyyy')— returns 2025-09-15T00:00:00ZparseDateTime(triggerBody()?['DateField'], 'en-US', 'MM/dd/yyyy')— returns ISO from US date string
dateDifference
dateDifference(startDate, endDate) → string
converts a non-ISO date string into a standard ISO 8601 timestamp that all other date functions can consume. dateDifference returns the duration between two timestamps as a formatted string. subtractFromTime, getPastTime, and getFutureTime are readable shortcuts for common relative-time calculations.
dateDifference(triggerBody()?['SubmittedAt'], utcNow())— returns 3.07:15:42″ — 3 days 7 hoursdateDifference('2025-09-12T08:00:00Z', '2025-09-15T15:15:42Z')— returns 3.07:15:42″ — 3 days 7h 15m
subtractFromTime
subtractFromTime(ts, interval, timeUnit, fmt?) → string
converts a non-ISO date string into a standard ISO 8601 timestamp that all other date functions can consume. dateDifference returns the duration between two timestamps as a formatted string. subtractFromTime, getPastTime, and getFutureTime are readable shortcuts for common relative-time calculations.
subtractFromTime(utcNow(), 7, 'Day', 'yyyy-MM-dd')— returns 2025-09-08″ — 7 days agosubtractFromTime('2025-09-15T00:00:00Z', 1, 'Month', 'yyyy-MM-dd')— returns 2025-08-15
getPastTime
getPastTime(interval, timeUnit, fmt?) → string
converts a non-ISO date string into a standard ISO 8601 timestamp that all other date functions can consume. dateDifference returns the duration between two timestamps as a formatted string. subtractFromTime, getPastTime, and getFutureTime are readable shortcuts for common relative-time calculations.
getPastTime — weekly report date range
Use as the lower bound in a filter expression for a rolling 7-day window.
getPastTime(7, 'Day', 'yyyy-MM-dd')— returns 2025-09-08getPastTime(30, 'Day')— returns 30 days ago in ISO format
// In OData filter:
concat('Created ge ', getPastTime(7, 'Day', 'yyyy-MM-ddTHH:mm:ssZ'))
// 'Created ge 2025-09-08T08:30:00Z'getFutureTime
getFutureTime(interval, timeUnit, fmt?) → string
converts a non-ISO date string into a standard ISO 8601 timestamp that all other date functions can consume. dateDifference returns the duration between two timestamps as a formatted string. subtractFromTime, getPastTime, and getFutureTime are readable shortcuts for common relative-time calculations.
getFutureTime(3, 'Month', 'yyyy-MM-dd')— returns 2025-12-15getFutureTime(1, 'Year')— returns one year from now
Part 6 — Type Conversion Functions
string
string(value: any) → string
Convert any value to the specified type. These are the bridges between formats — numbers come as strings from forms, booleans come as integers from webhooks, arrays come as JSON strings from HTTP responses.
string(variables('Amount'))— returns 42″ — number to stringstring(true)— returns true
int
int(value) → integer
Convert any value to the specified type. These are the bridges between formats — numbers come as strings from forms, booleans come as integers from webhooks, arrays come as JSON strings from HTTP responses.
int(triggerBody()?['Quantity'])— returns 5int(float('3.7'))— returns 3 — decimal truncated
float
float(value) → float
Convert any value to the specified type. These are the bridges between formats — numbers come as strings from forms, booleans come as integers from webhooks, arrays come as JSON strings from HTTP responses.
float(triggerBody()?['Price'])— returns 49.99mul(float('49.99'), float('5'))— returns 249.95
decimal
decimal(value) → decimal
Convert any value to the specified type. These are the bridges between formats — numbers come as strings from forms, booleans come as integers from webhooks, arrays come as JSON strings from HTTP responses.
decimal(triggerBody()?['Amount'])— returns high-precision decimaldecimal('1250.75')— returns 1250.75
bool
bool(value: string|integer) → boolean
Convert any value to the specified type. These are the bridges between formats — numbers come as strings from forms, booleans come as integers from webhooks, arrays come as JSON strings from HTTP responses.
Common traps: int('3.7') throws — use int(float('3.7')) to truncate. bool only accepts 'true', 'false', 1, and 0 — nothing else.
bool('true')— returns true — string converted to booleanbool('true')— returns true as boolean type
int('3.7') throws. Use int(float('3.7')) to truncate. bool() only accepts ‘true’,’false’,1,0.json
json(value: string | XML) → object | array
Parses a JSON string into a navigable object or array. When a field or HTTP response delivers JSON as a raw string — not a parsed object — you cannot access its properties directly. json() unlocks it.
json + coalesce — safe property access
Parse the JSON string, then immediately wrap the property access in coalesce in case the property is optional.
| Parameter | Type | Required | Description |
|---|---|---|---|
`value` | string or XML | yes | A valid JSON string to parse into a navigable object. Throws on malformed JSON. |
json('{"MaxRetries":3,"Timeout":30}')?['MaxRetries']— returns 3json('{"data":{"userId":42,"name":"Alice"}}')?['data']?['userId']— returns 42
coalesce(json(triggerBody()?['ConfigJSON'])?['MaxRetries'], 3)
// Returns 3 if MaxRetries is not in the JSONbase64
base64(value: string) → string
Encode a string to base64, or decode a base64 string back to plain text or binary. Used for building Basic Auth headers, decoding email attachment content, and working with systems that communicate in base64-encoded payloads.
base64 + concat — HTTP Basic Auth header
The standard pattern for APIs that use Basic Authentication.
concat('Basic ', base64(concat(username, ':', password)))— returns Basic Y2xhdWRl…” — Basic Authbase64('Hello World')— returns SGVsbG8gV29ybGQ=
concat('Basic ', base64(concat('myuser', ':', 'mypassword')))
// Result: 'Basic bXl1c2VyOm15cGFzc3dvcmQ='
// Use as the Authorization header valuebase64ToString
base64ToString(value: string) → string [alias: decodeBase64]
Encode a string to base64, or decode a base64 string back to plain text or binary. Used for building Basic Auth headers, decoding email attachment content, and working with systems that communicate in base64-encoded payloads.
base64ToString('SGVsbG8gV29ybGQ=')— returns Hello Worldbase64ToString('SGVsbG8gV29ybGQ=')— returns Hello World
base64ToBinary
base64ToBinary(value: string) → binary
Encode a string to base64, or decode a base64 string back to plain text or binary. Used for building Basic Auth headers, decoding email attachment content, and working with systems that communicate in base64-encoded payloads.
base64ToBinary(body('GetFile')?['$content'])— returns binary content ready for uploadbase64ToBinary('SGVsbG8gV29ybGQ=')— returns binary — pass to OneDrive or SharePoint upload
decodeBase64
Encode a string to base64, or decode a base64 string back to plain text or binary. Used for building Basic Auth headers, decoding email attachment content, and working with systems that communicate in base64-encoded payloads.
decodeBase64('Q29udG9zbw==')— returns ContosodecodeBase64('SGVsbG8=')— returns Hello
encodeUriComponent
encodeUriComponent(value) → string [alias: uriComponent]
Encode and decode URL components and data URIs. encodeUriComponent makes dynamic values safe for use in URLs. The data URI functions convert between data URI format and binary or string content. Several of these have aliases: uriComponent = encodeUriComponent, uriComponentToString = decodeUriComponent.
encodeUriComponent + concat — safe search URL
Always encode dynamic values before embedding them in a URL. Without encoding, a & or space in the search term breaks the URL silently.
concat('https://api.example.com/search?q=', encodeUriComponent('Power Apps & Canvas'))— returns safe search URLencodeUriComponent('Power Apps & Canvas 2025')— returns Power%20Apps%20%26%20Canvas%202025
concat(
'https://api.example.com/search?q=',
encodeUriComponent(triggerBody()?['SearchTerm'])
)
// SearchTerm = 'Power Apps & Canvas'
// Result: '...?q=Power%20Apps%20%26%20Canvas'decodeUriComponent
decodeUriComponent(value) → string [alias: uriComponentToString]
Encode and decode URL components and data URIs. encodeUriComponent makes dynamic values safe for use in URLs. The data URI functions convert between data URI format and binary or string content. Several of these have aliases: uriComponent = encodeUriComponent, uriComponentToString = decodeUriComponent.
decodeUriComponent('Power%20Apps%20%26%20Canvas')— returns Power Apps & CanvasdecodeUriComponent('Power%20Apps%20%26%20Canvas')— returns Power Apps & Canvas
uriComponent and data URI functions
Encode and decode URL components and data URIs. encodeUriComponent makes dynamic values safe for use in URLs. The data URI functions convert between data URI format and binary or string content. Several of these have aliases: uriComponent = encodeUriComponent, uriComponentToString = decodeUriComponent.
encodeUriComponent('Power Apps & Canvas')— returns Power%20Apps%20%26%20CanvasdecodeUriComponent('Power%20Apps%20%26%20Canvas')— returns Power Apps & Canvas
Part 7 — Workflow and Runtime Functions
triggerBody
triggerBody() → object
trigger() → object
triggerOutputs() → object)
returns the body payload of the flow’s trigger — the form data, HTTP body, or Dataverse record that started the flow. trigger() and its alias triggerOutputs() return the full trigger output including headers, query parameters, and metadata.
Always use the ? null-safe operator. triggerBody()['MissingField'] throws. triggerBody()?['MissingField'] returns null safely.
triggerBody + coalesce — safe optional field
Optional fields may not always be present. Wrap the access in coalesce to return a safe fallback instead of null.
triggerBody()?['CustomerName']— returns Contoso Ltd” — value sent in the trigger payloadtriggerBody()?['OrderItems'][0]?['SKU']— returns SKU-2025-001″ — SKU of the first order line
coalesce(triggerBody()?['PreferredName'], triggerBody()?['FirstName'], 'there')
// Works even if PreferredName is missing from the payloadbody
body('actionName': string) → object
outputs('actionName': string) → object
actions('actionName': string) → object
returns the response body of a named previous action. actionBody and actionOutputs are aliases — they work identically. outputs() returns the full output including headers and status code. actions() returns everything — body, inputs, status, and error information.
body + coalesce — nullable response field
APIs sometimes omit optional fields from responses. Guard the access with coalesce.
| Parameter | Type | Required | Description |
|---|---|---|---|
`actionName` | string | yes | The internal name of the action. Spaces become underscores. Case-sensitive. |
body('Get_customer')?['cr_email']— returns john.doe@contoso.com” — reads cr_email from a previous ‘Get a row’ Dataverse action named ‘Get customer’body('Send_HTTP')?['token']— returns eyJhbGciOi…” — reads the token field from a previous HTTP action named ‘Send HTTP’
coalesce(body('Get_customer')?['cr_phone'], 'No phone on file')
// Returns the fallback string if phone is absentvariables
variables('variableName': string) → any
Reads the current value of a flow variable by name. Variables are declared with the “Initialize variable” action and updated with “Set variable” or “Append to string variable”. Use variables() anywhere in an expression to read the latest value.
variables — running total inside Apply to each
Accumulate a sum across loop iterations. Initialize a float variable before the loop, then add to it inside.
| Parameter | Type | Required | Description |
|---|---|---|---|
`variableName` | string | yes | The exact name of the variable as declared in the Initialize variable action. |
variables('RetryCount')— returns 3 — reads the current value of the RetryCount variablevariables('InvoiceNumber')— returns INV-2025-00123″ — current value of the variable
// Initialize: TotalAmount = 0.0
// Inside Apply to each:
// Set variable: TotalAmount = add(variables('TotalAmount'), float(item()?['Amount']))
// After loop: variables('TotalAmount') → total sumparameters
parameters('parameterName': string) → any
Reads a solution or environment parameter by name. Parameters are defined at the solution level in Power Apps/Power Automate and can have different values in dev, test, and production. Use them for SharePoint URLs, approval thresholds, email addresses — anything that changes between environments.
| Parameter | Type | Required | Description |
|---|---|---|---|
`parameterName` | string | yes | The name of the solution parameter as defined in the Power Apps/Automate solution. |
concat('https://contoso.sharepoint.com/sites/Ops', '/lists/Projects')— returns https://contoso.sharepoint.com/sites/Ops/lists/Projectsparameters('MaxApprovalAmount')— returns 10000″ — value set per environment
result
result('scopeName': string) → array
Returns an array of action results from inside a named Scope block. Use this inside a “Catch” Scope (in a Try/Catch pattern) to read what failed and why. The array always has at least one element when a Scope has failed — access it with [0].
result + Scope — Try/Catch error handling
Wrap risky actions in a Scope named “Try”. Add a second Scope named “Catch” that runs only on failure. Inside Catch, use result() to read what went wrong.
result(‘Try’)[0]?[‘name’]
// → ‘Send_HTTP’ — the action that failed
| Parameter | Type | Required | Description |
|---|---|---|---|
`scopeName` | string | yes | The display name of the Scope action. Spaces are allowed here — use the exact name shown in the designer. |
result('Try_Block')[0]?['error']?['message']— returns The request limit has been exceeded” — error text from the failed actionresult('Try_Block')[0]?['status']— returns Failed” or “Succeeded” — status of the first action in the Scope
// Inside 'Catch' Scope:
result('Try')[0]?['error']?['message']
// → 'The request limit has been exceeded'
result('Try')[0]?['name']
// → 'Send_HTTP' — the action that failedworkflow
workflow() → object
Returns information about the current flow run — its name, run ID, and other metadata. Use it to build self-identifying log messages so you always know which flow and which run produced a given log entry.
> ✅ Use parameters for values that differ between environments. Flows move between dev/test/prod without code changes.
workflow()?['name']— returns Sync_Contracts” — use in log prefixworkflow()?['run']?['name']— returns 08585621921797…” — unique run identifier
items
items('loopName') → any
accesses the current element of a named Apply to each loop — critical in nested loops. iterationIndexes('loopName') gives the zero-based iteration count of an Until loop (not Apply to each). The form data functions extract values from form-encoded request payloads. listCallbackUrl() returns this flow’s own callback URL for webhook registration.
items('Loop_Orders')?['OrderID']— returns ORD-00042″ — current order in outer loopitems('Loop_Orders')?['CustomerName']— returns Contoso Ltd” — customer of current order
iterationIndexes
iterationIndexes('loopName') → integer
accesses the current element of a named Apply to each loop — critical in nested loops. iterationIndexes('loopName') gives the zero-based iteration count of an Until loop (not Apply to each). The form data functions extract values from form-encoded request payloads. listCallbackUrl() returns this flow’s own callback URL for webhook registration.
iterationIndexes — retry counter with hard cap
Use the iteration count as the exit condition to limit retries to exactly N attempts.
iterationIndexes('Until_Retry')— returns 0 on 1st attempt · 1 on 2nd · 2 on 3rdgreater(iterationIndexes('Until_Retry'), 4)— returns true on the 6th pass — loop exits
// Until loop exit condition:
greater(iterationIndexes('Until_Retry'), 4)
// Loop runs on pass 0,1,2,3,4 then exits
// Maximum 5 attempts totalformDataValue
formDataValue('action','key') → string
accesses the current element of a named Apply to each loop — critical in nested loops. iterationIndexes('loopName') gives the zero-based iteration count of an Until loop (not Apply to each). The form data functions extract values from form-encoded request payloads. listCallbackUrl() returns this flow’s own callback URL for webhook registration.
formDataValue('Submit_Form', 'access_token')— returns ya29.A0ARrd…” — OAuth access tokentriggerFormDataValue('email')— returns john.doe@contoso.com” — submitted form value
listCallbackUrl and more
listCallbackUrl() → string
accesses the current element of a named Apply to each loop — critical in nested loops. iterationIndexes('loopName') gives the zero-based iteration count of an Until loop (not Apply to each). The form data functions extract values from form-encoded request payloads. listCallbackUrl() returns this flow’s own callback URL for webhook registration.
listCallbackUrl()— returns https://prod-xx.logic.azure.com/…” — this flow’s webhook URLiterationIndexes('Until_Retry')— returns 0, 1, 2… — attempt counter
Part 8 — URI Parsing Functions
uriScheme
uriScheme(uri: string) → string
Parse a URL string and extract one specific component. Used when a connector or trigger returns a full URL and you need just the hostname, just the path, or just the query string.
uriScheme('https://contoso.sharepoint.com/sites/Sales')— returns httpsequals(uriScheme(callbackUrl), 'https')— returns validate HTTPS
uriHost
uriHost(uri: string) → string
Parse a URL string and extract one specific component. Used when a connector or trigger returns a full URL and you need just the hostname, just the path, or just the query string.
uriHost('https://contoso.sharepoint.com/sites/Sales')— returns contoso.sharepoint.comuriHost(triggerBody()?['WebhookUrl'])— returns domain for validation
uriPort
uriPort(uri: string) → string
Parse a URL string and extract one specific component. Used when a connector or trigger returns a full URL and you need just the hostname, just the path, or just the query string.
uriPort('https://contoso.sharepoint.com')— returns 443uriPort('http://api.example.com:8080/endpoint')— returns 8080
uriPath
uriPath(uri: string) → string
Parse a URL string and extract one specific component. Used when a connector or trigger returns a full URL and you need just the hostname, just the path, or just the query string.
uriPath('https://contoso.sharepoint.com/sites/Sales/Lists')— returns /sites/Sales/ListsuriPath(triggerBody()?['ResourceUrl'])— returns path portion of URL
uriQuery
uriQuery(uri: string) → string
Parse a URL string and extract one specific component. Used when a connector or trigger returns a full URL and you need just the hostname, just the path, or just the query string.
uriQuery('https://api.example.com/search?q=power&page=2')— returns ?q=power&page=2uriQuery(triggerBody()?['CallbackUrl'])— returns query string from URL
uriPathAndQuery
uriPathAndQuery(uri: string) → string
Parse a URL string and extract one specific component. Used when a connector or trigger returns a full URL and you need just the hostname, just the path, or just the query string.
uriPathAndQuery('https://api.example.com/search?q=power')— returns /search?q=poweruriPathAndQuery('https://api.example.com/search?q=power')— returns /search?q=power
Part 9 — JSON and XML Functions
addProperty
addProperty(object, property: string, value: any) → object
Mutate JSON objects without rebuilding them from scratch. addProperty adds a new key-value pair. setProperty adds or updates a key (safe whether the key exists or not). removeProperty strips a key — safe even if the key does not exist. All three return a new object — the original is not mutated.
addProperty + setProperty — build and stamp an object
Chain the two calls: first update an existing field with setProperty, then add a new timestamp with addProperty. The original object is never modified.
| Parameter | Type | Required | Description |
|---|---|---|---|
`object` | object | yes | The JSON object to add a property to. The original is not modified. |
`property` | string | yes | The name of the new key. Throws if the key already exists — use setProperty if unsure. |
`value` | any | yes | The value to assign to the new key. |
addProperty({'OrderId':42,'Status':'Pending'}, 'ProcessedAt', '2025-09-15T10:30:00Z')— returns {“OrderId”:42,”Status”:”Pending”,”ProcessedAt”:”2025-09-15T10:30:00Z”}addProperty({'Total':250}, 'TaxTotal', mul(float('250'), 0.22))— returns {“Total”:250,”TaxTotal”:55.0}
addProperty(
setProperty(triggerBody(), 'Status', 'Processing'),
'StartedAt',
utcNow()
)
// Result: original object + Status='Processing' + StartedAt=nowsetProperty
setProperty(object, property: string, value: any) → object
Mutate JSON objects without rebuilding them from scratch. addProperty adds a new key-value pair. setProperty adds or updates a key (safe whether the key exists or not). removeProperty strips a key — safe even if the key does not exist. All three return a new object — the original is not mutated.
| Parameter | Type | Required | Description |
|---|---|---|---|
`object` | object | yes | The JSON object to modify. The original is not modified. |
`property` | string | yes | The key to add or update. Safe to call whether the key exists or not. |
`value` | any | yes | The value to assign. |
setProperty({'OrderId':42,'Status':'Pending'}, 'Status', 'Approved')— returns {“OrderId”:42,”Status”:”Approved”}setProperty({'OrderId':42,'Status':'Pending'}, 'ApprovedBy', 'alice@contoso.com')— returns {“OrderId”:42,”Status”:”Pending”,”ApprovedBy”:”alice@contoso.com”}
removeProperty
removeProperty(object, property: string) → object
Mutate JSON objects without rebuilding them from scratch. addProperty adds a new key-value pair. setProperty adds or updates a key (safe whether the key exists or not). removeProperty strips a key — safe even if the key does not exist. All three return a new object — the original is not mutated.
removeProperty — strip sensitive fields before logging
Never log raw trigger payloads that contain PII or credentials. Strip the fields first.
| Parameter | Type | Required | Description |
|---|---|---|---|
`object` | object | yes | The JSON object to modify. The original is not modified. |
`property` | string | yes | The key to remove. Safe to call even if the key does not exist. |
removeProperty({'OrderId':42,'Status':'OK','InternalNotes':'check credit'}, 'InternalNotes')— returns {“OrderId”:42,”Status”:”OK”}removeProperty({'Total':250,'CreditCardLast4':'1234'}, 'CreditCardLast4')— returns {“Total”:250}
removeProperty(
removeProperty(triggerBody(), 'Password'),
'CreditCardNumber'
)
// Safe to log — sensitive fields removedxpath
xpath(xml: XML, xpathExpression: string) → array
xml(value: string | object) → XML
converts a JSON object or string to XML. xpath() evaluates an XPath expression against an XML object and returns matching nodes or values. Used when working with SOAP APIs, EDI integrations, or any legacy system that communicates in XML.
xml + xpath — parse a SOAP response
Convert the HTTP response body to XML, then extract the field you need with an XPath expression. Always access [0] — xpath always returns an array.
| Parameter | Type | Required | Description |
|---|---|---|---|
`xml` | XML | yes | An XML object — parse raw XML strings with xml() first. |
`xpathExpression` | string | yes | A valid XPath expression. Always returns an array — access the first result with [0]. |
xpath(xml(body('SOAP_Call')), '//OrderStatus/text()')[0]— returns Confirmed” — from SOAP responsexpath(xml(body('ERP_Response')), '//TotalAmount/text()')[0]— returns total from XML body
xpath(
xml(body('Call_SOAP_API')),
'//OrderStatus/text()'
)[0]
// Result: 'Confirmed'
