From 899518bf1a93aebef0c03981ff43f72295639f7a Mon Sep 17 00:00:00 2001 From: mmalden Date: Tue, 2 Jun 2026 14:49:17 -0400 Subject: [PATCH] [Workflows] Document scheduled Workflow triggers --- .../workflows/2026-05-13-cron-workflows.mdx | 29 ++++++++++ .../workflows-course/series/workflows-3.mdx | 34 ++++++++---- .../workers/configuration/cron-triggers.mdx | 2 +- .../docs/workers/wrangler/configuration.mdx | 7 ++- .../workflows/build/events-and-parameters.mdx | 36 ++++++++++--- .../workflows/build/trigger-workflows.mdx | 52 ++++++++++++++++-- .../docs/workflows/build/workers-api.mdx | 13 ++++- .../docs/workflows/examples/backup-d1.mdx | 53 ++++++++----------- .../docs/workflows/get-started/guide.mdx | 35 ++++++++++-- src/content/docs/workflows/index.mdx | 4 +- .../docs/workflows/reference/limits.mdx | 8 ++- 11 files changed, 216 insertions(+), 57 deletions(-) create mode 100644 src/content/changelog/workflows/2026-05-13-cron-workflows.mdx diff --git a/src/content/changelog/workflows/2026-05-13-cron-workflows.mdx b/src/content/changelog/workflows/2026-05-13-cron-workflows.mdx new file mode 100644 index 00000000000..afc9507e533 --- /dev/null +++ b/src/content/changelog/workflows/2026-05-13-cron-workflows.mdx @@ -0,0 +1,29 @@ +--- +title: Schedule Workflow instances directly from your Workflow binding +description: Add cron schedules to a Workflow binding in wrangler.jsonc to create Workflow instances automatically on a recurring interval. +products: + - workflows + - workers +date: 2026-06-02 15:00:00 UTC +--- + +You can now attach cron schedules directly to a Workflow binding in `wrangler.jsonc`. Each scheduled run creates a new Workflow instance automatically, so you do not need to define a separate Worker with a `scheduled` handler just to trigger your Workflow on an interval. + +For example, you can configure hourly, every-15-minute, or weekday schedules on the same Workflow: + +```jsonc +{ + "workflows": [ + { + "name": "my-scheduled-workflow", + "binding": "MY_WORKFLOW", + "class_name": "MyScheduledWorkflow", + "schedules": ["0 * * * *", "*/15 * * * *", "0 9 * * MON-FRI"], + }, + ], +} +``` + +This makes it easier to build recurring jobs such as database backups, invoice generation, report aggregation, and cleanup tasks without wiring up a separate Cron Trigger entrypoint. + +For more information, refer to [Trigger Workflows](/workflows/build/trigger-workflows/). diff --git a/src/content/docs/learning-paths/workflows-course/series/workflows-3.mdx b/src/content/docs/learning-paths/workflows-course/series/workflows-3.mdx index 3e4a708a58d..282c7e127da 100644 --- a/src/content/docs/learning-paths/workflows-course/series/workflows-3.mdx +++ b/src/content/docs/learning-paths/workflows-course/series/workflows-3.mdx @@ -5,18 +5,25 @@ sidebar: order: 3 tableOfContents: false description: | - Cloudflare Workflows provide a powerful way to manage asynchronous, durable processes. The ability to explicitly schedule tasks using cron triggers and pause execution with `step.sleep` allows developers to build sophisticated, time-aware applications. + Cloudflare Workflows provide a powerful way to manage asynchronous, durable processes. The ability to explicitly schedule tasks using scheduled handlers and pause execution with `step.sleep` allows developers to build sophisticated, time-aware applications. products: - workflows - workers --- -import { Render, Tabs, TabItem, Card, YouTube } from "~/components"; +import { + Render, + Tabs, + TabItem, + Card, + WranglerConfig, + YouTube, +} from "~/components"; -Cloudflare Workflows provide a powerful way to manage asynchronous, durable processes. The ability to explicitly schedule tasks using cron triggers and pause execution with `step.sleep` allows developers to build sophisticated, time-aware applications. +Cloudflare Workflows provide a powerful way to manage asynchronous, durable processes. The ability to explicitly schedule tasks using scheduled handlers and pause execution with `step.sleep` allows developers to build sophisticated, time-aware applications. @@ -59,18 +66,27 @@ Cloudflare Workflows provide a powerful way to manage asynchronous, durable proc ### Wrangler Configuration - The Wrangler configuration file is used to configure your Worker and Workflows. This includes defining bindings to resources like KV namespaces and setting up triggers for workflows. + The Wrangler configuration file is used to configure your Worker and Workflows. This includes defining bindings to resources like KV namespaces and setting up schedules for workflows. + + The episode repository uses the older pattern of a top-level `[triggers]` section plus a `scheduled` handler in the main Worker to create a `LEADERBOARD_WORKFLOW` instance on a timer. - Open the Wrangler configuration file and find the `[triggers]` section. [See here](https://github.com/craigsdennis/punderful-workflows/blob/main/wrangler.toml#L68) - The `crons` array allows you to define scheduled triggers for your main Worker. The example shows a cron job configured to run every 30 minutes. + In current Workflows projects, you can usually schedule the Workflow directly on its binding instead: + + - Locate the `scheduled` handler in your main Worker code (`src/index.tsx`). This handler is executed when a cron trigger fires. + ```toml + [[workflows]] + name = "leaderboard-workflow" + binding = "LEADERBOARD_WORKFLOW" + class_name = "LeaderboardWorkflow" + schedules = ["*/30 * * * *"] + ``` - [See here](https://github.com/craigsdennis/punderful-workflows/blob/main/src/index.tsx#L315) + - This handler creates an instance of the `LEADERBOARD_WORKFLOW`, initiating the leaderboard update process on a schedule. + Use a separate Cron Trigger and `scheduled` handler only when you need custom logic before deciding whether to create a Workflow instance. Use the latest Wrangler release when configuring Workflow schedules. ### Puntificator: Using AI to Develop More Puns Automatically diff --git a/src/content/docs/workers/configuration/cron-triggers.mdx b/src/content/docs/workers/configuration/cron-triggers.mdx index 7c3744ba9e0..d7fa05ff835 100644 --- a/src/content/docs/workers/configuration/cron-triggers.mdx +++ b/src/content/docs/workers/configuration/cron-triggers.mdx @@ -23,7 +23,7 @@ Cron Triggers are ideal for running periodic jobs, such as for maintenance or ca :::note -Cron Triggers can also be combined with [Workflows](/workflows/) to trigger multi-step, long-running tasks. You can [bind to a Workflow](/workflows/build/workers-api/) directly from your Cron Trigger to execute a Workflow on a schedule. +Cron Triggers are also available directly in [Workflows](/workflows/) via `schedules` on the Workflow binding instead. Refer to [Trigger Workflows](/workflows/build/trigger-workflows/) for details, and use a Wrangler release that supports Workflow schedules. ::: diff --git a/src/content/docs/workers/wrangler/configuration.mdx b/src/content/docs/workers/wrangler/configuration.mdx index f32639e194e..ca35353ded9 100644 --- a/src/content/docs/workers/wrangler/configuration.mdx +++ b/src/content/docs/workers/wrangler/configuration.mdx @@ -1194,6 +1194,11 @@ To bind Workflows to your Worker, assign an array of the below object to the `wo - `script_name` - The name of the Worker script where the Workflow class is defined. Only required if the Workflow is defined in a different Worker than the one the binding is configured on. +- `schedules` + - A list of cron schedules that create new instances of this Workflow automatically. + - Use this when you want to run a Workflow on a recurring interval without defining top-level `triggers.crons` and a separate `scheduled` handler. + - Use a Wrangler release that supports Workflow schedules. If your local schema does not recognize `schedules`, update Wrangler first. + Example: @@ -1896,4 +1901,4 @@ And the `.wrangler/deploy/config.json` contains the path to the generated config { "configPath": "../../dist/wrangler.jsonc" } -``` \ No newline at end of file +``` diff --git a/src/content/docs/workflows/build/events-and-parameters.mdx b/src/content/docs/workflows/build/events-and-parameters.mdx index a5a86fe9f45..eefaff05d48 100644 --- a/src/content/docs/workflows/build/events-and-parameters.mdx +++ b/src/content/docs/workflows/build/events-and-parameters.mdx @@ -191,19 +191,43 @@ You can send an event to a Workflow instance _before_ it reaches the correspondi ## TypeScript and type parameters -By default, the `WorkflowEvent` passed to the `run` method of your Workflow definition has a type that conforms to the following, with `payload` (your data), `timestamp`, and `instanceId` properties: +By default, the `WorkflowEvent` passed to the `run` method of your Workflow definition has a type that conforms to the following: ```ts +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 = { - // The data passed as the parameter when the Workflow instance was triggered - payload: T; - // The timestamp that the Workflow was triggered + /** The data passed as the parameter when the Workflow instance was triggered. */ + payload: Readonly; + /** The timestamp that the Workflow was triggered. */ timestamp: Date; - // ID of the current Workflow instance + /** ID of the current Workflow instance. */ instanceId: string; + /** Name of the current Workflow. */ + workflowName: string; + /** Metadata for Workflow instances created by a cron schedule. */ + schedule?: WorkflowCronSchedule; }; ``` +When a Workflow instance is created by a cron schedule configured on a Workflow binding, `event.schedule` includes the cron expression that created the instance and the scheduled trigger time: + +```ts +export class MyWorkflow extends WorkflowEntrypoint { + async run(event: WorkflowEvent, step: WorkflowStep) { + if (event.schedule) { + console.log(event.schedule.cron); + console.log(new Date(event.schedule.scheduledTime)); + } + } +} +``` + You can optionally type these events by defining your own type and passing it as a [type parameter](https://www.typescriptlang.org/docs/handbook/2/generics.html#working-with-generic-type-variables) to the `WorkflowEvent`: ```ts @@ -239,4 +263,4 @@ export class MyWorkflow extends WorkflowEntrypoint { -[^1]: Match pattern: `^[a-zA-Z0-9_][a-zA-Z0-9-_]*$` \ No newline at end of file +[^1]: Match pattern: `^[a-zA-Z0-9_][a-zA-Z0-9-_]*$` diff --git a/src/content/docs/workflows/build/trigger-workflows.mdx b/src/content/docs/workflows/build/trigger-workflows.mdx index 0fe5cfe2ae2..df3720de979 100644 --- a/src/content/docs/workflows/build/trigger-workflows.mdx +++ b/src/content/docs/workflows/build/trigger-workflows.mdx @@ -15,17 +15,19 @@ import { TypeScriptExample, WranglerConfig } from "~/components"; You can trigger Workflows both programmatically and via the Workflows APIs, including: 1. With [Workers](/workers) via HTTP requests in a `fetch` handler, or bindings from a `queue` or `scheduled` handler -2. Using the [Workflows REST API](/api/resources/workflows/methods/list/) -3. Via the [wrangler CLI](/workers/wrangler/commands/workflows/#workflows) in your terminal +2. On a recurring interval by defining `schedules` on a Workflow binding in your Wrangler configuration +3. Using the [Workflows REST API](/api/resources/workflows/methods/list/) +4. Via the [wrangler CLI](/workers/wrangler/commands/workflows/#workflows) in your terminal ## Workers API (Bindings) You can interact with Workflows programmatically from any Worker script by creating a binding to a Workflow. A Worker can bind to multiple Workflows, including Workflows defined in other Workers projects (scripts) within your account. -You can interact with a Workflow: +You can trigger a Workflow: - Directly over HTTP via the [`fetch`](/workers/runtime-apis/handlers/fetch/) handler - From a [Queue consumer](/queues/configuration/javascript-apis/#consumer) inside a `queue` handler +- On a recurring schedule by defining `schedules` on the Workflow binding in `wrangler.jsonc` - From a [Cron Trigger](/workers/configuration/cron-triggers/) inside a `scheduled` handler - Within a [Durable Object](/durable-objects/) @@ -64,6 +66,50 @@ To bind to a Workflow from your Workers code, you need to define a [binding](/wo The `binding = "MY_WORKFLOW"` line defines the JavaScript variable that our Workflow methods are accessible on, including `create` (which triggers a new instance) or `get` (which returns the status of an existing instance). +### Schedule a Workflow directly + +If you want to create Workflow instances on a recurring interval, add a `schedules` array to the Workflow binding in your Wrangler configuration: + + + +```jsonc +{ + "$schema": "./node_modules/wrangler/config-schema.json", + "name": "workflows-tutorial", + "main": "src/index.ts", + "compatibility_date": "$today", + "workflows": [ + { + "name": "workflows-tutorial", + "binding": "MY_WORKFLOW", + "class_name": "MyWorkflow", + "schedules": ["0 * * * *"] + } + ] +} +``` + + + +Each matching cron expression creates a new Workflow instance automatically. Use this when you want to run a Workflow on a schedule without defining top-level `triggers.crons` and a separate `scheduled` handler. + +Scheduled instances include the matching cron expression and scheduled trigger time on `event.schedule`: + +```ts +export class MyWorkflow extends WorkflowEntrypoint { + async run(event: WorkflowEvent, step: WorkflowStep) { + if (event.schedule) { + console.log(event.schedule.cron); + console.log(new Date(event.schedule.scheduledTime)); + } + } +} +``` + +On [Workers Paid](/workers/platform/pricing/#workers), Workflow instances created by `schedules` can run for up to one hour per cron firing without consuming a Workflow concurrency slot. If the instance pauses or sleeps after that window, the instance yields and enters the normal concurrency queue upon resume. It resumes when a concurrency slot is available. + +Use the latest Wrangler release when configuring Workflow schedules. If your local Wrangler schema does not recognize `schedules` yet, update Wrangler before deploying. + The following example shows how you can manage Workflows from within a Worker, including: - Retrieving the status of an existing Workflow instance by its ID diff --git a/src/content/docs/workflows/build/workers-api.mdx b/src/content/docs/workflows/build/workers-api.mdx index f7a91c2a50d..e75ab914733 100644 --- a/src/content/docs/workflows/build/workers-api.mdx +++ b/src/content/docs/workflows/build/workers-api.mdx @@ -59,17 +59,28 @@ Finally, any JS control-flow primitive (if conditions, loops, `try...catch` bloc ## WorkflowEvent ```ts +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 = { payload: Readonly; timestamp: Date; instanceId: string; + workflowName: string; + schedule?: WorkflowCronSchedule; }; ``` -- The `WorkflowEvent` is the first argument to a Workflow's `run` method, and includes an optional `payload` parameter and a `timestamp` property. +- 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](/workflows/build/events-and-parameters/) documentation for how to handle events within your Workflow code. diff --git a/src/content/docs/workflows/examples/backup-d1.mdx b/src/content/docs/workflows/examples/backup-d1.mdx index a22c45b46fd..76deb83c2aa 100644 --- a/src/content/docs/workflows/examples/backup-d1.mdx +++ b/src/content/docs/workflows/examples/backup-d1.mdx @@ -12,9 +12,9 @@ description: Send invoice when shopping cart is checked out and paid for reviewed: 2024-12-30 --- -import { TabItem, Tabs, WranglerConfig } from "~/components"; +import { WranglerConfig } from "~/components"; -In this example, we implement a Workflow periodically triggered by a [Cron Trigger](/workers/configuration/cron-triggers). That Workflow initiates a backup for a D1 database using the REST API, and then stores the SQL dump in an [R2](/r2) bucket. +In this example, we implement a Workflow that runs on a schedule using the `schedules` field on the Workflow binding. That Workflow initiates a backup for a D1 database using the REST API, and then stores the SQL dump in an [R2](/r2) bucket. When the Workflow is triggered, it fetches the REST API to initiate an export job for a specific database. Then it fetches the same endpoint to check if the backup job is ready and the SQL dump is available to download. @@ -40,18 +40,15 @@ type Env = { BACKUP_WORKFLOW: Workflow; D1_REST_API_TOKEN: string; BACKUP_BUCKET: R2Bucket; -}; - -// Workflow parameters: we expect accountId and databaseId -type Params = { - accountId: string; - databaseId: string; + ACCOUNT_ID: string; + DATABASE_ID: string; }; // Workflow logic -export class backupWorkflow extends WorkflowEntrypoint { - async run(event: WorkflowEvent, step: WorkflowStep) { - const { accountId, databaseId } = event.payload; +export class backupWorkflow extends WorkflowEntrypoint { + async run(_event: WorkflowEvent, step: WorkflowStep) { + const accountId = this.env.ACCOUNT_ID; + const databaseId = this.env.DATABASE_ID; const url = `https://api.cloudflare.com/client/v4/accounts/${accountId}/d1/database/${databaseId}/export`; const method = "POST"; @@ -105,18 +102,6 @@ export default { async fetch(req: Request, env: Env): Promise { return new Response("Not found", { status: 404 }); }, - async scheduled( - controller: ScheduledController, - env: Env, - ctx: ExecutionContext, - ) { - const params: Params = { - accountId: "{accountId}", - databaseId: "{databaseId}", - }; - const instance = await env.BACKUP_WORKFLOW.create({ params }); - console.log(`Started workflow: ${instance.id}`); - }, }; ``` @@ -130,6 +115,8 @@ Here is a minimal package.json: } ``` +Create `D1_REST_API_TOKEN` as a [secret](/workers/configuration/secrets/) with permission to export the target D1 database. + Here is a [Wrangler configuration file](/workers/wrangler/configuration/): @@ -143,11 +130,16 @@ Here is a [Wrangler configuration file](/workers/wrangler/configuration/): "compatibility_flags": [ "nodejs_compat" ], + "vars": { + "ACCOUNT_ID": "account-id", + "DATABASE_ID": "database-id" + }, "workflows": [ { "name": "backup-workflow", "binding": "BACKUP_WORKFLOW", - "class_name": "backupWorkflow" + "class_name": "backupWorkflow", + "schedules": ["0 0 * * *"] } ], "r2_buckets": [ @@ -155,13 +147,14 @@ Here is a [Wrangler configuration file](/workers/wrangler/configuration/): "binding": "BACKUP_BUCKET", "bucket_name": "d1-backups" } - ], - "triggers": { - "crons": [ - "0 0 * * *" - ] - } + ] } ``` + +Each scheduled run creates a new Workflow instance automatically. + +Scheduled instances include the matching cron expression and scheduled trigger time on `event.schedule`. + +Use the latest Wrangler release when configuring Workflow schedules. If your local Wrangler schema does not recognize `schedules` yet, update Wrangler before deploying. diff --git a/src/content/docs/workflows/get-started/guide.mdx b/src/content/docs/workflows/get-started/guide.mdx index d1705c6cf92..5b3983b387e 100644 --- a/src/content/docs/workflows/get-started/guide.mdx +++ b/src/content/docs/workflows/get-started/guide.mdx @@ -168,11 +168,40 @@ Follow the steps below to learn how to build a Workflow from scratch. } ``` - + - 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`). + 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](/workers/runtime-apis/bindings/) (such as [KV](/kv/), [R2](/r2/), or [D1](/d1/)) via `this.env` within your Workflow. For more information on bindings within Workers, refer to [Bindings (env)](/workers/runtime-apis/bindings/). + If you want the same Workflow to run automatically on a recurring interval, add `schedules` to the Workflow definition: + + + + ```jsonc + { + "$schema": "node_modules/wrangler/config-schema.json", + "name": "my-workflow", + "main": "src/index.ts", + "compatibility_date": "$today", + "workflows": [ + { + "name": "my-workflow", + "binding": "MY_WORKFLOW", + "class_name": "MyWorkflow", + "schedules": ["0 * * * *"] + } + ] + } + ``` + + + + Each matching cron expression creates a new Workflow instance automatically, so you do not need top-level `triggers.crons` and a separate `scheduled` handler for Workflow-specific recurring runs. + + Scheduled instances include the matching cron expression and scheduled trigger time on `event.schedule`. + + Use the latest Wrangler release when configuring Workflow schedules. If your local Wrangler schema does not recognize `schedules` yet, update Wrangler before deploying. + + You can also access [bindings](/workers/runtime-apis/bindings/) (such as [KV](/kv/), [R2](/r2/), or [D1](/d1/)) via `this.env` within your Workflow. For more information on bindings within Workers, refer to [Bindings (env)](/workers/runtime-apis/bindings/). 2. Now, generate types for your bindings: diff --git a/src/content/docs/workflows/index.mdx b/src/content/docs/workflows/index.mdx index 6df785c87f9..be52e493f54 100644 --- a/src/content/docs/workflows/index.mdx +++ b/src/content/docs/workflows/index.mdx @@ -61,7 +61,7 @@ An image processing workflow that fetches from R2, generates an AI description, export class ImageProcessingWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { const imageData = await step.do('fetch image', async () => { - const object = await this.env.BUCKET.get(event.params.imageKey); + const object = await this.env.BUCKET.get(event.payload.imageKey); return await object.arrayBuffer(); }); @@ -80,7 +80,7 @@ export class ImageProcessingWorkflow extends WorkflowEntrypoint { }); await step.do('publish', async () => { - await this.env.BUCKET.put(`public/${event.params.imageKey}`, imageData); + await this.env.BUCKET.put(`public/${event.payload.imageKey}`, imageData); }); } } diff --git a/src/content/docs/workflows/reference/limits.mdx b/src/content/docs/workflows/reference/limits.mdx index a23c1e02c17..f3b86701e2a 100644 --- a/src/content/docs/workflows/reference/limits.mdx +++ b/src/content/docs/workflows/reference/limits.mdx @@ -51,7 +51,7 @@ Workflows cannot be deployed to Workers for Platforms namespaces, as Workflows d [^6]: Workflows will return a HTTP 429 rate limited error if you exceed the rate of new Workflow instance creation. -[^7]: Only instances with a `running` state count towards the concurrency limits. Instances in the `waiting` state are excluded from these limits. +[^7]: Only instances with a `running` state count towards the concurrency limits. Instances in the `waiting` state are excluded from these limits. Workers Paid cron-triggered Workflow instances have a separate one-hour cron concurrency budget per firing. [^8]: Each instance created or restarted counts towards this limit @@ -113,6 +113,12 @@ export class MyWorkflow extends WorkflowEntrypoint { While a given Workflow instance is waiting for 30 days, it will transition to the `waiting` state, allowing other `queued` instances to run if concurrency limits are reached. +### Cron-triggered instances on Workers Paid + +On [Workers Paid](/workers/platform/pricing/#workers), Workflow instances created by `schedules` can run for up to one hour per cron firing without consuming a Workflow concurrency slot. + +After that budget is used, the instance yields and enters the normal concurrency queue. It resumes when a concurrency slot is available. The instance does not fail, time out, or terminate because it used this cron concurrency budget. + ### Increasing Workflow step limits Each Workflow instance supports 10,000 steps by default, but this can be increased up to 25,000 steps in your Wrangler configuration. Refer to [Workflow step limits](/workflows/build/workers-api/#workflow-step-limits) for more information.