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
| Binding | Meaning |
|---|---|
context.payload | Normalized trigger or workflow-call input. |
context.steps.<id> | Guaranteed completed output visible at this graph position. |
attempt.number | One-based current attempt. |
attempt.maxAttempts | Compiled total attempt limit. |
attempt.idempotencyKey | Stable logical step identity across retries. |
services | Built-ins and imported module aliases. |
secrets.NAME | Statically referenced secret value. |
fetch | Bun native Fetch API. |
console | Redacting terminal logging. |
Example:
<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:
| Binding | Meaning |
|---|---|
context.item | Current array item. |
context.iteration.index | Zero-based item index. |
context.iteration.total | Total 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.
<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.
<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.