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.
Install via CLI
openskills install diegosouzapw/awesome-omni-skill---
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
```bash
npm i workflow
# or: pnpm add workflow | yarn add workflow | bun add workflow
```
## Core Concepts
### Directives
```typescript
"use workflow"; // First line - makes async function durable
"use step"; // First line - makes function a cached, retryable unit
```
### Workflow Function
Orchestrates steps, survives restarts, replays deterministically:
```typescript
import { sleep } from "workflow";
export async function onboardUser(email: string) {
"use workflow";
const user = await createUser(email); // Step 1
await sendWelcomeEmail(user); // Step 2
await sleep("3 days"); // Pause (no resources consumed)
await sendFollowupEmail(user); // Step 3
return { userId: user.id };
}
```
### Step Function
Has full Node.js access, auto-retries on failure:
```typescript
async function createUser(email: string) {
"use step";
return await db.users.create({ email });
}
```
## Essential APIs
### sleep - Pause Workflow
```typescript
import { sleep } from "workflow";
await sleep("5s"); // 5 seconds
await sleep("10m"); // 10 minutes
await sleep("1h"); // 1 hour
await sleep("7 days"); // 7 days
await sleep(5000); // milliseconds
await sleep(new Date("2025-01-01")); // specific date
```
### createHook - Wait for External Input
```typescript
import { createHook } from "workflow";
const hook = createHook<{ approved: boolean }>();
console.log("Token:", hook.token); // Send to external system
const result = await hook; // Pauses until resumed
```
Resume externally:
```typescript
import { resumeHook } from "workflow/api";
await resumeHook(token, { approved: true });
```
### createWebhook - HTTP Callback Endpoint
```typescript
import { createWebhook } from "workflow";
const webhook = createWebhook();
await sendToExternalService(webhook.url); // Auto-generated URL
const request = await webhook; // Pauses until HTTP POST
const data = await request.json();
```
### start - Trigger Workflow
```typescript
import { start } from "workflow/api";
const run = await start(onboardUser, ["user@example.com"]);
console.log(run.runId); // Returns immediately
```
### Error Handling
```typescript
import { FatalError, RetryableError } from "workflow";
// Stop retries permanently
throw new FatalError("User not found");
// Retry with delay
throw new RetryableError("Rate limited", { retryAfter: "5m" });
```
## Fetch in Workflows
Workflows run sandboxed. Import fetch from workflow:
```typescript
import { fetch } from "workflow";
export async function myWorkflow() {
"use workflow";
globalThis.fetch = fetch; // Enable for libraries (AI SDK)
const response = await fetch("https://api.example.com/data");
}
```
## Streaming (Steps Only)
```typescript
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:
```typescript
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](references/frameworks.md) for Next.js, Express, Hono, Vite, Astro, Fastify, Nitro, Nuxt, SvelteKit
- **AI Agents:** See [ai-agents.md](references/ai-agents.md) for DurableAgent and streaming patterns
- **Advanced Patterns:** See [patterns.md](references/patterns.md) for hooks, webhooks, control flow
- **Common Errors:** See [errors.md](references/errors.md) for troubleshooting
- **API Reference:** See [api-reference.md](references/api-reference.md) for complete API
## Quick Checklist
- [ ] Add `"use workflow"` as first line of workflow function
- [ ] Add `"use step"` as first line of step functions
- [ ] Import `fetch` from `workflow` and assign to `globalThis.fetch`
- [ ] Use `sleep()` instead of setTimeout
- [ ] Move Node.js module usage to step functions
- [ ] Use `FatalError` for permanent failures, `RetryableError` for retries
- [ ] Configure framework (withWorkflow, nitro module, etc.)
Scanned 5/31/2026
No comments yet. Be the first to comment!