> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/vercel/workflow/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart Guide

> Get started with Workflow DevKit in under 5 minutes. Build your first durable workflow with automatic retries and state management.

# Quickstart Guide

This guide will walk you through creating your first durable workflow with Workflow DevKit. You'll learn how to:

* Install and configure Workflow DevKit
* Write a simple workflow with steps
* Handle errors and retries
* Start and monitor workflow runs

<Note>
  This guide uses **Next.js** as an example, but Workflow DevKit works with SvelteKit, Nuxt, Astro, and other frameworks. See [Framework Guides](/frameworks) for other options.
</Note>

## Installation

<Steps>
  <Step title="Install the package">
    Install Workflow DevKit in your project:

    <CodeGroup>
      ```bash npm theme={null}
      npm install workflow
      ```

      ```bash pnpm theme={null}
      pnpm add workflow
      ```

      ```bash yarn theme={null}
      yarn add workflow
      ```
    </CodeGroup>
  </Step>

  <Step title="Configure your framework">
    Add the Workflow plugin to your framework configuration.

    <Tabs>
      <Tab title="Next.js">
        Update your `next.config.ts` (or `next.config.js`):

        ```typescript next.config.ts theme={null}
        import type { NextConfig } from 'next';
        import { withWorkflow } from 'workflow/next';

        const nextConfig: NextConfig = {
          // Your existing Next.js config
        };

        export default withWorkflow(nextConfig);
        ```
      </Tab>

      <Tab title="SvelteKit">
        Update your `svelte.config.js`:

        ```javascript svelte.config.js theme={null}
        import adapter from '@sveltejs/adapter-auto';
        import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
        import { sveltekit } from 'workflow/sveltekit';

        /** @type {import('@sveltejs/kit').Config} */
        const config = {
          preprocess: vitePreprocess(),
          kit: {
            adapter: adapter(),
          },
          // Add Workflow plugin
          plugins: [sveltekit()]
        };

        export default config;
        ```
      </Tab>

      <Tab title="Nuxt">
        Add to your `nuxt.config.ts`:

        ```typescript nuxt.config.ts theme={null}
        export default defineNuxtConfig({
          modules: ['workflow/nuxt'],
        });
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="Initialize the runtime (Next.js only)">
    For Next.js, create an `instrumentation.ts` file in your project root:

    ```typescript instrumentation.ts theme={null}
    export async function register() {
      if (process.env.NEXT_RUNTIME === 'nodejs') {
        const { getWorld } = await import('workflow/runtime');
        await getWorld().start?.();
      }
    }
    ```

    <Note>
      Make sure `instrumentationHook` is enabled in your `next.config.ts`:

      ```typescript theme={null}
      const nextConfig: NextConfig = {
        experimental: {
          instrumentationHook: true,
        },
      };
      ```
    </Note>
  </Step>
</Steps>

## Create Your First Workflow

Let's build a simple user signup workflow that creates a user, sends a welcome email, and then sends a follow-up email after a delay.

<Steps>
  <Step title="Create a workflow file">
    Create a new file `workflows/user-signup.ts`:

    ```typescript workflows/user-signup.ts theme={null}
    import { sleep } from 'workflow';

    export async function handleUserSignup(email: string) {
      'use workflow';

      // Create the user account
      const user = await createUser(email);
      console.log('User created:', user.id);

      // Send welcome email
      await sendWelcomeEmail(user);
      console.log('Welcome email sent');

      // Wait 5 seconds before follow-up
      await sleep('5s');

      // Send follow-up email
      await sendFollowUpEmail(user);
      console.log('Follow-up email sent');

      return { userId: user.id, status: 'onboarded' };
    }

    async function createUser(email: string) {
      'use step';

      // Simulate API call that might fail
      if (Math.random() < 0.3) {
        throw new Error('Database temporarily unavailable');
      }

      console.log(`Creating user: ${email}`);
      return {
        id: crypto.randomUUID(),
        email,
        createdAt: new Date().toISOString(),
      };
    }

    async function sendWelcomeEmail(user: { id: string; email: string }) {
      'use step';

      console.log(`Sending welcome email to: ${user.email}`);

      // Simulate email service API call
      await new Promise(resolve => setTimeout(resolve, 100));
    }

    async function sendFollowUpEmail(user: { id: string; email: string }) {
      'use step';

      console.log(`Sending follow-up email to: ${user.email}`);

      // Simulate email service API call
      await new Promise(resolve => setTimeout(resolve, 100));
    }
    ```

    <Note>
      **Key concepts:**

      * `'use workflow'` marks the main workflow function
      * `'use step'` marks individual steps that are automatically retried
      * Each step's result is persisted before moving to the next step
      * `sleep()` pauses the workflow for a specified duration
    </Note>
  </Step>

  <Step title="Create an API route to start workflows">
    Create an API route to trigger your workflow:

    <Tabs>
      <Tab title="Next.js App Router">
        Create `app/api/signup/route.ts`:

        ```typescript app/api/signup/route.ts theme={null}
        import { NextRequest, NextResponse } from 'next/server';
        import { start } from 'workflow/api';
        import { handleUserSignup } from '@/workflows/user-signup';

        export async function POST(request: NextRequest) {
          const { email } = await request.json();

          if (!email) {
            return NextResponse.json(
              { error: 'Email is required' },
              { status: 400 }
            );
          }

          // Start the workflow
          const run = await start(handleUserSignup, [email]);

          return NextResponse.json({
            runId: run.runId,
            message: 'Signup workflow started',
          });
        }
        ```
      </Tab>

      <Tab title="Next.js Pages Router">
        Create `pages/api/signup.ts`:

        ```typescript pages/api/signup.ts theme={null}
        import type { NextApiRequest, NextApiResponse } from 'next';
        import { start } from 'workflow/api';
        import { handleUserSignup } from '@/workflows/user-signup';

        export default async function handler(
          req: NextApiRequest,
          res: NextApiResponse
        ) {
          if (req.method !== 'POST') {
            return res.status(405).json({ error: 'Method not allowed' });
          }

          const { email } = req.body;

          if (!email) {
            return res.status(400).json({ error: 'Email is required' });
          }

          const run = await start(handleUserSignup, [email]);

          return res.json({
            runId: run.runId,
            message: 'Signup workflow started',
          });
        }
        ```
      </Tab>

      <Tab title="SvelteKit">
        Create `src/routes/api/signup/+server.ts`:

        ```typescript src/routes/api/signup/+server.ts theme={null}
        import { json } from '@sveltejs/kit';
        import { start } from 'workflow/api';
        import { handleUserSignup } from '$lib/workflows/user-signup';
        import type { RequestHandler } from './$types';

        export const POST: RequestHandler = async ({ request }) => {
          const { email } = await request.json();

          if (!email) {
            return json({ error: 'Email is required' }, { status: 400 });
          }

          const run = await start(handleUserSignup, [email]);

          return json({
            runId: run.runId,
            message: 'Signup workflow started',
          });
        };
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="Start your development server">
    Run your development server:

    <CodeGroup>
      ```bash npm theme={null}
      npm run dev
      ```

      ```bash pnpm theme={null}
      pnpm dev
      ```

      ```bash yarn theme={null}
      yarn dev
      ```
    </CodeGroup>
  </Step>

  <Step title="Test your workflow">
    Trigger your workflow with a POST request:

    ```bash cURL theme={null}
    curl -X POST http://localhost:3000/api/signup \
      -H "Content-Type: application/json" \
      -d '{"email":"user@example.com"}'
    ```

    You should receive a response like:

    ```json theme={null}
    {
      "runId": "run_abc123xyz",
      "message": "Signup workflow started"
    }
    ```

    Check your server logs to see the workflow executing:

    ```
    Creating user: user@example.com
    User created: 550e8400-e29b-41d4-a716-446655440000
    Welcome email sent
    Sending welcome email to: user@example.com
    Sending follow-up email to: user@example.com
    Follow-up email sent
    ```
  </Step>
</Steps>

## Understanding Steps and Workflows

### Workflows vs Steps

* **Workflows** (`'use workflow'`) are the main orchestration functions
* **Steps** (`'use step'`) are individual units of work that can fail and retry

```typescript theme={null}
export async function processOrder(orderId: string) {
  'use workflow';  // Main workflow - orchestrates steps

  const order = await fetchOrder(orderId);     // Step 1
  const payment = await processPayment(order); // Step 2
  await fulfillOrder(order);                   // Step 3
  await sendConfirmation(order);               // Step 4

  return { orderId, status: 'complete' };
}

async function fetchOrder(orderId: string) {
  'use step';  // This step will retry if it fails
  const response = await fetch(`/api/orders/${orderId}`);
  return response.json();
}
```

### Automatic Retries

Steps automatically retry when they throw an error. By default, retries happen immediately, but you can customize this:

```typescript theme={null}
import { RetryableError, FatalError } from 'workflow';

async function chargeCustomer(amount: number) {
  'use step';

  try {
    const result = await stripe.charges.create({ amount });
    return result;
  } catch (error) {
    if (error.type === 'card_error') {
      // Don't retry - this is a permanent error
      throw new FatalError('Invalid card: ' + error.message);
    }

    // Retry after 30 seconds for network errors
    throw new RetryableError('Stripe API unavailable', {
      retryAfter: '30s'
    });
  }
}
```

<Note>
  **Error Types:**

  * Regular `Error`: Retried immediately (default behavior)
  * `RetryableError`: Retried after a specified delay
  * `FatalError`: Not retried - workflow fails permanently
</Note>

## Monitoring Workflow Runs

You can check the status and result of a workflow run:

```typescript app/api/runs/[runId]/route.ts theme={null}
import { NextRequest, NextResponse } from 'next/server';
import { getRun } from 'workflow/api';

export async function GET(
  request: NextRequest,
  { params }: { params: { runId: string } }
) {
  const run = await getRun(params.runId);

  if (!run) {
    return NextResponse.json(
      { error: 'Run not found' },
      { status: 404 }
    );
  }

  // Wait for the workflow to complete (if still running)
  const result = await run.returnValue;

  return NextResponse.json({
    runId: run.runId,
    status: run.status,
    result,
  });
}
```

## Using `sleep()` for Delays

The `sleep()` function pauses workflow execution without blocking your server:

```typescript theme={null}
import { sleep } from 'workflow';

export async function scheduleReminder(taskId: string) {
  'use workflow';

  await createTask(taskId);

  // Wait 1 day
  await sleep('24h');
  await sendReminder(taskId);

  // Wait 7 days
  await sleep('7d');
  await sendFinalReminder(taskId);
}
```

<Note>
  **Supported duration formats:**

  * Milliseconds: `sleep(5000)` or `sleep('5000ms')`
  * Seconds: `sleep('30s')`
  * Minutes: `sleep('5m')`
  * Hours: `sleep('2h')`
  * Days: `sleep('7d')`
  * Date objects: `sleep(new Date('2026-12-31'))`
</Note>

## Next Steps

Now that you've built your first workflow, explore more advanced features:

<CardGroup cols={2}>
  <Card title="Webhooks & Hooks" icon="webhook" href="/features/webhooks">
    Pause workflows and resume them via external callbacks
  </Card>

  <Card title="AI Workflows" icon="brain" href="/examples/ai-agent">
    Build durable AI agents with tool calling
  </Card>

  <Card title="Error Handling" icon="shield" href="/features/error-handling">
    Advanced retry strategies and error recovery
  </Card>

  <Card title="Streaming" icon="stream" href="/features/streaming">
    Stream real-time updates from long-running workflows
  </Card>
</CardGroup>

## Common Patterns

### Pattern 1: Multi-Step API Orchestration

```typescript theme={null}
export async function syncUserData(userId: string) {
  'use workflow';

  // Each external API call is a step
  const user = await fetchFromCRM(userId);
  const profile = await fetchFromAuth0(userId);
  const analytics = await fetchFromMixpanel(userId);

  // Combine and save
  await saveToDatabase({
    ...user,
    ...profile,
    analytics,
  });
}
```

### Pattern 2: Human-in-the-Loop

```typescript theme={null}
import { createWebhook } from 'workflow';

export async function contentApproval(contentId: string) {
  'use workflow';

  const content = await fetchContent(contentId);

  // Create webhook for approval
  const webhook = createWebhook();
  await sendApprovalRequest(content, webhook.url);

  // Wait for approval (webhook will be called)
  const approval = await webhook;

  if (approval.approved) {
    await publishContent(contentId);
  }
}
```

### Pattern 3: Scheduled Tasks

```typescript theme={null}
import { sleep } from 'workflow';

export async function trialExpiration(userId: string) {
  'use workflow';

  await sendTrialStartEmail(userId);

  // Wait 7 days
  await sleep('7d');
  await sendTrialEndingEmail(userId);

  // Wait 1 more day
  await sleep('1d');
  await sendTrialExpiredEmail(userId);
  await downgradeAccount(userId);
}
```

## Troubleshooting

<AccordionGroup>
  <Accordion title="Workflow not starting">
    Make sure you've:

    1. Added the framework plugin to your config
    2. Created `instrumentation.ts` (Next.js only)
    3. Enabled `instrumentationHook` in next.config (Next.js only)
    4. Restarted your development server
  </Accordion>

  <Accordion title="Steps not retrying">
    Steps only retry when they throw an error. Check that:

    * Your step function has `'use step'` directive
    * The function is actually throwing an error (not returning it)
    * You're not catching and suppressing errors
  </Accordion>

  <Accordion title="Module not found errors">
    If you see "Cannot find module 'workflow'":

    1. Make sure you've installed the package: `npm install workflow`
    2. Restart your TypeScript server in your editor
    3. Clear your framework's build cache and restart
  </Accordion>
</AccordionGroup>

<Card title="Need Help?" icon="question" href="https://github.com/vercel/workflow/discussions">
  Join the discussion on GitHub for community support
</Card>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.