Workers API (5)·续1
Key each entry by the name of the class that extends WorkflowEntrypoint. Set type to "workflow" and name to the name of the Workflow:
{
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "billing-worker",
"main": "src/index.ts",
"upload_source_maps": true,
// Set this to today's date
"compatibility_date": "2026-10-27",
"exports": {
"MyWorkflow": {
"type": "workflow",
"name": "billing-workflow",
"schedules": ["0 * * * *"],
},
},
}
"$schema" = "./node_modules/wrangler/config-schema.json"
name = "billing-worker"
main = "src/index.ts"
upload_source_maps = true
# Set this to today's date
compatibility_date = "2026-10-27"
[exports.MyWorkflow]
type = "workflow"
name = "billing-workflow"
schedules = [ "0 * * * *" ]
A workflow export accepts the same settings as a workflows binding: limits, schedules, and default_retention. When you run wrangler deploy, Wrangler creates or updates the Workflow with these settings. Refer to Workflow exports for the full list of fields.
You can declare the same Workflow as both a binding and an export. Both declarations must use the same class. They cannot set the same setting to different values.
A workflows binding to a Workflow in another Worker cannot use the same name as a Workflow export in this Worker. Workflow names are unique per account.
Call a Workflow through ctx.exports
Note
Local development requires one of the following, depending on your tooling:
- Wrangler 4.142.0 or above
@cloudflare/vite-plugin1.61.0 or above@cloudflare/vitest-plugin1.3.0 or above
Each Workflow declared in exports is available on ctx.exports, keyed by class name. It has the same API as a Workflow binding, so the Worker does not need a workflows binding to call it.
The following Worker creates an instance of MyWorkflow through ctx.exports, and gets the status of an existing instance:
src/index.jsjs
import {
WorkflowEntrypoint,
WorkflowEvent,
WorkflowStep,
} from "cloudflare:workers";
export class MyWorkflow extends WorkflowEntrypoint {
async run(event, step) {
return await step.do("greet", async () => `Hello, ${event.payload.name}!`);
}
}
export default {
async fetch(request, env, ctx) {
const id = new URL(request.url).searchParams.get("id");
// Get the status of an existing instance
if (id !== null) {
const instance = await ctx.exports.MyWorkflow.get(id);
return Response.json(await instance.status());
}
// Create a new instance
const instance = await ctx.exports.MyWorkflow.create({
params: { name: "World" },
});
return Response.json({ id: instance.id });
},
};
src/index.tsts
import {
WorkflowEntrypoint,
WorkflowEvent,
WorkflowStep,
} from "cloudflare:workers";
type Params = { name: string };
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
return await step.do("greet", async () => `Hello, ${event.payload.name}!`);
}
}
export default {
async fetch(request, env, ctx): Promise<Response> {
const id = new URL(request.url).searchParams.get("id");
// Get the status of an existing instance
if (id !== null) {
const instance = await ctx.exports.MyWorkflow.get(id);
return Response.json(await instance.status());
}
// Create a new instance
const instance = await ctx.exports.MyWorkflow.create({
params: { name: "World" },
});
return Response.json({ id: instance.id });
},
} satisfies ExportedHandler<Env>;
If you use TypeScript, run wrangler types to type ctx.exports.MyWorkflow as Workflow<Params>. This requires Wrangler 4.142.0 or above.
A workflows binding and a workflow export with the same name share their instances. This includes instances created before you declared the Workflow in exports, so you can move a Workflow from a binding to an export without losing its instances.
To test a Workflow declared in exports with the Vitest integration, refer to Introspect Workflows declared in exports.
NonRetryableError
throw new NonRetryableError(message: string, name string optional):NonRetryableError- When thrown inside
step.do(), this error stops step retries, propagating the error to the top level (the run function). Any error not handled at this top level will cause the Workflow instance to fail. - Refer to the documentation on sleeping and retrying to learn more about how Workflows steps are retried.
Call Workflows from Workers
Workflows exposes an API directly to your Workers scripts via the bindings concept. Bindings allow you to securely call a Workflow without having to manage API keys or clients.
You can bind to a Workflow by defining a [[workflows]] binding within your Wrangler configuration.
For example, to bind to a Workflow called workflows-starter and to make it available on the MY_WORKFLOW variable to your Worker script, you would configure the following fields within the [[workflows]] binding definition:
{
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "workflows-starter",
"main": "src/index.ts",
"upload_source_maps": true,
// Set this to today's date
"compatibility_date": "2026-10-27",
"workflows": [
{
// name of your workflow
"name": "workflows-starter",
// binding name env.MY_WORKFLOW
"binding": "MY_WORKFLOW",
// this is class that extends the Workflow class in src/index.ts
"class_name": "MyWorkflow",
},
],
}
"$schema" = "./node_modules/wrangler/config-schema.json"
name = "workflows-starter"
main = "src/index.ts"
upload_source_maps = true
# Set this to today's date
compatibility_date = "2026-10-27"
[[workflows]]
name = "workflows-starter"
binding = "MY_WORKFLOW"
class_name = "MyWorkflow"
Bind from Pages
You can bind and trigger Workflows from Pages Functions by deploying a Workers project with your Workflow definition and then invoking that Worker using service bindings or a standard fetch() call.
Visit the documentation on calling Workflows from Pages for examples.
Cross-script calls
You can also bind to a Workflow that is defined in a different Worker script from the script your Workflow definition is in. To do this, provide the script_name key with the name of the script to the [[workflows]] binding definition in your Wrangler configuration.
For example, if your Workflow is defined in a Worker script named billing-worker, but you are calling it from your web-api-worker script, your Wrangler configuration file would resemble the following:
{
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "web-api-worker",
"main": "src/index.ts",
"upload_source_maps": true,
// Set this to today's date
"compatibility_date": "2026-10-27",
"workflows": [
{
// name of your workflow
"name": "billing-workflow",
// binding name env.MY_WORKFLOW
"binding": "MY_WORKFLOW",
// this is class that extends the Workflow class in src/index.ts
"class_name": "MyWorkflow",
// the script name where the Workflow is defined.
// required if the Workflow is defined in another script.
"script_name": "billing-worker",
},
],
}
"$schema" = "./node_modules/wrangler/config-schema.json"
name = "web-api-worker"
main = "src/index.ts"
upload_source_maps = true
# Set this to today's date
compatibility_date = "2026-10-27"
[[workflows]]
name = "billing-workflow"
binding = "MY_WORKFLOW"
class_name = "MyWorkflow"
script_name = "billing-worker"
If you're using TypeScript, run wrangler types whenever you modify your Wrangler configuration file. This generates types for the env object based on your bindings, as well as runtime types.
Workflow
Note
Ensure you have a compatibility date 2024-10-22 or later installed when binding to Workflows from within a Workers project.
The Workflow type provides methods that allow you to create, inspect the status, and manage running Workflow instances from within a Worker script. It is part of the generated types produced by wrangler types.
./worker-configuration.d.tsts
interface Env {
// The 'MY_WORKFLOW' variable should match the "binding" value set in the Wrangler config file
MY_WORKFLOW: Workflow;
}
The Workflow type exports the following methods:
create
Create (trigger) a new instance of the given Workflow.
create(options?: WorkflowInstanceCreateOptions): Promise<WorkflowInstance>options- optional properties to pass when creating an instance, including a user-provided ID and payload parameters.
An ID is automatically generated, but a user-provided ID can be specified (up to 100 characters <sup>1</sup>). This can be useful when mapping Workflows to users, merchants or other identifiers in your system. You can also provide a JSON object as the params property, allowing you to pass data for the Workflow instance to act on as its WorkflowEvent.
// Create a new Workflow instance with your own ID and pass params to the Workflow instance
let instance = await env.MY_WORKFLOW.create({
id: myIdDefinedFromOtherSystem,
params: { hello: "world" },
});
return Response.json({
id: instance.id,
details: await instance.status(),
});
Returns a WorkflowInstance.
Throws an error if the provided ID is already used by an existing instance that has not yet passed its retention limit. To re-run a workflow with the same ID, you can restart the existing instance.
Caution
Providing a type parameter does not validate that the incoming event matches your type definition. In TypeScript, properties (fields) that do not exist or conform to the type you provided will be dropped. If you need to validate incoming events, we recommend a library such as zod ↗︎ or your own validator logic.
You can also provide a type parameter to the Workflows type when creating (triggering) a Workflow instance using the create method of the Workers API. Note that this does not propagate type information into the Workflow itself, as TypeScript types are a build-time construct.
To provide an optional type parameter to the Workflow, pass a type argument with your type when defining your Workflow bindings:
interface User {
email: string;
createdTimestamp: number;
}
interface Env {
// Pass our User type as the type parameter to the Workflow definition
MY_WORKFLOW: Workflow<User>;
}
export default {
async fetch(request, env, ctx) {
// More likely to come from your database or via the request body!
const user: User = {
email: user@example.com,
createdTimestamp: Date.now()
}
let instance = await env.MY_WORKFLOW.create({
// params expects the type User
params: user
})
return Response.json({
id: instance.id,
details: await instance.status(),
});
}
}
createBatch
Create (trigger) a batch of new instances of the given Workflow, up to 100 instances at a time.
This is useful when you are scheduling multiple instances at once. A call to createBatch is treated the same as a call to create (for a single instance) and allows you to work within the instance creation limit.
createBatch(options: WorkflowBatchCreateOptions): Promise<WorkflowBatchCreateResult>options- either acountof instances to create with shared options, or a list ofinstances, each with its own options.
Note
To use this form of createBatch in local development and get its types from wrangler types, use Wrangler 4.148.0 or later.
To create instances that share the same options, pass count with optional params and retention. Each instance receives a generated ID:
// Create 3 Workflow instances with generated IDs and the same params
const result = await env.MY_WORKFLOW.createBatch({
count: 3,
params: { hello: "world" },
});
// Create 3 Workflow instances with generated IDs and the same params
const result = await env.MY_WORKFLOW.createBatch({
count: 3,
params: { hello: "world" },
});
To give each instance its own ID or options, pass instances. Each entry accepts the same WorkflowInstanceCreateOptions as create:
// Create 3 Workflow instances, each with its own ID and params
const result = await env.MY_WORKFLOW.createBatch({
instances: [
{ id: "id-abc123", params: { hello: "world-0" } },
{ id: "id-def456", params: { hello: "world-1" } },
{ id: "id-ghi789", params: { hello: "world-2" } },
],
});
// Create 3 Workflow instances, each with its own ID and params
const result = await env.MY_WORKFLOW.createBatch({
instances: [
{ id: "id-abc123", params: { hello: "world-0" } },
{ id: "id-def456", params: { hello: "world-1" } },
{ id: "id-ghi789", params: { hello: "world-2" } },
],
});
options takes one of the following shapes:
type WorkflowBatchCreateOptions =
| {
count: number;
params?: unknown;
retention?: {
successRetention?: WorkflowRetentionDuration;
errorRetention?: WorkflowRetentionDuration;
};
}
| {
instances: WorkflowInstanceCreateOptions[];
};
count must be an integer from 1 to 100. instances must contain from 1 to 100 entries. You cannot pass both count and instances in the same call.
The operation returns the created instances and per-instance errors:
type WorkflowBatchCreateResult = {
created: WorkflowInstance[];
errors: {
index: number;
id?: string;
code: number;
message: string;
}[];
};
created contains a WorkflowInstance for each created instance, in input order. errors contains the entries that were not created. Each error includes the position of the entry in the input (index), its id if one was provided, and an error code and message.
Unlike create, createBatch does not throw when an ID is already in use. Instead, it reports the entry in errors with one of the following codes:
| Code | Description |
|---|---|
10405 |
An instance with this ID already exists. |
10415 |
An earlier entry in the same batch uses this ID. Only the first entry with the ID is created. |
If any entry has invalid options, such as an invalid ID or retention duration, the call throws an error before it creates any instances.
Migrate from the array form
Caution
Passing an array to createBatch is deprecated. Use the instances option instead.
createBatch(batch: WorkflowInstanceCreateOptions[]): Promise<WorkflowInstance[]>
The array form returns an array of WorkflowInstance. It skips IDs that already exist or repeat within the batch, and excludes them from the returned array without reporting an error.
