Script Binding Reference

WOML injects a small, explicit runtime environment into scripts. You write ordinary JavaScript directly; you do not declare a wrapper function or import these bindings.

Ordinary scripts

BindingMeaning
context.payloadNormalized trigger or workflow-call input.
context.steps.<id>Guaranteed completed output visible at this graph position.
attempt.numberOne-based current attempt.
attempt.maxAttemptsCompiled total attempt limit.
attempt.idempotencyKeyStable logical step identity across retries.
servicesBuilt-ins and imported module aliases.
secrets.NAMEStatically referenced secret value.
fetchBun native Fetch API.
consoleRedacting terminal logging.

Example:

WOML
<step id="buildReceipt" retry="3">
  <script>
    console.info('Building receipt', context.payload.orderId);

    const response = await services.http.request({
      url: `https://api.example.com/orders/${context.payload.orderId}`,
      responseType: 'json'
    }, {
      name: 'load-order'
    });

    return {
      order: response.data,
      attempt: attempt.number
    };
  </script>
</step>

The returned value becomes context.steps.buildReceipt for guaranteed downstream consumers. Durable values must be finite and JSON-compatible; functions, symbols, cyclic objects, and non-finite numbers are rejected.

Context availability follows the graph

context.steps.someId exists only when that producer is guaranteed to have completed before the current node. WOML rejects references that cross uncertain branches, parallel siblings, or unjoined fork paths. This compile-time rule prevents a workflow from depending on a value that may not exist.

Trigger or workflow-call input is always context.payload. The old context.trigger spelling is not part of the current language.

Loop scripts

Inside <for-each>, WOML adds:

BindingMeaning
context.itemCurrent array item.
context.iteration.indexZero-based item index.
context.iteration.totalTotal number of input items.

Outer dominating outputs remain available, such as context.steps.scan. Outputs created inside one iteration belong to that iteration and do not leak into another.

WOML
<for-each id="normalize" items="{{context.steps.load.records}}">
  <step id="normalizeRecord">
    <script>
      return {
        index: context.iteration.index,
        id: context.item.id,
        email: context.item.email.trim().toLowerCase()
      };
    </script>
  </step>
  <result from="{{context.steps.normalizeRecord}}" />
</for-each>

Lifecycle scripts

lifecycle exposes event, workflow identity/outcome, optional step identity/outcome/attempt count, and optional failure code/message.

Use lifecycle scripts for observation, cleanup, and notification—not to smuggle business control flow outside <steps>. Fields are present according to the event profile, so a workflow start does not pretend to have a failed step.

WOML
<lifecycle>
  <on-step-failure>
    <script>
      console.error(
        `Step ${lifecycle.step.id} failed`,
        lifecycle.failure?.code
      );
    </script>
  </on-step-failure>
</lifecycle>

Reusable definitions

Reusable steps receive props, attempt, services, fetch, and passed secret props. Reusable providers receive props, notification, services, and fetch. Modules receive services and fetch automatically.

Reusable definitions do not receive a caller's entire context. Their declared props form the boundary, making the component easier to understand and validate. Secret props are supplied through the reviewed secret path and should not be returned or logged.

Native platform bindings

fetch is Bun's familiar Fetch implementation instrumented by WOML; it keeps standard Request, Response, streaming, and abort behavior. services.http.request() is the structured managed alternative when you want bounded parsing, explicit accepted statuses, operation metadata, and durable supervision.

console uses familiar log, info, warn, and error methods. WOML redacts platform-owned sensitive fields, but it cannot infer that a string you deliberately log contains customer data.

context.run and context.env do not exist. New source must not use deprecated context.trigger.

Secrets are available only by static property name such as secrets.PAYMENTS_TOKEN. Enumeration and dynamic lookup are intentionally unsupported.