| name | workflow |
| description | Creates durable, resumable workflows using Vercel's Workflow DevKit. Use when building workflows that need to survive restarts, pause for external events, retry on failure, or coordinate multi-step operations over time. Triggers on mentions of "workflow", "durable functions", "resumable", "workflow devkit", or step-based orchestration. |
Workflow DevKit
TypeScript framework for building durable, resumable functions. Workflows survive crashes, deployments, and can pause for days/months.
Website: https://useworkflow.dev | GitHub: https://github.com/vercel/workflow
Installation
npm i workflow
Core Concepts
Directives
"use workflow";
"use step";
Workflow Function
Orchestrates steps, survives restarts, replays deterministically:
import { sleep } from "workflow";
export async function onboardUser(email: string) {
"use workflow";
const user = await createUser(email);
await sendWelcomeEmail(user);
await sleep("3 days");
await sendFollowupEmail(user);
return { userId: user.id };
}
Step Function
Has full Node.js access, auto-retries on failure:
async function createUser(email: string) {
"use step";
return await db.users.create({ email });
}
Essential APIs
sleep - Pause Workflow
import { sleep } from "workflow";
await sleep("5s");
await sleep("10m");
await sleep("1h");
await sleep("7 days");
await sleep(5000);
await sleep(new Date("2025-01-01"));
createHook - Wait for External Input
import { createHook } from "workflow";
const hook = createHook<{ approved: boolean }>();
console.log("Token:", hook.token);
const result = await hook;
Resume externally:
import { resumeHook } from "workflow/api";
await resumeHook(token, { approved: true });
createWebhook - HTTP Callback Endpoint
import { createWebhook } from "workflow";
const webhook = createWebhook();
await sendToExternalService(webhook.url);
const request = await webhook;
const data = await request.json();
start - Trigger Workflow
import { start } from "workflow/api";
const run = await start(onboardUser, ["user@example.com"]);
console.log(run.runId);
Error Handling
import { FatalError, RetryableError } from "workflow";
throw new FatalError("User not found");
throw new RetryableError("Rate limited", { retryAfter: "5m" });
Fetch in Workflows
Workflows run sandboxed. Import fetch from workflow:
import { fetch } from "workflow";
export async function myWorkflow() {
"use workflow";
globalThis.fetch = fetch;
const response = await fetch("https://api.example.com/data");
}
Streaming (Steps Only)
import { getWritable } from "workflow";
async function streamData(items: string[]) {
"use step";
const writer = getWritable();
for (const item of items) {
await writer.write({ item });
}
writer.releaseLock();
}
Idempotency
Use stepId for external API calls:
import { getStepMetadata } from "workflow";
async function chargeCard(amount: number) {
"use step";
const { stepId } = getStepMetadata();
await stripe.charges.create(
{ amount },
{ idempotencyKey: `charge:${stepId}` }
);
}
What Cannot Be Serialized
- Functions/callbacks - define logic in steps
- Class instances with methods - use plain objects
- Symbols, WeakMap, WeakSet
- Node.js modules (fs, http, crypto) - use in steps only
Run Status
pending | running | completed | failed | cancelled
References
- Framework Setup: See frameworks.md for Next.js, Express, Hono, Vite, Astro, Fastify, Nitro, Nuxt, SvelteKit
- AI Agents: See ai-agents.md for DurableAgent and streaming patterns
- Advanced Patterns: See patterns.md for hooks, webhooks, control flow
- Common Errors: See errors.md for troubleshooting
- API Reference: See api-reference.md for complete API
Quick Checklist