Workers API (5)

Last updated Oct 12, 2026 This guide details the Workflows API within Cloudflare Workers, including methods, types, and usage examples.

WorkflowEntrypoint

The WorkflowEntrypoint class is the core element of a Workflow definition. A Workflow must extend this class and define a run method with at least one step call to be considered a valid Workflow.

export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
    async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
        // Steps here
    }
}

run

  • run(event: WorkflowEvent<T>, step: WorkflowStep): Promise<T>
  • event - the event passed to the Workflow, including an optional payload containing data (parameters)
  • step - the WorkflowStep type that provides the step methods for your Workflow

The run method can optionally return data, which is available when querying the instance status via the Workers API, REST API and the Workflows dashboard. This can be useful if your Workflow is computing a result, returning the key to data stored in object storage, or generating some kind of identifier you need to act on.

export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
    async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
        // Steps here
        let someComputedState = await step.do("my step", async () => {});

// Optional: return state from our run() method
        return someComputedState;
    }
}

The WorkflowEvent type accepts an optional type parameter ↗︎ that allows you to provide a type for the payload property within the WorkflowEvent.

Refer to the events and parameters documentation for how to handle events within your Workflow code.

Finally, any JS control-flow primitive (if conditions, loops, try...catch blocks, promises, and more) can be used to manage steps inside the run method.

WorkflowEvent

export type WorkflowCronSchedule = {
    /** Cron expression that triggered this event. */
    cron: string;
    /** Timestamp of the scheduled trigger, in milliseconds since the Unix epoch. */
    scheduledTime: number;
};

export type WorkflowEvent<T> = {
    payload: Readonly<T>;
    timestamp: Date;
    instanceId: string;
    workflowName: string;
    schedule?: WorkflowCronSchedule;
};
  • The WorkflowEvent is the first argument to a Workflow's run method.
  • payload - a default type of any or type T if a type parameter is provided.
  • timestamp - a Date object set to the time the Workflow instance was created (triggered).
  • instanceId - the ID of the associated instance.
  • workflowName - the name of the associated Workflow.
  • schedule - metadata for Workflow instances created by a cron schedule, including the cron expression and scheduledTime in milliseconds since the Unix epoch.

Refer to the events and parameters documentation for how to handle events within your Workflow code.

WorkflowStep

step

  • step.do(name: string, callback: (ctx: WorkflowStepContext): RpcSerializable): Promise<T>
  • step.do(name: string, callback: (ctx: WorkflowStepContext): RpcSerializable, rollbackOptions?: WorkflowStepRollbackOptions<T>): Promise<T>
  • step.do(name: string, config?: WorkflowStepConfig, callback: (ctx: WorkflowStepContext): RpcSerializable): Promise<T>
  • name - the name of the step, up to 256 characters.
  • config (optional) - an optional WorkflowStepConfig for configuring step specific retry behaviour.
  • callback - an asynchronous function that receives a WorkflowStepContext and optionally returns serializable state for the Workflow to persist. In JavaScript Workflows, this includes a fresh, unlocked ReadableStream<Uint8Array> for large binary output.
  • step.do(name: string, config?: WorkflowStepConfig, callback: (ctx: WorkflowStepContext): RpcSerializable, rollbackOptions?: WorkflowStepRollbackOptions<T>): Promise<T>
  • name - the name of the step, up to 256 characters.
  • config (optional) - an optional WorkflowStepConfig for configuring step specific retry behaviour.
  • callback - an asynchronous function that receives a WorkflowStepContext and optionally returns serializable state for the Workflow to persist. In JavaScript Workflows, this includes a fresh, unlocked ReadableStream<Uint8Array> for large binary output.
  • rollbackOptions (optional) - register rollback logic for the step. If the Workflow later fails, registered rollbacks run in reverse step-start order.

Returning state

When returning state from a step, ensure that the object you return is serializable.

Primitive types like string, number, and boolean, along with composite structures such as Array and Object (provided they only contain serializable values), can be serialized. Any structured-cloneable ↗︎ type can be serialized, as long it is no longer than 1 MB.

On the other hand, objects that include Function or Symbol types, and objects with circular references, cannot be serialized. The Workflow instance will throw an error if objects with those types is returned.

In JavaScript Workflows, ReadableStream<Uint8Array> is a supported serializable return type when a step needs to persist larger binary output than the normal 1 MiB non-stream step-result limit.

Return a new stream from the callback.

Caution

Do not return a locked stream or a stream that has already been read. BYOB streams and BYOB readers are not supported.

After a ReadableStream<Uint8Array> object has been persisted within a step, it should not be reused - rely on the new fresh stream that gets returned from step. The bytes are preserved from the original stream, but the implementation might differ.

export class MyWorkflow extends WorkflowEntrypoint {
    async run(_event, step) {
        const reportStream = await step.do("read report from R2", async () => {
            const object = await this.env.MY_BUCKET.get("reports/latest.csv");

if (!object?.body) {
                throw new Error("Could not read reports/latest.csv from R2.");
            }

return object.body;
        });

const preview = await new Response(reportStream).text();
        return { preview };
    }
}
type Env = {
    MY_BUCKET: R2Bucket;
};

export class MyWorkflow extends WorkflowEntrypoint<Env> {
    async run(_event: WorkflowEvent<unknown>, step: WorkflowStep) {
        const reportStream = await step.do("read report from R2", async () => {
            const object = await this.env.MY_BUCKET.get("reports/latest.csv");

if (!object?.body) {
                throw new Error("Could not read reports/latest.csv from R2.");
            }

return object.body;
        });

const preview = await new Response(reportStream).text();
        return { preview };
    }
}
  • step.sleep(name: string, duration: WorkflowDuration): Promise<void>
  • name - the name of the step.
  • duration - the duration to sleep for, as a number in milliseconds or as a WorkflowDuration-compatible string.
  • Refer to the documentation on sleeping and retrying to learn more about how Workflows are retried.
  • step.sleepUntil(name: string, timestamp: Date | number): Promise<void>
  • name - the name of the step.
  • timestamp - a JavaScript Date object or milliseconds from the Unix epoch to sleep the Workflow instance until.

Note

step.sleep and step.sleepUntil methods do not count towards the maximum Workflow steps limit.

More information about the limits imposed on Workflow can be found in the Workflows limits documentation.

  • step.waitForEvent(name: string, options: ): Promise<void>- name - the name of the step. - options - an object with properties for type (up to 100 characters <sup>1</sup>), which determines which event type this waitForEvent call will match on when calling instance.sendEvent, and an optional timeout property, which defines how long the waitForEvent call will block for before throwing a timeout exception. The default timeout is 24 hours.
export class MyWorkflow extends WorkflowEntrypoint {
    async run(event, step) {
        // Other steps in your Workflow
        let stripeEvent = await step.waitForEvent(
            "receive invoice paid webhook from Stripe",
            { type: "stripe-webhook", timeout: "1 hour" },
        );
        // Rest of your Workflow
    }
}
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
    async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
        // Other steps in your Workflow
        let stripeEvent = await step.waitForEvent<IncomingStripeWebhook>(
            "receive invoice paid webhook from Stripe",
            { type: "stripe-webhook", timeout: "1 hour" },
        );
        // Rest of your Workflow
    }
}

Review the documentation on events and parameters to learn how to send events to a running Workflow instance.

WorkflowStepConfig

export type WorkflowDynamicDelayContext = {
    ctx: WorkflowStepContext;
    error: Error;
};

export type WorkflowDelayFunction = (
    input: WorkflowDynamicDelayContext,
) => string | number | Promise<string | number>;

export type WorkflowStepConfig = {
    retries?: {
        limit: number;
        delay: string | number | WorkflowDelayFunction;
        backoff?: WorkflowBackoff;
    };
    timeout?: string | number;
};
  • A WorkflowStepConfig is an optional argument to the do method of a WorkflowStep and defines properties that allow you to configure the retry behaviour of that step.
  • Set retries.delay to a fixed duration, or pass a WorkflowDelayFunction to calculate the next retry delay from the current step context and thrown error.

Refer to the documentation on sleeping and retrying to learn more about how Workflows are retried.

Rollback options

type WorkflowRollbackContext<T = unknown> = {
    ctx: WorkflowStepContext;
    error: Error;
    output: T | undefined;
};

type WorkflowRollbackHandler<T = unknown> = (
    ctx: WorkflowRollbackContext<T>,
) => Promise<void>;

type WorkflowStepRollbackConfig = Pick<
    WorkflowStepConfig,
    "retries" | "timeout"
>;

type WorkflowStepRollbackOptions<T = unknown> = {
    rollback: WorkflowRollbackHandler<T>;
    rollbackConfig?: WorkflowStepRollbackConfig;
};
  • Pass this WorkflowStepRollbackOptions object as the final argument to step.do() to register a compensating action for a successful step.
  • rollback receives the original step context, the error that caused the Workflow to fail, and the step output returned by the forward step.
  • rollbackConfig applies retry and timeout settings to the rollback handler itself.
export class BillingWorkflow extends WorkflowEntrypoint {
    async run(_event, step) {
        await step.do(
            "create charge",
            async () => {
                const charge = await createCharge();
                return { chargeId: charge.id };
            },
            {
                rollback: async ({ ctx, output, error }) => {
                    const { chargeId } = output;
                    await refundCharge(chargeId, {
                        reason: `${ctx.step.name}: ${error.message}`,
                    });
                },
                rollbackConfig: {
                    retries: {
                        limit: 3,
                        delay: "30 seconds",
                        backoff: "linear",
                    },
                    timeout: "5 minutes",
                },
            },
        );
    }
}
export class BillingWorkflow extends WorkflowEntrypoint<Env> {
    async run(_event: WorkflowEvent<unknown>, step: WorkflowStep) {
        await step.do(
            "create charge",
            async () => {
                const charge = await createCharge();
                return { chargeId: charge.id };
            },
            {
                rollback: async ({ ctx, output, error }) => {
                    const { chargeId } = output as { chargeId: string };
                    await refundCharge(chargeId, {
                        reason: `${ctx.step.name}: ${error.message}`,
                    });
                },
                rollbackConfig: {
                    retries: {
                        limit: 3,
                        delay: "30 seconds",
                        backoff: "linear",
                    },
                    timeout: "5 minutes",
                },
            },
        );
    }
}

WorkflowStepContext

export type WorkflowStepContext = {
    step: {
        name: string;
        count: number;
    };
    attempt: number;
    config: WorkflowStepConfig;
};
  • The WorkflowStepContext is passed as the first argument to the step.do callback function. It provides runtime information about the current step.
  • step.name - the name of the step as passed to step.do.
  • step.count - how many times step.do has been called with this name in the current Workflow run (1-indexed).
  • attempt - the current attempt number (1-indexed). 1 on the first try, 2 on the first retry, and so on.
  • config - the resolved WorkflowStepConfig for this step, including any defaults applied by the runtime.

Refer to the step context documentation for usage examples.

Workflow step limits

Each workflow on Workers Paid supports 10,000 steps by default. You can increase this up to 25,000 steps by configuring steps within the limits property of your Workflow definition in your Wrangler configuration:

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "workflows": [
    {
      "name": "my-workflow",
      "binding": "MY_WORKFLOW",
      "class_name": "MyWorkflow",
      "limits": {
        "steps": 25000
      }
    }
  ]
}
[[workflows]]
name = "my-workflow"
binding = "MY_WORKFLOW"
class_name = "MyWorkflow"

[workflows.limits]
steps = 25_000

step.sleep does not count towards the maximum steps limit.

Note that Workflows on Workers Free have a limit of 1,024 steps. Refer to Workflow limits for more information.

Default instance retention

By default, the state of a finished Workflow instance is retained for the maximum retention period available on your account. To retain instances for a shorter period, configure default_retention on the Workflow binding in your Wrangler configuration:

{
    "workflows": [
        {
            "name": "my-workflow",
            "binding": "MY_WORKFLOW",
            "class_name": "MyWorkflow",
            "default_retention": {
                "success_retention": "3 days",
                "error_retention": "7 days",
            },
        },
    ],
}
[[workflows]]
name = "my-workflow"
binding = "MY_WORKFLOW"
class_name = "MyWorkflow"

[workflows.default_retention]
  success_retention = "3 days"
  error_retention = "7 days"
  • success_retention applies to instances that complete successfully and to instances that are terminated, and error_retention applies to instances that end in an errored state.
  • Both fields are optional and accept either a duration string, such as "3 days", or a whole number of milliseconds.

Instance-level retention settings take precedence over Workflow binding settings, which take precedence over the account default. The retention period cannot exceed your account's retention limit.

Note

default_retention cannot be combined with script_name. If you bind to a Workflow defined in another Worker, set default_retention on the Worker that defines the Workflow.

Declare Workflows in exports

Note

Requires Wrangler 4.139.0 or above.

You can declare the Workflows a Worker defines in the exports field of your Wrangler configuration, instead of in a workflows binding:

  • The Worker that declares the Workflow calls it through ctx.exports.
  • Other Workers call the Workflow through a cross-script binding. Set script_name to the Worker that declares the export, and class_name to the key of the export.