Appearance
Data & references
A flow's nodes share one running scratchpad — the refs object. Nodes write named values into it, and later nodes read those values back with dotted paths and ${...} placeholders. This page explains exactly how that works.
The refs object
Think of refs as the flow's memory while it runs. It's a set of named buckets, and each bucket is itself a small object of fields:
refs = {
start: { email: "sam@acme.com", amount: 42 }, // the request inputs
response: { status: 200, ok: true, body: {...} }, // an HTTP node's output
error: { message: "...", nodeId: "..." } // set when a node fails
}When the flow is triggered, the request's inputs (path, query, headers, and JSON body — see Data & references for triggers) are placed in a bucket named after your Start node. A Start named "Start" gives you start; a Start named "New order" gives you new-order (names are lower-cased and hyphenated).
How a node names its output
Every working node writes one bucket into refs. The bucket's name comes from a field in the node's config — resultVar on most nodes, var on Set and Template — and each has a sensible default:
| Node | Config field | Default name | Shape of the bucket |
|---|---|---|---|
| HTTP | resultVar | response | { status, ok, body } |
| MongoDB | resultVar | result | the document (or { value }) |
resultVar | email | { sent, reason } | |
| Set | var | value | the copied/literal value |
| Template | var | text | { value: "<rendered text>" } |
| ForEach | itemVar | item | the current element each pass |
NOTE
A bucket is always an object. If a node produces a plain value (a number, a string, an array), it's wrapped as { value: ... } — so you read it as response.value, not response. Objects are stored as-is.
Referencing upstream values
There are two ways to read from refs, and which one you use depends on the field.
Dotted paths (bucket.field) — used by fields that take a reference directly, such as a Set node's source, a ForEach collection, or a Switch source. The first segment is the bucket name, the rest walks into it:
start.email → the email from the request
response.body.id → the id inside an HTTP response body
order.items → an array to loop over with ForEach${bucket.field} placeholders — used inside free text such as a Template body or an Email subject. Each placeholder is replaced with the referenced value (objects and arrays are serialised to JSON):
Hi ${start.name}, your order ${order.id} is confirmed.Unknown paths resolve to an empty string rather than erroring, so a typo blanks out instead of crashing the run.
Environment and secret placeholders
Two special namespaces resolve from outside the flow, and they work in any text field:
${env.NAME}— a server environment variable, read fromprocess.env.${secrets.NAME}— a value from your workspace's encrypted secrets vault.
Authorization: Bearer ${secrets.STRIPE_KEY}
Base URL: ${env.API_BASE_URL}Names must be uppercase letters, digits, and underscores.
WARNING
Secrets are fail-closed. An unresolved ${secrets.MISSING} becomes an empty string — it never falls back to an environment variable — so a missing secret can't accidentally leak an unrelated env var into a request.
Order of resolution
In a text field, resolution happens in a defined order:
${env.X} and ${secrets.X} are substituted first, then the remaining ${bucket.field} placeholders are read from refs. This is why a secret can't be overwritten by a same-named ref, and why you can safely mix both in one string.