Skip to content
Cloudflare Docs

Build your first Workflow

Workflows allow you to build durable, multi-step applications using the Workers platform. A Workflow can automatically retry, persist state, run for hours or days, and coordinate between third-party APIs.

You can build Workflows to post-process file uploads to R2 object storage, automate generation of Workers AI embeddings into a Vectorize vector database, or to trigger user lifecycle emails using Email Service.

In this guide, you will create and deploy a Workflow that fetches data, pauses, and processes results.

Quick start

If you want to skip the steps and pull down the complete Workflow we are building in this guide, run:

Terminal window
npm create cloudflare@latest workflows-starter -- --template "cloudflare/workflows-starter"

Use this option if you are familiar with Cloudflare Workers or want to explore the code first and learn the details later.

Follow the steps below to learn how to build a Workflow from scratch.

Prerequisites

  1. Sign up for a Cloudflare account.
  2. Install Node.js.

Node.js version manager

Use a Node version manager like Volta or nvm to avoid permission issues and change Node.js versions. Wrangler, discussed later in this guide, requires a Node version of 16.17.0 or later.

1. Create a new Worker project

  1. Open a terminal and run the create cloudflare (C3) CLI tool to create your Worker project:

    Terminal window
    npm create cloudflare@latest -- my-workflow

    For setup, select the following options:

    • For What would you like to start with?, choose Hello World example.
    • For Which template would you like to use?, choose Worker only.
    • For Which language do you want to use?, choose TypeScript.
    • For Do you want to use git for version control?, choose Yes.
    • For Do you want to deploy your application?, choose No (we will be making some changes before deploying).
  2. Move into your new project directory:

    Terminal window
    cd my-workflow

    What files did C3 create?

    In your project directory, C3 will have generated the following:

    • wrangler.jsonc: Your Wrangler configuration file.
    • src/index.ts: A minimal Worker written in TypeScript.
    • package.json: A minimal Node dependencies configuration file.
    • tsconfig.json: TypeScript configuration.

2. Write your Workflow

  1. Create a new file src/workflow.ts:

    src/workflow.ts
    import { WorkflowEntrypoint, WorkflowStep } from "cloudflare:workers";
    import type { WorkflowEvent } from "cloudflare:workers";
    type Params = { name: string };
    type IPResponse = { result: { ipv4_cidrs: string[] } };
    export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
    async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
    const data = await step.do("fetch data", async () => {
    const response = await fetch("https://api.cloudflare.com/client/v4/ips");
    return await response.json<IPResponse>();
    });
    await step.sleep("pause", "20 seconds");
    const result = await step.do(
    "process data",
    { retries: { limit: 3, delay: "5 seconds", backoff: "linear" } },
    async () => {
    return {
    name: event.payload.name,
    ipCount: data.result.ipv4_cidrs.length,
    };
    },
    );
    return result;
    }
    }

    A Workflow extends WorkflowEntrypoint and implements a run method. This code also passes in our Params type as a type parameter so that events that trigger our Workflow are typed.

    The step object is the core of the Workflows API. It provides methods to define durable steps in your Workflow:

    • step.do(name, callback) - Executes code and persists the result. If the Workflow is interrupted or retried, it resumes from the last successful step rather than re-running completed work.
    • step.sleep(name, duration) - Pauses the Workflow for a duration (e.g., "10 seconds", "1 hour").

    You can pass a retry configuration to step.do() to customize how failures are handled. See the full step API for additional methods like sleepUntil and waitForEvent.

    When deciding whether to break code into separate steps, ask yourself: "Do I want all of this code to run again if just one part fails?" Separate steps are ideal for operations like calling external APIs, querying databases, or reading files from storage — if a later step fails, your Workflow can retry from that point using data already fetched, avoiding redundant API calls or database queries.

    For more guidance on how to define your Workflow logic, refer to Rules of Workflows.

3. Configure your Workflow

  1. Open wrangler.jsonc, which is your Wrangler configuration file for your Workers project and your Workflow, and add the workflows configuration:

    {
    "$schema": "node_modules/wrangler/config-schema.json",
    "name": "my-workflow",
    "main": "src/index.ts",
    "compatibility_date": "2025-12-21",
    "observability": {
    "enabled": true
    },
    "workflows": [
    {
    "name": "my-workflow",
    "binding": "MY_WORKFLOW",
    "class_name": "MyWorkflow"
    }
    ]
    }

    The class_name must match your exported class, and binding is the variable name you use to access the Workflow in your code (like env.MY_WORKFLOW).

    You can also access bindings (such as KV, R2, or D1) via this.env within your Workflow. For more information on bindings within Workers, refer to Bindings (env).

  2. Now, generate types for your bindings:

    Terminal window
    npx wrangler types

    This creates a worker-configuration.d.ts file with the Env type that includes your MY_WORKFLOW binding.

4. Write your API

Now, you'll need a place to call your Workflow.

  1. Replace src/index.ts with a fetch handler to start and check Workflow instances:

    src/index.ts
    export { MyWorkflow } from "./workflow";
    export default {
    async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const instanceId = url.searchParams.get("instanceId");
    if (instanceId) {
    const instance = await env.MY_WORKFLOW.get(instanceId);
    return Response.json(await instance.status());
    }
    const instance = await env.MY_WORKFLOW.create();
    return Response.json({ instanceId: instance.id });
    },
    } satisfies ExportedHandler<Env>;

5. Develop locally

  1. Start a local development server:

    Terminal window
    npx wrangler dev
  2. To start a Workflow instance, open a new terminal window and run:

    Terminal window
    curl http://localhost:8787

    An instanceId will be automatically generated:

    { "instanceId": "abc-123-def" }
  3. Check the status using the returned instanceId:

    Terminal window
    curl "http://localhost:8787?instanceId=abc-123-def"

    The Workflow will progress through its steps. After about 20 seconds (the sleep duration), it will complete.

6. Deploy your Workflow

  1. Deploy your Workflow:

    Terminal window
    npx wrangler deploy

    Test in production using the same curl commands against your deployed URL. You can also trigger a workflow instance in production via Workers, Wrangler, or the Cloudflare dashboard.

    Once deployed, you can also inspect Workflow instances with the CLI:

    Terminal window
    npx wrangler workflows instances describe my-workflow latest

    The output of instances describe shows:

    • The status (success, failure, running) of each step
    • Any state emitted by the step
    • Any sleep state, including when the Workflow will wake up
    • Retries associated with each step
    • Errors, including exception messages

Learn more