docs/install/architecture/waitpoints.mdx
A waitpoint is the durable row that represents a paused step on a flow run. The flow run row only carries status (PAUSED, RUNNING, …); the why lives on the waitpoint.
stateDiagram-v2
[*] --> PENDING: createWaitpoint
PENDING --> COMPLETED: resume signal
COMPLETED --> [*]: flow resumes
[*] --> COMPLETED: pre-completed (resume arrived first)
| Field | Meaning |
|---|---|
flowRunId, stepName | Which run/step is paused. Unique together: a step has at most one waitpoint. |
type | DELAY or WEBHOOK. |
status | PENDING until the resume signal arrives, then COMPLETED. |
resumeDateTime | For DELAY: when to fire the resume. |
responseToSend | For WEBHOOK: optional HTTP response returned immediately to the original webhook trigger. |
resumePayload | { body, headers, queryParams } from the resume call, surfaced to the piece as ctx.resumePayload. |
DELAY: resumes at resumeDateTime. The server schedules a one-time job for that timestamp. The delay is bounded by a configurable server-side maximum.WEBHOOK: resumes on any HTTP call to the waitpoint's resume URL. If responseToSend is set, it is replied immediately to the original trigger so a single webhook can respond-then-pause.ctx.run.createWaitpoint({ type, ... }) + ctx.run.waitForWaitpoint(id). The engine marks the step as paused and asks the server to insert a PENDING row. The insert is idempotent on the (flow run, step) pair.PAUSED.{ body, headers, queryParams }.ctx.executionType === ExecutionType.RESUME and ctx.resumePayload populated.sequenceDiagram
participant Piece
participant Engine
participant Server
participant Queue as Job queue
participant Caller as Resume caller
Piece->>Engine: createWaitpoint + waitForWaitpoint
Engine->>Server: register waitpoint
Server-->>Engine: { id, resumeUrl }
Engine->>Engine: checkpoint context, status = PAUSED
Note over Piece,Server: ...time passes...
Caller->>Server: HTTP call on resumeUrl
Server->>Queue: enqueue resume job
Queue->>Engine: resume
Engine->>Piece: re-invoke with resumePayload
A callback can arrive before the flow run has finished writing PAUSED to disk. The protocol absorbs the race:
PENDING row. If it exists, it flips to COMPLETED and stores the resumePayload. If it does not, a pre-completed row is inserted instead.PAUSED, the server checks for a matching COMPLETED waitpoint and enqueues the resume job immediately.Duplicate callbacks are absorbed by the uniqueness constraint; the engine never processes a resume twice.
| Method | Path | Notes |
|---|---|---|
POST | /v1/waitpoints | Engine-only. Creates a PENDING waitpoint, returns its resume URL. |
ALL | /v1/flow-runs/:id/waitpoints/:waitpointId | Resume, async. |
ALL | /v1/flow-runs/:id/waitpoints/:waitpointId/sync | Resume, sync. The HTTP response is whatever the flow produces after resuming. |
Piece authors create waitpoints with ctx.run.createWaitpoint / ctx.run.waitForWaitpoint. Patterns for WEBHOOK, DELAY, and responseToSend are in Flow Control.