Context and Data Flow
context is the deeply read-only view of workflow data available to the current script. It is derived from durable run history at that exact graph position. Return values create durable data; assigning to context does not.
Trigger payload
Every trigger, workflow call, and workflow start supplies normalized input through:
context.payloadThe payload shape depends on the ingress:
- A webhook receives its validated request body.
- A schedule or interval receives scheduled and triggered timestamps.
- A communication trigger receives a normalized provider message.
- An event receives the published JSON payload.
- A child workflow receives the payload passed to
call()orstart(). - A keyboard manual run currently receives
{}.
Use context.payload in new source. context.trigger exists only as compatibility behavior for older compiled definitions and should not appear in new workflows.
Step results
A successful step return becomes:
context.steps.<stepId>For example:
<step id="prepare">
<script>
return { quantity: 3, unitPrice: 20 };
</script>
</step>
<step id="calculate">
<script>
const prepared = context.steps.prepare;
return { total: prepared.quantity * prepared.unitPrice };
</script>
</step>The second step can read prepare because the source order guarantees that prepare succeeded first. Only results guaranteed to exist at the current graph position are visible. A step cannot read a future step, a concurrently running sibling, or an output that exists only on an unselected route.
Structural primitives use stable result identities. A <choose>, <switch>, <approval>, or <for-each> can publish one predictable result under its own ID so downstream steps do not need to know which internal route produced it.
Loop context
Inside <for-each>, scripts additionally receive:
context.item
context.iteration.index
context.iteration.totalcontext.item is the current input item. context.iteration.index is its stable zero-based position, and context.iteration.total is the captured input size.
Body results are isolated per item. An iteration never reads another iteration's local body output. After the loop, downstream steps see the ordered aggregate published at context.steps.<forEachId>.
Context is not durable user state
Do not mutate context to remember something:
context.counter = 10;The mutation exists only inside that worker invocation and is discarded. To communicate with the next step, return a value. To remember small data across future runs, use services.state. To query business records, use a database.
Return only JSON-compatible values
Durable outputs may contain null, booleans, strings, finite safe numbers, arrays, and plain objects. Do not return undefined, BigInt, functions, circular objects, database clients, response streams, or class instances.
const response = await fetch("https://api.example.com/orders");
const data = await response.json();
return { orders: data.orders };Return the bounded data that later steps need, not the live Response object.
Keep context intentional
Every returned field becomes part of durable workflow history. Keep context intentional and avoid returning credentials, full provider envelopes, unnecessary personal data, or large binary content.
Put larger durable values in Object Storage, reusable optimization data in Cache, cross-run workflow memory in Durable State, and application records in Databases.