website/src/content/docs/actors/workflows.mdx
Use workflows for durable, multi-step execution with replay safety.
A workflow is a durable, replayable run handler for a Rivet Actor.
Use this when you need a short multi-step sequence.
<CodeGroup> <CodeSnippet file="examples/docs/actors-workflows/simple-workflow/index.ts" title="index.ts" /> <CodeSnippet file="examples/docs/actors-workflows/simple-workflow/client.ts" title="client.ts" /> </CodeGroup>This is the recommended workflow shape for most actor workloads.
Use this when the workflow should initialize resources, process queued commands, then clean up.
<CodeGroup> <CodeSnippet file="examples/docs/actors-workflows/setup-teardown/index.ts" title="index.ts" /> <CodeSnippet file="examples/docs/actors-workflows/setup-teardown/client.ts" title="client.ts" /> </CodeGroup>Use this for fire-and-forget commands where the client does not need a reply.
Use the Loops example above as the baseline pattern.
Use this when the caller needs a response from queued processing.
<CodeGroup> <CodeSnippet file="examples/docs/actors-workflows/request-response/index.ts" title="index.ts" /> <CodeSnippet file="examples/docs/actors-workflows/request-response/client.ts" title="client.ts" /> </CodeGroup>Use queue messages as the trigger source, then sleep durably inside the workflow.
<CodeGroup> <CodeSnippet file="examples/docs/actors-workflows/timers/index.ts" title="index.ts" /> <CodeSnippet file="examples/docs/actors-workflows/timers/client.ts" title="client.ts" /> </CodeGroup>Use join when several independent tasks can run in parallel.
Use race when you need first-winner behavior.
Use step timeouts and retries for slow or flaky dependencies.
Step timeouts are critical by default and fail immediately. Set retryOnTimeout: true if a timeout should retry like any other error using maxRetries.
Use tryStep when a step failure should produce data instead of failing the whole workflow.
Use try when you want to recover from terminal step, join, or race failures inside a named block.
async function runPaymentFlow(ctx: any) {
return await ctx.try("payment-flow", async (blockCtx: any) => {
const auth = await blockCtx.step("authorize", async (blockCtx) =>
authorizeOrder("order-123"),
);
const capture = await blockCtx.step("capture", async (blockCtx) =>
captureOrder("order-123"),
);
return { auth, capture };
});
}
async function authorizeOrder(orderId: string): Promise<string> {
return `auth-${orderId}`;
}
async function captureOrder(orderId: string): Promise<string> {
return `capture-${orderId}`;
}
tryStep and try only catch terminal failures. Retry backoff, sleeps, queue waits, eviction, and history divergence still rethrow.RollbackError is not caught by default. Pass catch: ["rollback"] when you want rollback failures returned as data.Use onError when you want a best-effort notification for workflow failures.
Use rollback checkpoints before steps that have compensating actions.
<CodeSnippet file="examples/docs/actors-workflows/rollback.ts" />Store progress in state so replay and recovery always restore it. Broadcast state changes so clients can render progress in realtime.
Rivet scheduling triggers actions. For cron-like workflows, use a small scheduled action as a bridge that enqueues work, then process that work in the workflow loop.
<CodeSnippet file="examples/docs/actors-workflows/cron.ts" />These are common workflow shapes used in production systems.
Use this when external systems enqueue work and the actor should process each item durably.
<CodeSnippet file="examples/docs/actors-workflows/queue-worker.ts" />Use this when you need one-time initialization before a long-lived loop, plus cleanup when the actor stops sleeping or is destroyed.
<CodeSnippet file="examples/docs/actors-workflows/setup-teardown-pattern.ts" />Use this when an operation must pause for a user or system decision before continuing.
<CodeSnippet file="examples/docs/actors-workflows/approval-gate.ts" />Use this when independent work items can run in parallel and you need a single merged result.
<CodeSnippet file="examples/docs/actors-workflows/fan-in-out.ts" />Use this when throughput matters and handling one message at a time is too expensive.
<CodeSnippet file="examples/docs/actors-workflows/batch-drainer.ts" />Use this when one actor orchestrates work by calling actions on other actors.
<CodeSnippet file="examples/docs/actors-workflows/coordinator-worker.ts" />Use this when you want decoupled actor-to-actor communication with durable waits and explicit completion.
<CodeGroup> <CodeSnippet file="examples/docs/actors-workflows/request-response-queue/index.ts" title="index.ts" /> <CodeSnippet file="examples/docs/actors-workflows/request-response-queue/client.ts" title="client.ts" /> </CodeGroup>Use this when multiple actors can process independent parts of a request in parallel, then return a merged response.
<CodeSnippet file="examples/docs/actors-workflows/scatter-gather.ts" />Use this when a primary actor call might be slow or unavailable and you need a deterministic fallback path.
<CodeSnippet file="examples/docs/actors-workflows/timeout-fallback.ts" />Use this when a workflow spans multiple actors and each side effect may need compensation.
<CodeSnippet file="examples/docs/actors-workflows/cross-actor-saga.ts" />Use this when workflow progress should be triggered by commands/events instead of fixed polling intervals.
<CodeSnippet file="examples/docs/actors-workflows/signal-control-loop.ts" />Use this when an external dependency has variable availability and retries should slow down after failures.
<CodeSnippet file="examples/docs/actors-workflows/poll-backoff.ts" />Use this when one workflow coordinates many child workers (actors or worker workflows) and manages their lifecycle.
<CodeSnippet file="examples/docs/actors-workflows/child-worker-orchestration.ts" />Use this when inbound work can spike and you need predictable per-iteration limits.
<CodeSnippet file="examples/docs/actors-workflows/bounded-drain.ts" />Use this when workflow structure changes across deployments and old histories must still replay.
<CodeSnippet file="examples/docs/actors-workflows/versioned-workflow.ts" />getVersionUse ctx.getVersion(name, latest) to branch behavior when you change a workflow's logic while old instances are still in flight. It returns the version this instance is pinned to at that point:
latest.1 (the implicit floor).The resolved version is recorded in history, so replays are deterministic and each instance stays on the branch it started on. Inside a loop, every iteration resolves independently, so in-flight iterations finish on the old branch while new iterations pick up latest.
latest must be an integer >= 1, and the gate name must be unique within its scope like any other entry. Once every old instance has drained, retire the gate by replacing the call with ctx.removed(name, "version_check").
Use this when you need reliable replay and resume semantics across crashes and restarts.
<CodeSnippet file="examples/docs/actors-workflows/checkpoint-friendly.ts" />ctx.removed(name, originalType).ctx.getVersion(name, latest) (see Version gates).state, vars, db, client(), and connection/event APIs are only valid inside ctx.step(...) callbacks.
Use non-step workflow code for orchestration only: queue waits, sleeps, loops, joins, races, and rollback boundaries. Keep actor-local side effects in steps.
GET /inspector/workflow-history returns workflow history status for an actor.isWorkflowEnabled and history.state and broadcast updates as progress changes.