Values & Expressions
Three kinds of interpolation, all written with ${...}:
| Form | When | Example |
|---|---|---|
${path} | Reference another payload value | "${products.rows}" |
${= expression} | Typed expression | "${= len(products.rows)}" |
${secret:path} | Pre-resolved secret | "${secret:db/warehouse:password}" |
Type preservation — the key distinction
Section titled “Type preservation — the key distinction”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 sentenceThis distinction is the source of a common bug — see Common Pitfalls. Rule of thumb: use ${path} for whole-value references; reserve ${= …} for actual expressions.
Expression built-ins
Section titled “Expression built-ins”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) }"Escaping
Section titled “Escaping”To write a literal ${foo} without resolution, escape it with a doubled dollar:
with: Template: "$${foo} stays literal"Where expressions are not wrapped
Section titled “Where expressions are not wrapped”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-evaluatedThe full grammar — operator precedence, every built-in, every diagnostic — lives in the flow language reference.