Back to skills library
Jake's pick

Vercel Workflow

Build durable workflows with Vercel Workflow DevKit using "use workflow" and "use step" directives. Use for long-running tasks, background jobs, AI agents, webhooks, scheduled tasks, retries, and workflow orchestration. Supports Next.js, Vite, Astro, Express, Fastify, Hono, Nitro, Nuxt, SvelteKit.

Skill: vercel-workflowUse case: Web developmentSource: jakerains/AgentSkillsAdded: Jun 29, 2026

Install

npx skills add jakerains/AgentSkills --skill vercel-workflow

Tags

vercelworkflow

Install Options

Project installnpx skills add jakerains/AgentSkills --skill vercel-workflow
Global installnpx skills add jakerains/AgentSkills --skill vercel-workflow -g
Use oncenpx skills use jakerains/AgentSkills --skill vercel-workflow

Works With

codexclaude-codecursoropencode

Source

Skill Preview

# Vercel Workflow DevKit

Build durable, resumable workflows that survive restarts, deployments, and failures using TypeScript directives.

## Quick Start (Next.js)

```bash
npm i workflow
```

```typescript
// next.config.ts
import { withWorkflow } from "workflow/next";
export default withWorkflow({});
```

```typescript
// workflows/signup.ts
import { sleep, FatalError } from "workflow";

export async function signupWorkflow(email: string) {
  "use workflow";

  const user = await createUser(email);
  await sendWelcome(user.id, email);
  await sleep("3d");
  await sendFollowUp(user.id);
  return { success: true };
}

async function createUser(email: string) {
  "use step";
  return { id: crypto.randomUUID(), email };
}

async function sendWelcome(userId: string, email: string) {
  "use step";
  const res = await fetch("https://api.email.com/send", {
    method: "POST",
    body: JSON.stringify({ to: email, template: "welcome" })
  });
  if (!res.ok) throw new Error("Failed"); // Auto-retried
}

async function sendFollowUp(userId: string) {
  "use step";
  // FatalError = no retry
  if (!userId) throw new FatalError("Missing userId");
  // ... send email
}
```

```typescript
// app/api/signup/route.ts
import { start } from "workflow/api";
import { signupWorkflow } from "@/workflows/signup";

export async function POST(request: Request) {
  const { email } = await request.json();
  const run = await start(signupWorkflow, [email]);
  return Response.json({ runId: run.id });
}
```

## Core Concepts

### The Two Directives

| Directive | Purpose | Rules |
|-----------|---------|-------|
| `"use workflow"` | Orchestrates steps, sleeps, suspends | **Deterministic**: No side effects, no Node.js modules, no global fetch |
| `"use step"` | Contains business logic, I/O, side effects | **Runs on separate request**: Auto-retries on failure |

### Why These Restrictions?

Workflow DevKit uses **event sourcing** - state changes are stored as events and replayed to reconstruct state. Workflows must be deterministic so replays produce identical step sequences. Steps run on separate requests, so data passes by value (mutations don't affect workflow variables).

### Data Flow: Pass-by-Value

```typescript
// WRONG - mutations lost
async function workflow() {
  "use workflow";
  const user = { name: "Alice" };
  await updateUser(user);
  console.log(user.name); // Still "Alice"!
}

// CORRECT - return modified data
async function workflow() {
  "use workflow";
  let user = { name: "Alice" };
  user = await updateUser(user);
  console.log(user.name); // "Bob"
}

async function updateUser(user: User) {
  "use step";
  user.name = "Bob";
  return user; // Must return!
}
```

## API Quick Reference

### workflow package

```typescript
import {
  sleep,              // Suspend for duration/until date
  fetch,              // HTTP with auto-retry (use in workflows)
  FatalError,         // Non-retryable error
  RetryableError,     // Explicit retry with delay
  createHook,         // Receive external payloads
  createWebhook,      // Receive HTTP requests
  defineHook,         // Type-safe hooks with validation
  getWritable,        // Stream output
  getWorkflowMetadata,// { workflowRunId, workflowStartedAt, url }
  getStepMetadata     // { stepId } - use for idempotency keys
} from "workflow";
```

### workflow/api package

```typescript
import {
  start,          // Start workflow run
  getRun,         // Get run status
  resumeHook,     // Resume via hook token
  resumeWebhook,  // Resume via webhook token
  getHookByToken  // Get hook details
} from "workflow/api";
```

## Sleep & Scheduling

```typescript
await sleep("10s");   // seconds
await sleep("5m");    // minutes
await sleep("2h");    // hours
await sleep("1d");    // days
await sleep("2w");    // weeks
await sleep(5000);    // milliseconds
await sleep(new Date("2025-12-25")); // until date
```

## Error Handling

```typescript
import { FatalError, RetryableError } from "workflow";

asyn
    Vercel Workflow | Skills Library | Jake Rains