The handler context
Every handler receives aStepContext: the triggering event, the durable step API, and the run’s
metadata.
{ name: string; data: TData }
required
The triggering event; event.data has the type passed to workflow.
Step
required
The durable step API - see below.
Webhook
required
ctx.webhook.send(id, { url, data? }): a durable outbound POST with retries and a per-attempt log.
Logger
required
Structured, replay-safe logging: ctx.log(msg, fields) plus .debug/.info/.warn/.error. See Logging.
string
required
This run id.
number
required
Retry attempt, starting at 1 and increasing on each retry.
string
required
The app this run belongs to.
string
required
The runner this run is pinned to; the empty string when the run is anycast.
Array<{ name: string; data: TData }>
Set only on a batched workflow: the coalesced events. event is events[0].
StepError
Set only inside an onFailure handler: the terminal error that failed the source run.
step
Every step takes a stableid, unique within the workflow. The result is recorded under that id, and
on replay after a crash or retry a completed step returns its saved result instead of running again.
<T>(id, fn) => Promise<T>
required
Run fn once and memoize its result under id. Overload: run(id, input, fn) also records an explicit input.
AIStep
required
Durable model calls: generate, wrap, embed, loop. See AI steps.
(id, reason?: string) => Promise<void>
required
Record a deliberately bypassed step as a terminal “skipped” step; the optional reason is stored as its output. Durable: id must be stable across replays.
(id, duration: string | number) => Promise<void>
required
Suspend the run for a relative duration - a string like ”30s”/“1h”, or a number of ms.
(id, at: Date | string | number) => Promise<void>
required
Suspend the run until an absolute time - a Date, an ISO string, or epoch ms.
<T>(id, { event, timeout, if? }) => Promise<T | null>
required
Suspend until a matching event arrives, returning its data; resolves to null when timeout elapses first. Optional CEL if filters on the event payload so the run resumes only on the correlated event.
<T>(id, probe, { every, timeout, until?, maxChecks? }) => Promise<T>
required
Re-check an external resource until it is ready, sleeping durably between checks, without spending the step’s retry budget. Returns the ready value; throws PollTimeoutError when the deadline passes.
<T>(id, { name, app?, runner?, data?, tags? }) => Promise<T>
required
Invoke another workflow as a child run and wait for its result.
(id, { name, app?, data? }) => Promise<void>
required
Emit an event from inside a run.
<A>(id, { tool, args?, risk?, summary?, policy?, context?, allow?, escalatesTo?, timeout?, onTimeout? }) => Promise<ApprovalResult<A>>
required
Park the run on a human decision and resolve to it on resume. onTimeout says what the timeout does: escalate (default), approve, reject, or fail.
run
The unit of durable work:fn executes once and its result is memoized under id. The three-argument
overload also records an explicit input, which the run inspector shows on the step’s Input tab and
hands to fn.
sleep and sleepUntil
The run is suspended, not blocked: it holds no worker while it waits, and it survives a restart.waitForEvent
Suspends until an event with the given name arrives, returning its data - ornull when timeout
elapses first, which is how you branch on the timeout.
poll
Re-checks an external resource until it is ready, sleeping durably between checks. Theprobe reads
the resource and returns its value once ready, or a “not ready yet” signal otherwise; a not-ready check
is a normal successful read, so it never spends the step’s retry budget. Returns the ready value, or
throws PollTimeoutError when timeout elapses first.
string | number
required
Delay between checks - a duration string like “5s” or a number of milliseconds.
string | number
required
Overall deadline for the whole wait. Once it passes, poll throws PollTimeoutError.
(value: T) => boolean
Readiness predicate. When omitted, a non-null probe value is treated as ready.
number
Safety cap on the number of checks.
PollTimeoutError fails the run and routes to onFailure; wrap the call in try/catch to treat a
missed deadline as a non-fatal branch instead. See Poll until ready.
Each check costs two durable steps (a step.run probe plus a step.sleep gap), not a free
suspension - every: "5s" over timeout: "10m" is up to 120 checks, 240 durable steps. Pick the
widest every the resource’s provisioning time tolerates.
runWorkflow
Invokes another workflow as a linked child run and waits for its result. Omitapp to resolve the
name in the caller’s app first, then any app in the project; set runner to pin the child to a
specific runner. Pass tags to attach run tags to the child - it also inherits
the parent run’s tags, with the child’s value winning on a shared key.
emit
Emits an event from inside a run, which fans out to whatever triggers match it. Omitapp to
broadcast project-wide; set it to narrow the event to one app’s triggers.
approval
Parks the run on a human decision. The run suspends inneeds_attention - checkpoint kept, no worker
held - until it is approved or denied, then resolves. args on the result are the effective
arguments: the decider’s edits when they changed them, otherwise the proposed ones. A timeout sets
a deadline and onTimeout says what reaching it does - by default it escalates and keeps waiting.
See Approvals.