Workflow steps and waits
Sleep and wait without holding compute
Pause for time or an external event while preserving workflow state and avoiding a long-running request.
8 minute lesson
A Workflow can pause for a day, a month, or until a human clicks a button, and it costs nothing while paused. Workflows can sleep until a duration or time and can wait for external events. The workflow does not need a Worker request or JavaScript timer to remain alive — the engine persists its position and resumes it when the wait ends.
Time-based waits are one line:
await step.sleep('wait for trial to end', '14 days')
await step.sleepUntil('send on launch morning', new Date('2026-09-01T08:00:00Z'))
Compare that with what it replaces: a cron job scanning a table of pending trials, state flags, and edge cases around restarts. Here the “what happens next” logic reads top to bottom in one function.
Event-based waits pause until something outside delivers a matching event:
const approval = await step.waitForEvent('wait for manager approval', {
type: 'approval',
timeout: '3 days',
})
The other side sends the event through the instance handle:
const instance = await env.REVIEW_WORKFLOW.get(reviewId)
await instance.sendEvent({ type: 'approval', payload: { approvedBy: userId } })
The type must match the waitForEvent call, and the sender needs the instance ID. That’s why you give instances stable IDs — create the workflow with id: reviewId and the approval endpoint can always address the right instance. An authenticated webhook or application action can then deliver the correct event without any lookup table.
Events are input — treat them as untrusted
The event payload arrives from your endpoint, and your endpoint is reachable. Validate event type, tenant, state, and replay behavior before acting: is this approver allowed to approve this tenant’s review, is the workflow actually in a waiting state, and has this approval already been consumed? A duplicate approval event should be recognized and ignored, not processed twice.
Define timeouts and the path for an event that never arrives. waitForEvent throws when its timeout expires — the default is 24 hours — so catch it and decide: escalate, auto-reject, or remind. A workflow with no timeout plan doesn’t fail, it just waits, invisibly, forever.
Build a review workflow that waits for approval, times out after a practice interval, and rejects duplicate approval events. Use a short timeout like two minutes so you can watch all three paths — approved, timed out, duplicate — actually run.
Lesson completed