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>

AttributeRequiredMeaning
idYesStable DAG node and context.steps.<id> output identity.
nameNoDisplay name.
descriptionNoDisplay explanation.
retryNoTotal attempts, 110; defaults to one.
retry-backoffNofixed or exponential; requires retry greater than one.
retry-delayNoDelay/initial delay from 1ms through 24h; defaults to 1s.
retry-max-delayNoExponential 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:

WOML
<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

BindingAvailabilityPurpose
contextBusiness and workflow-lifecycle scriptsRead-only trigger payload and durable step outputs.
attemptBusiness and workflow-lifecycle scriptsAttempt number, maximum attempts, and stable idempotency key.
servicesScripts and local modulesBuilt-in and imported managed capabilities.
secretsScripts with statically proven literal secret readsResolved values for exact secrets.NAME access.
fetchScripts and local modulesBun-native Fetch API.
consoleScriptsRedacting terminal logging.
lifecycleLifecycle scripts onlyRead-only lifecycle event data.
propsReusable step/provider scriptsDeclared invocation props.
notificationReusable notification providersBounded 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:

JavaScript
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

JavaScript
attempt.number          // one-based current attempt
attempt.maxAttempts     // compiled total-attempt limit
attempt.idempotencyKey  // stable across attempts of this logical step/run

Use 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:

JavaScript
secrets.PAYMENTS_API_TOKEN

Dynamic enumeration and secrets[name] are not supported. Compiled models store names, not values. Attribute syntax uses {{secrets.NAME}}, not JavaScript syntax.