Routing, transforms, conditions, and payloads are CEL expressions (Google's Common Expression Language). They compile once when the flow builds — so a typo fails at startup, not in production — and run read-only over each message. The upstream spec is terse; here's everything you actually need.
Every message-driven block evaluates against the same activation. These are the variables in scope:
| Variable | Type | What it is |
|---|---|---|
body | JSON value | The decoded message body — usually a map, sometimes a list/scalar. Rewritten by set-payload. |
vars | map | Per-message variables. Read here; write with set-variable. Sources seed it (see below). |
env | map | Resolved values of the flow's declared env: entries — e.g. env.HTTP_PORT. Only declared keys exist. |
eventID | string | Stable unique id for this message. |
correlationID | string | Caller-supplied id for grouping related messages (optional). |
vars.| From | Variable | Example |
|---|---|---|
| http source | vars.method | vars.method == "POST" |
| http source | vars.query (always a map) | has(vars.query.currency) |
http path /orders/{id} | vars.<param> | vars.id |
| http captured header | vars["X-Header"] | vars["X-Tenant"] |
| cron source payload | now | string(now) |
| error path | vars.error | vars.error.message · .flow · .block |
Every snippet below is lifted from a working sample in samples/.
"hello, " + body.name + "!"
"processing " + string(size(body.orders)) + " orders"
has(vars.query.currency) ? vars.query.currency : "USD"
vars.method == "POST"
body.amount >= 1000.0
size(body.orders) > 0
{"orderId": vars.id, "currency": vars.currency, "status": "found"}
body.current.temperature_2m
vars["X-Tenant"]
"listening on " + env.HTTP_PORT
items: body.orders # the list to split
as: order # each item -> vars.order
vars.order.amount >= vars.threshold
{"error": vars.error.message,
"failedBlock": vars.error.block,
"flow": vars.error.flow}
double (float) — wrap with int(...) when you need a whole number.
Use has(map.key) before reading a key that might be missing, or the expression errors.
Only env: entries you declare are in scope — an undeclared env.X fails at build time.
Expressions never mutate state: read vars/body here, and change them with
set-variable / set-payload. Use bracket syntax (vars["X-Tenant"]) for keys with dashes.