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.pdf
  • concat('[', formatDateTime(utcNow(),'yyyy-MM-dd HH:mm'), '] Contract signed') — returns [2025-09-15 14:22] Contract signed

⚠️ Watch out: Every argument must be a string. Wrap numbers: string(variables('Amount')) before concatenating.
Works well withconcat + formatDateTime — timestamped filenameBuild a unique filename that includes the current date. Every time the flow runs, the filename is different — no overwrite risk.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 Ltd
  • trim(' john.doe@contoso.com ') — returns john.doe@contoso.com

⚠️ Watch out: trim removes outer whitespace only. Use replace(text,' ','') to strip all spaces.
Works well withtrim + toLower — normalize before comparisonForm fields and SharePoint columns often carry invisible whitespace. Always trim before toLower when building keys or doing comparisons.equals(trim(toLower(triggerBody()?['Email'])), 'john@contoso.com')
// Matches ' JOHN@CONTOSO.COM ' correctly


toLower

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.com
  • toLower('ACTIVE') — returns active

⚠️ Watch out: Case-sensitive comparison is the #1 source of silent mismatches. Always normalize before comparing user input.
Works well withtoLower + contains — case-insensitive keyword checkWithout normalization, “URGENT” and “urgent” are different strings. Wrap both sides in toLower to catch any casing.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 0742123456
  • replace('Q3/2025 Report', '/', '-') — returns Q3-2025 Report

⚠️ Watch out: replace is case-sensitive. Use toLower on a copy before replacing if case-insensitive matching is needed.
Works well withreplace + replace — chain two cleanupsEach replace() returns a new string you can pipe directly into the next call. Clean in two passes without intermediate variables.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”]

⚠️ Watch out: Trailing delimiter produces empty last element. split('Apple,Orange,', ',') -> ["Apple","Orange",""]. Always trim first.
Works well withsplit + last — extract filename from pathSplit on the separator and take the last element. Works regardless of how deep the path is.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 2025
  • substring('john@contoso.com', 0, 4) — returns john

Works well withindexOf + substring — extract before delimiterFind the position of a character you do not know in advance, then cut everything before it.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 .pdf
  • slice('REF-2025-INV-00123', -5) — returns 00123

⚠️ Watch out: Prefer slice over substring for variable-length strings. substring throws if start+len exceeds string length.
Works well withslice + toLower — normalize file extensionExtract the last 4 characters (the extension) and lowercase it for a safe comparison.toLower(slice('Report_Q3.PDF', -4))
// Result: '.pdf' — safe for comparison


indexOf

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 4
  • indexOf('REF-2025-INV-00123', '-') — returns 3

Works well withindexOf + substring — extract username from emailFind where the @ is, then take everything before it.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 9
  • lastIndexOf('/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 8
  • nthIndexOf('a.b.c.d', '.', 3) — returns 5

⚠️ Watch out: Always check for -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 true
  • startsWith('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 / false
  • endsWith('contoso.sharepoint.com', '.sharepoint.com') — returns true

⚠️ Watch out: Case-sensitive. 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 5
  • length(['Alpha','Beta','Gamma']) — returns 3

⚠️ Watch out: 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.75
  • formatNumber(0.875, 'P1', 'en-US') — returns 87.5%

⚠️ Watch out: Returns a string. Cannot do math on the result — convert back with 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.

ParameterTypeRequiredDescription
`format`stringno“D” (default, with hyphens), “N” (no hyphens), “B” (braces), “P” (parentheses).
  • guid() — returns a7f3c2d1-8b4e-4f1a-92c3-d6e1b5f0c2a8
  • guid('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],…]

⚠️ Watch out: On strings, splits by character count — cuts through words. chunk('Hello World', 5) -> ['Hello',' Worl','d']. Use split for word-aware splitting.
Works well withchunk + Apply to each — batch API processingSplit a large array into fixed-size batches, then process each batch in a loop. Keeps you under API rate limits.// chunk source:
chunk(body('Get_all_records')?['value'], 50)
// Each Apply to each iteration:
// item() = ['record1','record2',...,'record50']
// Send as one batch to the API



Part 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 incremented
  • add(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 6500
  • sub(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.95
  • mul(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 cost
  • div(float(7), 2) — returns 3.5

⚠️ Watch out: 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 B
  • equals(mod(4, 2), 0) — returns true — 4 is even

⚠️ Watch out: mod(n, 0) throws divide-by-zero. Validate divisor when it comes from user input.
Works well withmod — round-robin queue assignmentDistribute incoming records evenly across N queues based on the record ID.// 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 maximum
  • max(3, 17, 8) — returns 17

⚠️ Watch out: Cannot mix array + scalar arguments. 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

⚠️ Watch out: maxValue is exclusive. rand(1,10) never returns 10. For 1-to-10 inclusive: rand(1,11).
Works well withrand + addSeconds — jittered retry delayWhen multiple flows retry simultaneously, they hammer the target API together. Add a random delay so they spread out.addSeconds(utcNow(), rand(5, 30))
// Each retry instance picks a different delay
// Prevents thundering herd on the target system


range

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]

Works well withrange + Apply to each — 7-day windowGenerate a sequence of day offsets and compute each date inside the loop. No pre-built date array needed.// 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+6



Part 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.

ParameterTypeRequiredDescription
`value1, value2, ...`anyyesTwo 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 returned
  • coalesce(null, 'N/A') — returns N/A” — phone field was missing, fallback used

Works well withcoalesce — three-level fallbackTry PreferredName first, fall back to FirstName, then to a hardcoded default. One expression, no Condition steps.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.

ParameterTypeRequiredDescription
`collection`array, string, or objectyesArray: checks membership. String: checks substring. Object: checks if key exists.
`value`anyyesThe item, substring, or key to look for.
  • contains(createArray('Draft','Active','Done'), 'Active') — returns true
  • contains(toLower('URGENT: Contract Review'), 'urgent') — returns true

⚠️ Watch out: Case-sensitive for strings. Apply 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.

ParameterTypeRequiredDescription
`collection`string, array, object, or nullyesReturns true for null, empty string ”, empty array [], and empty object {}.
  • empty('') — returns true — empty string
  • empty(createArray()) — returns true — empty array

Works well withempty + if — safe first/last accessfirst() and last() throw on empty arrays. Always guard before calling them.if(empty(body('Get_rows')?['value']), 'No records', first(body('Get_rows')?['value'])?['Title'])
// Returns 'No records' instead of throwing


first

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.

ParameterTypeRequiredDescription
`collection`array or stringyesThrows a runtime error if empty. Always guard with empty() first.
  • first(body('GetApprovals')?['value'])?['ApprovedBy'] — returns first approver name
  • first(split('Power Apps,Dataverse,Canvas', ',')) — returns Power Apps

⚠️ Watch out: first([]) and last([]) both throw. Always guard: if(empty(arr), null, first(arr))
Works well withsort + reverse + first — get maximum valueSort ascending, flip to descending, take the first item. Clean and readable without scanning manually.first(reverse(sort(body('Get_items')?['value'], 'Budget')))?['Title']
// Returns the project with the highest Budget


join

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.

ParameterTypeRequiredDescription
`collection`array of stringsyesAll elements must be strings. Use a Select action to convert numbers first.
`delimiter`stringyesPlaced between each element. e.g. “, ” or “;”
  • join(createArray('Power Apps','Dataverse','Canvas'), ', ') — returns Power Apps, Dataverse, Canvas
  • join(createArray('alice@co.com','bob@co.com'), ';') — returns alice@co.com;bob@co.com

⚠️ Watch out: All elements must be strings. Use a Select action to stringify array elements before calling join.
Works well withjoin — build a semicolon-separated recipient listCollect approver emails in an array variable inside a loop, then join once at the end. Cleaner than string concatenation inside the loop.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”]

Works well withunion — merge two tag lists without duplicatesWhen two sources each provide a list of tags, union merges them and automatically removes any tag that appears in both.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”]

⚠️ Watch out: Reliable for strings and numbers. For JSON objects, compares by reference not content — two identical objects with different runtime references are treated as different.
Works well withintersection — find users in multiple groupsOnly return users who are members of every specified group simultaneously.intersection(
variables('betaTesters'),
variables('euRegionUsers')
)
// Only users who appear in both lists


skip

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 removed
  • take(skip(createArray(1,2,3,4,5,6,7,8,9,10), 3), 3) — returns [4,5,6] — page 2 of size 3

Works well withskip + take — manual paginationPage 2 of 10 items per page: skip the first 10, then take the next 10.take(skip(body('Get_all')?['value'], 10), 10)
// Items 11-20 from the full array


sort

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]

⚠️ Watch out: sort is ascending only. For descending: always follow with reverse(). Sorting an empty array throws — check empty() first.
Works well withsort + reverse + first — top recordSort ascending by a property, flip it, take the first item — the record with the highest value.first(reverse(sort(body('Get_projects')?['value'], 'Budget')))
// Returns the project with the highest Budget field


createArray

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

Works well withcreateArray + contains — inline allowlist checkCheck membership against a fixed set without a separate variable or Compose action.contains(createArray('Active','Pending','On Hold'), triggerBody()?['Status'])
// true if Status is one of the three allowed values


array

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”]

⚠️ Watch out: 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 record
  • item()?['Status'] — returns Active” — Status field of the current record

⚠️ Watch out: 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.

ParameterTypeRequiredDescription
`expression`booleanyesThe condition to evaluate. Must resolve to true or false.
`valueIfTrue`anyyesReturned when expression is true.
`valueIfFalse`anyyesReturned when expression is false.
  • if(greater(15000, 10000), 'Senior Approval', 'Standard') — returns Senior Approval
  • if(empty(''), 'No notes', '') — returns No notes” — empty string returns fallback

⚠️ Watch out: Both branches always evaluate. If the unused branch would throw, use a Condition step instead.
Works well withif + and — compound approval routingReplace two nested Condition steps with a single expression.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 met
  • and(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 matches
  • or(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 list
  • not(empty('Project review scheduled for Q4')) — returns true — Notes has content

⚠️ Watch out: No && or || in Power Automate expressions. Must use the function form.
Works well withnot + contains — exclusion filterExpress “status is not in this list” cleanly, without chaining multiple ne comparisons.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 true
  • equals(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 true
  • greater(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 required
  • greaterOrEquals(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 limit
  • less(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 left
  • lessOrEquals(45, 60) — returns true — score is failing

⚠️ Watch out: equals is type-sensitive: 1 != ‘1’. Never compare dates as strings — use ticks() instead.


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 true
  • if(isInt('42'), int('42'), 0) — returns 42 — valid integer

⚠️ Watch out: 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.

ParameterTypeRequiredDescription
`format`stringno.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.0000000Z
  • utcNow('yyyy-MM-dd') — returns 2025-09-15

⚠️ Watch out: utcNow() captures flow start time. Two calls in the same flow return the same value.
Works well withutcNow + formatDateTime — date in specific formatGet today in exactly the format a system or API expects.formatDateTime(utcNow(), 'yyyy-MM-dd')
// '2025-09-15' — ISO date for OData filter


addDays

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 deadline
  • addDays('2025-09-30', -7, 'yyyy-MM-dd') — returns 2025-09-23

Works well withaddDays — due date in email bodyCalculate and format a deadline in one expression — no separate Compose needed.addDays(utcNow(), 30, 'dd MMMM yyyy')
// '15 October 2025' — ready for email body


addHours

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 meeting
  • addHours(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 now
  • addMinutes(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 now
  • addSeconds(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 today
  • addMonths('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 today
  • addToTime(utcNow(), 6, 'Month') — returns 6 months from now

⚠️ Watch out: addMonths(‘2025-01-31’, 1) -> 2025-02-28. February has no 31st. Build explicit logic for month-end billing scenarios.


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 time
  • convertTimeZone(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:30
  • convertFromUtc('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:00Z
  • convertToUtc('2025-09-15 08:00', 'Eastern Standard Time', 'yyyy-MM-ddTHH:mm:ssZ') — returns 2025-09-15T12:00:00Z

⚠️ Watch out: IANA names like ‘Europe/Rome’ do NOT work. Only Windows time zone identifiers accepted.


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-15
  • formatDateTime(triggerBody()?['CreatedAt'], 'dd/MM/yyyy HH:mm') — returns 15/09/2025 08:30

⚠️ Watch out: Uppercase MM = month. Lowercase mm = minutes. ‘MM/mm/yyyy’ gives month/minute — the #1 date formatting bug in Power Automate.
Works well withconvertFromUtc + formatDateTime — display local timeConvert UTC to local time zone, then format for display in an email or notification.formatDateTime(
convertFromUtc(utcNow(), 'W. Europe Standard Time'),
'dd/MM/yyyy HH:mm'
)
// '15/09/2025 10:30' — Italian local time


ticks

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.

ParameterTypeRequiredDescription
`timestamp`stringyesAn 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 Sep
  • div(sub(ticks('2025-09-15T09:30:00Z'), ticks('2025-09-15T08:00:00Z')), 600000000) — returns 90 — 9:30 minus 8:00 = 90 minutes

⚠️ Watch out: Never compare dates as strings. greater(’15/09/2025′,’10/09/2025′) is false lexicographically — wrong result, no error. Always use ticks().
Works well withticks + sub + div — elapsed time in minutesSubtract two tick values to get the raw duration, then divide to convert to the unit you need.div(
sub(ticks(utcNow()), ticks(variables('startTime'))),
600000000
)
// 600000000 ticks = 1 minute
// Result: elapsed minutes as integer


startOfDay

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 midnight
  • startOfDay(utcNow(), 'yyyy-MM-dd') — returns 2025-09-15″ — for OData filter

Works well withstartOfDay + convertTimeZone — local midnightFor a user in a specific time zone, get their local midnight as a UTC value — the correct lower bound for a “today” query.convertToUtc(
startOfDay(convertFromUtc(utcNow(), 'W. Europe Standard Time')),
'W. Europe Standard Time'
)
// Returns UTC equivalent of 00:00 Italian time


startOfMonth

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 month
  • startOfMonth(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 hour
  • startOfHour(utcNow(), 'HH:mm') — returns 08:00

⚠️ Watch out: Operates on UTC. For local start-of-day: convert to local time, apply startOfDay, convert back.


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 Sunday
  • and(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 day
  • equals(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 year
  • greater(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 number
  • greaterOrEquals(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 2025
  • concat('Report_', string(year(utcNow())), '.pdf') — returns Report_2025.pdf

⚠️ Watch out: dayOfWeek: Sunday = 0, Saturday = 6. Map values explicitly for Monday-first European conventions.


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:00Z
  • parseDateTime(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 hours
  • dateDifference('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 ago
  • subtractFromTime('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-08
  • getPastTime(30, 'Day') — returns 30 days ago in ISO format

Works well withgetPastTime — weekly report date rangeUse as the lower bound in a filter expression for a rolling 7-day window.// 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-15
  • getFutureTime(1, 'Year') — returns one year from now

⚠️ Watch out: parseDateTime throws if format does not match input exactly. dateDifference returns a string — use ticks() for numeric comparisons.



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 string
  • string(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 5
  • int(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.99
  • mul(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 decimal
  • decimal('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 boolean
  • bool('true') — returns true as boolean type

⚠️ Watch out: 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.

ParameterTypeRequiredDescription
`value`string or XMLyesA valid JSON string to parse into a navigable object. Throws on malformed JSON.
  • json('{"MaxRetries":3,"Timeout":30}')?['MaxRetries'] — returns 3
  • json('{"data":{"userId":42,"name":"Alice"}}')?['data']?['userId'] — returns 42

⚠️ Watch out: Throws on malformed JSON. Validate the source or wrap in error handling.
Works well withjson + coalesce — safe property accessParse the JSON string, then immediately wrap the property access in coalesce in case the property is optional.coalesce(json(triggerBody()?['ConfigJSON'])?['MaxRetries'], 3)
// Returns 3 if MaxRetries is not in the JSON


base64

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 Auth
  • base64('Hello World') — returns SGVsbG8gV29ybGQ=

Works well withbase64 + concat — HTTP Basic Auth headerThe standard pattern for APIs that use Basic Authentication.concat('Basic ', base64(concat('myuser', ':', 'mypassword')))
// Result: 'Basic bXl1c2VyOm15cGFzc3dvcmQ='
// Use as the Authorization header value


base64ToString

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 World
  • base64ToString('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 upload
  • base64ToBinary('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 Contoso
  • decodeBase64('SGVsbG8=') — returns Hello

⚠️ Watch out: Double-encoding — encoding already-encoded content — produces garbage. Only use when you know the value is not already encoded.


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 URL
  • encodeUriComponent('Power Apps & Canvas 2025') — returns Power%20Apps%20%26%20Canvas%202025

Works well withencodeUriComponent + concat — safe search URLAlways 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(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 & Canvas
  • decodeUriComponent('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%20Canvas
  • decodeUriComponent('Power%20Apps%20%26%20Canvas') — returns Power Apps & Canvas

⚠️ Watch out: Encode VALUES not full URLs. Encoding slashes and colons in the base URL breaks it.



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 payload
  • triggerBody()?['OrderItems'][0]?['SKU'] — returns SKU-2025-001″ — SKU of the first order line

⚠️ Watch out: Always use ? null-safe operator. triggerBody()[‘Field’] throws — triggerBody()?[‘Field’] returns null safely.
Works well withtriggerBody + coalesce — safe optional fieldOptional fields may not always be present. Wrap the access in coalesce to return a safe fallback instead of null.coalesce(triggerBody()?['PreferredName'], triggerBody()?['FirstName'], 'there')
// Works even if PreferredName is missing from the payload


body

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.

ParameterTypeRequiredDescription
`actionName`stringyesThe 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’

⚠️ Watch out: Spaces become underscores: body(‘Get_items’) not body(‘Get items’). Case-sensitive.
Works well withbody + coalesce — nullable response fieldAPIs sometimes omit optional fields from responses. Guard the access with coalesce.coalesce(body('Get_customer')?['cr_phone'], 'No phone on file')
// Returns the fallback string if phone is absent


variables

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.

ParameterTypeRequiredDescription
`variableName`stringyesThe exact name of the variable as declared in the Initialize variable action.
  • variables('RetryCount') — returns 3 — reads the current value of the RetryCount variable
  • variables('InvoiceNumber') — returns INV-2025-00123″ — current value of the variable

Works well withvariables — running total inside Apply to eachAccumulate a sum across loop iterations. Initialize a float variable before the loop, then add to it inside.// Initialize: TotalAmount = 0.0
// Inside Apply to each:
// Set variable: TotalAmount = add(variables('TotalAmount'), float(item()?['Amount']))
// After loop: variables('TotalAmount') → total sum


parameters

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.

ParameterTypeRequiredDescription
`parameterName`stringyesThe 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/Projects
  • parameters('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

ParameterTypeRequiredDescription
`scopeName`stringyesThe 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 action
  • result('Try_Block')[0]?['status'] — returns Failed” or “Succeeded” — status of the first action in the Scope

Works well withresult + Scope — Try/Catch error handlingWrap 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.// Inside 'Catch' Scope:
result('Try')[0]?['error']?['message']
// → 'The request limit has been exceeded'

result('Try')[0]?['name']
// → 'Send_HTTP' — the action that failed


workflow

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 prefix
  • workflow()?['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 loop
  • items('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 3rd
  • greater(iterationIndexes('Until_Retry'), 4) — returns true on the 6th pass — loop exits

Works well withiterationIndexes — retry counter with hard capUse the iteration count as the exit condition to limit retries to exactly N attempts.// Until loop exit condition:
greater(iterationIndexes('Until_Retry'), 4)
// Loop runs on pass 0,1,2,3,4 then exits
// Maximum 5 attempts total


formDataValue

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 token
  • triggerFormDataValue('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 URL
  • iterationIndexes('Until_Retry') — returns 0, 1, 2… — attempt counter

⚠️ Watch out: iterationIndexes works with Until loops only — not Apply to each. listCallbackUrl() changes on every save — never hardcode it.



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 https
  • equals(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.com
  • uriHost(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 443
  • uriPort('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/Lists
  • uriPath(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=2
  • uriQuery(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=power
  • uriPathAndQuery('https://api.example.com/search?q=power') — returns /search?q=power

⚠️ Watch out: All functions throw on malformed URIs. Sanitize dynamic URL portions with encodeUriComponent before parsing.



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.

ParameterTypeRequiredDescription
`object`objectyesThe JSON object to add a property to. The original is not modified.
`property`stringyesThe name of the new key. Throws if the key already exists — use setProperty if unsure.
`value`anyyesThe 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}

Works well withaddProperty + setProperty — build and stamp an objectChain the two calls: first update an existing field with setProperty, then add a new timestamp with addProperty. The original object is never modified.addProperty(
setProperty(triggerBody(), 'Status', 'Processing'),
'StartedAt',
utcNow()
)
// Result: original object + Status='Processing' + StartedAt=now


setProperty

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.

ParameterTypeRequiredDescription
`object`objectyesThe JSON object to modify. The original is not modified.
`property`stringyesThe key to add or update. Safe to call whether the key exists or not.
`value`anyyesThe 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.

ParameterTypeRequiredDescription
`object`objectyesThe JSON object to modify. The original is not modified.
`property`stringyesThe 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}

⚠️ Watch out: addProperty throws if key exists. Use setProperty when unsure — it handles both add and update safely.
Works well withremoveProperty — strip sensitive fields before loggingNever log raw trigger payloads that contain PII or credentials. Strip the fields first.removeProperty(
removeProperty(triggerBody(), 'Password'),
'CreditCardNumber'
)
// Safe to log — sensitive fields removed


xpath

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.

ParameterTypeRequiredDescription
`xml`XMLyesAn XML object — parse raw XML strings with xml() first.
`xpathExpression`stringyesA 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 response
  • xpath(xml(body('ERP_Response')), '//TotalAmount/text()')[0] — returns total from XML body

⚠️ Watch out: xpath always returns an array — access first result with [0]. xml() requires a single root element: wrap with {“Root”: {…}}
Works well withxml + xpath — parse a SOAP responseConvert the HTTP response body to XML, then extract the field you need with an XPath expression. Always access [0] — xpath always returns an array.xpath(
xml(body('Call_SOAP_API')),
'//OrderStatus/text()'
)[0]
// Result: 'Confirmed'


Categorized in:

Power Automate,