---
title: Astro
description: Set up your first durable workflow in an Astro application.
docs_index: /llms.txt
lastUpdated: 2026-10-13
type: guide
summary: Set up Workflow SDK in an Astro app.
prerequisites:
  - /docs/getting-started
related:
  - /docs/foundations/workflows-and-steps
---

> For an index of all documentation, see [/llms.txt](/llms.txt).

**Prompt for coding agents**

In this Astro app, run \`npm i workflow\`. In \`astro.config.mjs\`, import \`workflow\` from \`workflow/astro\` and add \`integrations: \[workflow()]\`. Add the TypeScript plugin \`\{ "name": "workflow" }\` to \`tsconfig.json\` if TypeScript is used. Create \`src/workflows/user-signup.ts\` exporting \`handleUserSignup(email)\` with \`"use workflow"\`, \`sleep\` from \`workflow\`, and \`"use step"\` helpers. Add \`src/pages/api/signup.ts\` exporting \`POST: APIRoute\` that reads \`\{ email }\`, calls \`start(handleUserSignup, \[email])\` from \`workflow/api\`, returns \`Response.json\`, and sets \`prerender = false\`. Run \`npm run dev\`, call \`curl -X POST --json '\{"email":"hello\@example.com"}' http\://localhost:4321/api/signup\`, and inspect with \`npx workflow inspect runs\`.

Set up your first durable workflow in an Astro app and learn the core Workflow SDK concepts.

***

## 1. Create your Astro project

Create an Astro project in a new directory named `my-workflow-app`:

```bash
npm create astro@latest my-workflow-app -- --template minimal --install --yes
```

Enter the newly made directory:

```bash
cd my-workflow-app
```

### Install `workflow`

```bash
npm i workflow
```

```bash
pnpm add workflow
```

```bash
yarn add workflow
```

```bash
bun add workflow
```

### Configure Astro

Add `workflow()` to your Astro config. This enables usage of the `"use workflow"` and `"use step"` directives.

```typescript title="astro.config.mjs" lineNumbers
// @ts-check
import { defineConfig } from "astro/config";
import { workflow } from "workflow/astro";

// https://astro.build/config
export default defineConfig({
  integrations: [workflow()],
});
```

`workflow()` accepts an options object:

| Option      | Type                                                      | Default                           | Description                                                                                                                                                                                                                                                                                                                                                                        |
| ----------- | --------------------------------------------------------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `sourcemap` | `boolean \| 'inline' \| 'linked' \| 'external' \| 'both'` | `'inline'` (dev) / `false` (prod) | Controls source maps on generated workflow bundles. Accepts the same values as esbuild's `sourcemap` option. Defaults to `'inline'` in development and `false` in production (smaller function bundles, which helps stay under the Vercel 250 MB function size limit). Set it explicitly, or use the `WORKFLOW_SOURCEMAP` environment variable, to override in either environment. |

### Set up IntelliSense for TypeScript (optional)

To enable helpful hints in your IDE, set up the workflow plugin in `tsconfig.json`:

```json title="tsconfig.json" lineNumbers
{
  "compilerOptions": {
    // ... rest of your TypeScript config
    "plugins": [
      {
        "name": "workflow" // [!code highlight]
      }
    ]
  }
}
```

## 2. Create your first workflow

Create a new file for our first workflow:

```typescript title="src/workflows/user-signup.ts" lineNumbers
import { sleep } from "workflow";

export async function handleUserSignup(email: string) {
  "use workflow"; // [!code highlight]

  const user = await createUser(email);
  await sendWelcomeEmail(user);

  await sleep("5s"); // Pause for 5s - doesn't consume any resources
  await sendOnboardingEmail(user);

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

```

We'll fill in those functions next, but first review this code:

- We define a **workflow** function with the directive `"use workflow"`. Think of the workflow function as the *orchestrator* of individual **steps**.
- The Workflow SDK's `sleep` function allows us to suspend execution of the workflow without using up any resources. A sleep can be a few seconds, hours, days, or even months long.

## Create your workflow steps

Define the missing functions.

```typescript title="src/workflows/user-signup.ts" lineNumbers
import { FatalError } from "workflow"

// Our workflow function defined earlier

async function createUser(email: string) {
  "use step"; // [!code highlight]

  console.log(`Creating user with email: ${email}`);

  // Full Node.js access - database calls, APIs, etc.
  return { id: crypto.randomUUID(), email };
}

async function sendWelcomeEmail(user: { id: string; email: string; }) {
  "use step"; // [!code highlight]

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

  if (Math.random() < 0.3) {
  // By default, steps will be retried for unhandled errors
   throw new Error("Retryable!");
  }
}

async function sendOnboardingEmail(user: { id: string; email: string}) {
  "use step"; // [!code highlight]

  if (!user.email.includes("@")) {
    // To skip retrying, throw a FatalError instead
    throw new FatalError("Invalid Email");
  }

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

Taking a look at this code:

- Business logic lives inside **steps**. When a workflow invokes a step, the workflow suspends while the step runs with full Node.js access. The combined handler may execute it inline; queued retries and continuations return through the same flow route.
- If a step throws an error, like in `sendWelcomeEmail`, the step will automatically be retried until it succeeds (or hits the step's max retry count).
- Steps can throw a `FatalError` if an error is intentional and should not be retried.

> We'll dive deeper into workflows, steps, and other ways to suspend or handle events in [Foundations](/docs/foundations).

## 3. Create your route handler

To invoke your new workflow, we'll have to add your workflow to a `POST` API route handler, `src/pages/api/signup.ts` with the following code:

```typescript title="src/pages/api/signup.ts"
import type { APIRoute } from "astro";
import { start } from "workflow/api";
import { handleUserSignup } from "../../workflows/user-signup";

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

  // Executes asynchronously and doesn't block your app
  await start(handleUserSignup, [email]);
  return Response.json({
    message: "User signup workflow started",
  });
};

export const prerender = false; // Don't prerender this page since it's an API route
```

This route handler creates a `POST` request endpoint at `/api/signup` that will trigger your workflow.

> Workflows can be triggered from API routes or any server-side code.

## Run in development

To start your development server, run the following command in your terminal in the Vite root directory:

```bash
npm run dev
```

Once your development server is running, you can trigger your workflow by running this command in the terminal:

```bash
curl -X POST --json '{"email":"hello@example.com"}' http://localhost:4321/api/signup
```

Check the Astro development server logs to see your workflow execute as well as the steps that are being processed.

Additionally, you can use the [Workflow SDK CLI or Web UI](/docs/observability) to inspect your workflow runs and steps in detail.

```bash
npx workflow inspect runs
# or add '--web' for an interactive Web based UI
```

<img alt="Workflow SDK Web UI" src={__img0} placeholder="blur" />

***

## Deploying to production

Workflow SDK apps currently work best when deployed to [Vercel](https://vercel.com/home) and need no special configuration.

To deploy your Astro project to Vercel, ensure that the [Astro Vercel adapter](https://docs.astro.build/en/guides/integrations-guide/vercel) is configured:

```bash
npx astro add vercel
```

Additionally, check the [Deploying](/docs/deploying) section to learn how your workflows can be deployed elsewhere.

## Troubleshooting

### `start()` says it received an invalid workflow function

If you see this error:

```text
'start' received an invalid workflow function. Ensure the Workflow SDK is configured correctly and the function includes a 'use workflow' directive.
```

Check both of these first:

1. The workflow function includes `"use workflow"`.
2. Your `astro.config.mjs` includes the `workflow()` integration.

See [start-invalid-workflow-function](/docs/errors/start-invalid-workflow-function) for full examples and fixes.

## Next steps

- Learn more about the [Foundations](/docs/foundations).
- Check [Errors](/docs/errors) if you encounter issues.
- Explore the [API Reference](/docs/api-reference).

---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)