Skip to content

Values & Expressions

Three kinds of interpolation, all written with ${...}:

FormWhenExample
${path}Reference another payload value"${products.rows}"
${= expression}Typed expression"${= len(products.rows)}"
${secret:path}Pre-resolved secret"${secret:db/warehouse:password}"

As an entire value, ${products.rows} preserves the original type — if the field is a list, you get a list, not a stringified list. Inside a string it is stringified and treated as formatting:

with:
Items: "${products.rows}" # list stays a list
Subject: "Report with ${= len(products.rows)} rows" # stringified into the sentence

This distinction is the source of a common bug — see Common Pitfalls. Rule of thumb: use ${path} for whole-value references; reserve ${= …} for actual expressions.

Expressions support arithmetic, comparison, logical operators, and a library of built-ins:

len() str() int() float() bool()
upper() lower() trim() contains()
isEmpty() toJson() split() join()
with:
Total: "${= price * quantity }"
Tag: "${= upper(trim(category)) }"
Empty: "${= isEmpty(items) }"
Json: "${= toJson(order) }"

To write a literal ${foo} without resolution, escape it with a doubled dollar:

with:
Template: "$${foo} stays literal"

Conditions in if: and switch: are plain expressions — they are already in expression context, so they take no ${= … } wrapper:

- if: count > 0 # correct
# - if: "${= count > 0}" # wrong — double-evaluated

The full grammar — operator precedence, every built-in, every diagnostic — lives in the flow language reference.