Skip to main content
Expressions let a flow reference live data at runtime. Anywhere you write {{ ... }} inside a Flow Definition, the platform resolves the enclosed path against the execution’s context — trigger input, definition variables, prior step outputs — just before the task runs.

Where expressions can appear

Context roots

Every expression starts from one of these roots:
var and account are distinct scopes. var is a constant local to a single definition; account is shared configuration your whole account owns and can change on the fly. See Account Variables for when to use which — and why secrets belong in a credential, never in either variable scope.
When a step contains exactly one task, you can skip the task ID: {{greet.output.message}} is shorthand for {{greet.echo_hello.output.message}}.

Dot-path traversal

Paths use dots to walk into nested objects: {{input.contact.address.city}}. Traversal is null-safe — if any segment along the path is missing, the expression resolves to null instead of raising an error. In a mixed string template, a null value renders as an empty string. To supply a fallback, use the default pipe:
The default value is coerced to its natural type: quoted strings stay strings, true/false become booleans, bare numbers become numbers, and null stays null.

Pipes

A pipe reshapes a value on its way into a parameter — trim the whitespace off a name, normalize a phone number, round a cost to two places — without needing a whole step to do it. Write one after a | inside the braces:
Pipes work on one value at a time. To reshape a whole set of records — deduplicate an upload, sort a list, filter it down — use a record transform step instead.

Chaining

Pipes chain left to right, to any depth. Each one receives whatever the one before it produced:
Position matters. The clearest case is default, which only ever sees what reaches it: Put default after the pipe whose failure you want to catch, not before it.

Arguments

Arguments follow the pipe name after a colon, separated by commas:
Each argument is coerced to its natural type, exactly like default’s fallback: quoted strings stay strings, true/false become booleans, bare numbers become numbers, and null stays null.

The full set

Text truncate’s suffix is spent out of the length, not added to it: truncate: 20, '...' returns at most 20 characters including the ellipsis. Truncation is usually there to fit a provider’s column limit, and a suffix that pushed the value past that limit would defeat the pipe you wrote it for. Dates Both read ISO-8601 — a date, or a datetime with or without an offset. Anything else fails rather than being guessed at, so 08/23/2026 is refused instead of being read as one of the two dates it could mean.
dateAdd accepts seconds, minutes, hours, days and weeks, and deliberately not months or years. “One month after 31 January” has no single correct answer — 28 February, 29 February, 2 March and 3 March are all defensible — and approximating a month as 30 days would put a silently wrong date somewhere you would never look for it. A negative amount shifts backwards.
Numbers toNumber returns a whole number when the value is integral, because 3.0 where 3 was meant is the kind of thing a provider rejects. true and false are refused rather than read as 1 and 0. Encoding urlEncode exempts nothing, / included — its job is to make a value safe as a single path or query segment, and a value containing a slash must not be able to escape that segment. base64Decode refuses text that is not valid base64 rather than quietly discarding the characters that do not belong.
base64Encode is encoding, not encryption. It is a reversible text format that anyone can decode with a single command; it protects nothing, hides nothing, and must never be used as though it did. A value that needs protecting belongs in a credential.
Null safety default substitutes only when the value is missing or an earlier pipe failed. A value that is present — including 0, false and an empty string — passes straight through untouched. Phone numbers This is the same normalizer the platform uses everywhere else a phone number is stored, so a number normalized in an expression and a number normalized on a data store write always agree. Correct shape is not enough — a well-formed string that is not a real number is refused.

What fails, and why

A pipe either produces a value or fails the step. There is no third outcome where a value quietly becomes empty and travels on:
  • A value that is present but cannot be transformed fails, with a reason that names the pipe — toNumber on "abc", formatDate on text that is not a date, toE164 on something that is not a number. The failure is immediate and is not retried, because running it again produces exactly the same result.
  • A value that is missing fails the same way entering any pipe other than default, with value is missing. Attaching a pipe is how you declare that a value has to be there — there is no separate “required” marker to remember.
  • A bare path with no pipe still resolves to null, exactly as it always has. Nothing you have already saved changes behaviour.
Failure messages name the pipe and what was wrong; they never quote the value itself, so a phone number or an email address in a bad row does not end up copied into an error log. Inside a record transform that works record by record, a failure is scoped to the record: that row is left out with a reason and the rest of the set continues. One unparseable phone number in fifty thousand does not fail the run.

The escape hatch

| default: is how you say “and if that does not work, use this”. It catches both a missing value and a failed pipe earlier in the chain:
Read the second one as: normalize the number if you can, and if there is no number or it is not a valid one, send an empty string rather than failing the record.

Type preservation

How a value resolves depends on whether the expression stands alone:
  • Whole-value expression — when the entire parameter value is a single {{ ... }}, the resolved value keeps its native type: an object stays an object, an array stays an array, a number stays a number. This is how you pass structured data (like a list of records) from one step to the next without serializing it.
  • Mixed template — when an expression is embedded in surrounding text ("Hello {{input.name}}"), the result is rendered as a string.

Conditions

Exit conditions compare two values with one of six operators: ==   !=   >   <   >=   <=
Either side can be an expression or a literal — quoted strings, bare numbers, true/false, or null. A condition with no operator at all is evaluated for truthiness: it passes if the expression resolves to a truthy value.
The ordering operators (>, <, >=, <=) require both sides to be numeric; if either side can’t be treated as a number, the condition evaluates to false. Equality (==, !=) works on any types.

Validation at creation time

When you create a definition, every string in the body is scanned for malformed expressions. If any are found, the request is rejected with 422 and a list of the problems:
These classes of error are caught at creation time:
  • Unclosed expressions — a {{ with no matching }}.
  • Unknown pipe operators — a pipe name that is not one of the sixteen above. Every stage of a chain is checked, not just the first, so a misspelling in the last position is caught at save time rather than at run time.
  • The wrong number of arguments for a piperound with no places, replace with only one, trim with any at all. The error names the pipe, how many it expects and how many it was given.
  • Unbalanced quotes in an argument list — a ' or " that is never closed.
  • An unknown context root — including row, which is accepted only inside a record transform step that works record by record, and rejected everywhere else. The error lists the roots that were available where you wrote it.
What is not checked is argument meaning: whether a formatDate pattern is the one you intended, or a dateAdd unit is spelled correctly, is decided at run time. A validator that guessed at those would reject definitions that work.
Path validity is still not checked at creation time. A bare path to a field that never exists is accepted and resolves to null at run time, exactly as before. A path followed by a pipe does not: it fails the step with value is missing. That difference is the whole point of attaching one — the pipe is where you say the value has to be there.

Next steps

Flow definitions

Where inputs, variables, steps, and tasks are declared.

Execution models

Immediate, scheduled, windowed, and signaled tasks.

Record transforms

Reshape a whole set of records — deduplicate, filter, sort, map fields.

Catalog

Every action’s full parameter and output reference.