Expressions
Expressions let you pull data out of triggers, steps, secrets, and run metadata.
Interpolation
Use {{ expression }} inside any string:
"Bearer {{ secrets.STRIPE_KEY }}"
"https://api.example.com/orders/{{ steps.create.response.body.id }}"
A bare {{ }} returns the raw value (map, list, whatever). If you mix it into a longer string, you get a string back.
Path (default)
Dot-paths, plus comparisons:
steps.validate.response.body.total
steps.validate.response.status == 200
status == 200 and total > 100
status in [200, 201, 202]
Operators: ==, !=, >, <, >=, <=, in, and, or, not
JSONPath
Set lang: "jsonpath" when you need a real query:
$.steps.validate.response.body.items[0].sku
$.items[?(@.price > 100)]
Lua
Set lang: "lua" when you need to transform data. Sandboxed, with a 5-second timeout:
acc.total = acc.total + result.amount
return acc
when conditions
when accepts either a bare string or an expression block { "expr": "...", "lang": "..." }.
A bare string dispatches by prefix — anything starting with return runs as Lua, everything else runs as a path expression:
"when": "steps.validate.response.body.valid == true"
"when": "trigger.body.email != null"
"when": "return crypto.hmac('sha256', secrets.KEY, trigger.raw_body) == trigger.headers['x-signature']"
Block form without lang defaults to the path runtime, same as bare strings.
Context
| Path | Description |
|---|---|
trigger.body.* |
Request body |
trigger.headers.* |
Request headers |
steps.<id>.response.status |
HTTP status |
steps.<id>.response.body.* |
Response body |
secrets.<NAME> |
Tenant secret |
run.id |
Run ID |
run.session |
Run-scoped session token (24h TTL) |