Steps and script execution
<steps>
<steps> is required and accepts no attributes. It contains one or more flow items. Document order means sequential dependency. A successful earlier item makes its guaranteed outputs visible to later items.
Valid direct flow items are <step>, <parallel>, <choose>, <switch>, <fork>, and <approval>. Empty root steps are invalid.
<step>
| Attribute | Required | Meaning |
|---|---|---|
id | Yes | Stable DAG node and context.steps.<id> output identity. |
name | No | Display name. |
description | No | Display explanation. |
retry | No | Total attempts, 1–10; defaults to one. |
retry-backoff | No | fixed or exponential; requires retry greater than one. |
retry-delay | No | Delay/initial delay from 1ms through 24h; defaults to 1s. |
retry-max-delay | No | Exponential cap through 24h; invalid for fixed backoff. |
A fundamental step contains exactly one <script>. Timeout belongs to the workflow <config> in v1; <script> accepts no attributes.
Retry uses multiplier two without jitter for exponential backoff. Only a definitive script_threw failure is retried automatically. Timeout, invalid JSON, size failure, process crash, interruption, and cancellation fail closed.
<script>
A script body is the body of an asynchronous JavaScript function. It may use await, conditions, loops, exceptions, Fetch, modules, and services without a function wrapper:
<script>
const response = await services.http.request({
url: "https://api.example.com/orders",
method: "POST",
json: context.payload
}, { name: "create-order" });
return response.data;
</script>A step contributes data only through its returned JSON value. Context mutation, locals, and worker globals do not persist. The successful return is recorded before downstream scheduling and becomes context.steps.<stepId>. The final value-producing node on the main route is the workflow's public JSON result. Structural choices/switches can make that output path-stable with <result>. Failed or cancelled runs never print a partial main-route value as a successful workflow result.
Script bindings
| Binding | Availability | Purpose |
|---|---|---|
context | Business and workflow-lifecycle scripts | Read-only trigger payload and durable step outputs. |
attempt | Business and workflow-lifecycle scripts | Attempt number, maximum attempts, and stable idempotency key. |
services | Scripts and local modules | Built-in and imported managed capabilities. |
secrets | Scripts with statically proven literal secret reads | Resolved values for exact secrets.NAME access. |
fetch | Scripts and local modules | Bun-native Fetch API. |
console | Scripts | Redacting terminal logging. |
lifecycle | Lifecycle scripts only | Read-only lifecycle event data. |
props | Reusable step/provider scripts | Declared invocation props. |
notification | Reusable notification providers | Bounded message, actions, and delivery idempotency data. |
context.run and context.env do not exist. context.trigger is a deprecated runtime compatibility alias for older compiled definitions; new source uses context.payload.
context
Public paths:
context.payload
context.steps.<stepId>
context.steps.<choiceOrSwitchOrApprovalId>context.payload always means workflow input, whether the run came from a webhook, provider, timer, event, manual trigger, workflow call, or workflow start. context.steps contains only outputs visible at the current DAG position. The object is a derived read-only projection, not authoritative mutable state.
attempt
attempt.number // one-based current attempt
attempt.maxAttempts // compiled total-attempt limit
attempt.idempotencyKey // stable across attempts of this logical step/runUse the idempotency key only with an external API that explicitly supports an idempotency field or header.
secrets
Scripts may use only literal, statically discoverable reads:
secrets.PAYMENTS_API_TOKENDynamic enumeration and secrets[name] are not supported. Compiled models store names, not values. Attribute syntax uses {{secrets.NAME}}, not JavaScript syntax.