| Flavor | Syntax | When to use |
|---|---|---|
| JSONata | {{ ... }} | Deterministic transformations: addressing fields, filtering arrays, basic math, type checks |
| AI | ${ ... } | Free-text classification, extraction, generation: anything an LLM does well |
JSONata Expressions
JSONata is a query language for JSON. The minimum you need:| Want to… | Write… |
|---|---|
| Get a top-level input field | {{ n3.customer_email }} or {{ input.customer_email }} |
| Get a step’s output field | {{ n3.body.id }} |
| Compare values | {{ n3.score > 50 }} |
| Boolean logic | {{ n3.score > 50 and n3.tier = 'gold' }} |
| Filter an array | {{ n3.contacts[tier = 'gold'] }} |
| Map an array | {{ n3.contacts.email }} (returns an array of emails) |
| Default value | {{ n3.name ? name : 'friend' }} |
| String concat | {{ n3.first_name & ' ' & n3.last_name }} |
AI Expressions
${...} sends the contents (with {{...}} interpolations resolved first) to an LLM and replaces the expression with the response.
Pure vs interpolated
A field whose entire value is one expression is pure:Why this matters
Pure expressions preserve types — ifn3.body.id is an integer, customer_id is an integer. Interpolated expressions always produce strings.
This affects HTTP request bodies (where APIs may require integer IDs, not strings) and Output schemas (where validation enforces types).
Evaluation order
When a step is about to run, the platform evaluates expressions in its config:- JSONata is resolved first, top to bottom, against the current
ctx - AI is resolved next, with
{{...}}interpolations already replaced
{{...}} is replaced with the actual transcript before the LLM sees the prompt.
Where expressions are evaluated
Most fields in most step types support expressions. A few notable places:| Step | Expression-evaluated fields |
|---|---|
| HTTP API | url, headers (values), query_params (values), body (recursively) |
| Condition | each condition’s condition |
| Output | each output_data value |
| Action | each operation_input value |
| Code | not in code itself — the code reads ctx directly |
ctx natively.
