FlowLibs
Browse Cloud FlowsConnectorsAI ToolsDev ToolsPricingAboutContact
Dev Tools/Expressions

Expressions & JSON Cheat Sheet

The Workflow Definition Language (WDL) expressions behind cloud flows — string, collection, logical, date, encoding, and reference functions, plus JSON handling, array/date/JSON cookbooks, and the gotchas that burn everyone. Filter the playground to find a function fast.

Power Automate18 min read·Updated 2026-06-16
ExpressionsWDLCloud FlowsJSONPatternsFunctions
Expression playground52
  • concatString

    Join strings or values

    concat('Hi ', variables('name'))
  • substringString

    Extract by index + length

    substring('FlowLibs', 0, 4)
  • sliceString

    Substring by start/end index (negatives OK)

    slice('report.csv', -4)
  • replaceString

    Replace all occurrences

    replace('a-b-c', '-', '/')
  • toLowerString

    Lowercase a string

    toLower('HELLO')
  • splitString

    String to array

    split('a,b,c', ',')
  • indexOfString

    Position of substring

    indexOf('a@b.com', '@')
  • nthIndexOfString

    Position of the nth match

    nthIndexOf('a.b.c.d', '.', 2)
  • guidString

    New globally-unique id

    guid()
  • uriComponentString

    URL-encode a value

    uriComponent('a b&c')
  • base64String

    Encode a string to base64

    base64('Hello')
  • dataUriString

    Build a data: URI

    dataUri('Hello')
  • firstCollection

    First item of an array

    first(body('Get_items')?['value'])
  • lastCollection

    Last item of an array

    last(variables('arr'))
  • lengthCollection

    String or array length

    length(variables('arr'))
  • emptyCollection

    Is empty / blank

    empty(variables('arr'))
  • containsCollection

    Membership test

    contains(variables('arr'), 'x')
  • joinCollection

    Array to delimited string

    join(variables('arr'), ', ')
  • unionCollection

    Merge + dedupe arrays

    union(arr1, arr2)
  • chunkCollection

    Split into equal-size groups

    chunk(variables('arr'), 100)
  • reverseCollection

    Reverse array order

    reverse(variables('arr'))
  • sortCollection

    Sort ascending

    sort(variables('arr'))
  • ifLogical

    Inline branch

    if(greater(x, 10), 'high', 'low')
  • andLogical

    Boolean AND

    and(equals(a, 1), greater(b, 2))
  • equalsLogical

    Equality test

    equals(status, 'Open')
  • coalesceLogical

    First non-null value

    coalesce(value, 'N/A')
  • greaterLogical

    Greater-than compare

    greater(ticks(a), ticks(b))
  • intLogical

    Cast string to integer

    int('42')
  • decimalLogical

    Exact decimal (avoids float error)

    decimal('1.99')
  • utcNowDate

    Current UTC timestamp

    utcNow()
  • formatDateTimeDate

    Format a timestamp

    formatDateTime(utcNow(), 'yyyy-MM-dd')
  • addDaysDate

    Shift a date by days

    addDays(utcNow(), 7)
  • addToTimeDate

    Shift by any unit

    addToTime(utcNow(), 1, 'Month')
  • getPastTimeDate

    Timestamp relative to now

    getPastTime(7, 'Day')
  • convertTimeZoneDate

    Change time zone

    convertTimeZone(utcNow(), 'UTC', 'Eastern Standard Time', 'yyyy-MM-dd')
  • dayOfWeekDate

    0 = Sun … 6 = Sat

    dayOfWeek(utcNow())
  • ticksDate

    100-ns ticks (for date math)

    ticks(utcNow())
  • jsonJSON

    Parse a JSON string

    json(variables('rawString'))
  • addPropertyJSON

    Add a key to an object

    addProperty(variables('obj'), 'status', 'open')
  • setPropertyJSON

    Overwrite a key

    setProperty(variables('obj'), 'status', 'done')
  • xpathJSON

    Query XML

    first(xpath(xml(body('HTTP')), '//Id/text()'))
  • triggerBodyReference

    The trigger's body output

    triggerBody()?['email']
  • triggerOutputsReference

    The whole trigger output

    triggerOutputs()?['headers']
  • outputsReference

    An action's raw output

    outputs('Compose')?['data']
  • bodyReference

    An action's body only

    body('Get_items')?['value']
  • itemsReference

    Current for-each item

    items('Apply_to_each')?['Title']
  • iterationIndexesReference

    Current Until-loop index

    iterationIndexes('Until')
  • actionsReference

    Full action object (status, etc.)

    actions('HTTP')?['status']
  • resultReference

    A scope's child inputs + outputs

    result('Scope')
  • variablesReference

    A variable's value

    variables('counter')
  • parametersReference

    A workflow/env parameter

    parameters('apiBaseUrl')
  • workflowReference

    Run + workflow metadata

    workflow()?['run']?['name']

On this page

  • WDL is not Power Fx
  • Reference functions — reaching runtime data
  • String functions
  • Encoding & conversion
  • Collection & array functions
  • Logical & comparison
  • Date & time
  • JSON & objects
  • XML & xpath deep-dive
  • Array cookbook
  • Date cookbook
  • JSON cookbook
  • No native regex in WDL
  • Common patterns
  • Common gotchas

WDL is not Power Fx

Cloud-flow expressions use the Workflow Definition Language (shared with Azure Logic Apps), not Power Fx. Functions are prefix-style — add(1,2), not 1 + 2 — and reference runtime data with triggerBody(), body('Action'), outputs('Action'), variables('x'), and items('Apply_to_each'). The same reference exists for Azure Logic Apps and Power Automate.

Null-safe navigation

Use ?[ ] to read properties that may be missing: body('HTTP')?['data']?['id']. Plain ['id'] throws when a parent is null; ?[ ] returns null instead.

@ vs @{ }

A field that is *only* an expression starts with a leading @. To embed an expression inside literal text, wrap it: Order @{outputs('Compose')} shipped. In the modern editor you rarely type these — but they show up when you peek at the code view.

Reference functions — reaching runtime data

Before any other function, you need a handle on the data flowing through the run. These functions pull values out of the trigger, prior actions, loops, variables, and the run itself. Most are *shorthands* — body('X') is just outputs('X')?['body'], and triggerBody() is trigger().outputs.body.

11/11
FunctionReturnsWhen it applies
triggerBody()The trigger's body outputThe data that started the flow — usually what you reach for first.
triggerOutputs()The whole trigger outputHeaders, queries, and body: triggerOutputs()?['headers']?['x-id'].
outputs('Action')An action's raw output objectAnything an action produced; drill in with ?['body']?['value'].
body('Action')An action's body onlyShorthand for outputs('Action')?['body'] — the common case.
items('Loop')Current item in a for-eachOnly inside Apply to each; arg = the loop action's name.
iterationIndexes('Loop')Current index in an Until loopOnly inside Until; gives the 0-based iteration counter.
actions('Action')Full action objectstatus / startTime / code, not just the body. Great for error handling.
result('Scope')Inputs + outputs of a scope's childrenAggregate or log every action inside a Scope / For each.
variables('name')A variable's current valueDeclared by an Initialize variable action.
parameters('name')A workflow parameter valueSolution environment variables surface through here in Power Automate.
workflow()Run + workflow metadataRun id via workflow()?['run']?['name']; also flow name and tags.

body() vs outputs()

Many connector actions nest their payload under body, so body('Get_items')?['value'] works but outputs('Get_items')?['value'] is usually null. When in doubt, run once and read the raw outputs in the run history before guessing the path.

String functions

13/13
FunctionWhat it doesExample
concatJoin strings/valuesconcat('Hi ', variables('name'))
substringExtract by index + lengthsubstring('FlowLibs', 0, 4)
sliceSubstring by start/end index (end exclusive, negatives OK)slice('FlowLibs', 0, 4)
replaceReplace all occurrencesreplace('a-b-c', '-', '/')
toLower / toUpperChange casetoLower('HELLO')
trimStrip leading/trailing spacetrim(' hi ')
splitString to arraysplit('a,b,c', ',')
indexOf / lastIndexOfPosition of substringindexOf('a@b.com', '@')
nthIndexOfPosition of the nth match (negative n = from end)nthIndexOf('a.b.c.d', '.', 2)
startsWith / endsWithPrefix / suffix testendsWith('x.pdf', '.pdf')
lengthString or array lengthlength('abc')
guidNew unique idguid()
formatNumberFormat a number as textformatNumber(12.5, 'C2', 'en-US')

slice vs substring

substring(text, start, length) needs a *length* and throws if it runs past the end. slice(text, start, end) takes an *end index*, tolerates overshooting, and accepts negative indexes — slice('report.csv', -4) returns .csv.

Encoding & conversion

12/12
FunctionWhat it doesExample
uriComponentURL-encode (escape URL-unsafe chars)uriComponent('a b&c')
uriComponentToStringDecode a URL-encoded stringuriComponentToString('a%20b%26c')
encodeUriComponentLegacy URL-encode (prefer uriComponent)encodeUriComponent('a b')
base64Encode a string to base64base64('Hello')
base64ToStringDecode base64 back to a stringbase64ToString('SGVsbG8=')
dataUriBuild a data: URI (e.g. for inline attachments)dataUri('Hello')
decimalExact decimal — avoids float rounding errordecimal('1.99')
int / floatCast string to a numberint('42')
stringCast any value to a stringstring(123)
boolCast to booleanbool('true')
jsonParse a JSON string into an objectjson(variables('rawString'))
xmlConvert a string to XML (for xpath)xml(body('HTTP'))

Encoding a query string

Always uriComponent() values before pasting them into a URL: concat('https://api/search?q=', uriComponent(variables('term'))). Skipping this is the usual cause of mysterious 400s when a search term contains a space or &.

decimal for money

add(0.1, 0.2) is 0.30000000000000004 in floating point. For currency, wrap operands in decimal(): add(decimal('0.1'), decimal('0.2')) returns exactly 0.3.

Collection & array functions

13/13
FunctionWhat it doesExample
first / lastFirst / last itemfirst(body('Get_items')?['value'])
lengthItem countlength(variables('arr'))
emptyIs empty / blankempty(variables('arr'))
containsMembership testcontains(variables('arr'), 'x')
joinArray to stringjoin(variables('arr'), ', ')
union / intersectionSet operations (union also dedupes)union(arr1, arr2)
take / skipSlice an arraytake(variables('arr'), 5)
chunkSplit into equal-size groupschunk(variables('arr'), 100)
reverseReverse array orderreverse(variables('arr'))
sortSort ascending (wrap in reverse for desc)sort(variables('arr'))
createArrayBuild an arraycreateArray('a', 'b', 'c')
rangeSequence of integersrange(1, 5)
item / itemsCurrent loop itemitems('Apply_to_each')?['Title']

chunk beats the 100-item batch limit

Connectors like Dataverse or SharePoint cap a batch at a fixed size. chunk(variables('rows'), 100) turns one big array into an array of ≤100-item arrays you can loop over and send one batch per iteration.

Logical & comparison

6/6
FunctionWhat it doesExample
ifInline branchif(greater(x, 10), 'high', 'low')
and / or / notBoolean compositionand(equals(a, 1), greater(b, 2))
equalsEquality testequals(status, 'Open')
greater / less / greaterOrEqualsCompare valuesgreater(ticks(a), ticks(b))
coalesceFirst non-null valuecoalesce(value, 'N/A')
int / float / string / boolType conversionsint('42')

Date & time

10/10
FunctionWhat it doesExample
utcNowCurrent UTC timestamputcNow()
formatDateTimeFormat a timestampformatDateTime(utcNow(), 'yyyy-MM-dd')
addDays / addHours / addMinutesShift a dateaddDays(utcNow(), 7)
addToTime / subtractFromTimeShift by any unitaddToTime(utcNow(), 1, 'Month')
getFutureTime / getPastTimeRelative to nowgetPastTime(7, 'Day')
convertTimeZoneChange time zoneconvertTimeZone(utcNow(), 'UTC', 'Eastern Standard Time', 'yyyy-MM-dd')
dayOfWeek / dayOfMonth / dayOfYearDay numbersdayOfWeek(utcNow())
ticks100-ns ticks for mathticks(utcNow())
startOfDay / startOfMonthTruncate a datestartOfDay(utcNow())
days between (pattern)No dateDiff() existsdiv(sub(ticks(b), ticks(a)), 864000000000)

The format-string trap

In formatDateTime, MM is month and mm is minutes; dd is day. formatDateTime(d, 'mm/dd/yyyy') silently emits minutes, not month.

JSON & objects

text
json(variables('rawString'))                       // string -> object
outputs('Compose')?['data']?['id']                  // null-safe read
addProperty(variables('obj'), 'status', 'open')     // add a key
setProperty(variables('obj'), 'status', 'closed')   // overwrite a key
removeProperty(variables('obj'), 'temp')            // drop a key
xpath(xml(body('HTTP')), '//Order/Id/text()')       // query XML
Parse a JSON string, build objects, and read nested values safely.
text
{
  "name": "@item()?['displayName']",
  "email": "@item()?['mail']"
}
Build an array of typed objects in a Select action (Map → text/code).

XML & xpath deep-dive

When a connector hands back XML (legacy SOAP, RSS, some HTTP APIs), convert it with xml() first, then query with xpath(). xpath returns an array of matches, so wrap single-value reads in first(). Use /text() to get the inner value rather than the node.

text
// single value (note: xpath returns an array)
first(xpath(xml(body('HTTP')), '//Order/Id/text()'))

// all matching values as an array
xpath(xml(body('HTTP')), '//Order/LineItem/Sku/text()')

// an attribute
first(xpath(xml(body('HTTP')), 'string(//Order/@status)'))

// count of nodes
xpath(xml(body('HTTP')), 'count(//Order/LineItem)')
Common xpath reads against xml(body(...)).

Namespaces break naive paths

If the XML declares a default namespace, //Order matches nothing. Either use local-name() — //*[local-name()='Order'] — or register the namespace in the xpath query.

Array cookbook

Reshape, filter, and aggregate arrays without an Apply to each wherever you can — Select and Filter array run server-side and are far faster than a loop.

Reshape objects with a Select action

text
{
  "id": "@item()?['ID']",
  "title": "@item()?['Title']",
  "owner": "@item()?['Author']?['DisplayName']"
}
Map mode → switch to text/code. Output is a new array of slimmed objects.

Filter array — advanced (query) syntax

text
@and(equals(item()?['Status'], 'Active'), greater(item()?['Amount'], 1000))
In Filter array, switch to 'Edit in advanced mode' and write a single boolean expression.

Dedupe a flat array

text
union(variables('tags'), variables('tags'))
union() of an array with itself removes duplicates.

Pull one column, then dedupe (group-by keys)

text
// Select (text/code) outputs an array of department strings:
@item()?['Department']

// then on that output:
union(body('Select_departments'), body('Select_departments'))
Select the key column into its own array, then union-dedupe to get distinct groups.

Flatten an array of arrays

text
union(variables('listA'), variables('listB'))
No flatten() — xpath the joined JSON, or union the chunks. Simplest: union-spread two known arrays.

Build an Adaptive Card payload from rows

text
{
  "type": "TextBlock",
  "text": "@{item()?['Title']} — @{item()?['Status']}",
  "wrap": true
}
Select rows into TextBlock elements, then drop the array into the card body.

Date cookbook

Start and end of the current month

text
startOfMonth(utcNow())                                           // first day, 00:00
addDays(startOfMonth(addToTime(utcNow(), 1, 'Month')), -1)       // last day of this month
End of month = start of next month minus one day.

Business days between two dates (approx)

text
sub(
  div(sub(ticks(variables('end')), ticks(variables('start'))), 864000000000),
  mul(2, div(div(sub(ticks(variables('end')), ticks(variables('start'))), 864000000000), 7))
)
Total days minus weekend days (2 per full week). Ignores holidays.

Age in whole years

text
div(sub(ticks(utcNow()), ticks(variables('dob'))), 315360000000000)
Floor of the tick difference divided by an average year.

Next occurrence of a weekday (e.g. next Monday)

text
addDays(startOfDay(utcNow()), add(mod(sub(8, dayOfWeek(utcNow())), 7), if(equals(dayOfWeek(utcNow()), 1), 7, 0)))
Add the gap to Monday (1). mod keeps it within the week; +7 then mod 7 avoids 0/negative today.

Calendar quarter (1–4)

text
add(div(sub(int(formatDateTime(utcNow(), 'MM')), 1), 3), 1)

Relative timestamps

text
getPastTime(24, 'Hour')        // 24h ago, ISO 8601
getFutureTime(2, 'Week')       // 2 weeks out
subtractFromTime(utcNow(), 30, 'Day')
Built-in helpers beat hand-rolled tick math for simple offsets.

ISO week numbers

WDL has no native ISO-week function. If you need it, compute it in an Office Script or a small Compose chain, or store the ISO week from the source system — the day-of-year div approximations drift around year boundaries.

JSON cookbook

Safe deep access with a default

text
coalesce(body('HTTP')?['data']?['user']?['email'], 'unknown@contoso.com')
Null-safe chain + coalesce so a missing node never fails the action.

Build a nested object inline

text
json(concat('{"id":', variables('id'), ',"name":"', variables('name'), '"}'))
json() over a composed string is the cleanest way to emit a typed object.

Merge two objects (shallow)

text
union(variables('defaults'), variables('overrides'))
union() merges objects too; keys in the second win on conflict.

Add a property only when a condition holds

text
if(empty(variables('note')),
   variables('payload'),
   addProperty(variables('payload'), 'note', variables('note')))
addProperty when present, else leave the object untouched.

Escape a value for embedding in JSON text

text
replace(replace(variables('text'), '\\', '\\\\'), '"', '\\"')
Building JSON by string concat? Escape quotes and newlines, or just use json() + object functions instead.

Prefer object functions over string-building

Concatenating JSON by hand breaks the moment a value contains a quote, newline, or unicode. Reach for addProperty / setProperty / json() first; only hand-build strings when the shape is trivial and the inputs are controlled.

No native regex in WDL

There is no regular-expression function in cloud-flow expressions. Most validation and extraction can still be done with the string primitives — and for anything heavier, push the work to a tool that does have regex.

  • Contains / matches — contains(), startsWith(), endsWith() cover prefix/suffix/substring checks.
  • Extract between markers — combine indexOf() / nthIndexOf() / lastIndexOf() with substring() or slice().
  • Tokenize — split() on a delimiter, then index into the resulting array.
  • Real regex — call an Office Scripts action (Excel) or a small Azure Function, both of which run JavaScript/.NET regex.
  • Fuzzy / semantic matching — AI Builder or a model action when the pattern isn't strictly lexical.
text
slice(
  triggerBody()?['text'],
  add(indexOf(triggerBody()?['text'], '('), 1),
  indexOf(triggerBody()?['text'], ')')
)
Extract the text between the first ( and the next ) without regex.

Common patterns

Real expressions that nest several functions — paste into the expression editor and swap the references for your own.

Days between two dates

text
div(sub(ticks(variables('endDate')), ticks(variables('startDate'))), 864000000000)
No dateDiff() — subtract ticks and divide (days = 864000000000).

Now, formatted in local time

text
formatDateTime(convertTimeZone(utcNow(), 'UTC', 'Eastern Standard Time'), 'yyyy-MM-dd HH:mm')

Safe nested read with a fallback

text
coalesce(body('HTTP')?['data']?['email'], 'no-email@contoso.com')

First name from a full name

text
first(split(triggerBody()?['name'], ' '))

Timestamped file name

text
concat('export-', formatDateTime(utcNow(), 'yyyyMMdd-HHmmss'), '.csv')

Inline if / else (ternary)

text
if(greater(int(triggerBody()?['amount']), 1000), 'High', 'Standard')

Percentage complete as text

text
concat(formatNumber(div(mul(float(variables('done')), 100), float(variables('total'))), 'F1'), '%')
Cast to float first — div() on two integers truncates.

Weekend check

text
or(equals(dayOfWeek(utcNow()), 0), equals(dayOfWeek(utcNow()), 6))

Normalize then compare (trim + lowercase)

text
equals(toLower(trim(triggerBody()?['email'])), 'admin@contoso.com')

Empty-safe join of an array

text
if(empty(variables('tags')), 'none', join(variables('tags'), ', '))

Domain from an email address

text
last(split(triggerBody()?['email'], '@'))

File extension (lowercased)

text
toLower(last(split(triggerBody()?['fileName'], '.')))

Pad a number to 4 digits

text
slice(concat('0000', string(variables('seq'))), -4)
Right-most 4 chars of a zero-padded string.

Title-case the first letter of a word

text
concat(toUpper(substring(variables('w'), 0, 1)), toLower(substring(variables('w'), 1)))

Strip the query string from a URL

text
if(contains(variables('url'), '?'), first(split(variables('url'), '?')), variables('url'))

Pluralize a label by count

text
concat(string(variables('n')), ' item', if(equals(variables('n'), 1), '', 's'))

Build a mailto link

text
concat('mailto:', variables('to'), '?subject=', uriComponent(variables('subject')))
uriComponent the subject so spaces and punctuation survive.

Minutes as h:mm

text
concat(string(div(variables('mins'), 60)), ':', slice(concat('0', string(mod(variables('mins'), 60))), -2))

Clamp a number into a range

text
max(0, min(100, variables('score')))

Common gotchas

  • No `dateDiff()` — subtract ticks() and divide (days = 864000000000, hours = 36000000000, minutes = 600000000).
  • `dayOfWeek` Sunday is `0`, not `7` — weekend test: or(equals(dayOfWeek(d),0), equals(dayOfWeek(d),6)).
  • No regex — use indexOf / split / slice, or offload to Office Scripts / AI Builder.
  • `body()` is not `outputs()` — most connector payloads live under body; outputs(...)?['body'] is the long form of body(...).
  • `xpath` returns an array — wrap single reads in first(), and watch for default XML namespaces breaking plain //Node paths.
  • Float math drifts — use decimal() for currency; div() on two integers truncates, so cast to float() first.
  • Strings vs interpolation — in a text field use @{expression}; a field that is *only* an expression uses a leading @.
  • Parse JSON nulls — make optional properties nullable in the schema or the action fails on a missing field.
  • Apply to each is slow — enable concurrency (Settings) for independent iterations, or replace the loop with Select / Filter array where possible.
← All Dev ToolsUse these in your AI assistant →
Spotted an error or have a suggestion? Let us know
FlowLibs

A curated library of production-grade Power Automate cloud flow patterns. Packaged as managed solutions, ready to import into your environment.

Library

  • Browse Cloud Flows
  • Approvals
  • Email & Notifications
  • Reporting
  • Security & Compliance

AI

  • AI Tools
  • MCP Server
  • Generate a Token

Resources

  • About
  • FAQ
  • Support
  • Status
  • Contact
  • Power Automate Docs
  • Connector Reference

© 2026 FlowLibs. All rights reserved.

  • Privacy
  • Terms
  • Refunds
  • Cookies
  • Acceptable Use
  • DMCA
Help