Retry a flaky call, fail fast on a bad one
A step retries on its policy.NonRetriableError ends the run on the first attempt - retrying a
declined card cannot fix it - and RetryAfterError retries on a delay the upstream dictated.
onFailure runs durably after the run has exhausted its retries and failed. It receives the original
event plus ctx.error, and cannot un-fail the run.
Retries
Fan one event out to many workflows
Every workflow subscribed touser.signup gets its own run. A CEL if filter narrows a subscription
to the events that match it.
Pause for hours, then continue
step.sleep parks the run - it holds no process and no connection. step.waitForEvent parks it until
a matching event arrives, and resolves to null if the timeout matures first.
Run on a schedule
Acron trigger needs no event. singleton: { mode: "skip" } drops a tick that would overlap a run
still in flight.
{ cron, scheduledFor }.
Triggers
Shape a burst of events into runs
Flow control is declared on the workflow and applied before a run starts.debounce coalesces a burst
into one run carrying the last event’s data; batch accumulates events into one run delivered as
ctx.events; rateLimit drops what is over the cap.
concurrency, throttle, rateLimit, debounce, batch, priority,
singleton, idempotency - and what each does to an event over its cap:
Flow control
Call a child workflow, in parallel
step.runWorkflow starts another workflow as a child run and returns its output. Steps that do not
depend on each other run concurrently under Promise.all.
app addresses the child to that app exactly; omit it to resolve the name in the caller’s app
first.
Workflows
Make a model call durable
step.ai.generate is one model call as a step: it runs once, and a retry after a crash returns the
recorded result instead of paying the model again. The call happens in your runner, with your provider
key.
validate triggers a durable re-ask, itself a memoized step.
AI · step.ai reference
Park a run on a human decision
step.approval suspends the run at its checkpoint, holding no worker, until someone approves or
denies. The decision resumes the run from that checkpoint and is memoized, so a replay never re-parks.
decision.args are the effective ones.
Approvals
Log, then watch a run live
ctx.log writes structured, leveled lines onto the run. runs.watch streams the run’s timeline -
status transitions, step transitions, and log lines - and ends on its own when the run is terminal.
runId is absent when the event started no run - it was deduped, dropped, debounced, or batched by a
flow-control policy.
Logging · Realtime
Replay a finished run
A replay forks a new run from the original’s trigger and links it back throughreplayOf; it does
not mutate the original. retryFromStep carries the steps before the named one as memoized and
resumes there, so completed work is not re-executed.
Receive and send webhooks
ctx.webhook.send is a durable outbound delivery: retried on a backoff, with every attempt recorded
in the delivery log.
POST becomes an
event that starts a run.
Webhooks