# Table of Contents - [Overview · Cloudflare Workflows docs](#overview-cloudflare-workflows-docs) - [Unknown](#unknown) - [Call Workflows from Pages · Cloudflare Workflows docs](#call-workflows-from-pages-cloudflare-workflows-docs) - [Workflows · Cloudflare Agents docs](#workflows-cloudflare-agents-docs) - [Test APIs · Cloudflare Workers docs](#test-apis-cloudflare-workers-docs) - [Build with Workflows · Cloudflare Workflows docs](#build-with-workflows-cloudflare-workflows-docs) - [Local Development · Cloudflare Workflows docs](#local-development-cloudflare-workflows-docs) - [Python Workflows SDK · Cloudflare Workflows docs](#python-workflows-sdk-cloudflare-workflows-docs) - [Videos · Cloudflare Workflows docs](#videos-cloudflare-workflows-docs) - [Interact with a Workflow · Cloudflare Workflows docs](#interact-with-a-workflow-cloudflare-workflows-docs) - [DAG Workflows · Cloudflare Workflows docs](#dag-workflows-cloudflare-workflows-docs) - [Sleeping and retrying · Cloudflare Workflows docs](#sleeping-and-retrying-cloudflare-workflows-docs) - [Events and parameters · Cloudflare Workflows docs](#events-and-parameters-cloudflare-workflows-docs) - [Human-in-the-Loop Image Tagging with waitForEvent · Cloudflare Workflows docs](#human-in-the-loop-image-tagging-with-waitforevent-cloudflare-workflows-docs) - [Get started · Cloudflare Workflows docs](#get-started-cloudflare-workflows-docs) - [Examples · Cloudflare Workflows docs](#examples-cloudflare-workflows-docs) - [Overview · Cloudflare Workflows docs](#overview-cloudflare-workflows-docs) - [Metrics and analytics · Cloudflare Workflows docs](#metrics-and-analytics-cloudflare-workflows-docs) - [Trigger Workflows · Cloudflare Workflows docs](#trigger-workflows-cloudflare-workflows-docs) - [Platform · Cloudflare Workflows docs](#platform-cloudflare-workflows-docs) - [Python Workers API · Cloudflare Workflows docs](#python-workers-api-cloudflare-workflows-docs) - [Glossary · Cloudflare Workflows docs](#glossary-cloudflare-workflows-docs) - [Observability · Cloudflare Workflows docs](#observability-cloudflare-workflows-docs) - [Pricing · Cloudflare Workflows docs](#pricing-cloudflare-workflows-docs) - [Event subscriptions · Cloudflare Workflows docs](#event-subscriptions-cloudflare-workflows-docs) - [Limits · Cloudflare Workflows docs](#limits-cloudflare-workflows-docs) - [Pay cart and send invoice · Cloudflare Workflows docs](#pay-cart-and-send-invoice-cloudflare-workflows-docs) - [Rules of Workflows · Cloudflare Workflows docs](#rules-of-workflows-cloudflare-workflows-docs) - [Build your first Workflow · Cloudflare Workflows docs](#build-your-first-workflow-cloudflare-workflows-docs) - [Export and save D1 database · Cloudflare Workflows docs](#export-and-save-d1-database-cloudflare-workflows-docs) - [Build a Durable AI Agent · Cloudflare Workflows docs](#build-a-durable-ai-agent-cloudflare-workflows-docs) - [Wrangler commands · Cloudflare Workflows docs](#wrangler-commands-cloudflare-workflows-docs) - [Changelog · Cloudflare Workflows docs](#changelog-cloudflare-workflows-docs) - [Cloudflare API | Workflows › List All Workflows](#cloudflare-api-workflows-list-all-workflows) - [Workers API · Cloudflare Workflows docs](#workers-api-cloudflare-workflows-docs) - [Integrate Workflows with Twilio · Cloudflare Workflows docs](#integrate-workflows-with-twilio-cloudflare-workflows-docs) - [Changelog | Workflows](#changelog-workflows) - [Cloudflare API | Workflows](#cloudflare-api-workflows) - [Cloudflare API | Workflows › Create Modify Workflow](#cloudflare-api-workflows-create-modify-workflow) - [Cloudflare API | Workflows › Get Workflow Details](#cloudflare-api-workflows-get-workflow-details) - [Cloudflare API | Workflows › Versions](#cloudflare-api-workflows-versions) - [Cloudflare API | Workflows › Deletes A Workflow](#cloudflare-api-workflows-deletes-a-workflow) - [Cloudflare API | Workflows › Instances](#cloudflare-api-workflows-instances) - [Agents · Cloudflare Agents docs](#agents-cloudflare-agents-docs) - [Cloudflare API | Workflows › List All Workflows](#cloudflare-api-workflows-list-all-workflows) --- # Overview · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/#_top) Copy page Cloudflare Workflows ==================== Build durable multi-step applications on Cloudflare Workers with Workflows. Available on Free and Paid plans With Workflows, you can build applications that chain together multiple steps, automatically retry failed tasks, and persist state for minutes, hours, or even weeks - with no infrastructure to manage. Use Workflows to build reliable AI applications, process data pipelines, manage user lifecycle with automated emails and trial expirations, and implement human-in-the-loop approval systems. **Workflows give you:** * Durable multi-step execution without timeouts * The ability to pause for external events or approvals * Automatic retries and error handling * Built-in observability and debugging Example ------- [](https://developers.cloudflare.com/workflows/#example) An image processing workflow that fetches from R2, generates an AI description, waits for approval, then publishes: 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); return await object.arrayBuffer(); }); const description = await step.do('generate description', async () => { const imageArray = Array.from(new Uint8Array(imageData)); return await this.env.AI.run('@cf/llava-hf/llava-1.5-7b-hf', { image: imageArray, prompt: 'Describe this image in one sentence', max_tokens: 50, }); }); await step.waitForEvent('await approval', { event: 'approved', timeout: '24 hours', }); await step.do('publish', async () => { await this.env.BUCKET.put(`public/${event.params.imageKey}`, imageData); }); }} [Get started](https://developers.cloudflare.com/workflows/get-started/guide/) [Browse the examples](https://developers.cloudflare.com/workflows/examples/) * * * Features -------- [](https://developers.cloudflare.com/workflows/#features) ### Durable step execution Break complex operations into durable steps with automatic retries and error handling. [Learn about steps](https://developers.cloudflare.com/workflows/build/workers-api/) ### Sleep and scheduling Pause workflows for seconds, hours, or days with `step.sleep()` and `step.sleepUntil()`. [Add delays](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/) ### Wait for external events Wait for webhooks, user input, or external system responses before continuing execution. [Handle events](https://developers.cloudflare.com/workflows/build/events-and-parameters/) ### Workflow lifecycle management Trigger, pause, resume, and terminate workflow instances programmatically or via API. [Manage instances](https://developers.cloudflare.com/workflows/build/trigger-workflows/) * * * Related products ---------------- [](https://developers.cloudflare.com/workflows/#related-products) **[Workers](https://developers.cloudflare.com/workers/) ** Build serverless applications and deploy instantly across the globe for exceptional performance, reliability, and scale. **[Pages](https://developers.cloudflare.com/pages/) ** Deploy dynamic front-end applications in record time. * * * More resources -------------- [](https://developers.cloudflare.com/workflows/#more-resources) [Pricing](https://developers.cloudflare.com/workflows/reference/pricing/) Learn more about how Workflows is priced. [Limits](https://developers.cloudflare.com/workflows/reference/limits/) Learn more about Workflow limits, and how to work within them. [Storage options](https://developers.cloudflare.com/workers/platform/storage-options/) Learn more about the storage and database options you can build on with Workers. [Developer Discord](https://discord.cloudflare.com/) Connect with the Workers community on Discord to ask questions, show what you are building, and discuss the platform with other developers. [@CloudflareDev](https://x.com/cloudflaredev) Follow @CloudflareDev on Twitter to learn about product announcements, and what is new in Cloudflare Developer Platform. Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings --- # Unknown --- title: Overview · Cloudflare Workflows docs description: >- With Workflows, you can build applications that chain together multiple steps, automatically retry failed tasks, and persist state for minutes, hours, or even weeks - with no infrastructure to manage. lastUpdated: 2025-12-11T17:16:40.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/ md: https://developers.cloudflare.com/workflows/index.md --- Build durable multi-step applications on Cloudflare Workers with Workflows. Available on Free and Paid plans With Workflows, you can build applications that chain together multiple steps, automatically retry failed tasks, and persist state for minutes, hours, or even weeks - with no infrastructure to manage. Use Workflows to build reliable AI applications, process data pipelines, manage user lifecycle with automated emails and trial expirations, and implement human-in-the-loop approval systems. \*\*Workflows give you:\*\* \* Durable multi-step execution without timeouts \* The ability to pause for external events or approvals \* Automatic retries and error handling \* Built-in observability and debugging ## Example An image processing workflow that fetches from R2, generates an AI description, waits for approval, then publishes: \`\`\`ts 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); return await object.arrayBuffer(); }); const description = await step.do('generate description', async () => { const imageArray = Array.from(new Uint8Array(imageData)); return await this.env.AI.run('@cf/llava-hf/llava-1.5-7b-hf', { image: imageArray, prompt: 'Describe this image in one sentence', max\_tokens: 50, }); }); await step.waitForEvent('await approval', { event: 'approved', timeout: '24 hours', }); await step.do('publish', async () => { await this.env.BUCKET.put(\`public/${event.params.imageKey}\`, imageData); }); } } \`\`\` \[Get started\](https://developers.cloudflare.com/workflows/get-started/guide/) \[Browse the examples\](https://developers.cloudflare.com/workflows/examples/) \*\*\* ## Features ### Durable step execution Break complex operations into durable steps with automatic retries and error handling. \[Learn about steps\](https://developers.cloudflare.com/workflows/build/workers-api/) ### Sleep and scheduling Pause workflows for seconds, hours, or days with \`step.sleep()\` and \`step.sleepUntil()\`. \[Add delays\](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/) ### Wait for external events Wait for webhooks, user input, or external system responses before continuing execution. \[Handle events\](https://developers.cloudflare.com/workflows/build/events-and-parameters/) ### Workflow lifecycle management Trigger, pause, resume, and terminate workflow instances programmatically or via API. \[Manage instances\](https://developers.cloudflare.com/workflows/build/trigger-workflows/) \*\*\* ## Related products \*\*\[Workers\](https://developers.cloudflare.com/workers/)\*\* Build serverless applications and deploy instantly across the globe for exceptional performance, reliability, and scale. \*\*\[Pages\](https://developers.cloudflare.com/pages/)\*\* Deploy dynamic front-end applications in record time. \*\*\* ## More resources \[Pricing\](https://developers.cloudflare.com/workflows/reference/pricing/) Learn more about how Workflows is priced. \[Limits\](https://developers.cloudflare.com/workflows/reference/limits/) Learn more about Workflow limits, and how to work within them. \[Storage options\](https://developers.cloudflare.com/workers/platform/storage-options/) Learn more about the storage and database options you can build on with Workers. \[Developer Discord\](https://discord.cloudflare.com) Connect with the Workers community on Discord to ask questions, show what you are building, and discuss the platform with other developers. \[@CloudflareDev\](https://x.com/cloudflaredev) Follow @CloudflareDev on Twitter to learn about product announcements, and what is new in Cloudflare Developer Platform. --- title: 404 - Page Not Found · Cloudflare Workflows docs chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/404/ md: https://developers.cloudflare.com/workflows/404/index.md --- # 404 Check the URL, try using our \[search\](https://developers.cloudflare.com/search/) or try our LLM-friendly \[llms.txt directory\](https://developers.cloudflare.com/llms.txt). --- title: Build with Workflows · Cloudflare Workflows docs lastUpdated: 2024-10-24T11:52:00.000Z chatbotDeprioritize: true source\_url: html: https://developers.cloudflare.com/workflows/build/ md: https://developers.cloudflare.com/workflows/build/index.md --- \* \[Workers API\](https://developers.cloudflare.com/workflows/build/workers-api/) \* \[Trigger Workflows\](https://developers.cloudflare.com/workflows/build/trigger-workflows/) \* \[Sleeping and retrying\](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/) \* \[Events and parameters\](https://developers.cloudflare.com/workflows/build/events-and-parameters/) \* \[Local Development\](https://developers.cloudflare.com/workflows/build/local-development/) \* \[Rules of Workflows\](https://developers.cloudflare.com/workflows/build/rules-of-workflows/) \* \[Call Workflows from Pages\](https://developers.cloudflare.com/workflows/build/call-workflows-from-pages/) \* \[Test Workflows\](https://developers.cloudflare.com/workers/testing/vitest-integration/test-apis/#workflows) --- title: Examples · Cloudflare Workflows docs description: Explore the following examples for Workflows. lastUpdated: 2025-08-18T14:27:42.000Z chatbotDeprioritize: true source\_url: html: https://developers.cloudflare.com/workflows/examples/ md: https://developers.cloudflare.com/workflows/examples/index.md --- Explore the following examples for Workflows. \[Pay cart and send invoice\](https://developers.cloudflare.com/workflows/examples/send-invoices/) \[Send invoice when shopping cart is checked out and paid for\](https://developers.cloudflare.com/workflows/examples/send-invoices/) \[Export and save D1 database\](https://developers.cloudflare.com/workflows/examples/backup-d1/) \[Send invoice when shopping cart is checked out and paid for\](https://developers.cloudflare.com/workflows/examples/backup-d1/) \[Integrate Workflows with Twilio\](https://developers.cloudflare.com/workflows/examples/twilio/) \[Integrate Workflows with Twilio. Learn how to receive and send text messages and phone calls via APIs and Webhooks.\](https://developers.cloudflare.com/workflows/examples/twilio/) \[Human-in-the-Loop Image Tagging with waitForEvent\](https://developers.cloudflare.com/workflows/examples/wait-for-event/) \[Human-in-the-loop Workflow with waitForEvent API\](https://developers.cloudflare.com/workflows/examples/wait-for-event/) --- title: Get started · Cloudflare Workflows docs lastUpdated: 2026-01-22T21:38:43.000Z chatbotDeprioritize: true source\_url: html: https://developers.cloudflare.com/workflows/get-started/ md: https://developers.cloudflare.com/workflows/get-started/index.md --- \* \[Build your first Workflow\](https://developers.cloudflare.com/workflows/get-started/guide/) \* \[Build a Durable AI Agent\](https://developers.cloudflare.com/workflows/get-started/durable-agents/) --- title: Observability · Cloudflare Workflows docs lastUpdated: 2024-10-24T11:52:00.000Z chatbotDeprioritize: true source\_url: html: https://developers.cloudflare.com/workflows/observability/ md: https://developers.cloudflare.com/workflows/observability/index.md --- \* \[Metrics and analytics\](https://developers.cloudflare.com/workflows/observability/metrics-analytics/) --- title: Python Workflows SDK · Cloudflare Workflows docs description: >- Workflow entrypoints can be declared using Python. To achieve this, you can export a WorkflowEntrypoint that runs on the Cloudflare Workers platform. Refer to Python Workers for more information about Python on the Workers runtime. lastUpdated: 2025-11-24T16:55:57.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/python/ md: https://developers.cloudflare.com/workflows/python/index.md --- Workflow entrypoints can be declared using Python. To achieve this, you can export a \`WorkflowEntrypoint\` that runs on the Cloudflare Workers platform. Refer to \[Python Workers\](https://developers.cloudflare.com/workers/languages/python) for more information about Python on the Workers runtime. Python Workflows are in beta, as well as the underlying platform. Join the #python-workers channel in the \[Cloudflare Developers Discord\](https://discord.cloudflare.com/) and let us know what you'd like to see next. ## Get Started The main entrypoint for a Python workflow is the \[\`WorkflowEntrypoint\`\](https://developers.cloudflare.com/workflows/build/workers-api/#workflowentrypoint) class. Your workflow logic should exist inside the \[\`run\`\](https://developers.cloudflare.com/workflows/build/workers-api/#run) handler. \`\`\`python from workers import WorkflowEntrypoint class MyWorkflow(WorkflowEntrypoint): async def run(self, event, step): # steps here \`\`\` For example, a Workflow may be defined as: \`\`\`python from workers import Response, WorkflowEntrypoint class PythonWorkflowStarter(WorkflowEntrypoint): async def run(self, event, step): @step.do('step1') async def step\_1(): # does stuff print('executing step1') @step.do('step2') async def step\_2(): # does stuff print('executing step2') await await\_step(step\_1,step\_2) async def on\_fetch(request, env): await env.MY\_WORKFLOW.create() return Response("Hello world!") \`\`\` You must add both \`python\_workflows\` and \`python\_workers\` compatibility flags to your \`wrangler.toml\` file. \* wrangler.jsonc \`\`\`jsonc { "$schema": "./node\_modules/wrangler/config-schema.json", "name": "hello-python", "main": "src/entry.py", "compatibility\_flags": \[\ "python\_workers",\ "experimental",\ "python\_workflows"\ \], "compatibility\_date": "2024-03-29", "workflows": \[\ {\ "name": "workflows-demo",\ "binding": "MY\_WORKFLOW",\ "class\_name": "PythonWorkflowStarter"\ }\ \] } \`\`\` \* wrangler.toml \`\`\`toml name = "hello-python" main = "src/entry.py" compatibility\_flags = \["python\_workers", "experimental", "python\_workflows"\] compatibility\_date = "2024-03-29" \[\[workflows\]\] name = "workflows-demo" binding = "MY\_WORKFLOW" class\_name = "PythonWorkflowStarter" \`\`\` To run a Python Workflow locally, use \[Wrangler\](https://developers.cloudflare.com/workers/wrangler/), the CLI for Cloudflare Workers: \`\`\`bash npx wrangler@latest dev \`\`\` To deploy a Python Workflow to Cloudflare, run \[\`wrangler deploy\`\](https://developers.cloudflare.com/workers/wrangler/commands/#deploy): \`\`\`bash npx wrangler@latest deploy \`\`\` Join the #python-workers channel in the \[Cloudflare Developers Discord\](https://discord.cloudflare.com/) and let us know what you would like to see next. --- title: Platform · Cloudflare Workflows docs lastUpdated: 2025-03-07T09:55:39.000Z chatbotDeprioritize: true source\_url: html: https://developers.cloudflare.com/workflows/reference/ md: https://developers.cloudflare.com/workflows/reference/index.md --- \* \[Pricing\](https://developers.cloudflare.com/workflows/reference/pricing/) \* \[Limits\](https://developers.cloudflare.com/workflows/reference/limits/) \* \[Event subscriptions\](https://developers.cloudflare.com/workflows/reference/event-subscriptions/) \* \[Glossary\](https://developers.cloudflare.com/workflows/reference/glossary/) \* \[Wrangler commands\](https://developers.cloudflare.com/workflows/reference/wrangler-commands/) \* \[Changelog\](https://developers.cloudflare.com/workflows/reference/changelog/) --- title: Videos · Cloudflare Workflows docs lastUpdated: 2025-05-08T09:06:01.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/videos/ md: https://developers.cloudflare.com/workflows/videos/index.md --- \[Build an application using Cloudflare Workflows \](https://developers.cloudflare.com/learning-paths/workflows-course/series/workflows-1/)In this series, we introduce Cloudflare Workflows and the term 'Durable Execution' which comes from the desire to run applications that can resume execution from where they left off, even if the underlying host or compute fails. --- title: Workflows REST API · Cloudflare Workflows docs lastUpdated: 2024-12-16T22:33:26.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/workflows-api/ md: https://developers.cloudflare.com/workflows/workflows-api/index.md --- --- title: Call Workflows from Pages · Cloudflare Workflows docs description: 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. lastUpdated: 2025-12-03T14:33:51.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/build/call-workflows-from-pages/ md: https://developers.cloudflare.com/workflows/build/call-workflows-from-pages/index.md --- Use Static Assets To call Workflows from Pages, you are required to deploy a separate Worker containing your Workflows. We recommend using \[\*\*Static Assets\*\*\](https://developers.cloudflare.com/workers/static-assets/) instead, as this allows you to add your Workflows directly to your Static Assets Worker. If you wish to migrate your Pages project to Static Assets, follow this \[guide\](https://developers.cloudflare.com/workers/static-assets/migration-guides/migrate-from-pages/). \*\*\* You can bind and trigger Workflows from \[Pages Functions\](https://developers.cloudflare.com/pages/functions/) by deploying a Workers project with your Workflow definition and then invoking that Worker using \[service bindings\](https://developers.cloudflare.com/pages/functions/bindings/#service-bindings) or a standard \`fetch()\` call. Note You will need to deploy your Workflow as a standalone Workers project first before your Pages Function can call it. If you have not yet deployed a Workflow, refer to the Workflows \[get started guide\](https://developers.cloudflare.com/workflows/get-started/guide/). ### Use Service Bindings \[Service Bindings\](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/) allow you to call a Worker from another Worker or a Pages Function without needing to expose it directly. To do this, you will need to: 1. Deploy your Workflow in a Worker 2. Create a Service Binding to that Worker in your Pages project 3. Call the Worker remotely using the binding For example, if you have a Worker called \`workflows-starter\`, you would create a new Service Binding in your Pages project as follows, ensuring that the \`service\` name matches the name of the Worker your Workflow is defined in: \* wrangler.jsonc \`\`\`jsonc { "$schema": "./node\_modules/wrangler/config-schema.json", "services": \[\ {\ "binding": "WORKFLOW\_SERVICE",\ "service": "workflows-starter"\ }\ \] } \`\`\` \* wrangler.toml \`\`\`toml services = \[\ { binding = "WORKFLOW\_SERVICE", service = "workflows-starter" }\ \] \`\`\` Your Worker can expose a specific method (or methods) that only other Workers or Pages Functions can call over the Service Binding. In the following example, we expose a specific \`createInstance\` method that accepts our \`Payload\` and returns the \[\`InstanceStatus\`\](https://developers.cloudflare.com/workflows/build/workers-api/#instancestatus) from the Workflows API: \* JavaScript \`\`\`js import { WorkerEntrypoint } from "cloudflare:workers"; export default class WorkflowsService extends WorkerEntrypoint { // Currently, entrypoints without a named handler are not supported async fetch() { return new Response(null, { status: 404 }); } async createInstance(payload) { let instance = await this.env.MY\_WORKFLOW.create({ params: payload, }); return Response.json({ id: instance.id, details: await instance.status(), }); } } \`\`\` \* TypeScript \`\`\`ts import { WorkerEntrypoint } from "cloudflare:workers"; interface Env { MY\_WORKFLOW: Workflow; } type Payload = { hello: string; }; export default class WorkflowsService extends WorkerEntrypoint { // Currently, entrypoints without a named handler are not supported async fetch() { return new Response(null, { status: 404 }); } async createInstance(payload: Payload) { let instance = await this.env.MY\_WORKFLOW.create({ params: payload, }); return Response.json({ id: instance.id, details: await instance.status(), }); } } \`\`\` Your Pages Function would resemble the following: \* JavaScript \`\`\`js export const onRequest = async (context) => { // This payload could be anything from within your app or from your frontend let payload = { hello: "world" }; return context.env.WORKFLOWS\_SERVICE.createInstance(payload); }; \`\`\` \* TypeScript \`\`\`ts interface Env { WORKFLOW\_SERVICE: Service; } export const onRequest: PagesFunction = async (context) => { // This payload could be anything from within your app or from your frontend let payload = { hello: "world" }; return context.env.WORKFLOWS\_SERVICE.createInstance(payload); }; \`\`\` To learn more about binding to resources from Pages Functions, including how to bind via the Cloudflare dashboard, refer to the \[bindings documentation for Pages Functions\](https://developers.cloudflare.com/pages/functions/bindings/#service-bindings). ### Using fetch Service Bindings vs. fetch We recommend using \[Service Bindings\](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/) when calling a Worker in your own account. Service Bindings don't require you to expose a public endpoint from your Worker, don't require you to configure authentication, and allow you to call methods on your Worker directly, avoiding the overhead of managing HTTP requests and responses. An alternative to setting up a Service Binding is to call the Worker over HTTP by using the Workflows \[Workers API\](https://developers.cloudflare.com/workflows/build/workers-api/#workflow) to \`create\` a new Workflow instance for each incoming HTTP call to the Worker: \* JavaScript \`\`\`js // This is in the same file as your Workflow definition export default { async fetch(req, env) { let instance = await env.MY\_WORKFLOW.create({ params: payload, }); return Response.json({ id: instance.id, details: await instance.status(), }); }, }; \`\`\` \* TypeScript \`\`\`ts // This is in the same file as your Workflow definition export default { async fetch(req: Request, env: Env): Promise { let instance = await env.MY\_WORKFLOW.create({ params: payload, }); return Response.json({ id: instance.id, details: await instance.status(), }); }, }; \`\`\` Your \[Pages Function\](https://developers.cloudflare.com/pages/functions/get-started/) can then make a regular \`fetch\` call to the Worker: \* JavaScript \`\`\`js export const onRequest = async (context) => { // Other code let payload = { hello: "world" }; const instanceStatus = await fetch("https://YOUR\_WORKER.workers.dev/", { method: "POST", body: JSON.stringify(payload), // Send a payload for our Worker to pass to the Workflow }); return Response.json(instanceStatus); }; \`\`\` \* TypeScript \`\`\`ts export const onRequest: PagesFunction = async (context) => { // Other code let payload = { hello: "world" }; const instanceStatus = await fetch("https://YOUR\_WORKER.workers.dev/", { method: "POST", body: JSON.stringify(payload), // Send a payload for our Worker to pass to the Workflow }); return Response.json(instanceStatus); }; \`\`\` You can also choose to authenticate these requests by passing a shared secret in a header and validating that in your Worker. ### Next steps \* Learn more about how to programatically call and trigger Workflows from the \[Workers API\](https://developers.cloudflare.com/workflows/build/workers-api/) \* Understand how to send \[events and parameters\](https://developers.cloudflare.com/workflows/build/events-and-parameters/) when triggering a Workflow \* Review the \[Rules of Workflows\](https://developers.cloudflare.com/workflows/build/rules-of-workflows/) and best practices for writing Workflows --- title: Events and parameters · Cloudflare Workflows docs description: When a Workflow is triggered, it can receive an optional event. This event can include data that your Workflow can act on, including request details, user data fetched from your database (such as D1 or KV) or from a webhook, or messages from a Queue consumer. lastUpdated: 2026-01-12T21:16:35.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/build/events-and-parameters/ md: https://developers.cloudflare.com/workflows/build/events-and-parameters/index.md --- When a Workflow is triggered, it can receive an optional event. This event can include data that your Workflow can act on, including request details, user data fetched from your database (such as D1 or KV) or from a webhook, or messages from a Queue consumer. Events are a powerful part of a Workflow, as you often want a Workflow to act on data. Because a given Workflow instance executes durably, events are a useful way to provide a Workflow with data that should be immutable (not changing) and/or represents data the Workflow needs to operate on at that point in time. ## Pass data to a Workflow You can pass parameters to a Workflow in three ways: \* As an optional argument to the \`create\` method on a \[Workflow binding\](https://developers.cloudflare.com/workers/wrangler/commands/#trigger) when triggering a Workflow from a Worker. \* Via the \`--params\` flag when using the \`wrangler\` CLI to trigger a Workflow. \* Via the \`step.waitForEvent\` API, which allows a Workflow instance to wait for an event (and optional data) to be received \*while it is running\*. Workflow instances can be sent events from external services over HTTP or via the Workers API for Workflows. You can pass any JSON-serializable object as a parameter. Warning A \`WorkflowEvent\` and its associated \`payload\` property are effectively \*immutable\*: any changes to an event are not persisted across the steps of a Workflow. This includes both cases when a Workflow is progressing normally, and in cases where a Workflow has to be restarted due to a failure. Store state durably by returning it from your \`step.do\` callbacks. \* JavaScript \`\`\`js export default { async fetch(req, env) { let someEvent = { url: req.url, createdTimestamp: Date.now() }; // Trigger our Workflow // Pass our event as the second parameter to the \`create\` method // on our Workflow binding. let instance = await env.MY\_WORKFLOW.create({ id: crypto.randomUUID(), params: someEvent, }); return Response.json({ id: instance.id, details: await instance.status(), }); }, }; \`\`\` \* TypeScript \`\`\`ts export default { async fetch(req: Request, env: Env) { let someEvent = { url: req.url, createdTimestamp: Date.now() }; // Trigger our Workflow // Pass our event as the second parameter to the \`create\` method // on our Workflow binding. let instance = await env.MY\_WORKFLOW.create({ id: crypto.randomUUID(), params: someEvent, }); return Response.json({ id: instance.id, details: await instance.status(), }); }, }; \`\`\` To pass parameters via the \`wrangler\` command-line interface, pass a JSON string as the second parameter to the \`workflows trigger\` sub-command: \`\`\`sh npx wrangler@latest workflows trigger workflows-starter '{"some":"data"}' \`\`\` \`\`\`sh 🚀 Workflow instance "57c7913b-8e1d-4a78-a0dd-dce5a0b7aa30" has been queued successfully \`\`\` ### Wait for events A running Workflow can wait for an event (or events) by calling \`step.waitForEvent\` within the Workflow, which allows you to send events to the Workflow in one of two ways: 1. Via the \[Workers API binding\](https://developers.cloudflare.com/workflows/build/workers-api/): call \`instance.sendEvent\` to send events to specific workflow instances. 2. Using the REST API (HTTP API)'s \[Events endpoint\](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/subresources/events/methods/create/). Because \`waitForEvent\` is part of the \`WorkflowStep\` API, you can call it multiple times within a Workflow, and use control flow to conditionally wait for an event. Calling \`waitForEvent\` requires you to specify an \`type\` (up to 100 characters \[1\](#user-content-fn-1)), which is used to match the corresponding \`type\` when sending an event to a Workflow instance. Warning The \`waitForEvent\` type parameter only supports letters, digits, \`-\`, and \`\_\`. Currently including \`.\` is not supported and will result in a \`workflow.invalid\_event\_type\` error. For example, to wait for billing webhook: \* JavaScript \`\`\`js export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // Other steps in your Workflow let event = await step.waitForEvent( "receive invoice paid webhook from Stripe", { type: "stripe-webhook", timeout: "1 hour" }, ); // Rest of your Workflow } } \`\`\` \* TypeScript \`\`\`ts export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // Other steps in your Workflow let event = await step.waitForEvent( "receive invoice paid webhook from Stripe", { type: "stripe-webhook", timeout: "1 hour" }, ); // Rest of your Workflow } } \`\`\` The above example: \* Calls \`waitForEvent\` with a \`type\` of \`stripe-webhook\` - the corresponding \`sendEvent\` call would thus be \`await instance.sendEvent({type: "stripe-webhook", payload: webhookPayload})\`. \* Uses a TypeScript \[type parameter\](https://www.typescriptlang.org/docs/handbook/2/generics.html) to type the return value of \`step.waitForEvent\` as our \`IncomingStripeWebhook\`. \* Continues on with the rest of the Workflow. The default timeout for a \`waitForEvent\` call is 24 hours, which can be changed by passing \`{ timeout: WorkflowTimeoutDuration }\` as the second argument to your \`waitForEvent\` call. \* JavaScript \`\`\`js let event = await step.waitForEvent("wait for human approval", { type: "approval-flow", timeout: "15 minutes", }); \`\`\` \* TypeScript \`\`\`ts let event = await step.waitForEvent( "wait for human approval", { type: "approval-flow", timeout: "15 minutes" }, ); \`\`\` You can specify a timeout between 1 second and up to 365 days. Timeout behavior When \`waitForEvent\` times out, the Workflow will throw an error and the instance will fail. If you want your Workflow to continue even if the event is not received, wrap the \`waitForEvent\` call in a try-catch block: \* JavaScript \`\`\`js try { const event = await step.waitForEvent("wait for approval", { type: "approval", timeout: "1 hour", }); // Handle the received event } catch (e) { // Timeout occurred - handle the case where no event was received console.log("No approval received, proceeding with default action"); } \`\`\` \* TypeScript \`\`\`ts try { const event = await step.waitForEvent("wait for approval", { type: "approval", timeout: "1 hour", }); // Handle the received event } catch (e) { // Timeout occurred - handle the case where no event was received console.log("No approval received, proceeding with default action"); } \`\`\` ### Send events to running workflows Workflow instances that are waiting on events using the \`waitForEvent\` API can be sent events using the \`instance.sendEvent\` API: \* JavaScript \`\`\`js export default { async fetch(req, env) { const instanceId = new URL(req.url).searchParams.get("instanceId"); const webhookPayload = await req.json(); let instance = await env.MY\_WORKFLOW.get(instanceId); // Send our event, with \`type\` matching the event type defined in // our step.waitForEvent call await instance.sendEvent({ type: "stripe-webhook", payload: webhookPayload, }); return Response.json({ status: await instance.status(), }); }, }; \`\`\` \* TypeScript \`\`\`ts export default { async fetch(req: Request, env: Env) { const instanceId = new URL(req.url).searchParams.get("instanceId"); const webhookPayload = await req.json(); let instance = await env.MY\_WORKFLOW.get(instanceId); // Send our event, with \`type\` matching the event type defined in // our step.waitForEvent call await instance.sendEvent({ type: "stripe-webhook", payload: webhookPayload, }); return Response.json({ status: await instance.status(), }); }, }; \`\`\` - Similar to the \[\`waitForEvent\`\](#wait-for-events) example in this guide, the \`type\` property in our \`waitForEvent\` and \`sendEvent\` fields must match. - To send multiple events to a Workflow that has multiple \`waitForEvent\` calls, call \`sendEvent\` with the corresponding \`type\` property set (up to 100 characters \[1\](#user-content-fn-1)). - Events can also be sent using the REST API (HTTP API)'s \[Events endpoint\](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/subresources/events/methods/create/). Event timing You can send an event to a Workflow instance \*before\* it reaches the corresponding \`waitForEvent\` call, as long as the instance has been created. The event will be buffered and delivered when the Workflow reaches the \`waitForEvent\` step with the matching \`type\`. ## 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: \`\`\`ts export type WorkflowEvent = { // The data passed as the parameter when the Workflow instance was triggered payload: T; // The timestamp that the Workflow was triggered timestamp: Date; // ID of the current Workflow instance instanceId: string; }; \`\`\` 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 // Define a type that conforms to the events your Workflow instance is // instantiated with interface YourEventType { userEmail: string; createdTimestamp: number; metadata?: Record; } \`\`\` When you pass your \`YourEventType\` to \`WorkflowEvent\` as a type parameter, the \`event.payload\` property now has the type \`YourEventType\` throughout your workflow definition: \`\`\`ts // Import the Workflow definition import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent} from 'cloudflare:workers'; export class MyWorkflow extends WorkflowEntrypoint { // Pass your type as a type parameter to WorkflowEvent // The 'payload' property will have the type of your parameter. async run(event: WorkflowEvent, step: WorkflowStep) { let state = step.do("my first step", async () => { // Access your properties via event.payload let userEmail = event.payload.userEmail let createdTimestamp = event.payload.createdTimestamp }) step.do("my second step", async () => { /\* your code here \*/ ) } } \`\`\` Warning 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\](https://zod.dev/) 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\](https://developers.cloudflare.com/workflows/build/workers-api/#workflow). Note that this does \*not\* propagate type information into the Workflow itself, as TypeScript types are a build-time construct. To provide the type of an incoming \`WorkflowEvent\`, refer to the \[TypeScript and type parameters\](https://developers.cloudflare.com/workflows/build/events-and-parameters/#typescript-and-type-parameters) section of the Workflows documentation. ## Footnotes 1. Match pattern: \`^\[a-zA-Z0-9\_\]\[a-zA-Z0-9-\_\]\*$\` \[↩\](#user-content-fnref-1) \[↩2\](#user-content-fnref-1-2) --- title: Local Development · Cloudflare Workflows docs description: Workflows support local development using Wrangler, the command-line interface for Workers. Wrangler runs an emulated version of Workflows compared to the one that Cloudflare runs globally. lastUpdated: 2025-09-18T22:01:54.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/build/local-development/ md: https://developers.cloudflare.com/workflows/build/local-development/index.md --- Workflows support local development using \[Wrangler\](https://developers.cloudflare.com/workers/wrangler/install-and-update/), the command-line interface for Workers. Wrangler runs an emulated version of Workflows compared to the one that Cloudflare runs globally. ## Prerequisites To develop locally with Workflows, you will need: \* \[Wrangler v3.89.0\](https://blog.cloudflare.com/wrangler3/) or later. \* Node.js version of \`18.0.0\` or later. Consider using a Node version manager like \[Volta\](https://volta.sh/) or \[nvm\](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node versions. \* If you are new to Workflows and/or Cloudflare Workers, refer to the \[Workflows Guide\](https://developers.cloudflare.com/workflows/get-started/guide/) to install \`wrangler\` and deploy their first Workflows. ## Start a local development session Open your terminal and run the following commands to start a local development session: \`\`\`sh # Confirm we are using wrangler v3.89.0+ npx wrangler --version \`\`\` \`\`\`sh ⛅️ wrangler 3.89.0 \`\`\` Start a local dev session \`\`\`sh # Start a local dev session: npx wrangler dev \`\`\` \`\`\`sh ------------------ Your worker has access to the following bindings: - Workflows: - MY\_WORKFLOW: MyWorkflow ⎔ Starting local server... \[wrangler:inf\] Ready on http://127.0.0.1:8787/ \`\`\` Local development sessions create a standalone, local-only environment that mirrors the production environment Workflows runs in so you can test your Workflows \*before\* you deploy to production. Refer to the \[\`wrangler dev\` documentation\](https://developers.cloudflare.com/workers/wrangler/commands/#dev) to learn more about how to configure a local development session. ## Known Issues Workflows are not supported as \[remote bindings\](https://developers.cloudflare.com/workers/development-testing/#remote-bindings) or when using \`npx wrangler dev --remote\`. Wrangler Workflows commands \`npx wrangler workflow \[cmd\]\` are not supported for local development, as they target production API. --- title: Rules of Workflows · Cloudflare Workflows docs description: A Workflow contains one or more steps. Each step is a self-contained, individually retriable component of a Workflow. Steps may emit (optional) state that allows a Workflow to persist and continue from that step, even if a Workflow fails due to a network or infrastructure issue. lastUpdated: 2026-01-22T21:38:43.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/build/rules-of-workflows/ md: https://developers.cloudflare.com/workflows/build/rules-of-workflows/index.md --- A Workflow contains one or more steps. Each step is a self-contained, individually retriable component of a Workflow. Steps may emit (optional) state that allows a Workflow to persist and continue from that step, even if a Workflow fails due to a network or infrastructure issue. This is a small guidebook on how to build more resilient and correct Workflows. ### Ensure API/Binding calls are idempotent Because a step might be retried multiple times, your steps should (ideally) be idempotent. For context, idempotency is a logical property where the operation (in this case a step), can be applied multiple times without changing the result beyond the initial application. As an example, let us assume you have a Workflow that charges your customers, and you really do not want to charge them twice by accident. Before charging them, you should check if they were already charged: \* JavaScript \`\`\`js export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { const customer\_id = 123456; // ✅ Good: Non-idempotent API/Binding calls are always done \*\*after\*\* checking if the operation is // still needed. await step.do( \`charge ${customer\_id} for its monthly subscription\`, async () => { // API call to check if customer was already charged const subscription = await fetch( \`https://payment.processor/subscriptions/${customer\_id}\`, ).then((res) => res.json()); // return early if the customer was already charged, this can happen if the destination service dies // in the middle of the request but still commits it, or if the Workflows Engine restarts. if (subscription.charged) { return; } // non-idempotent call, this operation can fail and retry but still commit in the payment // processor - which means that, on retry, it would mischarge the customer again if the above checks // were not in place. return await fetch( \`https://payment.processor/subscriptions/${customer\_id}\`, { method: "POST", body: JSON.stringify({ amount: 10.0 }), }, ); }, ); } } \`\`\` \* TypeScript \`\`\`ts export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { const customer\_id = 123456; // ✅ Good: Non-idempotent API/Binding calls are always done \*\*after\*\* checking if the operation is // still needed. await step.do( \`charge ${customer\_id} for its monthly subscription\`, async () => { // API call to check if customer was already charged const subscription = await fetch( \`https://payment.processor/subscriptions/${customer\_id}\`, ).then((res) => res.json()); // return early if the customer was already charged, this can happen if the destination service dies // in the middle of the request but still commits it, or if the Workflows Engine restarts. if (subscription.charged) { return; } // non-idempotent call, this operation can fail and retry but still commit in the payment // processor - which means that, on retry, it would mischarge the customer again if the above checks // were not in place. return await fetch( \`https://payment.processor/subscriptions/${customer\_id}\`, { method: "POST", body: JSON.stringify({ amount: 10.0 }), }, ); }, ); } } \`\`\` Note Guaranteeing idempotency might be optional in your specific use-case and implementation, but we recommend that you always try to guarantee it. ### Make your steps granular Steps should be as self-contained as possible. This allows your own logic to be more durable in case of failures in third-party APIs, network errors, and so on. You can also think of it as a transaction, or a unit of work. \* ✅ Minimize the number of API/binding calls per step (unless you need multiple calls to prove idempotency). - JavaScript \`\`\`js export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // ✅ Good: Unrelated API/Binding calls are self-contained, so that in case one of them fails // it can retry them individually. It also has an extra advantage: you can control retry or // timeout policies for each granular step - you might not to want to overload http.cat in // case of it being down. const httpCat = await step.do("get cutest cat from KV", async () => { return await env.KV.get("cutest-http-cat"); }); const image = await step.do("fetch cat image from http.cat", async () => { return await fetch(\`https://http.cat/${httpCat}\`); }); } } \`\`\` - TypeScript \`\`\`ts export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // ✅ Good: Unrelated API/Binding calls are self-contained, so that in case one of them fails // it can retry them individually. It also has an extra advantage: you can control retry or // timeout policies for each granular step - you might not to want to overload http.cat in // case of it being down. const httpCat = await step.do("get cutest cat from KV", async () => { return await env.KV.get("cutest-http-cat"); }); const image = await step.do("fetch cat image from http.cat", async () => { return await fetch(\`https://http.cat/${httpCat}\`); }); } } \`\`\` Otherwise, your entire Workflow might not be as durable as you might think, and you may encounter some undefined behaviour. You can avoid them by following the rules below: \* 🔴 Do not encapsulate your entire logic in one single step. \* 🔴 Do not call separate services in the same step (unless you need it to prove idempotency). \* 🔴 Do not make too many service calls in the same step (unless you need it to prove idempotency). \* 🔴 Do not do too much CPU-intensive work inside a single step - sometimes the engine may have to restart, and it will start over from the beginning of that step. - JavaScript \`\`\`js export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // 🔴 Bad: you are calling two separate services from within the same step. This might cause // some extra calls to the first service in case the second one fails, and in some cases, makes // the step non-idempotent altogether const image = await step.do("get cutest cat from KV", async () => { const httpCat = await env.KV.get("cutest-http-cat"); return fetch(\`https://http.cat/${httpCat}\`); }); } } \`\`\` - TypeScript \`\`\`ts export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // 🔴 Bad: you are calling two separate services from within the same step. This might cause // some extra calls to the first service in case the second one fails, and in some cases, makes // the step non-idempotent altogether const image = await step.do("get cutest cat from KV", async () => { const httpCat = await env.KV.get("cutest-http-cat"); return fetch(\`https://http.cat/${httpCat}\`); }); } } \`\`\` ### Do not rely on state outside of a step Workflows may hibernate and lose all in-memory state. This will happen when engine detects that there is no pending work and can hibernate until it needs to wake-up (because of a sleep, retry, or event). This means that you should not store state outside of a step: \* JavaScript \`\`\`js function getRandomInt(min, max) { const minCeiled = Math.ceil(min); const maxFloored = Math.floor(max); return Math.floor(Math.random() \* (maxFloored - minCeiled) + minCeiled); // The maximum is exclusive and the minimum is inclusive } export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // 🔴 Bad: \`imageList\` will be not persisted across engine's lifetimes. Which means that after hibernation, // \`imageList\` will be empty again, even though the following two steps have already ran. const imageList = \[\]; await step.do("get first cutest cat from KV", async () => { const httpCat = await env.KV.get("cutest-http-cat-1"); imageList.append(httpCat); }); await step.do("get second cutest cat from KV", async () => { const httpCat = await env.KV.get("cutest-http-cat-2"); imageList.append(httpCat); }); // A long sleep can (and probably will) hibernate the engine which means that the first engine lifetime ends here await step.sleep("💤💤💤💤", "3 hours"); // When this runs, it will be on the second engine lifetime - which means \`imageList\` will be empty. await step.do( "choose a random cat from the list and download it", async () => { const randomCat = imageList.at(getRandomInt(0, imageList.length)); // this will fail since \`randomCat\` is undefined because \`imageList\` is empty return await fetch(\`https://http.cat/${randomCat}\`); }, ); } } \`\`\` \* TypeScript \`\`\`ts function getRandomInt(min, max) { const minCeiled = Math.ceil(min); const maxFloored = Math.floor(max); return Math.floor(Math.random() \* (maxFloored - minCeiled) + minCeiled); // The maximum is exclusive and the minimum is inclusive } export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // 🔴 Bad: \`imageList\` will be not persisted across engine's lifetimes. Which means that after hibernation, // \`imageList\` will be empty again, even though the following two steps have already ran. const imageList: string\[\] = \[\]; await step.do("get first cutest cat from KV", async () => { const httpCat = await env.KV.get("cutest-http-cat-1"); imageList.append(httpCat); }); await step.do("get second cutest cat from KV", async () => { const httpCat = await env.KV.get("cutest-http-cat-2"); imageList.append(httpCat); }); // A long sleep can (and probably will) hibernate the engine which means that the first engine lifetime ends here await step.sleep("💤💤💤💤", "3 hours"); // When this runs, it will be on the second engine lifetime - which means \`imageList\` will be empty. await step.do( "choose a random cat from the list and download it", async () => { const randomCat = imageList.at(getRandomInt(0, imageList.length)); // this will fail since \`randomCat\` is undefined because \`imageList\` is empty return await fetch(\`https://http.cat/${randomCat}\`); }, ); } } \`\`\` Instead, you should build top-level state exclusively comprised of \`step.do\` returns: \* JavaScript \`\`\`js function getRandomInt(min, max) { const minCeiled = Math.ceil(min); const maxFloored = Math.floor(max); return Math.floor(Math.random() \* (maxFloored - minCeiled) + minCeiled); // The maximum is exclusive and the minimum is inclusive } export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // ✅ Good: imageList state is exclusively comprised of step returns - this means that in the event of // multiple engine lifetimes, imageList will be built accordingly const imageList = await Promise.all(\[\ step.do("get first cutest cat from KV", async () => {\ return await env.KV.get("cutest-http-cat-1");\ }),\ \ \ step.do("get second cutest cat from KV", async () => {\ return await env.KV.get("cutest-http-cat-2");\ }),\ \]); // A long sleep can (and probably will) hibernate the engine which means that the first engine lifetime ends here await step.sleep("💤💤💤💤", "3 hours"); // When this runs, it will be on the second engine lifetime - but this time, imageList will contain // the two most cutest cats await step.do( "choose a random cat from the list and download it", async () => { const randomCat = imageList.at(getRandomInt(0, imageList.length)); // this will eventually succeed since \`randomCat\` is defined return await fetch(\`https://http.cat/${randomCat}\`); }, ); } } \`\`\` \* TypeScript \`\`\`ts function getRandomInt(min, max) { const minCeiled = Math.ceil(min); const maxFloored = Math.floor(max); return Math.floor(Math.random() \* (maxFloored - minCeiled) + minCeiled); // The maximum is exclusive and the minimum is inclusive } export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // ✅ Good: imageList state is exclusively comprised of step returns - this means that in the event of // multiple engine lifetimes, imageList will be built accordingly const imageList: string\[\] = await Promise.all(\[\ step.do("get first cutest cat from KV", async () => {\ return await env.KV.get("cutest-http-cat-1");\ }),\ \ \ step.do("get second cutest cat from KV", async () => {\ return await env.KV.get("cutest-http-cat-2");\ }),\ \]); // A long sleep can (and probably will) hibernate the engine which means that the first engine lifetime ends here await step.sleep("💤💤💤💤", "3 hours"); // When this runs, it will be on the second engine lifetime - but this time, imageList will contain // the two most cutest cats await step.do( "choose a random cat from the list and download it", async () => { const randomCat = imageList.at(getRandomInt(0, imageList.length)); // this will eventually succeed since \`randomCat\` is defined return await fetch(\`https://http.cat/${randomCat}\`); }, ); } } \`\`\` ### Avoid doing side effects outside of a \`step.do\` It is not recommended to write code with any side effects outside of steps, unless you would like it to be repeated, because the Workflow engine may restart while an instance is running. If the engine restarts, the step logic will be preserved, but logic outside of the steps may be duplicated. For example, a \`console.log()\` outside of workflow steps may cause the logs to print twice when the engine restarts. However, logic involving non-serializable resources, like a database connection, should be executed outside of steps. Operations ouside of a \`step.do\` might be repeated more than once, due to the nature of the Workflows' instance lifecycle. \* JavaScript \`\`\`js export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // 🔴 Bad: creating instances outside of steps // This might get called more than once creating more instances than expected const myNewInstance = await this.env.ANOTHER\_WORKFLOW.create(); // 🔴 Bad: using non-deterministic functions outside of steps // this will produce different results if the instance has to restart, different runs of the same instance // might go through different paths const myRandom = Math.random(); if (myRandom > 0) { // do some stuff } // ⚠️ Warning: This log may happen many times console.log("This might be logged more than once"); await step.do("do some stuff and have a log for when it runs", async () => { // do some stuff // this log will only appear once console.log("successfully did stuff"); }); // ✅ Good: wrap non-deterministic function in a step // after running successfully will not run again const myRandom = await step.do("create a random number", async () => { return Math.random(); }); // ✅ Good: calls that have no side effects can be done outside of steps const db = createDBConnection(this.env.DB\_URL, this.env.DB\_TOKEN); // ✅ Good: run funtions with side effects inside of a step // after running successfully will not run again const myNewInstance = await step.do( "good step that returns state", async () => { const myNewInstance = await this.env.ANOTHER\_WORKFLOW.create(); return myNewInstance; }, ); } } \`\`\` \* TypeScript \`\`\`ts export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // 🔴 Bad: creating instances outside of steps // This might get called more than once creating more instances than expected const myNewInstance = await this.env.ANOTHER\_WORKFLOW.create(); // 🔴 Bad: using non-deterministic functions outside of steps // this will produce different results if the instance has to restart, different runs of the same instance // might go through different paths const myRandom = Math.random(); if (myRandom > 0) { // do some stuff } // ⚠️ Warning: This log may happen many times console.log("This might be logged more than once"); await step.do("do some stuff and have a log for when it runs", async () => { // do some stuff // this log will only appear once console.log("successfully did stuff"); }); // ✅ Good: wrap non-deterministic function in a step // after running successfully will not run again const myRandom = await step.do("create a random number", async () => { return Math.random(); }); // ✅ Good: calls that have no side effects can be done outside of steps const db = createDBConnection(this.env.DB\_URL, this.env.DB\_TOKEN); // ✅ Good: run funtions with side effects inside of a step // after running successfully will not run again const myNewInstance = await step.do( "good step that returns state", async () => { const myNewInstance = await this.env.ANOTHER\_WORKFLOW.create(); return myNewInstance; }, ); } } \`\`\` ### Do not mutate your incoming events The \`event\` passed to your Workflow's \`run\` method is immutable: changes you make to the event are not persisted across steps and/or Workflow restarts. \* JavaScript \`\`\`js export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // 🔴 Bad: Mutating the event // This will not be persisted across steps and \`event.payload\` will // take on its original value. await step.do("bad step that mutates the incoming event", async () => { let userData = await env.KV.get(event.payload.user); event.payload = userData; }); // ✅ Good: persist data by returning it as state from your step // Use that state in subsequent steps let userData = await step.do("good step that returns state", async () => { return await env.KV.get(event.payload.user); }); let someOtherData = await step.do( "following step that uses that state", async () => { // Access to userData here // Will always be the same if this step is retried }, ); } } \`\`\` \* TypeScript \`\`\`ts interface MyEvent { user: string; data: string; } export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // 🔴 Bad: Mutating the event // This will not be persisted across steps and \`event.payload\` will // take on its original value. await step.do("bad step that mutates the incoming event", async () => { let userData = await env.KV.get(event.payload.user); event.payload = userData; }); // ✅ Good: persist data by returning it as state from your step // Use that state in subsequent steps let userData = await step.do("good step that returns state", async () => { return await env.KV.get(event.payload.user); }); let someOtherData = await step.do( "following step that uses that state", async () => { // Access to userData here // Will always be the same if this step is retried }, ); } } \`\`\` ### Name steps deterministically Steps should be named deterministically (that is, not using the current date/time, randomness, etc). This ensures that their state is cached, and prevents the step from being rerun unnecessarily. Step names act as the "cache key" in your Workflow. \* JavaScript \`\`\`js export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // 🔴 Bad: Naming the step non-deterministically prevents it from being cached // This will cause the step to be re-run if subsequent steps fail. await step.do(\`step #1 running at: ${Date.now()}\`, async () => { let userData = await env.KV.get(event.payload.user); // Do not mutate event.payload event.payload = userData; }); // ✅ Good: give steps a deterministic name. // Return dynamic values in your state, or log them instead. let state = await step.do("fetch user data from KV", async () => { let userData = await env.KV.get(event.payload.user); console.log(\`fetched at ${Date.now}\`); return userData; }); // ✅ Good: steps that are dynamically named are constructed in a deterministic way. // In this case, \`catList\` is a step output, which is stable, and \`catList\` is // traversed in a deterministic fashion (no shuffles or random accesses) so, // it's fine to dynamically name steps (e.g: create a step per list entry). let catList = await step.do("get cat list from KV", async () => { return await env.KV.get("cat-list"); }); for (const cat of catList) { await step.do(\`get cat: ${cat}\`, async () => { return await env.KV.get(cat); }); } } } \`\`\` \* TypeScript \`\`\`ts export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // 🔴 Bad: Naming the step non-deterministically prevents it from being cached // This will cause the step to be re-run if subsequent steps fail. await step.do(\`step #1 running at: ${Date.now()}\`, async () => { let userData = await env.KV.get(event.payload.user); // Do not mutate event.payload event.payload = userData; }); // ✅ Good: give steps a deterministic name. // Return dynamic values in your state, or log them instead. let state = await step.do("fetch user data from KV", async () => { let userData = await env.KV.get(event.payload.user); console.log(\`fetched at ${Date.now}\`); return userData; }); // ✅ Good: steps that are dynamically named are constructed in a deterministic way. // In this case, \`catList\` is a step output, which is stable, and \`catList\` is // traversed in a deterministic fashion (no shuffles or random accesses) so, // it's fine to dynamically name steps (e.g: create a step per list entry). let catList = await step.do("get cat list from KV", async () => { return await env.KV.get("cat-list"); }); for (const cat of catList) { await step.do(\`get cat: ${cat}\`, async () => { return await env.KV.get(cat); }); } } } \`\`\` ### Take care with \`Promise.race()\` and \`Promise.any()\` Workflows allows the usage steps within the \`Promise.race()\` or \`Promise.any()\` methods as a way to achieve concurrent steps execution. However, some considerations must be taken. Due to the nature of Workflows' instance lifecycle, and given that a step inside a Promise will run until it finishes, the step that is returned during the first passage may not be the actual cached step, as \[steps are cached by their names\](#name-steps-deterministically). \* JavaScript \`\`\`js // helper sleep method const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // 🔴 Bad: The \`Promise.race\` is not surrounded by a \`step.do\`, which may cause undeterministic caching behavior. const race\_return = await Promise.race(\[\ step.do("Promise first race", async () => {\ await sleep(1000);\ return "first";\ }),\ step.do("Promise second race", async () => {\ return "second";\ }),\ \]); await step.sleep("Sleep step", "2 hours"); return await step.do("Another step", async () => { // This step will return \`first\`, even though the \`Promise.race\` first returned \`second\`. return race\_return; }); } } \`\`\` \* TypeScript \`\`\`ts // helper sleep method const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // 🔴 Bad: The \`Promise.race\` is not surrounded by a \`step.do\`, which may cause undeterministic caching behavior. const race\_return = await Promise.race(\[\ step.do("Promise first race", async () => {\ await sleep(1000);\ return "first";\ }),\ step.do("Promise second race", async () => {\ return "second";\ }),\ \]); await step.sleep("Sleep step", "2 hours"); return await step.do("Another step", async () => { // This step will return \`first\`, even though the \`Promise.race\` first returned \`second\`. return race\_return; }); } } \`\`\` To ensure consistency, we suggest to surround the \`Promise.race()\` or \`Promise.any()\` within a \`step.do()\`, as this will ensure caching consistency across multiple passages. \* JavaScript \`\`\`js // helper sleep method const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // ✅ Good: The \`Promise.race\` is surrounded by a \`step.do\`, ensuring deterministic caching behavior. const race\_return = await step.do("Promise step", async () => { return await Promise.race(\[\ step.do("Promise first race", async () => {\ await sleep(1000);\ return "first";\ }),\ step.do("Promise second race", async () => {\ return "second";\ }),\ \]); }); await step.sleep("Sleep step", "2 hours"); return await step.do("Another step", async () => { // This step will return \`second\` because the \`Promise.race\` was surround by the \`step.do\` method. return race\_return; }); } } \`\`\` \* TypeScript \`\`\`ts // helper sleep method const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // ✅ Good: The \`Promise.race\` is surrounded by a \`step.do\`, ensuring deterministic caching behavior. const race\_return = await step.do("Promise step", async () => { return await Promise.race(\[\ step.do("Promise first race", async () => {\ await sleep(1000);\ return "first";\ }),\ step.do("Promise second race", async () => {\ return "second";\ }),\ \]); }); await step.sleep("Sleep step", "2 hours"); return await step.do("Another step", async () => { // This step will return \`second\` because the \`Promise.race\` was surround by the \`step.do\` method. return race\_return; }); } } \`\`\` ### Instance IDs are unique Workflow \[instance IDs\](https://developers.cloudflare.com/workflows/build/workers-api/#workflowinstance) are unique per Workflow. The ID is the unique identifier that associates logs, metrics, state and status of a run to a specific instance, even after completion. Allowing ID re-use would make it hard to understand if a Workflow instance ID referred to an instance that run yesterday, last week or today. It would also present a problem if you wanted to run multiple different Workflow instances with different \[input parameters\](https://developers.cloudflare.com/workflows/build/events-and-parameters/) for the same user ID, as you would immediately need to determine a new ID mapping. If you need to associate multiple instances with a specific user, merchant or other "customer" ID in your system, consider using a composite ID or using randomly generated IDs and storing the mapping in a database like \[D1\](https://developers.cloudflare.com/d1/). \* JavaScript \`\`\`js // This is in the same file as your Workflow definition export default { async fetch(req, env) { // 🔴 Bad: Use an ID that isn't unique across future Workflow invocations let userId = getUserId(req); // Returns the userId let badInstance = await env.MY\_WORKFLOW.create({ id: userId, params: payload, }); // ✅ Good: use an ID that is unique // e.g. a transaction ID, order ID, or task ID are good options let instanceId = getTransactionId(); // e.g. assuming transaction IDs are unique // or: compose a composite ID and store it in your database // so that you can track all instances associated with a specific user or merchant. instanceId = \`${getUserId(req)}-${crypto.randomUUID().slice(0, 6)}\`; let { result } = await addNewInstanceToDB(userId, instanceId); let goodInstance = await env.MY\_WORKFLOW.create({ id: instanceId, params: payload, }); return Response.json({ id: goodInstance.id, details: await goodInstance.status(), }); }, }; \`\`\` \* TypeScript \`\`\`ts // This is in the same file as your Workflow definition export default { async fetch(req: Request, env: Env): Promise { // 🔴 Bad: Use an ID that isn't unique across future Workflow invocations let userId = getUserId(req); // Returns the userId let badInstance = await env.MY\_WORKFLOW.create({ id: userId, params: payload, }); // ✅ Good: use an ID that is unique // e.g. a transaction ID, order ID, or task ID are good options let instanceId = getTransactionId(); // e.g. assuming transaction IDs are unique // or: compose a composite ID and store it in your database // so that you can track all instances associated with a specific user or merchant. instanceId = \`${getUserId(req)}-${crypto.randomUUID().slice(0, 6)}\`; let { result } = await addNewInstanceToDB(userId, instanceId); let goodInstance = await env.MY\_WORKFLOW.create({ id: instanceId, params: payload, }); return Response.json({ id: goodInstance.id, details: await goodInstance.status(), }); }, }; \`\`\` ### \`await\` your steps When calling \`step.do\` or \`step.sleep\`, use \`await\` to avoid introducing bugs and race conditions into your Workflow code. If you don't call \`await step.do\` or \`await step.sleep\`, you create a dangling Promise. This occurs when a Promise is created but not properly \`await\`ed, leading to potential bugs and race conditions. This happens when you do not use the \`await\` keyword or fail to chain \`.then()\` methods to handle the result of a Promise. For example, calling \`fetch(GITHUB\_URL)\` without awaiting its response will cause subsequent code to execute immediately, regardless of whether the fetch completed. This can cause issues like premature logging, exceptions being swallowed (and not terminating the Workflow), and lost return values (state). \* JavaScript \`\`\`js export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // 🔴 Bad: The step isn't await'ed, and any state or errors is swallowed before it returns. const issues = step.do(\`fetch issues from GitHub\`, async () => { // The step will return before this call is done let issues = await getIssues(event.payload.repoName); return issues; }); // ✅ Good: The step is correctly await'ed. const issues = await step.do(\`fetch issues from GitHub\`, async () => { let issues = await getIssues(event.payload.repoName); return issues; }); // Rest of your Workflow goes here! } } \`\`\` \* TypeScript \`\`\`ts export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // 🔴 Bad: The step isn't await'ed, and any state or errors is swallowed before it returns. const issues = step.do(\`fetch issues from GitHub\`, async () => { // The step will return before this call is done let issues = await getIssues(event.payload.repoName); return issues; }); // ✅ Good: The step is correctly await'ed. const issues = await step.do(\`fetch issues from GitHub\`, async () => { let issues = await getIssues(event.payload.repoName); return issues; }); // Rest of your Workflow goes here! } } \`\`\` ### Use conditional logic carefully You can use \`if\` statements, loops, and other control flow outside of steps. However, conditions must be based on \*\*deterministic values\*\* — either values from \`event.payload\` or return values from previous steps. Non-deterministic conditions (such as \`Math.random()\` or \`Date.now()\`) outside of steps can cause unexpected behavior if the Workflow restarts. \* JavaScript \`\`\`js export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { const config = await step.do("fetch config", async () => { return await this.env.KV.get("feature-flags", { type: "json" }); }); // ✅ Good: Condition based on step output (deterministic) if (config.enableEmailNotifications) { await step.do("send email", async () => { // Send email logic }); } // ✅ Good: Condition based on event payload (deterministic) if (event.payload.userType === "premium") { await step.do("premium processing", async () => { // Premium-only logic }); } // 🔴 Bad: Condition based on non-deterministic value outside a step // This could behave differently if the Workflow restarts if (Math.random() > 0.5) { await step.do("maybe do something", async () => {}); } // ✅ Good: Wrap non-deterministic values in a step const shouldProcess = await step.do("decide randomly", async () => { return Math.random() > 0.5; }); if (shouldProcess) { await step.do("conditionally do something", async () => {}); } } } \`\`\` \* TypeScript \`\`\`ts export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { const config = await step.do("fetch config", async () => { return await this.env.KV.get("feature-flags", { type: "json" }); }); // ✅ Good: Condition based on step output (deterministic) if (config.enableEmailNotifications) { await step.do("send email", async () => { // Send email logic }); } // ✅ Good: Condition based on event payload (deterministic) if (event.payload.userType === "premium") { await step.do("premium processing", async () => { // Premium-only logic }); } // 🔴 Bad: Condition based on non-deterministic value outside a step // This could behave differently if the Workflow restarts if (Math.random() > 0.5) { await step.do("maybe do something", async () => {}); } // ✅ Good: Wrap non-deterministic values in a step const shouldProcess = await step.do("decide randomly", async () => { return Math.random() > 0.5; }); if (shouldProcess) { await step.do("conditionally do something", async () => {}); } } } \`\`\` ### Batch multiple Workflow invocations When creating multiple Workflow instances, use the \[\`createBatch\`\](https://developers.cloudflare.com/workflows/build/workers-api/#createBatch) method to batch the invocations together. This allows you to create multiple Workflow instances in a single request, which will reduce the number of requests made to the Workflows API. However, each individual instance in the batch will still count towards the \[creation rate limit\](https://developers.cloudflare.com/workflows/reference/limits/). \* JavaScript \`\`\`js export default { async fetch(req, env) { let instances = \[\ { id: "user1", params: { name: "John" } },\ { id: "user2", params: { name: "Jane" } },\ { id: "user3", params: { name: "Alice" } },\ { id: "user4", params: { name: "Bob" } },\ \]; // 🔴 Bad: Create them one by one, which is more likely to hit creation rate limits. for (let instance of instances) { await env.MY\_WORKFLOW.create({ id: instance.id, params: instance.params, }); } // ✅ Good: Batch calls together // This improves throughput. let instances = await env.MY\_WORKFLOW.createBatch(instances); return Response.json({ instances }); }, }; \`\`\` \* TypeScript \`\`\`ts export default { async fetch(req: Request, env: Env): Promise { let instances = \[\ { id: "user1", params: { name: "John" } },\ { id: "user2", params: { name: "Jane" } },\ { id: "user3", params: { name: "Alice" } },\ { id: "user4", params: { name: "Bob" } },\ \]; // 🔴 Bad: Create them one by one, which is more likely to hit creation rate limits. for (let instance of instances) { await env.MY\_WORKFLOW.create({ id: instance.id, params: instance.params, }); } // ✅ Good: Batch calls together // This improves throughput. let instances = await env.MY\_WORKFLOW.createBatch(instances); return Response.json({ instances }); }, }; \`\`\` ### Limit timeouts to 30 minutes or less When setting a \[WorkflowStep timeout\](https://developers.cloudflare.com/workflows/build/workers-api/#workflowstep), ensure that its duration is 30 minutes or less. If your use case requires a timeout greater than 30 minutes, consider using \`step.waitForEvent()\` instead. ### Keep step return values under 1 MiB Each step can persist up to 1 MiB (2^20 bytes) of state. If your step returns data exceeding this limit, the step will fail. This is a common issue when fetching large API responses or processing large files. To work around this limit, store large data externally (for example, in R2 or KV) and return only a reference: \* JavaScript \`\`\`js export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // 🔴 Bad: Returning a large response that may exceed 1 MiB const largeData = await step.do("fetch large dataset", async () => { const response = await fetch("https://api.example.com/large-dataset"); return await response.json(); // Could exceed 1 MiB }); // ✅ Good: Store large data externally and return a reference const dataRef = await step.do("fetch and store large dataset", async () => { const response = await fetch("https://api.example.com/large-dataset"); const data = await response.json(); // Store in R2 and return a reference await this.env.MY\_BUCKET.put("dataset-123", JSON.stringify(data)); return { key: "dataset-123" }; }); // Retrieve the data in a later step when needed const data = await step.do("process dataset", async () => { const stored = await this.env.MY\_BUCKET.get(dataRef.key); return processData(await stored.json()); }); } } \`\`\` \* TypeScript \`\`\`ts export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // 🔴 Bad: Returning a large response that may exceed 1 MiB const largeData = await step.do("fetch large dataset", async () => { const response = await fetch("https://api.example.com/large-dataset"); return await response.json(); // Could exceed 1 MiB }); // ✅ Good: Store large data externally and return a reference const dataRef = await step.do("fetch and store large dataset", async () => { const response = await fetch("https://api.example.com/large-dataset"); const data = await response.json(); // Store in R2 and return a reference await this.env.MY\_BUCKET.put("dataset-123", JSON.stringify(data)); return { key: "dataset-123" }; }); // Retrieve the data in a later step when needed const data = await step.do("process dataset", async () => { const stored = await this.env.MY\_BUCKET.get(dataRef.key); return processData(await stored.json()); }); } } \`\`\` --- title: Sleeping and retrying · Cloudflare Workflows docs description: This guide details how to sleep a Workflow and/or configure retries for a Workflow step. lastUpdated: 2025-08-12T10:10:23.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/ md: https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/index.md --- This guide details how to sleep a Workflow and/or configure retries for a Workflow step. ## Sleep a Workflow You can set a Workflow to sleep as an explicit step, which can be useful when you want a Workflow to wait, schedule work ahead, or pause until an input or other external state is ready. Note A Workflow instance that is resuming from sleep will take priority over newly scheduled (queued) instances. This helps ensure that older Workflow instances can run to completion and are not blocked by newer instances. ### Sleep for a relative period Use \`step.sleep\` to have a Workflow sleep for a relative period of time: \`\`\`ts await step.sleep("sleep for a bit", "1 hour") \`\`\` The second argument to \`step.sleep\` accepts both \`number\` (milliseconds) or a human-readable format, such as "1 minute" or "26 hours". The accepted units for \`step.sleep\` when used this way are as follows: \`\`\`ts | "second" | "minute" | "hour" | "day" | "week" | "month" | "year" \`\`\` ### Sleep until a fixed date Use \`step.sleepUntil\` to have a Workflow sleep to a specific \`Date\`: this can be useful when you have a timestamp from another system or want to "schedule" work to occur at a specific time (e.g. Sunday, 9AM UTC). \`\`\`ts // sleepUntil accepts a Date object as its second argument const workflowsLaunchDate = Date.parse("24 Oct 2024 13:00:00 UTC"); await step.sleepUntil("sleep until X times out", workflowsLaunchDate) \`\`\` You can also provide a UNIX timestamp (milliseconds since the UNIX epoch) directly to \`sleepUntil\`. ## Retry steps Each call to \`step.do\` in a Workflow accepts an optional \`StepConfig\`, which allows you define the retry behaviour for that step. If you do not provide your own retry configuration, Workflows applies the following defaults: \`\`\`ts const defaultConfig: WorkflowStepConfig = { retries: { limit: 5, delay: 10000, backoff: 'exponential', }, timeout: '10 minutes', }; \`\`\` When providing your own \`StepConfig\`, you can configure: \* The total number of attempts to make for a step (accepts \`Infinity\` for unlimited retries) \* The delay between attempts (accepts both \`number\` (ms) or a human-readable format) \* What backoff algorithm to apply between each attempt: any of \`constant\`, \`linear\`, or \`exponential\` \* When to timeout (in duration) before considering the step as failed (including during a retry attempt, as the timeout is set per attempt) For example, to limit a step to 10 retries and have it apply an exponential delay (starting at 10 seconds) between each attempt, you would pass the following configuration as an optional object to \`step.do\`: \`\`\`ts let someState = step.do("call an API", { retries: { limit: 10, // The total number of attempts delay: "10 seconds", // Delay between each retry backoff: "exponential" // Any of "constant" | "linear" | "exponential"; }, timeout: "30 minutes", }, async () => { /\* Step code goes here /\* } \`\`\` ## Force a Workflow instance to fail You can also force a Workflow instance to fail and \*not\* retry by throwing a \`NonRetryableError\` from within the step. This can be useful when you detect a terminal (permanent) error from an upstream system (such as an authentication failure) or other errors where retrying would not help. \`\`\`ts // Import the NonRetryableError definition import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from 'cloudflare:workers'; import { NonRetryableError } from 'cloudflare:workflows'; // In your step code: export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { await step.do("some step", async () => { if (!event.payload.data) { throw new NonRetryableError("event.payload.data did not contain the expected payload") } }) } } \`\`\` The Workflow instance itself will fail immediately, no further steps will be invoked, and the Workflow will not be retried. ## Catch Workflow errors Any uncaught exceptions that propagate to the top level, or any steps that reach their retry limit, will cause the Workflow to end execution in an \`Errored\` state. If you want to avoid this, you can catch exceptions emitted by a \`step\`. This can be useful if you need to trigger clean-up tasks or have conditional logic that triggers additional steps. To allow the Workflow to continue its execution, surround the intended steps that are allowed to fail with a \`try-catch\` block. \`\`\`ts ... await step.do('task', async () => { // work to be done }); try { await step.do('non-retryable-task', async () => { // work not to be retried throw new NonRetryableError('oh no'); }); } catch(e as Error) { console.log(\`Step failed: ${e.message}\`); await step.do('clean-up-task', async () => { // Clean up code here }); } // the Workflow will not fail and will continue its execution await step.do('next-task', async() => { // more work to be done }); ... \`\`\` --- title: Test Workflows · Cloudflare Workflows docs lastUpdated: 2025-09-12T15:23:11.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/build/test-workflows/ md: https://developers.cloudflare.com/workflows/build/test-workflows/index.md --- --- title: Trigger Workflows · Cloudflare Workflows docs description: "You can trigger Workflows both programmatically and via the Workflows APIs, including:" lastUpdated: 2026-01-22T21:38:43.000Z chatbotDeprioritize: false tags: Bindings source\_url: html: https://developers.cloudflare.com/workflows/build/trigger-workflows/ md: https://developers.cloudflare.com/workflows/build/trigger-workflows/index.md --- You can trigger Workflows both programmatically and via the Workflows APIs, including: 1. With \[Workers\](https://developers.cloudflare.com/workers) via HTTP requests in a \`fetch\` handler, or bindings from a \`queue\` or \`scheduled\` handler 2. Using the \[Workflows REST API\](https://developers.cloudflare.com/api/resources/workflows/methods/list/) 3. Via the \[wrangler CLI\](https://developers.cloudflare.com/workers/wrangler/commands/#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: \* Directly over HTTP via the \[\`fetch\`\](https://developers.cloudflare.com/workers/runtime-apis/handlers/fetch/) handler \* From a \[Queue consumer\](https://developers.cloudflare.com/queues/configuration/javascript-apis/#consumer) inside a \`queue\` handler \* From a \[Cron Trigger\](https://developers.cloudflare.com/workers/configuration/cron-triggers/) inside a \`scheduled\` handler \* Within a \[Durable Object\](https://developers.cloudflare.com/durable-objects/) Note New to Workflows? Start with the \[Workflows tutorial\](https://developers.cloudflare.com/workflows/get-started/guide/) to deploy your first Workflow and familiarize yourself with Workflows concepts. To bind to a Workflow from your Workers code, you need to define a \[binding\](https://developers.cloudflare.com/workers/wrangler/configuration/) to a specific Workflow. For example, to bind to the Workflow defined in the \[get started guide\](https://developers.cloudflare.com/workflows/get-started/guide/), you would configure the \[Wrangler configuration file\](https://developers.cloudflare.com/workers/wrangler/configuration/) with the below: \* wrangler.jsonc \`\`\`jsonc { "$schema": "./node\_modules/wrangler/config-schema.json", "name": "workflows-tutorial", "main": "src/index.ts", "compatibility\_date": "2024-10-22", "workflows": \[\ {\ "name": "workflows-tutorial",\ "binding": "MY\_WORKFLOW",\ "class\_name": "MyWorkflow"\ }\ \] } \`\`\` \* wrangler.toml \`\`\`toml name = "workflows-tutorial" main = "src/index.ts" compatibility\_date = "2024-10-22" \[\[workflows\]\] # The name of the Workflow name = "workflows-tutorial" # The binding name, which must be a valid JavaScript variable name. This will # be how you call (run) your Workflow from your other Workers handlers or # scripts. binding = "MY\_WORKFLOW" # Must match the class defined in your code that extends the Workflow class class\_name = "MyWorkflow" \`\`\` 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). 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 \* Creating (triggering) a new Workflow instance \* Returning the status of a given instance ID \`\`\`ts interface Env { MY\_WORKFLOW: Workflow; } export default { async fetch(req: Request, env: Env) { // Get instanceId from query parameters const instanceId = new URL(req.url).searchParams.get("instanceId"); // If an ?instanceId= query parameter is provided, fetch the status // of an existing Workflow by its ID. if (instanceId) { let instance = await env.MY\_WORKFLOW.get(instanceId); return Response.json({ status: await instance.status(), }); } // Else, create a new instance of our Workflow, passing in any (optional) // params and return the ID. const newId = crypto.randomUUID(); let instance = await env.MY\_WORKFLOW.create({ id: newId }); return Response.json({ id: instance.id, details: await instance.status(), }); }, }; \`\`\` ### Inspect a Workflow's status You can inspect the status of any running Workflow instance by calling \`status\` against a specific instance ID. This allows you to programmatically inspect whether an instance is queued (waiting to be scheduled), actively running, paused, or errored. \`\`\`ts let instance = await env.MY\_WORKFLOW.get("abc-123"); let status = await instance.status(); // Returns an InstanceStatus \`\`\` The possible values of status are as follows: \`\`\`ts status: | "queued" // means that instance is waiting to be started (see concurrency limits) | "running" | "paused" | "errored" | "terminated" // user terminated the instance while it was running | "complete" | "waiting" // instance is hibernating and waiting for sleep or event to finish | "waitingForPause" // instance is finishing the current work to pause | "unknown"; error?: { name: string, message: string }; output?: unknown; \`\`\` ### Explicitly pause a Workflow You can explicitly pause a Workflow instance (and later resume it) by calling \`pause\` against a specific instance ID. \`\`\`ts let instance = await env.MY\_WORKFLOW.get("abc-123"); await instance.pause(); // Returns Promise \`\`\` ### Resume a Workflow You can resume a paused Workflow instance by calling \`resume\` against a specific instance ID. \`\`\`ts let instance = await env.MY\_WORKFLOW.get("abc-123"); await instance.resume(); // Returns Promise \`\`\` Calling \`resume\` on an instance that is not currently paused will have no effect. ### Stop a Workflow You can stop/terminate a Workflow instance by calling \`terminate\` against a specific instance ID. \`\`\`ts let instance = await env.MY\_WORKFLOW.get("abc-123"); await instance.terminate(); // Returns Promise \`\`\` Once stopped/terminated, the Workflow instance \*cannot\* be resumed. ### Restart a Workflow \`\`\`ts let instance = await env.MY\_WORKFLOW.get("abc-123"); await instance.restart(); // Returns Promise \`\`\` Restarting an instance will immediately cancel any in-progress steps, erase any intermediate state, and treat the Workflow as if it was run for the first time. ### Trigger a Workflow from another Workflow You can create a new Workflow instance from within a step of another Workflow. The parent Workflow will not block waiting for the child Workflow to complete — it continues execution immediately after the child instance is successfully created. \* JavaScript \`\`\`js export class ParentWorkflow extends WorkflowEntrypoint { async run(event, step) { // Perform initial work const result = await step.do("initial processing", async () => { // ... processing logic return { fileKey: "output.pdf" }; }); // Trigger a child workflow for additional processing const childInstance = await step.do("trigger child workflow", async () => { return await this.env.CHILD\_WORKFLOW.create({ id: \`child-${event.instanceId}\`, params: { fileKey: result.fileKey }, }); }); // Parent continues immediately - not blocked by child workflow await step.do("continue with other work", async () => { console.log(\`Started child workflow: ${childInstance.id}\`); // This runs right away, regardless of child workflow status }); } } \`\`\` \* TypeScript \`\`\`ts export class ParentWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // Perform initial work const result = await step.do("initial processing", async () => { // ... processing logic return { fileKey: "output.pdf" }; }); // Trigger a child workflow for additional processing const childInstance = await step.do("trigger child workflow", async () => { return await this.env.CHILD\_WORKFLOW.create({ id: \`child-${event.instanceId}\`, params: { fileKey: result.fileKey }, }); }); // Parent continues immediately - not blocked by child workflow await step.do("continue with other work", async () => { console.log(\`Started child workflow: ${childInstance.id}\`); // This runs right away, regardless of child workflow status }); } } \`\`\` If the child Workflow fails to start, the step will fail and be retried according to your retry configuration. Once the child instance is successfully created, it runs independently from the parent. ## REST API (HTTP) Refer to the \[Workflows REST API documentation\](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/methods/create/). ## Command line (CLI) Refer to the \[CLI quick start\](https://developers.cloudflare.com/workflows/get-started/guide/) to learn more about how to manage and trigger Workflows via the command-line. --- title: Workers API · Cloudflare Workflows docs description: This guide details the Workflows API within Cloudflare Workers, including methods, types, and usage examples. lastUpdated: 2025-11-28T15:38:15.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/build/workers-api/ md: https://developers.cloudflare.com/workflows/build/workers-api/index.md --- 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. \`\`\`ts export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // Steps here } } \`\`\` ### run \* \`run(event: WorkflowEvent, step: WorkflowStep): Promise\` \* \`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\](https://developers.cloudflare.com/workflows/build/workers-api/#instancestatus), \[REST API\](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/subresources/status/) 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. \`\`\`ts export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, 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\](https://www.typescriptlang.org/docs/handbook/2/generics.html#working-with-generic-type-variables) that allows you to provide a type for the \`payload\` property within the \`WorkflowEvent\`. Refer to the \[events and parameters\](https://developers.cloudflare.com/workflows/build/events-and-parameters/) documentation for how to handle events within your Workflow code. Finally, any JS control-flow primitive (if conditions, loops, try-catches, promises, etc) can be used to manage steps inside the \`run\` method. ## WorkflowEvent \`\`\`ts export type WorkflowEvent = { payload: Readonly; timestamp: Date; instanceId: string; }; \`\`\` \* The \`WorkflowEvent\` is the first argument to a Workflow's \`run\` method, and includes an optional \`payload\` parameter and a \`timestamp\` property. \* \`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. Refer to the \[events and parameters\](https://developers.cloudflare.com/workflows/build/events-and-parameters/) documentation for how to handle events within your Workflow code. ## WorkflowStep ### step \* \`step.do(name: string, callback: (): RpcSerializable): Promise\` \* \`step.do(name: string, config?: WorkflowStepConfig, callback: (): RpcSerializable): Promise\` \* \`name\` - the name of the step, up to 256 characters. \* \`config\` (optional) - an optional \`WorkflowStepConfig\` for configuring \[step specific retry behaviour\](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/). \* \`callback\` - an asynchronous function that optionally returns serializable state for the Workflow to persist. 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. Objects that include \`Function\` or \`Symbol\` types, and objects with circular references, cannot be serialized and the Workflow instance will throw an error if objects with those types is returned. \* \`step.sleep(name: string, duration: WorkflowDuration): Promise\` \* \`name\` - the name of the step. \* \`duration\` - the duration to sleep until, in either seconds or as a \`WorkflowDuration\` compatible string. \* Refer to the \[documentation on sleeping and retrying\](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/) to learn more about how Workflows are retried. - \`step.sleepUntil(name: string, timestamp: Date | number): Promise\` \* \`name\` - the name of the step. \* \`timestamp\` - a JavaScript \`Date\` object or seconds 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\](https://developers.cloudflare.com/workflows/reference/limits/). \* \`step.waitForEvent(name: string, options: ): Promise\` \* \`name\` - the name of the step. \* \`options\` - an object with properties for \`type\` (up to 100 characters \[1\](#user-content-fn-1)), 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. - JavaScript \`\`\`js export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // Other steps in your Workflow let event = await step.waitForEvent( "receive invoice paid webhook from Stripe", { type: "stripe-webhook", timeout: "1 hour" }, ); // Rest of your Workflow } } \`\`\` - TypeScript \`\`\`ts export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // Other steps in your Workflow let event = await step.waitForEvent( "receive invoice paid webhook from Stripe", { type: "stripe-webhook", timeout: "1 hour" }, ); // Rest of your Workflow } } \`\`\` Review the documentation on \[events and parameters\](https://developers.cloudflare.com/workflows/build/events-and-parameters/) to learn how to send events to a running Workflow instance. ## WorkflowStepConfig \`\`\`ts export type WorkflowStepConfig = { retries?: { limit: number; delay: string | number; 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. Refer to the \[documentation on sleeping and retrying\](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/) to learn more about how Workflows are retried. ## NonRetryableError \* \`throw new NonRetryableError(message: string, name string optional)\`: NonRetryableError \* When thrown inside \[\`step.do()\`\](https://developers.cloudflare.com/workflows/build/workers-api/#step), this error stops step retries, propagating the error to the top level (the \[run\](https://developers.cloudflare.com/workflows/build/workers-api/#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\](https://developers.cloudflare.com/workflows/build/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\](https://developers.cloudflare.com/workers/runtime-apis/bindings/#what-is-a-binding) 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: \* wrangler.jsonc \`\`\`jsonc { "$schema": "./node\_modules/wrangler/config-schema.json", "name": "workflows-starter", "main": "src/index.ts", "compatibility\_date": "2024-10-22", "workflows": \[\ {\ "name": "workflows-starter",\ "binding": "MY\_WORKFLOW",\ "class\_name": "MyWorkflow"\ }\ \] } \`\`\` \* wrangler.toml \`\`\`toml #:schema node\_modules/wrangler/config-schema.json name = "workflows-starter" main = "src/index.ts" compatibility\_date = "2024-10-22" \[\[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" \`\`\` ### Bind from Pages You can bind and trigger Workflows from \[Pages Functions\](https://developers.cloudflare.com/pages/functions/) by deploying a Workers project with your Workflow definition and then invoking that Worker using \[service bindings\](https://developers.cloudflare.com/pages/functions/bindings/#service-bindings) or a standard \`fetch()\` call. Visit the documentation on \[calling Workflows from Pages\](https://developers.cloudflare.com/workflows/build/call-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\](https://developers.cloudflare.com/workers/wrangler/configuration/) would resemble the following: \* wrangler.jsonc \`\`\`jsonc { "$schema": "./node\_modules/wrangler/config-schema.json", "name": "web-api-worker", "main": "src/index.ts", "compatibility\_date": "2024-10-22", "workflows": \[\ {\ "name": "billing-workflow",\ "binding": "MY\_WORKFLOW",\ "class\_name": "MyWorkflow",\ "script\_name": "billing-worker"\ }\ \] } \`\`\` \* wrangler.toml \`\`\`toml #:schema node\_modules/wrangler/config-schema.json name = "web-api-worker" main = "src/index.ts" compatibility\_date = "2024-10-22" \[\[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" \`\`\` If you're using TypeScript, run \[\`wrangler types\`\](https://developers.cloudflare.com/workers/wrangler/commands/#types) whenever you modify your Wrangler configuration file. This generates types for the \`env\` object based on your bindings, as well as \[runtime types\](https://developers.cloudflare.com/workers/languages/typescript/). ## 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\`\](https://developers.cloudflare.com/workers/wrangler/commands/#types). \`\`\`ts 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\` \* \`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 \[1\](#user-content-fn-1)). 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\`\](https://developers.cloudflare.com/workflows/build/events-and-parameters/). \`\`\`ts // 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\](https://developers.cloudflare.com/workflows/reference/limits/). To re-run a workflow with the same ID, you can \[\`restart\`\](https://developers.cloudflare.com/workflows/build/trigger-workflows/#restart-a-workflow) the existing instance. Warning 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\](https://zod.dev/) 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\](https://developers.cloudflare.com/workflows/build/workers-api/#workflow). Note that this does \*not\* propagate type information into the Workflow itself, as TypeScript types are a build-time construct. To provide the type of an incoming \`WorkflowEvent\`, refer to the \[TypeScript and type parameters\](https://developers.cloudflare.com/workflows/build/events-and-parameters/#typescript-and-type-parameters) section of the Workflows documentation. To provide an optional type parameter to the \`Workflow\`, pass a type argument with your type when defining your Workflow bindings: \`\`\`ts interface User { email: string; createdTimestamp: number; } interface Env { // Pass our User type as the type parameter to the Workflow definition MY\_WORKFLOW: Workflow; } 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 instance 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\](https://developers.cloudflare.com/workflows/reference/limits/). \* \`createBatch(batch: WorkflowInstanceCreateOptions\[\]): Promise\` \* \`batch\` - list of Options to pass when creating an instance, including a user-provided ID and payload parameters. Each element of the \`batch\` list is expected to include both \`id\` and \`params\` properties: \`\`\`ts // Create a new batch of 3 Workflow instances, each with its own ID and pass params to the Workflow instances const listOfInstances = \[\ { id: "id-abc123", params: { hello: "world-0" } },\ { id: "id-def456", params: { hello: "world-1" } },\ { id: "id-ghi789", params: { hello: "world-2" } },\ \]; let instances = await env.MY\_WORKFLOW.createBatch(listOfInstances); \`\`\` Returns an array of \`WorkflowInstance\`. Unlike \[\`create\`\](https://developers.cloudflare.com/workflows/build/workers-api/#create), this operation is idempotent and will not fail if an ID is already in use. If an existing instance with the same ID is still within its \[retention limit\](https://developers.cloudflare.com/workflows/reference/limits/), it will be skipped and excluded from the returned array. ### get Get a specific Workflow instance by ID. \* \`get(id: string): Promise\`- \`id\` - the ID of the Workflow instance. Returns a \`WorkflowInstance\`. Throws an exception if the instance ID does not exist. \`\`\`ts // Fetch an existing Workflow instance by ID: try { let instance = await env.MY\_WORKFLOW.get(id); return Response.json({ id: instance.id, details: await instance.status(), }); } catch (e: any) { // Handle errors // .get will throw an exception if the ID doesn't exist or is invalid. const msg = \`failed to get instance ${id}: ${e.message}\`; console.error(msg); return Response.json({ error: msg }, { status: 400 }); } \`\`\` ## WorkflowInstanceCreateOptions Optional properties to pass when creating an instance. \`\`\`ts interface WorkflowInstanceCreateOptions { /\*\* \* An id for your Workflow instance. Must be unique within the Workflow. \*/ id?: string; /\*\* \* The event payload the Workflow instance is triggered with \*/ params?: unknown; } \`\`\` ## WorkflowInstance Represents a specific instance of a Workflow, and provides methods to manage the instance. \`\`\`ts declare abstract class WorkflowInstance { public id: string; /\*\* \* Pause the instance. \*/ public pause(): Promise; /\*\* \* Resume the instance. If it is already running, an error will be thrown. \*/ public resume(): Promise; /\*\* \* Terminate the instance. If it is errored, terminated or complete, an error will be thrown. \*/ public terminate(): Promise; /\*\* \* Restart the instance. \*/ public restart(): Promise; /\*\* \* Returns the current status of the instance. \*/ public status(): Promise; } \`\`\` ### id Return the id of a Workflow. \* \`id: string\` ### status Return the status of a running Workflow instance. \* \`status(): Promise\` ### pause Pause a running Workflow instance. \* \`pause(): Promise\` ### resume Resume a paused Workflow instance. \* \`resume(): Promise\` ### restart Restart a Workflow instance. \* \`restart(): Promise\` ### terminate Terminate a Workflow instance. \* \`terminate(): Promise\` ### sendEvent \[Send an event\](https://developers.cloudflare.com/workflows/build/events-and-parameters/) to a running Workflow instance. \* \`sendEvent(): Promise\` \* \`options\` - the event \`type\` (up to 100 characters \[1\](#user-content-fn-1)) and \`payload\` to send to the Workflow instance. The \`type\` must match the \`type\` in the corresponding \`waitForEvent\` call in your Workflow. Return \`void\` on success; throws an exception if the Workflow is not running or is an errored state. \* JavaScript \`\`\`js export default { async fetch(req, env) { const instanceId = new URL(req.url).searchParams.get("instanceId"); const webhookPayload = await req.json(); let instance = await env.MY\_WORKFLOW.get(instanceId); // Send our event, with \`type\` matching the event type defined in // our step.waitForEvent call await instance.sendEvent({ type: "stripe-webhook", payload: webhookPayload, }); return Response.json({ status: await instance.status(), }); }, }; \`\`\` \* TypeScript \`\`\`ts export default { async fetch(req: Request, env: Env) { const instanceId = new URL(req.url).searchParams.get("instanceId"); const webhookPayload = await req.json(); let instance = await env.MY\_WORKFLOW.get(instanceId); // Send our event, with \`type\` matching the event type defined in // our step.waitForEvent call await instance.sendEvent({ type: "stripe-webhook", payload: webhookPayload, }); return Response.json({ status: await instance.status(), }); }, }; \`\`\` You can call \`sendEvent\` multiple times, setting the value of the \`type\` property to match the specific \`waitForEvent\` calls in your Workflow. This allows you to wait for multiple events at once, or use \`Promise.race\` to wait for multiple events and allow the first event to progress the Workflow. ### InstanceStatus Details the status of a Workflow instance. \`\`\`ts type InstanceStatus = { status: | "queued" // means that instance is waiting to be started (see concurrency limits) | "running" | "paused" | "errored" | "terminated" // user terminated the instance while it was running | "complete" | "waiting" // instance is hibernating and waiting for sleep or event to finish | "waitingForPause" // instance is finishing the current work to pause | "unknown"; error?: { name: string, message: string }; output?: unknown; }; \`\`\` ## Footnotes 1. Match pattern: \`^\[a-zA-Z0-9\_\]\[a-zA-Z0-9-\_\]\*$\` \[↩\](#user-content-fnref-1) \[↩2\](#user-content-fnref-1-2) \[↩3\](#user-content-fnref-1-3) --- title: Agents · Cloudflare Workflows docs description: Build AI-powered Agents on Cloudflare lastUpdated: 2025-01-29T20:30:56.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/examples/agents/ md: https://developers.cloudflare.com/workflows/examples/agents/index.md --- --- title: Export and save D1 database · Cloudflare Workflows docs description: Send invoice when shopping cart is checked out and paid for lastUpdated: 2025-10-13T13:40:40.000Z chatbotDeprioritize: false tags: TypeScript source\_url: html: https://developers.cloudflare.com/workflows/examples/backup-d1/ md: https://developers.cloudflare.com/workflows/examples/backup-d1/index.md --- In this example, we implement a Workflow periodically triggered by a \[Cron Trigger\](https://developers.cloudflare.com/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\](https://developers.cloudflare.com/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. As shown in this example, Workflows handles both the responses and failures, thereby removing the burden from the developer. Workflows retries the following steps: \* API calls until it gets a successful response \* Fetching the backup from the URL provided \* Saving the file to \[R2\](https://developers.cloudflare.com/r2) The Workflow can run until the backup file is ready, handling all of the possible conditions until it is completed. This example provides simplified steps for backing up a \[D1\](https://developers.cloudflare.com/d1) database to help you understand the possibilities of Workflows. In every step, it uses the \[default\](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying) sleeping and retrying configuration. In a real-world scenario, more steps and additional logic would likely be needed. \`\`\`ts import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent, } from "cloudflare:workers"; // We are using R2 to store the D1 backup 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; }; // Workflow logic export class backupWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { const { accountId, databaseId } = event.payload; const url = \`https://api.cloudflare.com/client/v4/accounts/${accountId}/d1/database/${databaseId}/export\`; const method = "POST"; const headers = new Headers(); headers.append("Content-Type", "application/json"); headers.append("Authorization", \`Bearer ${this.env.D1\_REST\_API\_TOKEN}\`); const bookmark = await step.do( \`Starting backup for ${databaseId}\`, async () => { const payload = { output\_format: "polling" }; const res = await fetch(url, { method, headers, body: JSON.stringify(payload), }); const { result } = (await res.json()) as any; // If we don't get \`at\_bookmark\` we throw to retry the step if (!result?.at\_bookmark) throw new Error("Missing \`at\_bookmark\`"); return result.at\_bookmark; }, ); await step.do("Check backup status and store it on R2", async () => { const payload = { current\_bookmark: bookmark }; const res = await fetch(url, { method, headers, body: JSON.stringify(payload), }); const { result } = (await res.json()) as any; // The endpoint sends \`signed\_url\` when the backup is ready to download. // If we don't get \`signed\_url\` we throw to retry the step. if (!result?.signed\_url) throw new Error("Missing \`signed\_url\`"); const dumpResponse = await fetch(result.signed\_url); if (!dumpResponse.ok) throw new Error("Failed to fetch dump file"); // Finally, stream the file directly to R2 await this.env.BACKUP\_BUCKET.put(result.filename, dumpResponse.body); }); } } 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}\`); }, }; \`\`\` Here is a minimal package.json: \`\`\`json { "devDependencies": { "wrangler": "^3.99.0" } } \`\`\` Here is a \[Wrangler configuration file\](https://developers.cloudflare.com/workers/wrangler/configuration/): \* wrangler.jsonc \`\`\`jsonc { "$schema": "./node\_modules/wrangler/config-schema.json", "name": "backup-d1", "main": "src/index.ts", "compatibility\_date": "2024-12-27", "compatibility\_flags": \[\ "nodejs\_compat"\ \], "workflows": \[\ {\ "name": "backup-workflow",\ "binding": "BACKUP\_WORKFLOW",\ "class\_name": "backupWorkflow"\ }\ \], "r2\_buckets": \[\ {\ "binding": "BACKUP\_BUCKET",\ "bucket\_name": "d1-backups"\ }\ \], "triggers": { "crons": \[\ "0 0 \* \* \*"\ \] } } \`\`\` \* wrangler.toml \`\`\`toml name = "backup-d1" main = "src/index.ts" compatibility\_date = "2024-12-27" compatibility\_flags = \[ "nodejs\_compat" \] \[\[workflows\]\] name = "backup-workflow" binding = "BACKUP\_WORKFLOW" class\_name = "backupWorkflow" \[\[r2\_buckets\]\] binding = "BACKUP\_BUCKET" bucket\_name = "d1-backups" \[triggers\] crons = \[ "0 0 \* \* \*" \] \`\`\` --- title: Pay cart and send invoice · Cloudflare Workflows docs description: Send invoice when shopping cart is checked out and paid for lastUpdated: 2025-10-13T13:40:40.000Z chatbotDeprioritize: false tags: TypeScript source\_url: html: https://developers.cloudflare.com/workflows/examples/send-invoices/ md: https://developers.cloudflare.com/workflows/examples/send-invoices/index.md --- In this example, we implement a Workflow for an e-commerce website that is triggered every time a shopping cart is created. Once a Workflow instance is triggered, it starts polling a \[D1\](https://developers.cloudflare.com/d1) database for the cart ID until it has been checked out. Once the shopping cart is checked out, we proceed to process the payment with an external provider doing a fetch POST. Finally, assuming everything goes well, we try to send an email using \[Email Workers\](https://developers.cloudflare.com/email-routing/email-workers/) with the invoice to the customer. As you can see, Workflows handles all the different service responses and failures; it will retry D1 until the cart is checked out, retry the payment processor if it fails for some reason, and retry sending the email with the invoice if it can't. The developer doesn't have to care about any of that logic, and the workflow can run for hours, handling all the possible conditions until it is completed. This is a simplified example of processing a shopping cart. We would assume more steps and additional logic in a real-life scenario, but this example gives you a good idea of what you can do with Workflows. \`\`\`ts import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent, } from "cloudflare:workers"; import { EmailMessage } from "cloudflare:email"; import { createMimeMessage } from "mimetext"; // We are using Email Routing to send emails out and D1 for our cart database type Env = { CART\_WORKFLOW: Workflow; SEND\_EMAIL: any; DB: any; }; // Workflow parameters: we expect a cartId type Params = { cartId: string; }; // Adjust this to your Cloudflare zone using Email Routing const merchantEmail = "merchant@example.com"; // Uses mimetext npm to generate Email const genEmail = (email: string, amount: number) => { const msg = createMimeMessage(); msg.setSender({ name: "Pet shop", addr: merchantEmail }); msg.setRecipient(email); msg.setSubject("You invoice"); msg.addMessage({ contentType: "text/plain", data: \`Your invoice for ${amount} has been paid. Your products will be shipped shortly.\`, }); return new EmailMessage(merchantEmail, email, msg.asRaw()); }; // Workflow logic export class cartInvoicesWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { await step.sleep("sleep for a while", "10 seconds"); // Retrieve the cart from the D1 database // if the cart hasn't been checked out yet retry every 2 minutes, 10 times, otherwise give up const cart = await step.do( "retrieve cart", { retries: { limit: 10, delay: 2000 \* 60, backoff: "constant", }, timeout: "30 seconds", }, async () => { const { results } = await this.env.DB.prepare( \`SELECT \* FROM cart WHERE id = ?\`, ) .bind(event.payload.cartId) .run(); // should return { checkedOut: true, amount: 250 , account: { email: "celsomartinho@gmail.com" }}; if (results\[0\].checkedOut === false) { throw new Error("cart hasn't been checked out yet"); } return results\[0\]; }, ); // Proceed to payment, retry 10 times every minute or give up const payment = await step.do( "payment", { retries: { limit: 10, delay: 1000 \* 60, backoff: "constant", }, timeout: "30 seconds", }, async () => { let resp = await fetch("https://payment-processor.example.com/", { method: "POST", headers: { "Content-Type": "application/json; charset=utf-8", }, body: JSON.stringify({ amount: cart.amount }), }); if (!resp.ok) { throw new Error("payment has failed"); } return { success: true, amount: cart.amount }; }, ); // Send invoice to the customer, retry 10 times every 5 minutes or give up // Requires that cart.account.email has previously been validated in Email Routing, // See https://developers.cloudflare.com/email-routing/email-workers/ await step.do( "send invoice", { retries: { limit: 10, delay: 5000 \* 60, backoff: "constant", }, timeout: "30 seconds", }, async () => { const message = genEmail(cart.account.email, payment.amount); try { await this.env.SEND\_EMAIL.send(message); } catch (e) { throw new Error("failed to send invoice"); } }, ); } } // Default page for admin // Remove in production export default { async fetch(req: Request, env: Env): Promise { let url = new URL(req.url); let id = new URL(req.url).searchParams.get("instanceId"); // Get the status of an existing instance, if provided if (id) { let instance = await env.CART\_WORKFLOW.get(id); return Response.json({ status: await instance.status(), }); } if (url.pathname.startsWith("/new")) { let instance = await env.CART\_WORKFLOW.create({ params: { cartId: "123", }, }); return Response.json({ id: instance.id, details: await instance.status(), }); } return new Response( \`new instance or add ?instanceId=...\`, { headers: { "content-type": "text/html;charset=UTF-8", }, }, ); }, }; \`\`\` Here's a minimal package.json: \`\`\`json { "devDependencies": { "wrangler": "^3.83.0" }, "dependencies": { "mimetext": "^3.0.24" } } \`\`\` And finally \[Wrangler configuration file\](https://developers.cloudflare.com/workers/wrangler/configuration/): \* wrangler.jsonc \`\`\`jsonc { "$schema": "./node\_modules/wrangler/config-schema.json", "name": "cart-invoices", "main": "src/index.ts", "compatibility\_date": "2024-10-22", "compatibility\_flags": \[\ "nodejs\_compat"\ \], "workflows": \[\ {\ "name": "cart-invoices-workflow",\ "binding": "CART\_WORKFLOW",\ "class\_name": "cartInvoicesWorkflow"\ }\ \], "send\_email": \[\ {\ "name": "SEND\_EMAIL"\ }\ \] } \`\`\` \* wrangler.toml \`\`\`toml name = "cart-invoices" main = "src/index.ts" compatibility\_date = "2024-10-22" compatibility\_flags = \["nodejs\_compat" \] \[\[workflows\]\] name = "cart-invoices-workflow" binding = "CART\_WORKFLOW" class\_name = "cartInvoicesWorkflow" \[\[send\_email\]\] name = "SEND\_EMAIL" \`\`\` If you're using TypeScript, run \[\`wrangler types\`\](https://developers.cloudflare.com/workers/wrangler/commands/#types) whenever you modify your Wrangler configuration file. This generates types for the \`env\` object based on your bindings, as well as \[runtime types\](https://developers.cloudflare.com/workers/languages/typescript/). --- title: Integrate Workflows with Twilio · Cloudflare Workflows docs description: Integrate Workflows with Twilio. Learn how to receive and send text messages and phone calls via APIs and Webhooks. lastUpdated: 2025-10-13T13:40:40.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/examples/twilio/ md: https://developers.cloudflare.com/workflows/examples/twilio/index.md --- Using the following \[repository\](https://github.com/craigsdennis/twilio-cloudflare-workflow), learn how to integrate Cloudflare Workflows with Twilio, a popular cloud communications platform that enables developers to integrate messaging, voice, video, and authentication features into applications via APIs. By the end of the video tutorial, you will become familiarized with the process of setting up Cloudflare Workflows to seamlessly interact with Twilio's APIs, enabling you to build interesting communication features directly into your applications. --- title: Human-in-the-Loop Image Tagging with waitForEvent · Cloudflare Workflows docs description: Human-in-the-loop Workflow with waitForEvent API lastUpdated: 2025-10-13T13:40:40.000Z chatbotDeprioritize: false tags: TypeScript source\_url: html: https://developers.cloudflare.com/workflows/examples/wait-for-event/ md: https://developers.cloudflare.com/workflows/examples/wait-for-event/index.md --- This example demonstrates how to use the \`waitForEvent()\` API in Cloudflare Workflows to introduce a human-in-the-loop step. The Workflow is triggered by an image upload, during which metadata is stored in a D1 database. The Workflow then waits for user approval, and upon approval, it uses Workers AI to generate image tags, which are stored in the database. An accompanying Next.js frontend application facilitates the image upload and approval process. Note The example on this page includes only a subset of the full implementation. For the complete codebase and deployment instructions, please refer to the \[GitHub repository\](https://github.com/cloudflare/docs-examples/tree/main/workflows/waitForEvent). ## Overview of the Workflow In this Workflow, we simulate a scenario where an uploaded image requires human approval before AI-based processing. An image is uploaded to R2, then Workflow performs the following steps: 1. Stores image metadata in a D1 database. 2. Pauses execution using \`waitForEvent()\` and waits for an external event sent from the Next.js frontend, indicating approval or rejection. 3. If approved, the Workflow uses Workers AI to generate image tags and stores the tags in the D1 database. 4. If rejected, the Workflow ends without further action. This pattern is useful in scenarios where certain operations should not proceed without explicit human consent, adding an extra layer of control and safety. ## Frontend Integration This example includes a Next.js frontend application that facilitates the image upload and approval process. The frontend provides an interface for uploading images, reviewing them, and approving or rejecting them. Upon image upload, the application triggers the Cloudflare Workflow, which then manages the subsequent steps, including waiting for user approval and performing AI-based image tagging upon approval. Refer to the \`/nextjs-workflow-frontend\` folder in the \[GitHub repository\](https://github.com/cloudflare/docs-examples/tree/main/workflows/waitForEvent) for the complete frontend implementation and deployment details. ## Workflow index.ts The \`index.ts\` file defines the core logic of the Cloudflare Workflow responsible for handling image uploads, awaiting human approval, and performing AI-based image tagging upon approval. It extends the \`WorkflowEntrypoint\` class and implements the \`run()\` method. For the complete implementation of the \`index.ts\` file, please refer to the \[GitHub repository\](https://github.com/cloudflare/docs-examples/blob/main/workflows/waitForEvent/workflow/src/index.ts). \* JavaScript \`\`\`js export class MyWorkflow extends WorkflowEntrypoint { db; async run(event, step) { this.db = new DatabaseService(this.env.DB); const { imageKey } = event.payload; await step.do("Insert image name into database", async () => { await this.db.insertImage(imageKey, event.instanceId); }); const waitForApproval = await step.waitForEvent( "Wait for AI Image tagging approval", { type: "approval-for-ai-tagging", timeout: "5 minute", }, ); const approvalPayload = waitForApproval.payload; if (approvalPayload?.approved) { const aiTags = await step.do("Generate AI tags", async () => { const image = await this.env.workflow\_demo\_bucket.get(imageKey); if (!image) throw new Error("Image not found"); const arrayBuffer = await image.arrayBuffer(); const uint8Array = new Uint8Array(arrayBuffer); const input = { image: Array.from(uint8Array), prompt: AI\_CONFIG.PROMPT, max\_tokens: AI\_CONFIG.MAX\_TOKENS, }; const response = await this.env.AI.run(AI\_CONFIG.MODEL, input); return response.description; }); await step.do("Update DB with AI tags", async () => { await this.db.updateImageTags(event.instanceId, aiTags); }); } } } \`\`\` \* TypeScript \`\`\`ts export class MyWorkflow extends WorkflowEntrypoint { private db!: DatabaseService; async run(event: WorkflowEvent, step: WorkflowStep) { this.db = new DatabaseService(this.env.DB); const { imageKey } = event.payload; await step.do('Insert image name into database', async () => { await this.db.insertImage(imageKey, event.instanceId); }); const waitForApproval = await step.waitForEvent('Wait for AI Image tagging approval', { type: 'approval-for-ai-tagging', timeout: '5 minute', }); const approvalPayload = waitForApproval.payload as ApprovalRequest; if (approvalPayload?.approved) { const aiTags = await step.do('Generate AI tags', async () => { const image = await this.env.workflow\_demo\_bucket.get(imageKey); if (!image) throw new Error('Image not found'); const arrayBuffer = await image.arrayBuffer(); const uint8Array = new Uint8Array(arrayBuffer); const input = { image: Array.from(uint8Array), prompt: AI\_CONFIG.PROMPT, max\_tokens: AI\_CONFIG.MAX\_TOKENS, }; const response = await this.env.AI.run(AI\_CONFIG.MODEL, input); return response.description; }); await step.do('Update DB with AI tags', async () => { await this.db.updateImageTags(event.instanceId, aiTags); }); } } } \`\`\` ## Workflow wrangler.jsonc The Workflow configuration is defined in the \`wrangler.jsonc\` file. This file includes bindings for the R2 bucket, D1 database, Workers AI, and the Workflow itself. Ensure that all necessary bindings and environment variables are correctly set up to match your Cloudflare account and services. \* wrangler.jsonc \`\`\`jsonc { "$schema": "node\_modules/wrangler/config-schema.json", "name": "workflows-waitforevent", "main": "src/index.ts", "compatibility\_date": "2025-04-14", "observability": { "enabled": true, "head\_sampling\_rate": 1, }, "ai": { "binding": "AI" }, "workflows": \[\ {\ "name": "workflows-starter",\ "binding": "MY\_WORKFLOW",\ "class\_name": "MyWorkflow"\ }\ \], "r2\_buckets": \[\ {\ "bucket\_name": "workflow-demo",\ "binding": "workflow\_demo\_bucket"\ }\ \], "d1\_databases": \[\ {\ "binding": "DB",\ "database\_name": "workflows-demo-d1",\ "database\_id": "66e4fbe9-06ac-4548-abba-2dc42088e13a"\ }\ \] } \`\`\` \* wrangler.toml \`\`\`toml "$schema" = "node\_modules/wrangler/config-schema.json" name = "workflows-waitforevent" main = "src/index.ts" compatibility\_date = "2025-04-14" \[observability\] enabled = true head\_sampling\_rate = 1 \[ai\] binding = "AI" \[\[workflows\]\] name = "workflows-starter" binding = "MY\_WORKFLOW" class\_name = "MyWorkflow" \[\[r2\_buckets\]\] bucket\_name = "workflow-demo" binding = "workflow\_demo\_bucket" \[\[d1\_databases\]\] binding = "DB" database\_name = "workflows-demo-d1" database\_id = "66e4fbe9-06ac-4548-abba-2dc42088e13a" \`\`\` For access to the codebase, deployment instructions, and reference architecture, please visit the \[GitHub repository\](https://github.com/cloudflare/docs-examples/tree/main/workflows/waitForEvent). This resource provides all the necessary tools and information to effectively implement the Workflow and Next.js frontend application. --- title: Build a Durable AI Agent · Cloudflare Workflows docs description: 'In this guide, you will build an AI agent that researches GitHub repositories. Give it a task like "Compare open-source LLM projects" and it will:' lastUpdated: 2026-01-26T15:55:56.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/get-started/durable-agents/ md: https://developers.cloudflare.com/workflows/get-started/durable-agents/index.md --- In this guide, you will build an AI agent that researches GitHub repositories. Give it a task like "Compare open-source LLM projects" and it will: 1. Search GitHub for relevant repositories 2. Fetch details about each one (stars, forks, activity) 3. Analyze and compare them 4. Return a recommendation Each LLM call and tool call becomes a step — a self-contained, individually retriable unit of work. If any step fails, Workflows retries it automatically. If the entire Workflow crashes mid-task, it resumes from the last successful step. | Challenge | Solution with Workflows | | - | - | | Long-running agent loops | Durable execution that survives any interruption | | Unreliable LLM and API calls | Automatic retry with independent checkpoints | | Waiting for human approval | \`waitForEvent()\` pauses for hours or days | | Polling for job completion | \`step.sleep()\` between checks without consuming resources | This guide uses the Anthropic SDK, but the same patterns apply to any LLM SDK (OpenAI, Google AI, Mistral, etc.). ## Quick start If you want to skip the steps and pull down the complete agent, utilizing \[AI Gateway\](https://developers.cloudflare.com/ai-gateway), run the following command: \`\`\`sh npm create cloudflare@latest -- --template cloudflare/docs-examples/workflows/durableAgent \`\`\` Use this option if you are familiar with Cloudflare Workflows or want to explore the code first. Follow the steps below to learn how to build a durable AI agent from scratch. ## Prerequisites 1. Sign up for a \[Cloudflare account\](https://dash.cloudflare.com/sign-up/workers-and-pages). 2. Install \[\`Node.js\`\](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm). Node.js version manager Use a Node version manager like \[Volta\](https://volta.sh/) or \[nvm\](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node.js versions. \[Wrangler\](https://developers.cloudflare.com/workers/wrangler/install-and-update/), discussed later in this guide, requires a Node version of \`16.17.0\` or later. You will also need an \[Anthropic API key\](https://platform.claude.com/settings/keys) for LLM calls. New accounts include free credits. ## 1. Create a new Worker project 1. Create a new Worker project by running the following command: \* npm \`\`\`sh npm create cloudflare@latest -- durable-ai-agent \`\`\` \* yarn \`\`\`sh yarn create cloudflare durable-ai-agent \`\`\` \* pnpm \`\`\`sh pnpm create cloudflare@latest durable-ai-agent \`\`\` 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 project: \`\`\`sh cd durable-ai-agent \`\`\` 3. Install dependencies: \`\`\`sh npm install @anthropic-ai/sdk \`\`\` ## 2. Define your tools Tools are functions the LLM can call to interact with external systems. You define the schema (what inputs the tool accepts) and the implementation (what it does). The LLM decides when to use each tool based on the task. 1. Create \`src/tools.ts\` with two complementary tools: \`\`\`ts export interface SearchReposInput { query: string; limit?: number; } export interface GetRepoInput { owner: string; repo: string; } interface GitHubSearchResponse { items: Array<{ full\_name: string; stargazers\_count: number }>; } interface GitHubRepoResponse { full\_name: string; description: string; stargazers\_count: number; forks\_count: number; open\_issues\_count: number; language: string; license: { name: string } | null; updated\_at: string; } export const searchReposTool = { name: "search\_repos" as const, description: "Search GitHub repositories by keyword. Returns top results. Use get\_repo for details.", input\_schema: { type: "object" as const, properties: { query: { type: "string", description: "Search query (e.g., 'typescript orm')", }, limit: { type: "number", description: "Max results (default 5)" }, }, required: \["query"\], }, run: async (input: SearchReposInput): Promise => { const response = await fetch( \`https://api.github.com/search/repositories?q=${encodeURIComponent(input.query)}&sort=stars&per\_page=${input.limit ?? 5}\`, { headers: { Accept: "application/vnd.github+json", "User-Agent": "DurableAgent/1.0", }, }, ); if (!response.ok) return \`Search failed: ${response.status}\`; const data = await response.json(); return JSON.stringify( data.items.map((r) => ({ name: r.full\_name, stars: r.stargazers\_count })), ); }, }; export const getRepoTool = { name: "get\_repo" as const, description: "Get detailed info about a GitHub repository including stars, forks, and description.", input\_schema: { type: "object" as const, properties: { owner: { type: "string", description: "Repository owner (e.g., 'cloudflare')", }, repo: { type: "string", description: "Repository name (e.g., 'workers-sdk')", }, }, required: \["owner", "repo"\], }, run: async (input: GetRepoInput): Promise => { const response = await fetch( \`https://api.github.com/repos/${input.owner}/${input.repo}\`, { headers: { Accept: "application/vnd.github+json", "User-Agent": "DurableAgent/1.0", }, }, ); if (!response.ok) return \`Repo not found: ${input.owner}/${input.repo}\`; const data = await response.json(); return JSON.stringify({ name: data.full\_name, description: data.description, stars: data.stargazers\_count, forks: data.forks\_count, issues: data.open\_issues\_count, language: data.language, license: data.license?.name ?? "None", updated: data.updated\_at, }); }, }; export const tools = \[searchReposTool, getRepoTool\]; \`\`\` These tools complement each other: \`search\_repos\` finds repositories, and \`get\_repo\` fetches details about specific ones. ## 3. Write your agent Workflow A Workflow extends \`WorkflowEntrypoint\` and implements a \`run\` method. \* The \[\`step\`\](https://developers.cloudflare.com/workflows/build/workers-api/#step) object provides methods to define durable steps. \* \`step.do(name, callback)\` executes code and persists the result. If the Workflow is interrupted, it resumes from the last successful step. For a gentler introduction, refer to \[Build your first Workflow\](https://developers.cloudflare.com/workflows/get-started/guide/). The agent loop sends messages to the LLM, executes any tool calls, and repeats until the task is complete. Each LLM call and tool execution is wrapped in \`step.do()\` for durability. 1. Create \`src/workflow.ts\`: \`\`\`ts import { WorkflowEntrypoint, WorkflowStep } from "cloudflare:workers"; import type { WorkflowEvent } from "cloudflare:workers"; import Anthropic from "@anthropic-ai/sdk"; import { tools, searchReposTool, getRepoTool, type SearchReposInput, type GetRepoInput, } from "./tools"; type Params = { task: string }; export class AgentWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { const client = new Anthropic({ apiKey: this.env.ANTHROPIC\_API\_KEY }); const messages: Anthropic.MessageParam\[\] = \[\ { role: "user", content: event.payload.task },\ \]; const toolDefinitions = tools.map(({ run, ...rest }) => rest); // Durable agent loop - each turn is checkpointed for (let turn = 0; turn < 10; turn++) { const response = (await step.do( \`llm-turn-${turn}\`, { retries: { limit: 3, delay: "10 seconds", backoff: "exponential" } }, async () => { const msg = await client.messages.create({ model: "claude-sonnet-4-5-20250929", max\_tokens: 4096, tools: toolDefinitions, messages, }); // Serialize for Workflow state return JSON.parse(JSON.stringify(msg)); }, )) as Anthropic.Message; if (!response || !response.content) continue; messages.push({ role: "assistant", content: response.content }); if (response.stop\_reason === "end\_turn") { const textBlock = response.content.find( (b): b is Anthropic.TextBlock => b.type === "text", ); return { status: "complete", turns: turn + 1, result: textBlock?.text ?? null, }; } const toolResults: Anthropic.ToolResultBlockParam\[\] = \[\]; for (const block of response.content) { if (block.type !== "tool\_use") continue; const result = await step.do( \`tool-${turn}-${block.id}\`, { retries: { limit: 2, delay: "5 seconds" } }, async () => { switch (block.name) { case "search\_repos": return searchReposTool.run(block.input as SearchReposInput); case "get\_repo": return getRepoTool.run(block.input as GetRepoInput); default: return \`Unknown tool: ${block.name}\`; } }, ); toolResults.push({ type: "tool\_result", tool\_use\_id: block.id, content: result, }); } messages.push({ role: "user", content: toolResults }); } return { status: "max\_turns\_reached", turns: 10 }; } } \`\`\` Why separate steps for LLM and tools? Each \`step.do()\` creates a checkpoint. If your Workflow crashes or the Worker restarts: \* \*\*After LLM step\*\*: The response is persisted. On resume, it skips the LLM call and moves to tool execution. \* \*\*After tool step\*\*: The result is persisted. If a later tool fails, earlier tools do not re-run. This is especially important for: \* \*\*LLM calls\*\*: Expensive and slow, should not repeat unnecessarily \* \*\*External APIs\*\*: May have rate limits or side effects \* \*\*Idempotency\*\*: Some tools (like sending emails) should not run twice ## 4. Configure your Workflow 1. Open \`wrangler.jsonc\` and add the \`workflow\` configuration: \* wrangler.jsonc \`\`\`jsonc { "$schema": "node\_modules/wrangler/config-schema.json", "name": "durable-ai-agent", "main": "src/index.ts", "compatibility\_date": "2025-01-01", "observability": { "enabled": true }, "workflows": \[\ {\ "name": "agent-workflow",\ "binding": "AGENT\_WORKFLOW",\ "class\_name": "AgentWorkflow"\ }\ \] } \`\`\` \* wrangler.toml \`\`\`toml "$schema" = "node\_modules/wrangler/config-schema.json" name = "durable-ai-agent" main = "src/index.ts" compatibility\_date = "2025-01-01" \[observability\] enabled = true \[\[workflows\]\] name = "agent-workflow" binding = "AGENT\_WORKFLOW" class\_name = "AgentWorkflow" \`\`\` 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.AGENT\_WORKFLOW\`). 2. Generate types for your bindings: \`\`\`sh npx wrangler types \`\`\` This creates a \`worker-configuration.d.ts\` file with the \`Env\` type that includes your \`AGENT\_WORKFLOW\` binding. ## 5. Write your API The Worker exposes an HTTP API to start new agent instances and check their status. Each instance runs independently and can be polled for results. 1. Replace \`src/index.ts\`: \`\`\`ts export { AgentWorkflow } from "./workflow"; export default { async fetch(request: Request, env: Env): Promise { const url = new URL(request.url); const instanceId = url.searchParams.get("instanceId"); if (instanceId) { const instance = await env.AGENT\_WORKFLOW.get(instanceId); const status = await instance.status(); return Response.json({ status: status.status, output: status.output, }); } if (request.method === "POST") { const { task } = await request.json<{ task: string }>(); const instance = await env.AGENT\_WORKFLOW.create({ params: { task }, }); return Response.json({ instanceId: instance.id }); } return new Response("POST a task to start an agent", { status: 400 }); }, } satisfies ExportedHandler; \`\`\` ## 6. Develop locally 1. Create a \[\`.env\` file\](https://developers.cloudflare.com/workers/wrangler/environments/#secrets-in-local-development) for local development: \`\`\`sh ANTHROPIC\_API\_KEY=your-api-key-here \`\`\` 2. Start the dev server: \`\`\`sh npx wrangler dev \`\`\` 3. Start an agent that searches and compares repositories: \`\`\`sh curl -X POST http://localhost:8787 \\ -H "Content-Type: application/json" \\ -d '{"task": "Compare open-source LLM projects"}' \`\`\` \`\`\`json { "instanceId": "abc-123-def" } \`\`\` 4. Check progress (may take a few seconds to complete): \`\`\`sh curl "http://localhost:8787?instanceId=abc-123-def" \`\`\` The agent will search for repositories, fetch details, and return a comparison. ## 7. Deploy 1. Deploy the Worker: \`\`\`sh npx wrangler deploy \`\`\` 2. Add your API key as a secret: \`\`\`sh npx wrangler secret put ANTHROPIC\_API\_KEY \`\`\` 3. Start an agent on your deployed Worker: \`\`\`sh curl -X POST https://durable-ai-agent..workers.dev \\ -H "Content-Type: application/json" \\ -d '{"task": "Compare open-source LLM projects"}' \`\`\` 4. Inspect agent runs with the CLI: \`\`\`sh npx wrangler workflows instances describe agent-workflow latest \`\`\` This shows every step the agent took, including LLM calls, tool executions, timing, and any retries. You can also view this in the Cloudflare dashboard under \*\*agent-workflow\*\*. \[Go to \*\*Workflows\*\*\](https://dash.cloudflare.com/?to=/:account/workers/workflows) ## Adding real-time updates with Agents SDK The polling approach works well for simple use cases, but for real-time UIs you can combine Workflows with the \[Agents SDK\](https://developers.cloudflare.com/agents/). The pattern is as follows: 1. Agent handles WebSocket connections and client state 2. Workflow runs the durable agent loop and pushes updates to the Agent 3. Agent broadcasts state changes to all connected clients In your Workflow, push updates to the Agent: \`\`\`ts // agentId passed via workflow params const agent = this.env.RESEARCH\_AGENT.get( this.env.RESEARCH\_AGENT.idFromName(agentId), ); await agent.updateProgress({ status: "searching", message: "Found 5 repositories...", }); \`\`\` In your Agent, receive updates and broadcast to clients: \`\`\`ts import { Agent } from "agents"; export class ResearchAgent extends Agent { async updateProgress(progress: { status: string; message: string }) { this.setState({ ...this.state, ...progress }); // pushes to all connected clients } } \`\`\` Clients use \`useAgent()\` to subscribe to state changes: \`\`\`tsx import { useAgent } from "agents/react"; const \[state, setState\] = useState(initialState); useAgent({ agent: "research-agent", onStateUpdate: (newState) => setState(newState), }); // state updates in real-time as the Workflow progresses \`\`\` This gives you durable execution (Workflows) with real-time UI updates (Agents SDK). For a complete example with a React UI, refer to the \[durableAgent template\](https://github.com/cloudflare/docs-examples/tree/main/workflows/durableAgent). ## Learn more \[Events and parameters \](https://developers.cloudflare.com/workflows/build/events-and-parameters/)Pass data to Workflows and pause for external events with waitForEvent. \[Sleeping and retrying \](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/)Configure retry behavior and sleep patterns. \[Workers API \](https://developers.cloudflare.com/workflows/build/workers-api/)Explore the full Workflows API for programmatic control. \[Agents SDK \](https://developers.cloudflare.com/agents/)For interactive agents with real-time chat and WebSocket connections. --- title: Build your first Workflow · Cloudflare Workflows docs description: 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. lastUpdated: 2026-01-22T21:38:43.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/get-started/guide/ md: https://developers.cloudflare.com/workflows/get-started/guide/index.md --- 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\](https://developers.cloudflare.com/r2/), automate generation of \[Workers AI\](https://developers.cloudflare.com/workers-ai/) embeddings into a \[Vectorize\](https://developers.cloudflare.com/vectorize/) vector database, or to trigger user lifecycle emails using \[Email Service\](https://developers.cloudflare.com/email-routing/). Note The term "Durable Execution" is widely used to describe this programming model. "Durable" describes the ability of the program to implicitly persist state without you having to manually write to an external store or serialize program state. 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: \`\`\`sh 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\](https://dash.cloudflare.com/sign-up/workers-and-pages). 2. Install \[\`Node.js\`\](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm). Node.js version manager Use a Node version manager like \[Volta\](https://volta.sh/) or \[nvm\](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node.js versions. \[Wrangler\](https://developers.cloudflare.com/workers/wrangler/install-and-update/), 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: \* npm \`\`\`sh npm create cloudflare@latest -- my-workflow \`\`\` \* yarn \`\`\`sh yarn create cloudflare my-workflow \`\`\` \* pnpm \`\`\`sh pnpm 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: \`\`\`sh cd my-workflow \`\`\` What files did C3 create? In your project directory, C3 will have generated the following: \* \`wrangler.jsonc\`: Your \[Wrangler configuration file\](https://developers.cloudflare.com/workers/wrangler/configuration/#sample-wrangler-configuration). \* \`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\`: \`\`\`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 { async run(event: WorkflowEvent, 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(); }); 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\](https://developers.cloudflare.com/workflows/build/events-and-parameters/) so that events that trigger our Workflow are typed. The \[\`step\`\](https://developers.cloudflare.com/workflows/build/workers-api/#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\](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/) to \`step.do()\` to customize how failures are handled. See the \[full step API\](https://developers.cloudflare.com/workflows/build/workers-api/#step) 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\](https://developers.cloudflare.com/workflows/build/rules-of-workflows/). ## 3. Configure your Workflow 1. Open \`wrangler.jsonc\`, which is your \[Wrangler configuration file\](https://developers.cloudflare.com/workers/wrangler/configuration/) for your Workers project and your Workflow, and add the \`workflows\` configuration: \* wrangler.jsonc \`\`\`jsonc { "$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"\ }\ \] } \`\`\` \* wrangler.toml \`\`\`toml "$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\](https://developers.cloudflare.com/workers/runtime-apis/bindings/) (such as \[KV\](https://developers.cloudflare.com/kv/), \[R2\](https://developers.cloudflare.com/r2/), or \[D1\](https://developers.cloudflare.com/d1/)) via \`this.env\` within your Workflow. For more information on bindings within Workers, refer to \[Bindings (env)\](https://developers.cloudflare.com/workers/runtime-apis/bindings/). 2. Now, generate types for your bindings: \`\`\`sh 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\](https://developers.cloudflare.com/workers/runtime-apis/handlers/fetch/) to start and check Workflow instances: \`\`\`ts export { MyWorkflow } from "./workflow"; export default { async fetch(request: Request, env: Env): Promise { 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; \`\`\` ## 5. Develop locally 1. Start a local development server: \`\`\`sh npx wrangler dev \`\`\` 2. To start a Workflow instance, open a new terminal window and run: \`\`\`sh curl http://localhost:8787 \`\`\` An \`instanceId\` will be automatically generated: \`\`\`json { "instanceId": "abc-123-def" } \`\`\` 3. Check the status using the returned \`instanceId\`: \`\`\`sh 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: \`\`\`sh npx wrangler deploy \`\`\` Test in production using the same curl commands against your deployed URL. You can also \[trigger a workflow instance\](https://developers.cloudflare.com/workflows/build/trigger-workflows/) in production via Workers, Wrangler, or the Cloudflare dashboard. Once deployed, you can also inspect Workflow instances with the CLI: \`\`\`sh 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 \[Events and parameters \](https://developers.cloudflare.com/workflows/build/events-and-parameters/)Pass data to Workflows and pause for external events with waitForEvent. \[Sleeping and retrying \](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/)Configure retry behavior and sleep patterns. \[Workers API \](https://developers.cloudflare.com/workflows/build/workers-api/)Explore the full Workflows API for programmatic control. \[Rules of Workflows \](https://developers.cloudflare.com/workflows/build/rules-of-workflows/)Understand the programming model and best practices. --- title: Metrics and analytics · Cloudflare Workflows docs description: Workflows expose metrics that allow you to inspect and measure Workflow execution, error rates, steps, and total duration across each (and all) of your Workflows. lastUpdated: 2025-12-03T22:57:02.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/observability/metrics-analytics/ md: https://developers.cloudflare.com/workflows/observability/metrics-analytics/index.md --- Workflows expose metrics that allow you to inspect and measure Workflow execution, error rates, steps, and total duration across each (and all) of your Workflows. The metrics displayed in the \[Cloudflare dashboard\](https://dash.cloudflare.com/) charts are queried from Cloudflare’s \[GraphQL Analytics API\](https://developers.cloudflare.com/analytics/graphql-api/). You can access the metrics \[programmatically\](#query-via-the-graphql-api) via GraphQL or HTTP client. ## Metrics Workflows currently export the below metrics within the \`workflowsAdaptiveGroups\` GraphQL dataset. | Metric | GraphQL Field Name | Description | | - | - | - | | Read Queries (qps) | \`readQueries\` | The number of read queries issued against a database. This is the raw number of read queries, and is not used for billing. | Metrics can be queried (and are retained) for the past 31 days. ### Labels and dimensions The \`workflowsAdaptiveGroups\` dataset provides the following dimensions for filtering and grouping query results: \* \`workflowName\` - Workflow name - e.g. \`my-workflow\` \* \`instanceId\` - Instance ID \* \`stepName\` - Step name \* \`eventType\` - Event type (see \[event types\](#event-types)) \* \`stepCount\` - Step number within a given instance \* \`date\` - The date when the Workflow was triggered \* \`datetimeFifteenMinutes\` - The date and time truncated to fifteen minutes \* \`datetimeFiveMinutes\` - The date and time truncated to five minutes \* \`datetimeHour\` - The date and time truncated to the hour \* \`datetimeMinute\` - The date and time truncated to the minute ### Event types The \`eventType\` metric allows you to filter (or groupBy) Workflows and steps based on their last observed status. The possible values for \`eventType\` are documented below: #### Workflows-level status labels \* \`WORKFLOW\_QUEUED\` - the Workflow is queued, but not currently running. This can happen when you are at the \[concurrency limit\](https://developers.cloudflare.com/workflows/reference/limits/) and new instances are waiting for currently running instances to complete. \* \`WORKFLOW\_START\` - the Workflow has started and is running. \* \`WORKFLOW\_SUCCESS\` - the Workflow finished without errors. \* \`WORKFLOW\_FAILURE\` - the Workflow failed due to errors (exhausting retries, errors thrown, etc). \* \`WORKFLOW\_TERMINATED\` - the Workflow was explicitly terminated. #### Step-level status labels \* \`STEP\_START\` - the step has started and is running. \* \`STEP\_SUCCESS\` - the step finished without errors. \* \`STEP\_FAILURE\` - the step failed due to an error. \* \`SLEEP\_START\` - the step is sleeping. \* \`SLEEP\_COMPLETE\` - the step last finished sleeping. \* \`ATTEMPT\_START\` - a step is retrying. \* \`ATTEMPT\_SUCCESS\` - the retry succeeded. \* \`ATTEMPT\_FAILURE\` - the retry attempt failed. ## View metrics in the dashboard Per-Workflow and instance analytics for Workflows are available in the Cloudflare dashboard. To view current and historical metrics for a database: 1. In the Cloudflare dashboard, go to the \*\*Workflows\*\* page. \[Go to \*\*Workflows\*\*\](https://dash.cloudflare.com/?to=/:account/workers/workflows) 2. Select a Workflow to view its metrics. You can optionally select a time window to query. This defaults to the last 24 hours. ## Query via the GraphQL API You can programmatically query analytics for your Workflows via the \[GraphQL Analytics API\](https://developers.cloudflare.com/analytics/graphql-api/). This API queries the same datasets as the Cloudflare dashboard, and supports GraphQL \[introspection\](https://developers.cloudflare.com/analytics/graphql-api/features/discovery/introspection/). Workflows GraphQL datasets require an \`accountTag\` filter with your Cloudflare account ID, and includes the \`workflowsAdaptiveGroups\` dataset. ### Examples To query the count (number of workflow invocations) and sum of \`wallTime\` for a given \`$workflowName\` between \`$datetimeStart\` and \`$datetimeEnd\`, grouping by \`date\`: \`\`\`graphql query WorkflowInvocationsExample( $accountTag: string! $datetimeStart: Time $datetimeEnd: Time $workflowName: string ) { viewer { accounts(filter: { accountTag: $accountTag }) { wallTime: workflowsAdaptiveGroups( limit: 10000 filter: { datetimeHour\_geq: $datetimeStart datetimeHour\_leq: $datetimeEnd workflowName: $workflowName } orderBy: \[count\_DESC\] ) { count sum { wallTime } dimensions { date: datetimeHour } } } } } \`\`\` \[Run in GraphQL API Explorer\](https://graphql.cloudflare.com/explorer?query=I4VwpgTgngBA6gewgawGYBsEHcCSA7ANwQGMBDAFwEsE8BnAUQA9SBbAB3TAAoAoGGACSlixBCDzkAKqQDmALhi1yESnhkBCPoIAmFMFRZgAyuVIRyCyZUNaBu8vuth6ebZae2sSNJiwA5VjAFJRU1HgBKGABvLQJKMCxIaK1+YVFxclouVEp0BwgFKJg0sQlpeUESjPKYAF9ImP4mmCxSdHQrQwUvFAxsWgBBXTYqAjAAcQgxNiyU5ph0a0oLGABGAAZN9bnmnLzIQp35+0dDAAkxCAB9GTBgBTs9A2NTcyPmk+eLkGvOe50nk4XNp3k0ej5sAEuoJwX1-IFQbVQUhtJAAEJQBQAbXSEiuABF6EYAMIAXSODVBuPIoNoIBYyXm81a7U6YERoO0Tjo1DojKZTROCk+Tm+EA5TKRzSldR4tSAA\\&variables=N4IghgxhD2CuB2AXAKmA5iAXCAggYTwHkBVAOWQH0BJAERABoQATMRAU0QEsBbNgZURgAToiwgATAAZxANgC0kgIxzZyKZnHjMkgOwAtBs1YcebAKLwmYqbIXLxO5NI1bdBxgHdoQgNYAzABtoD1IwXjEAJTMABQAZfDMKAHUqZAAJCj5kCKpSAHEQAF8gA) Here we are doing the same for \`wallTime\`, \`instanceRuns\` and \`stepCount\` in the same query: \`\`\`graphql query WorkflowInvocationsExample2( $accountTag: string! $datetimeStart: Time $datetimeEnd: Time $workflowName: string ) { viewer { accounts(filter: { accountTag: $accountTag }) { instanceRuns: workflowsAdaptiveGroups( limit: 10000 filter: { datetimeHour\_geq: $datetimeStart datetimeHour\_leq: $datetimeEnd workflowName: $workflowName eventType: "WORKFLOW\_START" } orderBy: \[count\_DESC\] ) { count dimensions { date: datetimeHour } } stepCount: workflowsAdaptiveGroups( limit: 10000 filter: { datetimeHour\_geq: $datetimeStart datetimeHour\_leq: $datetimeEnd workflowName: $workflowName eventType: "WORKFLOW\_START" } orderBy: \[count\_DESC\] ) { count dimensions { date: datetimeHour } } wallTime: workflowsAdaptiveGroups( limit: 10000 filter: { datetimeHour\_geq: $datetimeStart datetimeHour\_leq: $datetimeEnd workflowName: $workflowName } orderBy: \[count\_DESC\] ) { count sum { wallTime } dimensions { date: datetimeHour } } } } } \`\`\` \[Run in GraphQL API Explorer\](https://graphql.cloudflare.com/explorer?query=I4VwpgTgngBA6gewgawGYBsEHcCSA7ANwQGMBDAFwEsE8BnAUQA9SBbAB3TACYAKAKBgwAJKWLEEIPOQAqpAOYAuGLXIRKeOQEIBwgCYUwVFmADK5UhHJLplYzqH7yh22Hp5d1l-axI0mLAByrGBKKmoafACUMADeOgSUYFiQsTqCouKS5LQ8qJToThBKMTAZElKyisJlWZUwAL7RcYItMOoqpHjEYABKkrRKPigY2LQAgvpsVARgAOIQEmw5aa0w6LaUVjAAjAAM+7srrXkFkMVHq47OxgASEhAA+nJgwEoOBkam5pYXrVefdxAj04rz0Hxcbl0vxaQz82CCxjesJGgWC0MEYBmFSgbBCMAARHAAPI9ADSADEADJEuAPEzSMY9aT46H1aFIXSQABCUCUAG1MlIHgARegmADCAF0Lk1oYLyNDdC46NQ6KlVpcDEp-i5ARBWRc2RqVGA2OLyltkf5xpNpnMFiAlvwNYJ1ixNko9gdoSdCucXS0dbd7k8Xm8g18LAqA4II3qHiDw+DjJD0TArfDgkjfCiEWA05iwNjcUpCSSKdTafTGcyDQGOdzeTABRaRWKpTL1S75YrlbRVbQuwGrtrk2A9XXVkbVlhSOh0DZEemc9aJqQppQZvNFssA26PTsDocA76zkOXXGQ89Qe8nJ8zFG05egQmw2C7xD3GmM6il0If3mk4ag2EA8vy8pthK0oarKAY9gGtAgCw54arO86LvmAbThqSrGCqNCDs0MYjjAz76lhhorNObL1EAA\\&variables=N4IghgxhD2CuB2AXAKmA5iAXCAggYTwHkBVAOWQH0BJAERABoQATMRAU0QEsBbNgZURgAToiwgATAAZxANgC0kgIxzZyKZnHjMkgOwAtBs1YcebAKLwmYqbIXLxO5NI1bdBxgHdoQgNYAzABtoD1IwXjEAJTMABQAZfDMKAHUqZAAJCj5kCKpSAHEQAF8gA) Here lets query \`workflowsAdaptive\` for raw data about \`$instanceId\` between \`$datetimeStart\` and \`$datetimeEnd\`: \`\`\`graphql query WorkflowsAdaptiveExample( $accountTag: string! $datetimeStart: Time $datetimeEnd: Time $instanceId: string ) { viewer { accounts(filter: { accountTag: $accountTag }) { workflowsAdaptive( limit: 100 filter: { datetime\_geq: $datetimeStart datetime\_leq: $datetimeEnd instanceId: $instanceId } orderBy: \[datetime\_ASC\] ) { datetime eventType workflowName instanceId stepCount wallTime } } } } \`\`\` \[Run in GraphQL API Explorer\](https://graphql.cloudflare.com/explorer?query=I4VwpgTgngBA6gewgawGYBsEHcDOBBAEwEMAHAFwEsA3MAUQA8iBbE9MACgCgYYASIgMYCEIAHZkAKkQDmALhg4yECqOkBCbn2JkwlJmADKZIhDLyJFfZt7bdluqILn71lYqKiBYAJJOFSlWlOAEoYAG9NKgowLEhwzR5BYTEyHHZUCnQdCHkwmCSRcSk5PgKU4pgAX1CInjqYLCQ0TFxCUkoaLnr69EsKMxgARgAGYYTujKzIXPHumFs9MAB9aTBgeRsiHUWjEzJZ7oX7JbZ1rS27fVpHA-q3Y08fP157jy9fW6rPpAJIACEoPIANpHfRLPAGADCAF0DjVPqCwJ8wDQilASEi5jxGigMNgAHLMTFY16PD5Y-xgEiQwr7ClYIjodAWKxzSrjdk8dmVIA\\&variables=N4IghgxhD2CuB2AXAKmA5iAXCAggYTwHkBVAOWQH0BJAERABoQATMRAU0QEsBbNgZURgAToiwgATAAZxANgC0kgIxzZyKZnHjMkgOwAtBs1YcebAKLwmYqbIXLxO5NI1bdBxp3gBnQfAhsqK2wAJTMABQAZfDMKAHUqZAAJajoAXyA) #### GraphQL query variables Example values for the query variables: \`\`\`json { "accountTag": "fedfa729a5b0ecfd623bca1f9000f0a22", "datetimeStart": "2024-10-20T00:00:00Z", "datetimeEnd": "2024-10-29T00:00:00Z", "workflowName": "shoppingCart", "instanceId": "ecc48200-11c4-22a3-b05f-88a3c1c1db81" } \`\`\` --- title: Interact with a Workflow · Cloudflare Workflows docs description: The Python Workers platform leverages FFI to access bindings to Cloudflare resources. Refer to the bindings documentation for more information. lastUpdated: 2026-01-22T21:38:43.000Z chatbotDeprioritize: true source\_url: html: https://developers.cloudflare.com/workflows/python/bindings/ md: https://developers.cloudflare.com/workflows/python/bindings/index.md --- Python Workflows are in beta, as well as the underlying platform. You must add both \`python\_workflows\` and \`python\_workers\` compatibility flags to your Wrangler config file. Also, Python Workflows requires \`compatibility\_date = "2025-08-01"\`, or later, to be set in your Wrangler config file. The Python Workers platform leverages \[FFI\](https://en.wikipedia.org/wiki/Foreign\_function\_interface) to access bindings to Cloudflare resources. Refer to the \[bindings\](https://developers.cloudflare.com/workers/languages/python/ffi/#using-bindings-from-python-workers) documentation for more information. From the configuration perspective, enabling Python Workflows requires adding the \`python\_workflows\` compatibility flag to your \`wrangler.toml\` file. \* wrangler.jsonc \`\`\`jsonc { "$schema": "./node\_modules/wrangler/config-schema.json", "name": "workflows-starter", "main": "src/index.ts", "compatibility\_date": "2025-10-24", "compatibility\_flags": \[\ "python\_workflows",\ "python\_workers"\ \], "workflows": \[\ {\ "name": "workflows-starter",\ "binding": "MY\_WORKFLOW",\ "class\_name": "MyWorkflow"\ }\ \] } \`\`\` \* wrangler.toml \`\`\`toml #:schema node\_modules/wrangler/config-schema.json name = "workflows-starter" main = "src/index.ts" compatibility\_date = "2025-10-24" compatibility\_flags = \["python\_workflows", "python\_workers"\] \[\[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" \`\`\` And this is how you use the payload in your workflow: \`\`\`python from pyodide.ffi import to\_js class DemoWorkflowClass(WorkflowEntrypoint): async def run(self, event, step): @step.do('step-name') async def first\_step(): payload = event\["payload"\] return payload \`\`\` ## Workflow The \`Workflow\` binding gives you access to the \[Workflow\](https://developers.cloudflare.com/workflows/build/workers-api/#workflow) class. All its methods are available on the binding. Under the hood, the \`Workflow\` binding is a Javascript object that is exposed to the Python script via \[JsProxy\](https://pyodide.org/en/stable/usage/api/python-api/ffi.html#pyodide.ffi.JsProxy). This means that the values returned by its methods are also \`JsProxy\` objects, and need to be converted back into Python objects using \`python\_from\_rpc\`. ### \`create\` Create (trigger) a new instance of a given Workflow. \* \`create(options=None)\`\\\* \`options\` - an \*\*optional\*\* dictionary of options to pass to the workflow instance. Should contain the same keys as the \[WorkflowInstanceCreateOptions\](https://developers.cloudflare.com/workflows/build/workers-api/#workflowinstancecreateoptions) type. \`\`\`python from js import Object from pyodide.ffi import to\_js from workers import WorkerEntrypoint, Response class Default(WorkerEntrypoint): async def fetch(self, request): event = {"foo": "bar"} options = to\_js({"params": event}, dict\_converter=Object.fromEntries) await self.env.MY\_WORKFLOW.create(options) return Response.json({"status": "success"}) \`\`\` Note Values returned from steps need to be converted into Javascript objects using \`to\_js\`. This is why we explicitly construct the payload using \`Object.fromEntries\`. The \`create\` method returns a \[\`WorkflowInstance\`\](https://developers.cloudflare.com/workflows/build/workers-api/#workflowinstance) object, which can be used to query the status of the workflow instance. Note that this is a Javascript object, and not a Python object. ### \`create\_batch\` Create (trigger) a batch of new workflow instances, up to 100 instances at a time. This is useful if you need to create multiple instances at once within the \[instance creation limit\](https://developers.cloudflare.com/workflows/reference/limits/). \* \`create\_batch(batch)\`\\\* \`batch\` - list of \`WorkflowInstanceCreateOptions\` to pass when creating an instance, including a user-provided ID and payload parameters. Each element of the \`batch\` list is expected to include both \`id\` and \`params\` properties: \`\`\`python from pyodide.ffi import to\_js from js import Object # Create a new batch of 3 Workflow instances, each with its own ID and pass params to the Workflow instances listOfInstances = \[\ to\_js({ "id": "id-abc123", "params": { "hello": "world-0" } }, dict\_converter=Object.fromEntries),\ to\_js({ "id": "id-def456", "params": { "hello": "world-1" } }, dict\_converter=Object.fromEntries),\ to\_js({ "id": "id-ghi789", "params": { "hello": "world-2" } }, dict\_converter=Object.fromEntries)\ \]; await env.MY\_WORKFLOW.create\_batch(listOfInstances); \`\`\` ### \`get\` Get a workflow instance by ID. \* \`get(id)\`\\\* \`id\` - the ID of the workflow instance to get. Returns a \[\`WorkflowInstance\`\](https://developers.cloudflare.com/workflows/build/workers-api/#workflowinstance) object, which can be used to query the status of the workflow instance. \`\`\`python instance = await env.MY\_WORKFLOW.get("abc-123") # FFI methods available for WorkflowInstance await instance.status() await instance.pause() await instance.resume() await instance.restart() await instance.terminate() \`\`\` ### \`send\_event\` Send an event to a workflow instance. \* \`send\_event(options)\`\\\* \`type\` - the type of event to send to the workflow instance. \\\* \`payload\` - the payload to send to the workflow instance. \`\`\`python from pyodide.ffi import to\_js from js import Object await env.MY\_WORKFLOW.send\_event(to\_js({ "type": "my-event-type", "payload": { "foo": "bar" } }, dict\_converter=Object.fromEntries)) \`\`\` Note Values passed to \`send\_event\` require explicit type translation into JS objects. ## REST API (HTTP) Refer to the \[Workflows REST API documentation\](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/methods/create/). ## Command line (CLI) Refer to the \[CLI quick start\](https://developers.cloudflare.com/workflows/get-started/guide/) to learn more about how to manage and trigger Workflows via the command-line. --- title: DAG Workflows · Cloudflare Workflows docs description: The Python Workflows SDK supports DAG workflows in a declarative way, using the step.do decorator with the depends parameter to define dependencies (other steps that must complete before this step can run). lastUpdated: 2026-01-21T14:49:43.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/python/dag/ md: https://developers.cloudflare.com/workflows/python/dag/index.md --- The Python Workflows SDK supports DAG workflows in a declarative way, using the \`step.do\` decorator with the \`depends\` parameter to define dependencies (other steps that must complete before this step can run). \`\`\`python from workers import Response, WorkflowEntrypoint class PythonWorkflowStarter(WorkflowEntrypoint): async def run(self, event, step): async def await\_step(fn): try: return await fn() except TypeError as e: print(f"Successfully caught {type(e).\_\_name\_\_}: {e}") step.sleep('demo sleep', '10 seconds') @step.do('dependency1') async def dep\_1(): # does stuff print('executing dep1') @step.do('dependency2') async def dep\_2(): # does stuff print('executing dep2') @step.do('demo do', depends=\[dep\_1, dep\_2\], concurrent=True) async def final\_step(res1, res2): # does stuff print('something') await await\_step(final\_step) async def on\_fetch(request, env): await env.MY\_WORKFLOW.create() return Response("Hello world!") \`\`\` On this example, \`dep\_1\` and \`dep\_2\` are run concurrently before execution of \`final\_step\`, which depends on both of them. Having \`concurrent=True\` allows the dependencies to be resolved concurrently. If one of the callables passed to \`depends\` has already completed, it will be skipped and its return value will be reused. This pattern is useful for diamond shaped workflows, where a step depends on two or more other steps that can run concurrently. --- title: Python Workers API · Cloudflare Workflows docs description: This guide covers the Python Workflows SDK, with instructions on how to build and create workflows using Python. lastUpdated: 2025-11-24T13:34:33.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/python/python-workers-api/ md: https://developers.cloudflare.com/workflows/python/python-workers-api/index.md --- This guide covers the Python Workflows SDK, with instructions on how to build and create workflows using Python. ## WorkflowEntrypoint The \`WorkflowEntrypoint\` is the main entrypoint for a Python workflow. It extends the \`WorkflowEntrypoint\` class, and implements the \`run\` method. \`\`\`python from workers import WorkflowEntrypoint class MyWorkflow(WorkflowEntrypoint): def run(self, event, step): # steps here \`\`\` ## WorkflowStep \* \`step.do(name, depends=\[\], concurrent=False, config=None)\` is a decorator that allows you to define a step in a workflow. \* \`name\` - the name of the step. \* \`depends\` - an optional list of steps that must complete before this step can run. See \[DAG Workflows\](https://developers.cloudflare.com/workflows/python/dag). \* \`concurrent\` - an optional boolean that indicates whether this step can run concurrently with other steps. \* \`config\` - an optional \[\`WorkflowStepConfig\`\](https://developers.cloudflare.com/workflows/build/workers-api/#workflowstepconfig) for configuring \[step specific retry behaviour\](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/). This is passed as a Python dictionary and then type translated into a \`WorkflowStepConfig\` object. \`\`\`python from workers import WorkflowEntrypoint class MyWorkflow(WorkflowEntrypoint): async def run(self, event, step): @step.do("my first step") async def my\_first\_step(): # do some work return "Hello World!" await my\_first\_step() \`\`\` Note that the decorator doesn't make the call to the step, it just returns a callable that can be used to invoke the step. You have to call the callable to make the step run. When returning state from a step, you must make sure that the returned value is serializable. Since steps run through an FFI layer, the returned value gets type translated via \[FFI.\](https://pyodide.org/en/stable/usage/api/python-api/ffi.html#pyodide.ffi.to\_js) Refer to \[Pyodide's documentation\](https://pyodide.org/en/stable/usage/type-conversions.html#type-translations-pyproxy-to-js) regarding type conversions for more information. \* \`step.sleep(name, duration)\` \* \`name\` - the name of the step. \* \`duration\` - the duration to sleep until, in either seconds or as a \`WorkflowDuration\` compatible string. \`\`\`python async def run(self, event, step): await step.sleep("my-sleep-step", "10 seconds") \`\`\` \* \`step.sleep\_until(name, timestamp)\` \* \`name\` - the name of the step. \* \`timestamp\` - a \`datetime.datetime\` object or seconds from the Unix epoch to sleep the workflow instance until. \`\`\`python async def run(self, event, step): await step.sleep\_until("my-sleep-step", datetime.datetime.now() + datetime.timedelta(seconds=10)) \`\`\` \* \`step.wait\_for\_event(name, event\_type, timeout="24 hours")\` \* \`name\` - the name of the step. \* \`event\_type\` - the type of event to wait for. \* \`timeout\` - the timeout for the \`wait\_for\_event\` call. The default timeout is 24 hours. \`\`\`python async def run(self, event, step): await step.wait\_for\_event("my-wait-for-event-step", "my-event-type") \`\`\` ### \`event\` parameter The \`event\` parameter is a dictionary that contains the payload passed to the workflow instance, along with other metadata: \* \`payload\` - the payload passed to the workflow instance. \* \`timestamp\` - the timestamp that the workflow was triggered. \* \`instanceId\` - the ID of the current workflow instance. \* \`workflowName\` - the name of the workflow. ## Error Handling Workflows semantics allow users to catch exceptions that get thrown to the top level. Catching specific exceptions within an \`except\` block may not work, as some Python errors will not be re-instantiated into the same type of error when they are passed through the RPC layer. Note Some built-in Python errors (e.g.: \`ValueError\`, \`TypeError\`) will work correctly. User defined exceptions, as well as other built-in Python errors will not and should be caught with the \`Exception\` class. \`\`\`python async def run(self, event, step): async def try\_step(fn): try: return await fn() except Exception as e: print(f"Successfully caught {type(e).\_\_name\_\_}: {e}") @step.do("my\_failing") async def my\_failing(): print("Executing my\_failing") raise TypeError("Intentional error in my\_failing") await try\_step(my\_failing) \`\`\` ### NonRetryableError The Python Workflows SDK provides a \`NonRetryableError\` class that can be used to signal that a step should not be retried. \`\`\`python from workers.workflows import NonRetryableError raise NonRetryableError(message) \`\`\` ## Configure a workflow instance You can bind a step to a specific retry policy by passing a \`WorkflowStepConfig\` object to the \`config\` parameter of the \`step.do\` decorator. With Python Workflows, you need to make sure that your \`dict\` respects the \[\`WorkflowStepConfig\`\](https://developers.cloudflare.com/workflows/build/workers-api/#workflowstepconfig) type. \`\`\`python class DemoWorkflowClass(WorkflowEntrypoint): async def run(self, event, step): @step.do('step-name', config={"retries": {"limit": 1, "delay": "10 seconds"}}) async def first\_step(): # do some work pass \`\`\` ### Create an instance via binding Note that \`env\` is a JavaScript object exposed to the Python script via \[JsProxy\](https://pyodide.org/en/stable/usage/api/python-api/ffi.html#pyodide.ffi.JsProxy). You can access the binding like you would on a JavaScript worker. Refer to the \[Workflow binding documentation\](https://developers.cloudflare.com/workflows/build/workers-api/#workflow) to learn more about the methods available. Let's consider the previous binding called \`MY\_WORKFLOW\`. Here's how you would create a new instance: \`\`\`python class Default(WorkerEntrypoint): async def fetch(self, request): instance = await self.env.MY\_WORKFLOW.create() return Response.json({"status": "success"}) \`\`\` --- title: Changelog · Cloudflare Workflows docs description: Subscribe to RSS lastUpdated: 2025-02-13T19:35:19.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/reference/changelog/ md: https://developers.cloudflare.com/workflows/reference/changelog/index.md --- \[Subscribe to RSS\](https://developers.cloudflare.com/workflows/reference/changelog/index.xml) ## 2025-09-12 \*\*Test Workflows locally\*\* Workflows can now be tested with new test APIs available in the "cloudflare:test" module. More information available in the Vitest integration \[docs\](https://developers.cloudflare.com/workers/testing/vitest-integration/test-apis/#workflows). ## 2025-08-22 \*\*Python Workflows is now open beta\*\* \[Python Workflows\](https://developers.cloudflare.com/workflows/python/) is now in open beta, and available to any developer a free or paid Workers plan. More information available in the \[changelog\](https://developers.cloudflare.com/changelog/2025-08-22-workflows-python-beta/). ## 2025-05-07 \*\*Search for specific Workflows\*\* With this release, you can search Workflows by name via API. ## 2025-04-29 \*\*Workflow deletion and more\*\* Workflows can now be deleted (from the Dashboard/UI or via API), and the maximum length limit for event types and instance IDs was increased to 100 characters. Also, this release fixes a bug where a delay of \`0\` in step config retries would fail. ## 2025-04-07 \*\*Workflows is now Generally Available\*\* Workflows is now Generally Available (or "GA"). This release includes the following new features: \* A new \`waitForEvent\` API that allows a Workflow to wait for an event to occur before continuing execution. \* Increased concurrency: you can run up to 4,500 Workflow instances concurrently — and this will continue to grow. \* Improved observability, including new CPU time metrics that allow you to better understand which Workflow instances are consuming the most resources and/or contributing to your bill. \* Support for vitest for testing Workflows locally and in CI/CD pipelines. More information available in the \[changelog\](https://developers.cloudflare.com/changelog/2025-04-07-workflows-ga/). ## 2025-02-25 \*\*Concurrent Workflow instances limits increased\*\* Workflows now supports up to 4,500 concurrent (running) instances, up from the previous limit of 100. More information available in the \[changelog\](https://developers.cloudflare.com/changelog/2025-02-25-workflows-concurrency-increased/). ## 2025-02-11 \*\*Behavior improvements\*\* Improved Workflows execution that prevents Workflows instances from getting stuck, and allows stuck instances to become unstuck. Also, improved the reliability of Workflows step retry counts, and improved Instance ID validation. ## 2025-01-23 \*\*Major bugfixes and improvements\*\* With this release, some bug were fixed: \* \`event.timestamp\` is now \`Date\`, fixing a regression. \* Fixed issue where instances without metadata were not terminated as expected. Also, this release makes Workflows execution more reliable for accounts with high loads. ## 2025-01-09 \*\*Improved Wrangler local dev experience for steps' output, matching production\*\* Previously, in local dev, the output field would return the list of successful steps outputs in the workflow. This is not expected behavior compared to production workflows (where the output is the actual return of the run function). This release aligns the local dev output field behavior with the production behavior. ## 2024-12-19 \*\*Better instance control, improved queued logic, and step limit increased\*\* Workflows can now be terminated and pause instances from a queued state and the ID of an instance is now exposed via the \`WorkflowEvent\` parameter. Also, the mechanism to queue instances was improved to force miss-behaved queued instances to be automatically errored. Workflows now allow you to define up to 1024 steps in a single Workflow definition, up from the previous limit of 512. This limit will continue to increase during the course of the open beta. ## 2024-12-09 \*\*New queue instances logic\*\* Introduction of a new mechanism to queue instances, which will prevent instances from getting stuck on queued status forever. ## 2024-11-30 \*\*Step limit increased\*\* Workflows now allow you to define up to 512 steps in a single Workflow definition, up from the previous limit of 256. This limit will continue to increase during the course of the open beta. If you have Workflows that need more steps, we recommend delegating additional work to other Workflows by \[triggering a new Workflow\](https://developers.cloudflare.com/workflows/build/trigger-workflows/) from within a step and passing any state as \[parameters to that Workflow instance\](https://developers.cloudflare.com/workflows/build/events-and-parameters/). ## 2024-11-21 \*\*Fixed create instance API in Workers bindings\*\* You can now call \`create()\` without any arguments when using the \[Workers API\](https://developers.cloudflare.com/workflows/build/workers-api/#create) for Workflows. Workflows will automatically generate the ID of the Workflow on your behalf. This addresses a bug that caused calls to \`create()\` to fail when provided with no arguments. ## 2024-11-20 \*\*Multiple Workflows in local development now supported\*\* Local development with \`wrangler dev\` now correctly supports multiple Workflow definitions per script. There is no change to production Workflows, where multiple Workflow definitions per Worker script was already supported. ## 2024-10-23 \*\*Workflows is now in public beta!\*\* Workflows, a new product for building reliable, multi-step workflows using Cloudflare Workers, is now in public beta. The public beta is available to any user with a \[free or paid Workers plan\](https://developers.cloudflare.com/workers/platform/pricing/). A Workflow allows you to define multiple, independent steps that encapsulate errors, automatically retry, persist state, and can run for seconds, minutes, hours or even days. A Workflow can be useful for post-processing data from R2 buckets before querying it, automating a Workers AI RAG pipeline, or managing user signup flows and lifecycle emails. You can learn more about Workflows in \[our announcement blog\](https://blog.cloudflare.com/building-workflows-durable-execution-on-workers/), or start building in our \[get started guide\](https://developers.cloudflare.com/workflows/get-started/guide/). --- title: Event subscriptions · Cloudflare Workflows docs description: Event subscriptions allow you to receive messages when events occur across your Cloudflare account. Cloudflare products (e.g., KV, Workers AI, Workers) can publish structured events to a queue, which you can then consume with Workers or HTTP pull consumers to build custom workflows, integrations, or logic. lastUpdated: 2025-11-06T01:33:23.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/reference/event-subscriptions/ md: https://developers.cloudflare.com/workflows/reference/event-subscriptions/index.md --- \[Event subscriptions\](https://developers.cloudflare.com/queues/event-subscriptions/) allow you to receive messages when events occur across your Cloudflare account. Cloudflare products (e.g., \[KV\](https://developers.cloudflare.com/kv/), \[Workers AI\](https://developers.cloudflare.com/workers-ai/), \[Workers\](https://developers.cloudflare.com/workers/)) can publish structured events to a \[queue\](https://developers.cloudflare.com/queues/), which you can then consume with Workers or \[HTTP pull consumers\](https://developers.cloudflare.com/queues/configuration/pull-consumers/) to build custom workflows, integrations, or logic. For more information on \[Event Subscriptions\](https://developers.cloudflare.com/queues/event-subscriptions/), refer to the \[management guide\](https://developers.cloudflare.com/queues/event-subscriptions/manage-event-subscriptions/). ## Available Workflows events #### \`instance.queued\` Triggered when an instance was created and is awaiting execution. \*\*Example:\*\* \`\`\`json { "type": "cf.workflows.workflow.instance.queued", "source": { "type": "workflows.workflow", "workflowName": "my-workflow" }, "payload": { "versionId": "v1", "instanceId": "inst-12345678-90ab-cdef-1234-567890abcdef" }, "metadata": { "accountId": "f9f79265f388666de8122cfb508d7776", "eventSubscriptionId": "1830c4bb612e43c3af7f4cada31fbf3f", "eventSchemaVersion": 1, "eventTimestamp": "2025-05-01T02:48:57.132Z" } } \`\`\` #### \`instance.started\` Triggered when an instance starts or resumes execution. \*\*Example:\*\* \`\`\`json { "type": "cf.workflows.workflow.instance.started", "source": { "type": "workflows.workflow", "workflowName": "my-workflow" }, "payload": { "versionId": "v1", "instanceId": "inst-12345678-90ab-cdef-1234-567890abcdef" }, "metadata": { "accountId": "f9f79265f388666de8122cfb508d7776", "eventSubscriptionId": "1830c4bb612e43c3af7f4cada31fbf3f", "eventSchemaVersion": 1, "eventTimestamp": "2025-05-01T02:48:57.132Z" } } \`\`\` #### \`instance.paused\` Triggered when an instance pauses execution. \*\*Example:\*\* \`\`\`json { "type": "cf.workflows.workflow.instance.paused", "source": { "type": "workflows.workflow", "workflowName": "my-workflow" }, "payload": { "versionId": "v1", "instanceId": "inst-12345678-90ab-cdef-1234-567890abcdef" }, "metadata": { "accountId": "f9f79265f388666de8122cfb508d7776", "eventSubscriptionId": "1830c4bb612e43c3af7f4cada31fbf3f", "eventSchemaVersion": 1, "eventTimestamp": "2025-05-01T02:48:57.132Z" } } \`\`\` #### \`instance.errored\` Triggered when an instance step throws an error. \*\*Example:\*\* \`\`\`json { "type": "cf.workflows.workflow.instance.errored", "source": { "type": "workflows.workflow", "workflowName": "my-workflow" }, "payload": { "versionId": "v1", "instanceId": "inst-12345678-90ab-cdef-1234-567890abcdef" }, "metadata": { "accountId": "f9f79265f388666de8122cfb508d7776", "eventSubscriptionId": "1830c4bb612e43c3af7f4cada31fbf3f", "eventSchemaVersion": 1, "eventTimestamp": "2025-05-01T02:48:57.132Z" } } \`\`\` #### \`instance.terminated\` Triggered when an instance is manually terminated. \*\*Example:\*\* \`\`\`json { "type": "cf.workflows.workflow.instance.terminated", "source": { "type": "workflows.workflow", "workflowName": "my-workflow" }, "payload": { "versionId": "v1", "instanceId": "inst-12345678-90ab-cdef-1234-567890abcdef" }, "metadata": { "accountId": "f9f79265f388666de8122cfb508d7776", "eventSubscriptionId": "1830c4bb612e43c3af7f4cada31fbf3f", "eventSchemaVersion": 1, "eventTimestamp": "2025-05-01T02:48:57.132Z" } } \`\`\` #### \`instance.completed\` Triggered when an instance finishes execution successfully. \*\*Example:\*\* \`\`\`json { "type": "cf.workflows.workflow.instance.completed", "source": { "type": "workflows.workflow", "workflowName": "my-workflow" }, "payload": { "versionId": "v1", "instanceId": "inst-12345678-90ab-cdef-1234-567890abcdef" }, "metadata": { "accountId": "f9f79265f388666de8122cfb508d7776", "eventSubscriptionId": "1830c4bb612e43c3af7f4cada31fbf3f", "eventSchemaVersion": 1, "eventTimestamp": "2025-05-01T02:48:57.132Z" } } \`\`\` --- title: Glossary · Cloudflare Workflows docs description: Review the definitions for terms used across Cloudflare's Workflows documentation. lastUpdated: 2024-10-24T11:52:00.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/reference/glossary/ md: https://developers.cloudflare.com/workflows/reference/glossary/index.md --- Review the definitions for terms used across Cloudflare's Workflows documentation. | Term | Definition | | - | - | | Durable Execution | "Durable Execution" is a programming model that allows applications to execute reliably, automatically persist state, retry, and be resistant to errors caused by API, network or even machine/infrastructure failures. Cloudflare Workflows provide a way to build and deploy applications that align with this model. | | Event | The event that triggered the Workflow instance. A \`WorkflowEvent\` may contain optional parameters (data) that a Workflow can operate on. | | instance | A specific instance (running, paused, errored) of a Workflow. A Workflow can have a potentially infinite number of instances. | | step | A step is self-contained, individually retriable component of a Workflow. Steps may emit (optional) state that allows a Workflow to persist and continue from that step, even if a Workflow fails due to a network or infrastructure issue. A Workflow can have one or more steps up to the \[step limit\](https://developers.cloudflare.com/workflows/reference/limits/). | | Workflow | The named Workflow definition, associated with a single Workers script. | --- title: Limits · Cloudflare Workflows docs description: Limits that apply to authoring, deploying, and running Workflows are detailed below. lastUpdated: 2026-01-22T21:38:43.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/reference/limits/ md: https://developers.cloudflare.com/workflows/reference/limits/index.md --- Limits that apply to authoring, deploying, and running Workflows are detailed below. Many limits are inherited from those applied to Workers scripts and as documented in the \[Workers limits\](https://developers.cloudflare.com/workers/platform/limits/) documentation. Note Workflows cannot be deployed to Workers for Platforms namespaces, as Workflows do not support Workers for Platforms. | Feature | Workers Free | Workers Paid | | - | - | - | | Workflow class definitions per script | 3MB max script size per \[Worker size limits\](https://developers.cloudflare.com/workers/platform/limits/#account-plan-limits) | 10MB max script size per \[Worker size limits\](https://developers.cloudflare.com/workers/platform/limits/#account-plan-limits) | | Total scripts per account | 100 | 500 (shared with \[Worker script limits\](https://developers.cloudflare.com/workers/platform/limits/#account-plan-limits) | | Compute time per step \[1\](#user-content-fn-3) | 10 ms | 30 seconds (default) / configurable to 5 minutes of \[active CPU time\](https://developers.cloudflare.com/workers/platform/limits/#cpu-time) | | Duration (wall clock) per step \[1\](#user-content-fn-3) | Unlimited | Unlimited - for example, waiting on network I/O calls or querying a database | | Maximum persisted state per step | 1MiB (2^20 bytes) | 1MiB (2^20 bytes) | | Maximum event \[payload size\](https://developers.cloudflare.com/workflows/build/events-and-parameters/) | 1MiB (2^20 bytes) | 1MiB (2^20 bytes) | | Maximum state that can be persisted per Workflow instance | 100MB | 1GB | | Maximum \`step.sleep\` duration | 365 days (1 year) | 365 days (1 year) | | Maximum steps per Workflow \[2\](#user-content-fn-5) | 1024 | 1024 | | Maximum Workflow executions | 100,000 per day \[shared with Workers daily limit\](https://developers.cloudflare.com/workers/platform/limits/#worker-limits) | Unlimited | | Concurrent Workflow instances (executions) per account \[3\](#user-content-fn-7) | 100 | 10,000 | | Maximum Workflow instance creation rate \[4\](#user-content-fn-8) | 100 per second \[5\](#user-content-fn-6) | 100 per second \[5\](#user-content-fn-6) | | Maximum number of \[queued instances\](https://developers.cloudflare.com/workflows/observability/metrics-analytics/#event-types) | 100,000 | 1,000,000 | | Retention limit for completed Workflow instance state | 3 days | 30 days \[6\](#user-content-fn-2) | | Maximum length of a Workflow name \[7\](#user-content-fn-4) | 64 characters | 64 characters | | Maximum length of a Workflow instance ID \[7\](#user-content-fn-4) | 100 characters | 100 characters | | Maximum number of subrequests per Workflow instance | 50/request | 1000/request | Need a higher limit? To request an adjustment to a limit, complete the \[Limit Increase Request Form\](https://forms.gle/ukpeZVLWLnKeixDu7). If the limit can be increased, Cloudflare will contact you with next steps. ### \`waiting\` instances do not count towards instance concurrency limits Instances that are in a \`waiting\` state — either sleeping via \`step.sleep\`, waiting for a retry, or waiting for an event via \`step.waitForEvent\` — do \*\*not\*\* count towards concurrency limits. This means you can have millions of Workflow instances sleeping or waiting for events simultaneously, as only actively \`running\` instances count toward the 10,000 concurrent instance limit. However, if there are 10,000 concurrent instances actively running, an instance that has been in a \`waiting\` state will be queued instead of resuming immediately. When an instance transitions from \`running\` to \`waiting\`, other \`queued\` instances will be scheduled (usually the oldest queued instance, on a best-effort basis). This state transition may not occur if the wait duration is very short. For example, consider a Workflow that does some work, waits for 30 days, and then continues with more work: \`\`\`ts import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent, } from "cloudflare:workers"; type Env = { MY\_WORKFLOW: Workflow; }; export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { const apiResponse = await step.do("initial work", async () => { let resp = await fetch("https://api.cloudflare.com/client/v4/ips"); return await resp.json(); }); await step.sleep("wait 30 days", "30 days"); await step.do( "make a call to write that could maybe, just might, fail", { retries: { limit: 5, delay: "5 second", backoff: "exponential", }, timeout: "15 minutes", }, async () => { if (Math.random() > 0.5) { throw new Error("API call to $STORAGE\_SYSTEM failed"); } }, ); } } \`\`\` 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. ### Increasing Workflow CPU limits Workflows are Worker scripts, and share the same \[per invocation CPU limits\](https://developers.cloudflare.com/workers/platform/limits/#worker-limits) as any Workers do. Note that CPU time is active processing time: not time spent waiting on network requests, storage calls, or other general I/O, which don't count towards your CPU time or Workflows compute consumption. By default, the maximum CPU time per Workflow invocation is set to 30 seconds, but can be increased for all invocations associated with a Workflow definition by setting \`limits.cpu\_ms\` in your Wrangler configuration: \* wrangler.jsonc \`\`\`jsonc { // ...rest of your configuration... "limits": { "cpu\_ms": 300000, // 300,000 milliseconds = 5 minutes }, // ...rest of your configuration... } \`\`\` \* wrangler.toml \`\`\`toml \[limits\] cpu\_ms = 300\_000 \`\`\` To learn more about CPU time and limits, \[review the Workers documentation\](https://developers.cloudflare.com/workers/platform/limits/#cpu-time). ## Footnotes 1. A Workflow instance can run forever, as long as each step does not take more than the CPU time limit and the maximum number of steps per Workflow is not reached. \[↩\](#user-content-fnref-3) \[↩2\](#user-content-fnref-3-2) 2. \`step.sleep\` do not count towards the max. steps limit \[↩\](#user-content-fnref-5) 3. Only instances with a \`running\` state count towards the concurrency limits. Instances in the \`waiting\` state are excluded from these limits. \[↩\](#user-content-fnref-7) 4. Each instance created or restarted counts towards this limit \[↩\](#user-content-fnref-8) 5. Workflows will return a HTTP 429 rate limited error if you exceed the rate of new Workflow instance creation. \[↩\](#user-content-fnref-6) \[↩2\](#user-content-fnref-6-2) 6. Workflow instance state and logs will be retained for 3 days on the Workers Free plan and for 30 days on the Workers Paid plan. \[↩\](#user-content-fnref-2) 7. Match pattern: \\\_\`^\[a-zA-Z0-9\_\]\[a-zA-Z0-9-\_\]\\\*$\`\\\_ \[↩\](#user-content-fnref-4) \[↩2\](#user-content-fnref-4-2) --- title: Pricing · Cloudflare Workflows docs description: "Workflows pricing is identical to Workers Standard pricing and are billed on three dimensions:" lastUpdated: 2025-04-08T09:39:12.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/reference/pricing/ md: https://developers.cloudflare.com/workflows/reference/pricing/index.md --- Note Workflows is included in both the Free and Paid \[Workers plans\](https://developers.cloudflare.com/workers/platform/pricing/#workers). Workflows pricing is identical to \[Workers Standard pricing\](https://developers.cloudflare.com/workers/platform/pricing/#workers) and are billed on three dimensions: \* \*\*CPU time\*\*: the total amount of compute (measured in milliseconds) consumed by a given Workflow. \* \*\*Requests\*\* (invocations): the number of Workflow invocations. \[Subrequests\](https://developers.cloudflare.com/workers/platform/limits/#subrequests) made from a Workflow do not incur additional request costs. \* \*\*Storage\*\*: the total amount of storage (measured in GB) persisted by your Workflows. A Workflow that is waiting on a response to an API call, paused as a result of calling \`step.sleep\`, or otherwise idle, does not incur CPU time. ### Workflows Pricing | Unit | Workers Free | Workers Paid | | - | - | - | | Requests (millions) | 100,000 per day (\[shared with Workers requests\](https://developers.cloudflare.com/workers/platform/pricing/#workers) | 10 million included per month + $0.30 per additional million | | CPU time (ms) | 10 milliseconds of CPU time per invocation | 30 million CPU milliseconds included per month + $0.02 per additional million CPU milliseconds | | Storage (GB-mo) | 1GB | 1GB included per month + $0.20/ GB-month | CPU limits You can increase the CPU limit available to your Workflow instances up to 5 minutes per Workflow by \[setting the \`limits.cpu\_ms\` property\](https://developers.cloudflare.com/workers/wrangler/configuration/#limits) in your Wrangler configuration. ### Storage Usage Note Storage billing for Workflows will go live on September 15th, 2025. Storage is billed using gigabyte-month (GB-month) as the billing metric, identical to \[Durable Objects SQL storage\](https://developers.cloudflare.com/durable-objects/platform/pricing/#sqlite-storage-backend). A GB-month is calculated by averaging the peak storage per day over a billing period (30 days). \* Storage is calculated across all instances, and includes running, errored, sleeping and completed instances. \* By default, instance state is retained for \[3 days on the Free plan\](https://developers.cloudflare.com/workflows/reference/limits/) and \[7 days on the Paid plan\](https://developers.cloudflare.com/workflows/reference/limits/). \* When creating a Workflow instance, you can set a shorter state retention period if you do not need to retain state for errored or completed Workflows. \* Deleting instances via the \[Workers API\](https://developers.cloudflare.com/workflows/build/workers-api/), \[Wrangler CLI\](https://developers.cloudflare.com/workers/wrangler/commands/#workflows), REST API, or dashboard will free up storage. Note that it may take a few minutes for storage limits to update. An instance that attempts to store state when your have reached the storage limit on the Free plan will cause an error to be thrown. ## Frequently Asked Questions Frequently asked questions related to Workflows pricing: ### Are there additional costs for Workflows? No. Workflows are priced based on the same compute (CPU time), requests (invocations) as Workers, as well as storage (state from a Workflow). ### Are Workflows available on the \[Workers Free\](https://developers.cloudflare.com/workers/platform/pricing/#workers) plan? Yes. ### What is a Workflow invocation? A Workflow invocation is when you trigger a new Workflow instance: for example, via the \[Workers API\](https://developers.cloudflare.com/workflows/build/workers-api/), wrangler CLI, or REST API. Steps within a Workflow are not invocations. ### How do Workflows show up on my bill? Workflows are billed as Workers, and share the same CPU time and request SKUs. ### Are there any limits to Workflows? Refer to the published \[limits\](https://developers.cloudflare.com/workflows/reference/limits/) documentation. --- title: Wrangler commands · Cloudflare Workflows docs description: List Workflows associated to account lastUpdated: 2025-11-14T14:44:20.000Z chatbotDeprioritize: false source\_url: html: https://developers.cloudflare.com/workflows/reference/wrangler-commands/ md: https://developers.cloudflare.com/workflows/reference/wrangler-commands/index.md --- ## \`workflows list\` List Workflows associated to account \* npm \`\`\`sh npx wrangler workflows list \`\`\` \* pnpm \`\`\`sh pnpm wrangler workflows list \`\`\` \* yarn \`\`\`sh yarn wrangler workflows list \`\`\` - \`--page\` number default: 1 Show a sepecific page from the listing, can configure page size using "per-page" - \`--per-page\` number Configure the maximum number of workflows to show per page Global flags \* \`--v\` boolean alias: --version Show version number \* \`--cwd\` string Run as if Wrangler was started in the specified directory instead of the current working directory \* \`--config\` string alias: --c Path to Wrangler configuration file \* \`--env\` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files \* \`--env-file\` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files \* \`--experimental-provision\` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning \* \`--experimental-auto-create\` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources ## \`workflows describe\` Describe Workflow resource \* npm \`\`\`sh npx wrangler workflows describe \[NAME\] \`\`\` \* pnpm \`\`\`sh pnpm wrangler workflows describe \[NAME\] \`\`\` \* yarn \`\`\`sh yarn wrangler workflows describe \[NAME\] \`\`\` - \`\[NAME\]\` string required Name of the workflow Global flags \* \`--v\` boolean alias: --version Show version number \* \`--cwd\` string Run as if Wrangler was started in the specified directory instead of the current working directory \* \`--config\` string alias: --c Path to Wrangler configuration file \* \`--env\` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files \* \`--env-file\` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files \* \`--experimental-provision\` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning \* \`--experimental-auto-create\` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources ## \`workflows delete\` Delete workflow - when deleting a workflow, it will also delete it's own instances \* npm \`\`\`sh npx wrangler workflows delete \[NAME\] \`\`\` \* pnpm \`\`\`sh pnpm wrangler workflows delete \[NAME\] \`\`\` \* yarn \`\`\`sh yarn wrangler workflows delete \[NAME\] \`\`\` - \`\[NAME\]\` string required Name of the workflow Global flags \* \`--v\` boolean alias: --version Show version number \* \`--cwd\` string Run as if Wrangler was started in the specified directory instead of the current working directory \* \`--config\` string alias: --c Path to Wrangler configuration file \* \`--env\` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files \* \`--env-file\` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files \* \`--experimental-provision\` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning \* \`--experimental-auto-create\` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources ## \`workflows trigger\` Trigger a workflow, creating a new instance. Can optionally take a JSON string to pass a parameter into the workflow instance \* npm \`\`\`sh npx wrangler workflows trigger \[NAME\] \[PARAMS\] \`\`\` \* pnpm \`\`\`sh pnpm wrangler workflows trigger \[NAME\] \[PARAMS\] \`\`\` \* yarn \`\`\`sh yarn wrangler workflows trigger \[NAME\] \[PARAMS\] \`\`\` - \`\[NAME\]\` string required Name of the workflow - \`\[PARAMS\]\` string default: Params for the workflow instance, encoded as a JSON string - \`--id\` string Custom instance ID, if not provided it will default to a random UUIDv4 Global flags \* \`--v\` boolean alias: --version Show version number \* \`--cwd\` string Run as if Wrangler was started in the specified directory instead of the current working directory \* \`--config\` string alias: --c Path to Wrangler configuration file \* \`--env\` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files \* \`--env-file\` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files \* \`--experimental-provision\` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning \* \`--experimental-auto-create\` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources ## \`workflows instances list\` Instance related commands (list, describe, terminate, pause, resume) \* npm \`\`\`sh npx wrangler workflows instances list \[NAME\] \`\`\` \* pnpm \`\`\`sh pnpm wrangler workflows instances list \[NAME\] \`\`\` \* yarn \`\`\`sh yarn wrangler workflows instances list \[NAME\] \`\`\` - \`\[NAME\]\` string required Name of the workflow - \`--reverse\` boolean default: false Reverse order of the instances table - \`--status\` string Filters list by instance status (can be one of: queued, running, paused, errored, terminated, complete) - \`--page\` number default: 1 Show a sepecific page from the listing, can configure page size using "per-page" - \`--per-page\` number Configure the maximum number of instances to show per page Global flags \* \`--v\` boolean alias: --version Show version number \* \`--cwd\` string Run as if Wrangler was started in the specified directory instead of the current working directory \* \`--config\` string alias: --c Path to Wrangler configuration file \* \`--env\` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files \* \`--env-file\` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files \* \`--experimental-provision\` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning \* \`--experimental-auto-create\` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources ## \`workflows instances describe\` Describe a workflow instance - see its logs, retries and errors \* npm \`\`\`sh npx wrangler workflows instances describe \[NAME\] \[ID\] \`\`\` \* pnpm \`\`\`sh pnpm wrangler workflows instances describe \[NAME\] \[ID\] \`\`\` \* yarn \`\`\`sh yarn wrangler workflows instances describe \[NAME\] \[ID\] \`\`\` - \`\[NAME\]\` string required Name of the workflow - \`\[ID\]\` string default: latest ID of the instance - instead of an UUID you can type 'latest' to get the latest instance and describe it - \`--step-output\` boolean default: true Don't output the step output since it might clutter the terminal - \`--truncate-output-limit\` number default: 5000 Truncate step output after x characters Global flags \* \`--v\` boolean alias: --version Show version number \* \`--cwd\` string Run as if Wrangler was started in the specified directory instead of the current working directory \* \`--config\` string alias: --c Path to Wrangler configuration file \* \`--env\` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files \* \`--env-file\` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files \* \`--experimental-provision\` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning \* \`--experimental-auto-create\` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources ## \`workflows instances send-event\` Send an event to a workflow instance \* npm \`\`\`sh npx wrangler workflows instances send-event \[NAME\] \[ID\] \`\`\` \* pnpm \`\`\`sh pnpm wrangler workflows instances send-event \[NAME\] \[ID\] \`\`\` \* yarn \`\`\`sh yarn wrangler workflows instances send-event \[NAME\] \[ID\] \`\`\` - \`\[NAME\]\` string required Name of the workflow - \`\[ID\]\` string required ID of the instance - instead of an UUID you can type 'latest' to get the latest instance and send an event to it - \`--type\` string required Type of the workflow event - \`--payload\` string default: {} JSON string for the workflow event (e.g., '{"key": "value"}') Global flags \* \`--v\` boolean alias: --version Show version number \* \`--cwd\` string Run as if Wrangler was started in the specified directory instead of the current working directory \* \`--config\` string alias: --c Path to Wrangler configuration file \* \`--env\` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files \* \`--env-file\` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files \* \`--experimental-provision\` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning \* \`--experimental-auto-create\` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources ## \`workflows instances terminate\` Terminate a workflow instance \* npm \`\`\`sh npx wrangler workflows instances terminate \[NAME\] \[ID\] \`\`\` \* pnpm \`\`\`sh pnpm wrangler workflows instances terminate \[NAME\] \[ID\] \`\`\` \* yarn \`\`\`sh yarn wrangler workflows instances terminate \[NAME\] \[ID\] \`\`\` - \`\[NAME\]\` string required Name of the workflow - \`\[ID\]\` string required ID of the instance - instead of an UUID you can type 'latest' to get the latest instance and describe it Global flags \* \`--v\` boolean alias: --version Show version number \* \`--cwd\` string Run as if Wrangler was started in the specified directory instead of the current working directory \* \`--config\` string alias: --c Path to Wrangler configuration file \* \`--env\` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files \* \`--env-file\` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files \* \`--experimental-provision\` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning \* \`--experimental-auto-create\` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources ## \`workflows instances restart\` Restart a workflow instance \* npm \`\`\`sh npx wrangler workflows instances restart \[NAME\] \[ID\] \`\`\` \* pnpm \`\`\`sh pnpm wrangler workflows instances restart \[NAME\] \[ID\] \`\`\` \* yarn \`\`\`sh yarn wrangler workflows instances restart \[NAME\] \[ID\] \`\`\` - \`\[NAME\]\` string required Name of the workflow - \`\[ID\]\` string required ID of the instance - instead of an UUID you can type 'latest' to get the latest instance and describe it Global flags \* \`--v\` boolean alias: --version Show version number \* \`--cwd\` string Run as if Wrangler was started in the specified directory instead of the current working directory \* \`--config\` string alias: --c Path to Wrangler configuration file \* \`--env\` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files \* \`--env-file\` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files \* \`--experimental-provision\` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning \* \`--experimental-auto-create\` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources ## \`workflows instances pause\` Pause a workflow instance \* npm \`\`\`sh npx wrangler workflows instances pause \[NAME\] \[ID\] \`\`\` \* pnpm \`\`\`sh pnpm wrangler workflows instances pause \[NAME\] \[ID\] \`\`\` \* yarn \`\`\`sh yarn wrangler workflows instances pause \[NAME\] \[ID\] \`\`\` - \`\[NAME\]\` string required Name of the workflow - \`\[ID\]\` string required ID of the instance - instead of an UUID you can type 'latest' to get the latest instance and pause it Global flags \* \`--v\` boolean alias: --version Show version number \* \`--cwd\` string Run as if Wrangler was started in the specified directory instead of the current working directory \* \`--config\` string alias: --c Path to Wrangler configuration file \* \`--env\` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files \* \`--env-file\` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files \* \`--experimental-provision\` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning \* \`--experimental-auto-create\` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources ## \`workflows instances resume\` Resume a workflow instance \* npm \`\`\`sh npx wrangler workflows instances resume \[NAME\] \[ID\] \`\`\` \* pnpm \`\`\`sh pnpm wrangler workflows instances resume \[NAME\] \[ID\] \`\`\` \* yarn \`\`\`sh yarn wrangler workflows instances resume \[NAME\] \[ID\] \`\`\` - \`\[NAME\]\` string required Name of the workflow - \`\[ID\]\` string required ID of the instance - instead of an UUID you can type 'latest' to get the latest instance and resume it Global flags \* \`--v\` boolean alias: --version Show version number \* \`--cwd\` string Run as if Wrangler was started in the specified directory instead of the current working directory \* \`--config\` string alias: --c Path to Wrangler configuration file \* \`--env\` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files \* \`--env-file\` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files \* \`--experimental-provision\` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning \* \`--experimental-auto-create\` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources --- # Call Workflows from Pages · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/build/call-workflows-from-pages/#_top) Copy page Call Workflows from Pages ========================= * * * You can bind and trigger Workflows from [Pages Functions](https://developers.cloudflare.com/pages/functions/) by deploying a Workers project with your Workflow definition and then invoking that Worker using [service bindings](https://developers.cloudflare.com/pages/functions/bindings/#service-bindings) or a standard `fetch()` call. ### Use Service Bindings [](https://developers.cloudflare.com/workflows/build/call-workflows-from-pages/#use-service-bindings) [Service Bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/) allow you to call a Worker from another Worker or a Pages Function without needing to expose it directly. To do this, you will need to: 1. Deploy your Workflow in a Worker 2. Create a Service Binding to that Worker in your Pages project 3. Call the Worker remotely using the binding For example, if you have a Worker called `workflows-starter`, you would create a new Service Binding in your Pages project as follows, ensuring that the `service` name matches the name of the Worker your Workflow is defined in: * [wrangler.jsonc](https://developers.cloudflare.com/workflows/build/call-workflows-from-pages/#tab-panel-4759) * [wrangler.toml](https://developers.cloudflare.com/workflows/build/call-workflows-from-pages/#tab-panel-4760) { "$schema": "./node_modules/wrangler/config-schema.json", "services": [ { "binding": "WORKFLOW_SERVICE", "service": "workflows-starter" } ]} services = [{ binding = "WORKFLOW_SERVICE", service = "workflows-starter" }] Your Worker can expose a specific method (or methods) that only other Workers or Pages Functions can call over the Service Binding. In the following example, we expose a specific `createInstance` method that accepts our `Payload` and returns the [`InstanceStatus`](https://developers.cloudflare.com/workflows/build/workers-api/#instancestatus) from the Workflows API: * [JavaScript](https://developers.cloudflare.com/workflows/build/call-workflows-from-pages/#tab-panel-4767) * [TypeScript](https://developers.cloudflare.com/workflows/build/call-workflows-from-pages/#tab-panel-4768) import { WorkerEntrypoint } from "cloudflare:workers"; export default class WorkflowsService extends WorkerEntrypoint { // Currently, entrypoints without a named handler are not supported async fetch() { return new Response(null, { status: 404 }); } async createInstance(payload) { let instance = await this.env.MY_WORKFLOW.create({ params: payload, }); return Response.json({ id: instance.id, details: await instance.status(), }); }} import { WorkerEntrypoint } from "cloudflare:workers"; interface Env { MY_WORKFLOW: Workflow;} type Payload = { hello: string;}; export default class WorkflowsService extends WorkerEntrypoint { // Currently, entrypoints without a named handler are not supported async fetch() { return new Response(null, { status: 404 }); } async createInstance(payload: Payload) { let instance = await this.env.MY_WORKFLOW.create({ params: payload, }); return Response.json({ id: instance.id, details: await instance.status(), }); }} Your Pages Function would resemble the following: * [JavaScript](https://developers.cloudflare.com/workflows/build/call-workflows-from-pages/#tab-panel-4761) * [TypeScript](https://developers.cloudflare.com/workflows/build/call-workflows-from-pages/#tab-panel-4762) export const onRequest = async (context) => { // This payload could be anything from within your app or from your frontend let payload = { hello: "world" }; return context.env.WORKFLOWS_SERVICE.createInstance(payload);}; interface Env { WORKFLOW_SERVICE: Service;} export const onRequest: PagesFunction = async (context) => { // This payload could be anything from within your app or from your frontend let payload = { hello: "world" }; return context.env.WORKFLOWS_SERVICE.createInstance(payload);}; To learn more about binding to resources from Pages Functions, including how to bind via the Cloudflare dashboard, refer to the [bindings documentation for Pages Functions](https://developers.cloudflare.com/pages/functions/bindings/#service-bindings) . ### Using fetch [](https://developers.cloudflare.com/workflows/build/call-workflows-from-pages/#using-fetch) An alternative to setting up a Service Binding is to call the Worker over HTTP by using the Workflows [Workers API](https://developers.cloudflare.com/workflows/build/workers-api/#workflow) to `create` a new Workflow instance for each incoming HTTP call to the Worker: * [JavaScript](https://developers.cloudflare.com/workflows/build/call-workflows-from-pages/#tab-panel-4763) * [TypeScript](https://developers.cloudflare.com/workflows/build/call-workflows-from-pages/#tab-panel-4764) // This is in the same file as your Workflow definitionexport default { async fetch(req, env) { let instance = await env.MY_WORKFLOW.create({ params: payload, }); return Response.json({ id: instance.id, details: await instance.status(), }); },}; // This is in the same file as your Workflow definitionexport default { async fetch(req: Request, env: Env): Promise { let instance = await env.MY_WORKFLOW.create({ params: payload, }); return Response.json({ id: instance.id, details: await instance.status(), }); },}; Your [Pages Function](https://developers.cloudflare.com/pages/functions/get-started/) can then make a regular `fetch` call to the Worker: * [JavaScript](https://developers.cloudflare.com/workflows/build/call-workflows-from-pages/#tab-panel-4765) * [TypeScript](https://developers.cloudflare.com/workflows/build/call-workflows-from-pages/#tab-panel-4766) export const onRequest = async (context) => { // Other code let payload = { hello: "world" }; const instanceStatus = await fetch("https://YOUR_WORKER.workers.dev/", { method: "POST", body: JSON.stringify(payload), // Send a payload for our Worker to pass to the Workflow }); return Response.json(instanceStatus);}; export const onRequest: PagesFunction = async (context) => { // Other code let payload = { hello: "world" }; const instanceStatus = await fetch("https://YOUR_WORKER.workers.dev/", { method: "POST", body: JSON.stringify(payload), // Send a payload for our Worker to pass to the Workflow }); return Response.json(instanceStatus);}; You can also choose to authenticate these requests by passing a shared secret in a header and validating that in your Worker. ### Next steps [](https://developers.cloudflare.com/workflows/build/call-workflows-from-pages/#next-steps) * Learn more about how to programatically call and trigger Workflows from the [Workers API](https://developers.cloudflare.com/workflows/build/workers-api/) * Understand how to send [events and parameters](https://developers.cloudflare.com/workflows/build/events-and-parameters/) when triggering a Workflow * Review the [Rules of Workflows](https://developers.cloudflare.com/workflows/build/rules-of-workflows/) and best practices for writing Workflows Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Workflows · Cloudflare Agents docs [Skip to content](https://developers.cloudflare.com/agents/concepts/workflows/#_top) Copy page Workflows ========= What are workflows? ------------------- [](https://developers.cloudflare.com/agents/concepts/workflows/#what-are-workflows) A workflow is the orchestration layer that coordinates how an agent's components work together. It defines the structured paths through which tasks are processed, tools are called, and results are managed. While agents make dynamic decisions about what to do, workflows provide the underlying framework that governs how those decisions are executed. ### Understanding workflows in agent systems [](https://developers.cloudflare.com/agents/concepts/workflows/#understanding-workflows-in-agent-systems) Think of a workflow like the operating procedures of a company. The company (agent) can make various decisions, but how those decisions get implemented follows established processes (workflows). For example, when you book a flight through a travel agent, they might make different decisions about which flights to recommend, but the process of actually booking the flight follows a fixed sequence of steps. Let's examine a basic agent workflow: ### Core components of a workflow [](https://developers.cloudflare.com/agents/concepts/workflows/#core-components-of-a-workflow) A workflow typically consists of several key elements: 1. **Input Processing** The workflow defines how inputs are received and validated before being processed by the agent. This includes standardizing formats, checking permissions, and ensuring all required information is present. 2. **Tool Integration** Workflows manage how external tools and services are accessed. They handle authentication, rate limiting, error recovery, and ensuring tools are used in the correct sequence. 3. **State Management** The workflow maintains the state of ongoing processes, tracking progress through multiple steps and ensuring consistency across operations. 4. **Output Handling** Results from the agent's actions are processed according to defined rules, whether that means storing data, triggering notifications, or formatting responses. Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Your Privacy Choices Back to top --- # Test APIs · Cloudflare Workers docs [Skip to content](https://developers.cloudflare.com/workflows/build/test-workflows/#_top) Copy page Test APIs ========= The Workers Vitest integration provides runtime helpers for writing tests in the `cloudflare:test` module. The `cloudflare:test` module is provided by the `@cloudflare/vitest-pool-workers` package, but can only be imported from test files that execute in the Workers runtime. `cloudflare:test` module definition ----------------------------------- [](https://developers.cloudflare.com/workflows/build/test-workflows/#cloudflaretest-module-definition) * `env`: import("cloudflare:test").ProvidedEnv * Exposes the [`env` object](https://developers.cloudflare.com/workers/runtime-apis/handlers/fetch/#parameters) for use as the second argument passed to ES modules format exported handlers. This provides access to [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) that you have defined in your [Vitest configuration file](https://developers.cloudflare.com/workers/testing/vitest-integration/configuration/) . import { env } from "cloudflare:test"; it("uses binding", async () => { await env.KV_NAMESPACE.put("key", "value"); expect(await env.KV_NAMESPACE.get("key")).toBe("value");}); To configure the type of this value, use an ambient module type: declare module "cloudflare:test" { interface ProvidedEnv { KV_NAMESPACE: KVNamespace; } // ...or if you have an existing `Env` type... interface ProvidedEnv extends Env {}} * `SELF`: Fetcher * [Service binding](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/) to the default export defined in the `main` Worker. Use this to write integration tests against your Worker. The `main` Worker runs in the same isolate/context as tests so any global mocks will apply to it too. import { SELF } from "cloudflare:test"; it("dispatches fetch event", async () => { const response = await SELF.fetch("https://example.com"); expect(await response.text()).toMatchInlineSnapshot(...);}); * `fetchMock`: import("undici").MockAgent * Declarative interface for mocking outbound `fetch()` requests. Deactivated by default and reset before running each test file. Refer to [`undici`'s `MockAgent` documentation ↗](https://undici.nodejs.org/#/docs/api/MockAgent) for more information. Note this only mocks `fetch()` requests for the current test runner Worker. Auxiliary Workers should mock `fetch()`es using the Miniflare `fetchMock`/`outboundService` options. Refer to [Configuration](https://developers.cloudflare.com/workers/testing/vitest-integration/configuration/#workerspooloptions) for more information. import { fetchMock } from "cloudflare:test";import { beforeAll, afterEach, it, expect } from "vitest"; beforeAll(() => { // Enable outbound request mocking... fetchMock.activate(); // ...and throw errors if an outbound request isn't mocked fetchMock.disableNetConnect();});// Ensure we matched every mock we definedafterEach(() => fetchMock.assertNoPendingInterceptors()); it("mocks requests", async () => { // Mock the first request to `https://example.com` fetchMock .get("https://example.com") .intercept({ path: "/" }) .reply(200, "body"); const response = await fetch("https://example.com/"); expect(await response.text()).toBe("body");}); ### Events [](https://developers.cloudflare.com/workflows/build/test-workflows/#events) * `createExecutionContext()`: ExecutionContext * Creates an instance of the [`context` object](https://developers.cloudflare.com/workers/runtime-apis/handlers/fetch/#parameters) for use as the third argument to ES modules format exported handlers. * `waitOnExecutionContext(ctx:ExecutionContext)`: Promise * Use this to wait for all Promises passed to `ctx.waitUntil()` to settle, before running test assertions on any side effects. Only accepts instances of `ExecutionContext` returned by `createExecutionContext()`. import { env, createExecutionContext, waitOnExecutionContext } from "cloudflare:test";import { it, expect } from "vitest";import worker from "./index.mjs"; it("calls fetch handler", async () => { const request = new Request("https://example.com"); const ctx = createExecutionContext(); const response = await worker.fetch(request, env, ctx); await waitOnExecutionContext(ctx); expect(await response.text()).toMatchInlineSnapshot(...);}); * `createScheduledController(options?:FetcherScheduledOptions)`: ScheduledController * Creates an instance of `ScheduledController` for use as the first argument to modules-format [`scheduled()`](https://developers.cloudflare.com/workers/runtime-apis/handlers/scheduled/) exported handlers. import { env, createScheduledController, createExecutionContext, waitOnExecutionContext } from "cloudflare:test";import { it, expect } from "vitest";import worker from "./index.mjs"; it("calls scheduled handler", async () => { const ctrl = createScheduledController({ scheduledTime: new Date(1000), cron: "30 * * * *" }); const ctx = createExecutionContext(); await worker.scheduled(ctrl, env, ctx); await waitOnExecutionContext(ctx);}); * `createMessageBatch(queueName:string, messages:ServiceBindingQueueMessage[])`: MessageBatch * Creates an instance of `MessageBatch` for use as the first argument to modules-format [`queue()`](https://developers.cloudflare.com/queues/configuration/javascript-apis/#consumer) exported handlers. * `getQueueResult(batch:MessageBatch, ctx:ExecutionContext)`: Promise * Gets the acknowledged/retry state of messages in the `MessageBatch`, and waits for all `ExecutionContext#waitUntil()`ed `Promise`s to settle. Only accepts instances of `MessageBatch` returned by `createMessageBatch()`, and instances of `ExecutionContext` returned by `createExecutionContext()`. import { env, createMessageBatch, createExecutionContext, getQueueResult } from "cloudflare:test";import { it, expect } from "vitest";import worker from "./index.mjs"; it("calls queue handler", async () => { const batch = createMessageBatch("my-queue", [ { id: "message-1", timestamp: new Date(1000), body: "body-1" } ]); const ctx = createExecutionContext(); await worker.queue(batch, env, ctx); const result = await getQueueResult(batch, ctx); expect(result.ackAll).toBe(false); expect(result.retryBatch).toMatchObject({ retry: false }); expect(result.explicitAcks).toStrictEqual(["message-1"]); expect(result.retryMessages).toStrictEqual([]);}); ### Durable Objects [](https://developers.cloudflare.com/workflows/build/test-workflows/#durable-objects) * `runInDurableObject(stub:DurableObjectStub, callback:(instance: O, state: DurableObjectState) => R | Promise)`: Promise * Runs the provided `callback` inside the Durable Object that corresponds to the provided `stub`. This temporarily replaces your Durable Object's `fetch()` handler with `callback`, then sends a request to it, returning the result. This can be used to call/spy-on Durable Object methods or seed/get persisted data. Note this can only be used with `stub`s pointing to Durable Objects defined in the `main` Worker. export class Counter { constructor(readonly state: DurableObjectState) {} async fetch(request: Request): Promise { let count = (await this.state.storage.get("count")) ?? 0; void this.state.storage.put("count", ++count); return new Response(count.toString()); }} import { env, runInDurableObject } from "cloudflare:test";import { it, expect } from "vitest";import { Counter } from "./index.ts"; it("increments count", async () => { const id = env.COUNTER.newUniqueId(); const stub = env.COUNTER.get(id); let response = await stub.fetch("https://example.com"); expect(await response.text()).toBe("1"); response = await runInDurableObject(stub, async (instance: Counter, state) => { expect(instance).toBeInstanceOf(Counter); expect(await state.storage.get("count")).toBe(1); const request = new Request("https://example.com"); return instance.fetch(request); }); expect(await response.text()).toBe("2");}); * `runDurableObjectAlarm(stub:DurableObjectStub)`: Promise * Immediately runs and removes the Durable Object pointed to by `stub`'s alarm if one is scheduled. Returns `true` if an alarm ran, and `false` otherwise. Note this can only be used with `stub`s pointing to Durable Objects defined in the `main` Worker. * `listDurableObjectIds(namespace:DurableObjectNamespace)`: Promise * Gets the IDs of all objects that have been created in the `namespace`. Respects `isolatedStorage` if enabled, meaning objects created in a different test will not be returned. import { env, listDurableObjectIds } from "cloudflare:test";import { it, expect } from "vitest"; it("increments count", async () => { const id = env.COUNTER.newUniqueId(); const stub = env.COUNTER.get(id); const response = await stub.fetch("https://example.com"); expect(await response.text()).toBe("1"); const ids = await listDurableObjectIds(env.COUNTER); expect(ids.length).toBe(1); expect(ids[0].equals(id)).toBe(true);}); ### D1 [](https://developers.cloudflare.com/workflows/build/test-workflows/#d1) * `applyD1Migrations(db:D1Database, migrations:D1Migration[], migrationTableName?:string)`: Promise * Applies all un-applied [D1 migrations](https://developers.cloudflare.com/d1/reference/migrations/) stored in the `migrations` array to database `db`, recording migrations state in the `migrationsTableName` table. `migrationsTableName` defaults to `d1_migrations`. Call the [`readD1Migrations()`](https://developers.cloudflare.com/workers/testing/vitest-integration/configuration/#readd1migrationsmigrationspath) function from the `@cloudflare/vitest-pool-workers/config` package inside Node.js to get the `migrations` array. Refer to the [D1 recipe ↗](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/d1) for an example project using migrations. ### Workflows [](https://developers.cloudflare.com/workflows/build/test-workflows/#workflows) * `introspectWorkflowInstance(workflow: Workflow, instanceId: string)`: Promise * Creates an **introspector** for a specific Workflow instance, used to **modify** its behavior, **await** outcomes, and **clear** its state during tests. This is the primary entry point for testing individual Workflow instances with a known ID. import { env, introspectWorkflowInstance } from "cloudflare:test"; it("should disable all sleeps, mock an event and complete", async () => { // 1. CONFIGURATION await using instance = await introspectWorkflowInstance(env.MY_WORKFLOW, "123456"); await instance.modify(async (m) => { await m.disableSleeps(); await m.mockEvent({ type: "user-approval", payload: { approved: true, approverId: "user-123" }, }); }); // 2. EXECUTION await env.MY_WORKFLOW.create({ id: "123456" }); // 3. ASSERTION await expect(instance.waitForStatus("complete")).resolves.not.toThrow(); const output = await instance.getOutput(); expect(output).toEqual({ success: true }); // 4. DISPOSE: is implicit and automatic here.}); * The returned `WorkflowInstanceIntrospector` object has the following methods: * `modify(fn: (m: WorkflowInstanceModifier) => Promise): Promise`: Applies modifications to the Workflow instance's behavior. * `waitForStepResult(step: { name: string; index?: number }): Promise`: Waits for a specific step to complete and returns a result. If multiple steps share the same name, use the optional `index` property (1-based, defaults to `1`) to target a specific occurrence. * `waitForStatus(status: InstanceStatus["status"]): Promise`: Waits for the Workflow instance to reach a specific [status](https://developers.cloudflare.com/workflows/build/workers-api/#instancestatus) (e.g., 'running', 'complete'). * `getOutput(): Promise`: Returns the output value of the successful completed Workflow instance. * `getError(): Promise<{name: string, message: string}>`: Returns the error information of the errored Workflow instance. The error information follows the form `{ name: string; message: string }`. * `dispose(): Promise`: Disposes the Workflow instance, which is crucial for test isolation. If this function isn't called and `await using` is not used, isolated storage will fail and the instance's state will persist across subsequent tests. For example, an instance that becomes completed in one test will already be completed at the start of the next. * `[Symbol.asyncDispose](): Promise`: Provides automatic dispose. It's invoked by the `await using` statement, which calls `dispose()`. * `introspectWorkflow(workflow: Workflow)`: Promise * Creates an **introspector** for a Workflow where instance IDs are unknown beforehand. This allows for defining modifications that will apply to **all subsequently created instances**. import { env, introspectWorkflow, SELF } from "cloudflare:test"; it("should disable all sleeps, mock an event and complete", async () => { // 1. CONFIGURATION await using introspector = await introspectWorkflow(env.MY_WORKFLOW); await introspector.modifyAll(async (m) => { await m.disableSleeps(); await m.mockEvent({ type: "user-approval", payload: { approved: true, approverId: "user-123" }, }); }); // 2. EXECUTION await env.MY_WORKFLOW.create(); // 3. ASSERTION const instances = introspector.get(); for(const instance of instances) { await expect(instance.waitForStatus("complete")).resolves.not.toThrow(); const output = await instance.getOutput(); expect(output).toEqual({ success: true }); } // 4. DISPOSE: is implicit and automatic here.}); The workflow instance doesn't have to be created directly inside the test. The introspector will capture **all** instances created after it is initialized. For example, you could trigger the creation of **one or multiple** instances via a single `fetch` event to your Worker: // This also works for the EXECUTION phase:await SELF.fetch("https://example.com/trigger-workflows"); * The returned `WorkflowIntrospector` object has the following methods: * `modifyAll(fn: (m: WorkflowInstanceModifier) => Promise): Promise`: Applies modifications to all Workflow instances created after calling `introspectWorkflow`. * `get(): Promise`: Returns all `WorkflowInstanceIntrospector` objects from instances created after `introspectWorkflow` was called. * `dispose(): Promise`: Disposes the Workflow introspector. All `WorkflowInstanceIntrospector` from created instances will also be disposed. This is crucial to prevent modifications and captured instances from leaking between tests. After calling this method, the `WorkflowIntrospector` should not be reused. * `[Symbol.asyncDispose](): Promise`: Provides automatic dispose. It's invoked by the `await using` statement, which calls `dispose()`. * `WorkflowInstanceModifier` * This object is provided to the `modify` and `modifyAll` callbacks to mock or alter the behavior of a Workflow instance's steps, events, and sleeps. * `disableSleeps(steps?: { name: string; index?: number }[])`: Disables sleeps, causing `step.sleep()` and `step.sleepUntil()` to resolve immediately. If `steps` is omitted, all sleeps are disabled. * `mockStepResult(step: { name: string; index?: number }, stepResult: unknown)`: Mocks the result of a `step.do()`, causing it to return the specified value instantly without executing the step's implementation. * `mockStepError(step: { name: string; index?: number }, error: Error, times?: number)`: Forces a `step.do()` to throw an error, simulating a failure. `times` is an optional number that sets how many times the step should error. If `times` is omitted, the step will error on every attempt, making the Workflow instance fail. * `forceStepTimeout(step: { name: string; index?: number }, times?: number)`: Forces a `step.do()` to fail by timing out immediately. `times` is an optional number that sets how many times the step should timeout. If `times` is omitted, the step will timeout on every attempt, making the Workflow instance fail. * `mockEvent(event: { type: string; payload: unknown })`: Sends a mock event to the Workflow instance, causing a `step.waitForEvent()` to resolve with the provided payload. `type` must match the `waitForEvent` type. * `forceEventTimeout(step: { name: string; index?: number })`: Forces a `step.waitForEvent()` to time out instantly, causing the step to fail. import { env, introspectWorkflowInstance } from "cloudflare:test"; // This example showcases explicit disposalit("should apply all modifier functions", async () => { // 1. CONFIGURATION const instance = await introspectWorkflowInstance(env.COMPLEX_WORKFLOW, "123456"); try { // Modify instance behavior await instance.modify(async (m) => { // Disables all sleeps to make the test run instantly await m.disableSleeps(); // Mocks the successful result of a data-fetching step await m.mockStepResult( { name: "get-order-details" }, { orderId: "abc-123", amount: 99.99 } ); // Mocks an incoming event to satisfy a `step.waitForEvent()` await m.mockEvent({ type: "user-approval", payload: { approved: true, approverId: "user-123" }, }); // Forces a step to fail once with a specific error to test retry logic await m.mockStepError( { name: "process-payment" }, new Error("Payment gateway timeout"), 1 // Fail only the first time ); // Forces a `step.do()` to time out immediately await m.forceStepTimeout({ name: "notify-shipping-partner" }); // Forces a `step.waitForEvent()` to time out await --- # Build with Workflows · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/build/#_top) Copy page Build with Workflows ==================== * [Workers API](https://developers.cloudflare.com/workflows/build/workers-api/) * [Trigger Workflows](https://developers.cloudflare.com/workflows/build/trigger-workflows/) * [Sleeping and retrying](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/) * [Events and parameters](https://developers.cloudflare.com/workflows/build/events-and-parameters/) * [Local Development](https://developers.cloudflare.com/workflows/build/local-development/) * [Rules of Workflows](https://developers.cloudflare.com/workflows/build/rules-of-workflows/) * [Call Workflows from Pages](https://developers.cloudflare.com/workflows/build/call-workflows-from-pages/) * [Test Workflows](https://developers.cloudflare.com/workers/testing/vitest-integration/test-apis/#workflows) Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Your Privacy Choices Back to top --- # Local Development · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/build/local-development/#_top) Copy page Local Development ================= **Last reviewed:** about 1 year ago Workflows support local development using [Wrangler](https://developers.cloudflare.com/workers/wrangler/install-and-update/) , the command-line interface for Workers. Wrangler runs an emulated version of Workflows compared to the one that Cloudflare runs globally. Prerequisites ------------- [](https://developers.cloudflare.com/workflows/build/local-development/#prerequisites) To develop locally with Workflows, you will need: * [Wrangler v3.89.0 ↗](https://blog.cloudflare.com/wrangler3/) or later. * Node.js version of `18.0.0` or later. Consider using a Node version manager like [Volta ↗](https://volta.sh/) or [nvm ↗](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node versions. * If you are new to Workflows and/or Cloudflare Workers, refer to the [Workflows Guide](https://developers.cloudflare.com/workflows/get-started/guide/) to install `wrangler` and deploy their first Workflows. Start a local development session --------------------------------- [](https://developers.cloudflare.com/workflows/build/local-development/#start-a-local-development-session) Open your terminal and run the following commands to start a local development session: # Confirm we are using wrangler v3.89.0+npx wrangler --version ⛅️ wrangler 3.89.0 Start a local dev session # Start a local dev session:npx wrangler dev ------------------Your worker has access to the following bindings:- Workflows: - MY_WORKFLOW: MyWorkflow⎔ Starting local server...[wrangler:inf] Ready on http://127.0.0.1:8787/ Local development sessions create a standalone, local-only environment that mirrors the production environment Workflows runs in so you can test your Workflows _before_ you deploy to production. Refer to the [`wrangler dev` documentation](https://developers.cloudflare.com/workers/wrangler/commands/#dev) to learn more about how to configure a local development session. Known Issues ------------ [](https://developers.cloudflare.com/workflows/build/local-development/#known-issues) Workflows are not supported as [remote bindings](https://developers.cloudflare.com/workers/development-testing/#remote-bindings) or when using `npx wrangler dev --remote`. Wrangler Workflows commands `npx wrangler workflow [cmd]` are not supported for local development, as they target production API. Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Your Privacy Choices Back to top --- # Python Workflows SDK · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/python/#_top) Copy page Python Workflows SDK ==================== Workflow entrypoints can be declared using Python. To achieve this, you can export a `WorkflowEntrypoint` that runs on the Cloudflare Workers platform. Refer to [Python Workers](https://developers.cloudflare.com/workers/languages/python) for more information about Python on the Workers runtime. Get Started ----------- [](https://developers.cloudflare.com/workflows/python/#get-started) The main entrypoint for a Python workflow is the [`WorkflowEntrypoint`](https://developers.cloudflare.com/workflows/build/workers-api/#workflowentrypoint) class. Your workflow logic should exist inside the [`run`](https://developers.cloudflare.com/workflows/build/workers-api/#run) handler. from workers import WorkflowEntrypoint class MyWorkflow(WorkflowEntrypoint): async def run(self, event, step): # steps here For example, a Workflow may be defined as: from workers import Response, WorkflowEntrypoint class PythonWorkflowStarter(WorkflowEntrypoint): async def run(self, event, step): @step.do('step1') async def step_1(): # does stuff print('executing step1') @step.do('step2') async def step_2(): # does stuff print('executing step2') await await_step(step_1,step_2) async def on_fetch(request, env): await env.MY_WORKFLOW.create() return Response("Hello world!") You must add both `python_workflows` and `python_workers` compatibility flags to your `wrangler.toml` file. * [wrangler.jsonc](https://developers.cloudflare.com/workflows/python/#tab-panel-4841) * [wrangler.toml](https://developers.cloudflare.com/workflows/python/#tab-panel-4842) { "$schema": "./node_modules/wrangler/config-schema.json", "name": "hello-python", "main": "src/entry.py", "compatibility_flags": [ "python_workers", "experimental", "python_workflows" ], "compatibility_date": "2024-03-29", "workflows": [ { "name": "workflows-demo", "binding": "MY_WORKFLOW", "class_name": "PythonWorkflowStarter" } ]} name = "hello-python"main = "src/entry.py"compatibility_flags = ["python_workers", "experimental", "python_workflows"]compatibility_date = "2024-03-29" [[workflows]]name = "workflows-demo"binding = "MY_WORKFLOW"class_name = "PythonWorkflowStarter" To run a Python Workflow locally, use [Wrangler](https://developers.cloudflare.com/workers/wrangler/) , the CLI for Cloudflare Workers: npx wrangler@latest dev To deploy a Python Workflow to Cloudflare, run [`wrangler deploy`](https://developers.cloudflare.com/workers/wrangler/commands/#deploy) : npx wrangler@latest deploy Join the #python-workers channel in the [Cloudflare Developers Discord ↗](https://discord.cloudflare.com/) and let us know what you would like to see next. Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Videos · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/videos/#_top) Copy page Videos ====== [Build an application using Cloudflare Workflows](https://developers.cloudflare.com/learning-paths/workflows-course/series/workflows-1/) In this series, we introduce Cloudflare Workflows and the term 'Durable Execution' which comes from the desire to run applications that can resume execution from where they left off, even if the underlying host or compute fails. Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Interact with a Workflow · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/python/bindings/#_top) Copy page Interact with a Workflow ======================== The Python Workers platform leverages [FFI ↗](https://en.wikipedia.org/wiki/Foreign_function_interface) to access bindings to Cloudflare resources. Refer to the [bindings](https://developers.cloudflare.com/workers/languages/python/ffi/#using-bindings-from-python-workers) documentation for more information. From the configuration perspective, enabling Python Workflows requires adding the `python_workflows` compatibility flag to your `wrangler.toml` file. * [wrangler.jsonc](https://developers.cloudflare.com/workflows/python/bindings/#tab-panel-4839) * [wrangler.toml](https://developers.cloudflare.com/workflows/python/bindings/#tab-panel-4840) { "$schema": "./node_modules/wrangler/config-schema.json", "name": "workflows-starter", "main": "src/index.ts", "compatibility_date": "2025-10-24", "compatibility_flags": [ "python_workflows", "python_workers" ], "workflows": [ { "name": "workflows-starter", "binding": "MY_WORKFLOW", "class_name": "MyWorkflow" } ]} #:schema node_modules/wrangler/config-schema.jsonname = "workflows-starter"main = "src/index.ts"compatibility_date = "2025-10-24"compatibility_flags = ["python_workflows", "python_workers"] [[workflows]]# name of your workflowname = "workflows-starter"# binding name env.MY_WORKFLOWbinding = "MY_WORKFLOW"# this is class that extends the Workflow class in src/index.tsclass_name = "MyWorkflow" And this is how you use the payload in your workflow: from pyodide.ffi import to_js class DemoWorkflowClass(WorkflowEntrypoint): async def run(self, event, step): @step.do('step-name') async def first_step(): payload = event["payload"] return payload Workflow -------- [](https://developers.cloudflare.com/workflows/python/bindings/#workflow) The `Workflow` binding gives you access to the [Workflow](https://developers.cloudflare.com/workflows/build/workers-api/#workflow) class. All its methods are available on the binding. Under the hood, the `Workflow` binding is a Javascript object that is exposed to the Python script via [JsProxy ↗](https://pyodide.org/en/stable/usage/api/python-api/ffi.html#pyodide.ffi.JsProxy) . This means that the values returned by its methods are also `JsProxy` objects, and need to be converted back into Python objects using `python_from_rpc`. ### `create` [](https://developers.cloudflare.com/workflows/python/bindings/#create) Create (trigger) a new instance of a given Workflow. * `create(options=None)`\* `options` - an **optional** dictionary of options to pass to the workflow instance. Should contain the same keys as the [WorkflowInstanceCreateOptions](https://developers.cloudflare.com/workflows/build/workers-api/#workflowinstancecreateoptions) type. from js import Objectfrom pyodide.ffi import to_jsfrom workers import WorkerEntrypoint, Response class Default(WorkerEntrypoint): async def fetch(self, request): event = {"foo": "bar"} options = to_js({"params": event}, dict_converter=Object.fromEntries) await self.env.MY_WORKFLOW.create(options) return Response.json({"status": "success"}) The `create` method returns a [`WorkflowInstance`](https://developers.cloudflare.com/workflows/build/workers-api/#workflowinstance) object, which can be used to query the status of the workflow instance. Note that this is a Javascript object, and not a Python object. ### `create_batch` [](https://developers.cloudflare.com/workflows/python/bindings/#create_batch) Create (trigger) a batch of new workflow instances, up to 100 instances at a time. This is useful if you need to create multiple instances at once within the [instance creation limit](https://developers.cloudflare.com/workflows/reference/limits/) . * `create_batch(batch)`\* `batch` - list of `WorkflowInstanceCreateOptions` to pass when creating an instance, including a user-provided ID and payload parameters. Each element of the `batch` list is expected to include both `id` and `params` properties: from pyodide.ffi import to_jsfrom js import Object # Create a new batch of 3 Workflow instances, each with its own ID and pass params to the Workflow instanceslistOfInstances = [ to_js({ "id": "id-abc123", "params": { "hello": "world-0" } }, dict_converter=Object.fromEntries), to_js({ "id": "id-def456", "params": { "hello": "world-1" } }, dict_converter=Object.fromEntries), to_js({ "id": "id-ghi789", "params": { "hello": "world-2" } }, dict_converter=Object.fromEntries)]; await env.MY_WORKFLOW.create_batch(listOfInstances); ### `get` [](https://developers.cloudflare.com/workflows/python/bindings/#get) Get a workflow instance by ID. * `get(id)`\* `id` - the ID of the workflow instance to get. Returns a [`WorkflowInstance`](https://developers.cloudflare.com/workflows/build/workers-api/#workflowinstance) object, which can be used to query the status of the workflow instance. instance = await env.MY_WORKFLOW.get("abc-123") # FFI methods available for WorkflowInstanceawait instance.status()await instance.pause()await instance.resume()await instance.restart()await instance.terminate() ### `send_event` [](https://developers.cloudflare.com/workflows/python/bindings/#send_event) Send an event to a workflow instance. * `send_event(options)`\* `type` - the type of event to send to the workflow instance. \* `payload` - the payload to send to the workflow instance. from pyodide.ffi import to_jsfrom js import Object await env.MY_WORKFLOW.send_event(to_js({ "type": "my-event-type", "payload": { "foo": "bar" } }, dict_converter=Object.fromEntries)) REST API (HTTP) --------------- [](https://developers.cloudflare.com/workflows/python/bindings/#rest-api-http) Refer to the [Workflows REST API documentation](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/methods/create/) . Command line (CLI) ------------------ [](https://developers.cloudflare.com/workflows/python/bindings/#command-line-cli) Refer to the [CLI quick start](https://developers.cloudflare.com/workflows/get-started/guide/) to learn more about how to manage and trigger Workflows via the command-line. Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # DAG Workflows · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/python/dag/#_top) Copy page DAG Workflows ============= The Python Workflows SDK supports DAG workflows in a declarative way, using the `step.do` decorator with the `depends` parameter to define dependencies (other steps that must complete before this step can run). from workers import Response, WorkflowEntrypoint class PythonWorkflowStarter(WorkflowEntrypoint): async def run(self, event, step): async def await_step(fn): try: return await fn() except TypeError as e: print(f"Successfully caught {type(e).__name__}: {e}") step.sleep('demo sleep', '10 seconds') @step.do('dependency1') async def dep_1(): # does stuff print('executing dep1') @step.do('dependency2') async def dep_2(): # does stuff print('executing dep2') @step.do('demo do', depends=[dep_1, dep_2], concurrent=True) async def final_step(res1, res2): # does stuff print('something') await await_step(final_step) async def on_fetch(request, env): await env.MY_WORKFLOW.create() return Response("Hello world!") On this example, `dep_1` and `dep_2` are run concurrently before execution of `final_step`, which depends on both of them. Having `concurrent=True` allows the dependencies to be resolved concurrently. If one of the callables passed to `depends` has already completed, it will be skipped and its return value will be reused. This pattern is useful for diamond shaped workflows, where a step depends on two or more other steps that can run concurrently. Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Sleeping and retrying · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/#_top) Copy page Sleeping and retrying ===================== This guide details how to sleep a Workflow and/or configure retries for a Workflow step. Sleep a Workflow ---------------- [](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/#sleep-a-workflow) You can set a Workflow to sleep as an explicit step, which can be useful when you want a Workflow to wait, schedule work ahead, or pause until an input or other external state is ready. ### Sleep for a relative period [](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/#sleep-for-a-relative-period) Use `step.sleep` to have a Workflow sleep for a relative period of time: await step.sleep("sleep for a bit", "1 hour") The second argument to `step.sleep` accepts both `number` (milliseconds) or a human-readable format, such as "1 minute" or "26 hours". The accepted units for `step.sleep` when used this way are as follows: | "second"| "minute"| "hour"| "day"| "week"| "month"| "year" ### Sleep until a fixed date [](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/#sleep-until-a-fixed-date) Use `step.sleepUntil` to have a Workflow sleep to a specific `Date`: this can be useful when you have a timestamp from another system or want to "schedule" work to occur at a specific time (e.g. Sunday, 9AM UTC). // sleepUntil accepts a Date object as its second argumentconst workflowsLaunchDate = Date.parse("24 Oct 2024 13:00:00 UTC");await step.sleepUntil("sleep until X times out", workflowsLaunchDate) You can also provide a UNIX timestamp (milliseconds since the UNIX epoch) directly to `sleepUntil`. Retry steps ----------- [](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/#retry-steps) Each call to `step.do` in a Workflow accepts an optional `StepConfig`, which allows you define the retry behaviour for that step. If you do not provide your own retry configuration, Workflows applies the following defaults: const defaultConfig: WorkflowStepConfig = { retries: { limit: 5, delay: 10000, backoff: 'exponential', }, timeout: '10 minutes',}; When providing your own `StepConfig`, you can configure: * The total number of attempts to make for a step (accepts `Infinity` for unlimited retries) * The delay between attempts (accepts both `number` (ms) or a human-readable format) * What backoff algorithm to apply between each attempt: any of `constant`, `linear`, or `exponential` * When to timeout (in duration) before considering the step as failed (including during a retry attempt, as the timeout is set per attempt) For example, to limit a step to 10 retries and have it apply an exponential delay (starting at 10 seconds) between each attempt, you would pass the following configuration as an optional object to `step.do`: let someState = step.do("call an API", { retries: { limit: 10, // The total number of attempts delay: "10 seconds", // Delay between each retry backoff: "exponential" // Any of "constant" | "linear" | "exponential"; }, timeout: "30 minutes",}, async () => { /* Step code goes here /* } Force a Workflow instance to fail --------------------------------- [](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/#force-a-workflow-instance-to-fail) You can also force a Workflow instance to fail and _not_ retry by throwing a `NonRetryableError` from within the step. This can be useful when you detect a terminal (permanent) error from an upstream system (such as an authentication failure) or other errors where retrying would not help. // Import the NonRetryableError definitionimport { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from 'cloudflare:workers';import { NonRetryableError } from 'cloudflare:workflows'; // In your step code:export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { await step.do("some step", async () => { if (!event.payload.data) { throw new NonRetryableError("event.payload.data did not contain the expected payload") } }) }} The Workflow instance itself will fail immediately, no further steps will be invoked, and the Workflow will not be retried. Catch Workflow errors --------------------- [](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/#catch-workflow-errors) Any uncaught exceptions that propagate to the top level, or any steps that reach their retry limit, will cause the Workflow to end execution in an `Errored` state. If you want to avoid this, you can catch exceptions emitted by a `step`. This can be useful if you need to trigger clean-up tasks or have conditional logic that triggers additional steps. To allow the Workflow to continue its execution, surround the intended steps that are allowed to fail with a `try-catch` block. ...await step.do('task', async () => { // work to be done}); try { await step.do('non-retryable-task', async () => { // work not to be retried throw new NonRetryableError('oh no'); });} catch(e as Error) { console.log(`Step failed: ${e.message}`); await step.do('clean-up-task', async () => { // Clean up code here });} // the Workflow will not fail and will continue its execution await step.do('next-task', async() => { // more work to be done});... Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Your Privacy Choices Back to top --- # Events and parameters · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/build/events-and-parameters/#_top) Copy page Events and parameters ===================== When a Workflow is triggered, it can receive an optional event. This event can include data that your Workflow can act on, including request details, user data fetched from your database (such as D1 or KV) or from a webhook, or messages from a Queue consumer. Events are a powerful part of a Workflow, as you often want a Workflow to act on data. Because a given Workflow instance executes durably, events are a useful way to provide a Workflow with data that should be immutable (not changing) and/or represents data the Workflow needs to operate on at that point in time. Pass data to a Workflow ----------------------- [](https://developers.cloudflare.com/workflows/build/events-and-parameters/#pass-data-to-a-workflow) You can pass parameters to a Workflow in three ways: * As an optional argument to the `create` method on a [Workflow binding](https://developers.cloudflare.com/workers/wrangler/commands/#trigger) when triggering a Workflow from a Worker. * Via the `--params` flag when using the `wrangler` CLI to trigger a Workflow. * Via the `step.waitForEvent` API, which allows a Workflow instance to wait for an event (and optional data) to be received _while it is running_. Workflow instances can be sent events from external services over HTTP or via the Workers API for Workflows. You can pass any JSON-serializable object as a parameter. * [JavaScript](https://developers.cloudflare.com/workflows/build/events-and-parameters/#tab-panel-4775) * [TypeScript](https://developers.cloudflare.com/workflows/build/events-and-parameters/#tab-panel-4776) export default { async fetch(req, env) { let someEvent = { url: req.url, createdTimestamp: Date.now() }; // Trigger our Workflow // Pass our event as the second parameter to the `create` method // on our Workflow binding. let instance = await env.MY_WORKFLOW.create({ id: crypto.randomUUID(), params: someEvent, }); return Response.json({ id: instance.id, details: await instance.status(), }); },}; export default { async fetch(req: Request, env: Env) { let someEvent = { url: req.url, createdTimestamp: Date.now() }; // Trigger our Workflow // Pass our event as the second parameter to the `create` method // on our Workflow binding. let instance = await env.MY_WORKFLOW.create({ id: crypto.randomUUID(), params: someEvent, }); return Response.json({ id: instance.id, details: await instance.status(), }); },}; To pass parameters via the `wrangler` command-line interface, pass a JSON string as the second parameter to the `workflows trigger` sub-command: npx wrangler@latest workflows trigger workflows-starter '{"some":"data"}' 🚀 Workflow instance "57c7913b-8e1d-4a78-a0dd-dce5a0b7aa30" has been queued successfully ### Wait for events [](https://developers.cloudflare.com/workflows/build/events-and-parameters/#wait-for-events) A running Workflow can wait for an event (or events) by calling `step.waitForEvent` within the Workflow, which allows you to send events to the Workflow in one of two ways: 1. Via the [Workers API binding](https://developers.cloudflare.com/workflows/build/workers-api/) : call `instance.sendEvent` to send events to specific workflow instances. 2. Using the REST API (HTTP API)'s [Events endpoint](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/subresources/events/methods/create/) . Because `waitForEvent` is part of the `WorkflowStep` API, you can call it multiple times within a Workflow, and use control flow to conditionally wait for an event. Calling `waitForEvent` requires you to specify an `type` (up to 100 characters 1), which is used to match the corresponding `type` when sending an event to a Workflow instance. For example, to wait for billing webhook: * [JavaScript](https://developers.cloudflare.com/workflows/build/events-and-parameters/#tab-panel-4771) * [TypeScript](https://developers.cloudflare.com/workflows/build/events-and-parameters/#tab-panel-4772) export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // Other steps in your Workflow let event = await step.waitForEvent( "receive invoice paid webhook from Stripe", { type: "stripe-webhook", timeout: "1 hour" }, ); // Rest of your Workflow }} export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // Other steps in your Workflow let event = await step.waitForEvent( "receive invoice paid webhook from Stripe", { type: "stripe-webhook", timeout: "1 hour" }, ); // Rest of your Workflow }} The above example: * Calls `waitForEvent` with a `type` of `stripe-webhook` - the corresponding `sendEvent` call would thus be `await instance.sendEvent({type: "stripe-webhook", payload: webhookPayload})`. * Uses a TypeScript [type parameter ↗](https://www.typescriptlang.org/docs/handbook/2/generics.html) to type the return value of `step.waitForEvent` as our `IncomingStripeWebhook`. * Continues on with the rest of the Workflow. The default timeout for a `waitForEvent` call is 24 hours, which can be changed by passing `{ timeout: WorkflowTimeoutDuration }` as the second argument to your `waitForEvent` call. * [JavaScript](https://developers.cloudflare.com/workflows/build/events-and-parameters/#tab-panel-4769) * [TypeScript](https://developers.cloudflare.com/workflows/build/events-and-parameters/#tab-panel-4770) let event = await step.waitForEvent("wait for human approval", { type: "approval-flow", timeout: "15 minutes",}); let event = await step.waitForEvent( "wait for human approval", { type: "approval-flow", timeout: "15 minutes" }, ); You can specify a timeout between 1 second and up to 365 days. ### Send events to running workflows [](https://developers.cloudflare.com/workflows/build/events-and-parameters/#send-events-to-running-workflows) Workflow instances that are waiting on events using the `waitForEvent` API can be sent events using the `instance.sendEvent` API: * [JavaScript](https://developers.cloudflare.com/workflows/build/events-and-parameters/#tab-panel-4777) * [TypeScript](https://developers.cloudflare.com/workflows/build/events-and-parameters/#tab-panel-4778) export default { async fetch(req, env) { const instanceId = new URL(req.url).searchParams.get("instanceId"); const webhookPayload = await req.json(); let instance = await env.MY_WORKFLOW.get(instanceId); // Send our event, with `type` matching the event type defined in // our step.waitForEvent call await instance.sendEvent({ type: "stripe-webhook", payload: webhookPayload, }); return Response.json({ status: await instance.status(), }); },}; export default { async fetch(req: Request, env: Env) { const instanceId = new URL(req.url).searchParams.get("instanceId"); const webhookPayload = await req.json(); let instance = await env.MY_WORKFLOW.get(instanceId); // Send our event, with `type` matching the event type defined in // our step.waitForEvent call await instance.sendEvent({ type: "stripe-webhook", payload: webhookPayload, }); return Response.json({ status: await instance.status(), }); },}; * Similar to the [`waitForEvent`](https://developers.cloudflare.com/workflows/build/events-and-parameters/#wait-for-events) example in this guide, the `type` property in our `waitForEvent` and `sendEvent` fields must match. * To send multiple events to a Workflow that has multiple `waitForEvent` calls, call `sendEvent` with the corresponding `type` property set (up to 100 characters 1). * Events can also be sent using the REST API (HTTP API)'s [Events endpoint](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/subresources/events/methods/create/) . TypeScript and type parameters ------------------------------ [](https://developers.cloudflare.com/workflows/build/events-and-parameters/#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: export type WorkflowEvent = { // The data passed as the parameter when the Workflow instance was triggered payload: T; // The timestamp that the Workflow was triggered timestamp: Date; // ID of the current Workflow instance instanceId: string;}; 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`: // Define a type that conforms to the events your Workflow instance is// instantiated withinterface YourEventType { userEmail: string; createdTimestamp: number; metadata?: Record;} When you pass your `YourEventType` to `WorkflowEvent` as a type parameter, the `event.payload` property now has the type `YourEventType` throughout your workflow definition: // Import the Workflow definitionimport { WorkflowEntrypoint, WorkflowStep, WorkflowEvent} from 'cloudflare:workers'; export class MyWorkflow extends WorkflowEntrypoint { // Pass your type as a type parameter to WorkflowEvent // The 'payload' property will have the type of your parameter. async run(event: WorkflowEvent, step: WorkflowStep) { let state = step.do("my first step", async () => { // Access your properties via event.payload let userEmail = event.payload.userEmail let createdTimestamp = event.payload.createdTimestamp }) step.do("my second step", async () => { /* your code here */ ) }} 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](https://developers.cloudflare.com/workflows/build/workers-api/#workflow) . Note that this does _not_ propagate type information into the Workflow itself, as TypeScript types are a build-time construct. To provide the type of an incoming `WorkflowEvent`, refer to the [TypeScript and type parameters](https://developers.cloudflare.com/workflows/build/events-and-parameters/#typescript-and-type-parameters) section of the Workflows documentation. Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Your Privacy Choices Back to top --- # Human-in-the-Loop Image Tagging with waitForEvent · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/examples/wait-for-event/#_top) Copy page Human-in-the-Loop Image Tagging with waitForEvent ================================================= Implement a Cloudflare Workflow that processes user-uploaded images, awaits human approval, and performs AI-based image tagging upon approval. This example demonstrates how to use the `waitForEvent()` API in Cloudflare Workflows to introduce a human-in-the-loop step. The Workflow is triggered by an image upload, during which metadata is stored in a D1 database. The Workflow then waits for user approval, and upon approval, it uses Workers AI to generate image tags, which are stored in the database. An accompanying Next.js frontend application facilitates the image upload and approval process. Overview of the Workflow ------------------------ [](https://developers.cloudflare.com/workflows/examples/wait-for-event/#overview-of-the-workflow) In this Workflow, we simulate a scenario where an uploaded image requires human approval before AI-based processing. An image is uploaded to R2, then Workflow performs the following steps: 1. Stores image metadata in a D1 database. 2. Pauses execution using `waitForEvent()` and waits for an external event sent from the Next.js frontend, indicating approval or rejection. 3. If approved, the Workflow uses Workers AI to generate image tags and stores the tags in the D1 database. 4. If rejected, the Workflow ends without further action. This pattern is useful in scenarios where certain operations should not proceed without explicit human consent, adding an extra layer of control and safety. Frontend Integration -------------------- [](https://developers.cloudflare.com/workflows/examples/wait-for-event/#frontend-integration) This example includes a Next.js frontend application that facilitates the image upload and approval process. The frontend provides an interface for uploading images, reviewing them, and approving or rejecting them. Upon image upload, the application triggers the Cloudflare Workflow, which then manages the subsequent steps, including waiting for user approval and performing AI-based image tagging upon approval. Refer to the `/nextjs-workflow-frontend` folder in the [GitHub repository ↗](https://github.com/cloudflare/docs-examples/tree/main/workflows/waitForEvent) for the complete frontend implementation and deployment details. Workflow index.ts ----------------- [](https://developers.cloudflare.com/workflows/examples/wait-for-event/#workflow-indexts) The `index.ts` file defines the core logic of the Cloudflare Workflow responsible for handling image uploads, awaiting human approval, and performing AI-based image tagging upon approval. It extends the `WorkflowEntrypoint` class and implements the `run()` method. For the complete implementation of the `index.ts` file, please refer to the [GitHub repository ↗](https://github.com/cloudflare/docs-examples/blob/main/workflows/waitForEvent/workflow/src/index.ts) . * [JavaScript](https://developers.cloudflare.com/workflows/examples/wait-for-event/#tab-panel-4827) * [TypeScript](https://developers.cloudflare.com/workflows/examples/wait-for-event/#tab-panel-4828) export class MyWorkflow extends WorkflowEntrypoint { db; async run(event, step) { this.db = new DatabaseService(this.env.DB); const { imageKey } = event.payload; await step.do("Insert image name into database", async () => { await this.db.insertImage(imageKey, event.instanceId); }); const waitForApproval = await step.waitForEvent( "Wait for AI Image tagging approval", { type: "approval-for-ai-tagging", timeout: "5 minute", }, ); const approvalPayload = waitForApproval.payload; if (approvalPayload?.approved) { const aiTags = await step.do("Generate AI tags", async () => { const image = await this.env.workflow_demo_bucket.get(imageKey); if (!image) throw new Error("Image not found"); const arrayBuffer = await image.arrayBuffer(); const uint8Array = new Uint8Array(arrayBuffer); const input = { image: Array.from(uint8Array), prompt: AI_CONFIG.PROMPT, max_tokens: AI_CONFIG.MAX_TOKENS, }; const response = await this.env.AI.run(AI_CONFIG.MODEL, input); return response.description; }); await step.do("Update DB with AI tags", async () => { await this.db.updateImageTags(event.instanceId, aiTags); }); } }} export class MyWorkflow extends WorkflowEntrypoint { private db!: DatabaseService; async run(event: WorkflowEvent, step: WorkflowStep) { this.db = new DatabaseService(this.env.DB); const { imageKey } = event.payload; await step.do('Insert image name into database', async () => { await this.db.insertImage(imageKey, event.instanceId); }); const waitForApproval = await step.waitForEvent('Wait for AI Image tagging approval', { type: 'approval-for-ai-tagging', timeout: '5 minute', }); const approvalPayload = waitForApproval.payload as ApprovalRequest; if (approvalPayload?.approved) { const aiTags = await step.do('Generate AI tags', async () => { const image = await this.env.workflow_demo_bucket.get(imageKey); if (!image) throw new Error('Image not found'); const arrayBuffer = await image.arrayBuffer(); const uint8Array = new Uint8Array(arrayBuffer); const input = { image: Array.from(uint8Array), prompt: AI_CONFIG.PROMPT, max_tokens: AI_CONFIG.MAX_TOKENS, }; const response = await this.env.AI.run(AI_CONFIG.MODEL, input); return response.description; }); await step.do('Update DB with AI tags', async () => { await this.db.updateImageTags(event.instanceId, aiTags); }); } }} Workflow wrangler.jsonc ----------------------- [](https://developers.cloudflare.com/workflows/examples/wait-for-event/#workflow-wranglerjsonc) The Workflow configuration is defined in the `wrangler.jsonc` file. This file includes bindings for the R2 bucket, D1 database, Workers AI, and the Workflow itself. Ensure that all necessary bindings and environment variables are correctly set up to match your Cloudflare account and services. * [wrangler.jsonc](https://developers.cloudflare.com/workflows/examples/wait-for-event/#tab-panel-4825) * [wrangler.toml](https://developers.cloudflare.com/workflows/examples/wait-for-event/#tab-panel-4826) { "$schema": "node_modules/wrangler/config-schema.json", "name": "workflows-waitforevent", "main": "src/index.ts", "compatibility_date": "2025-04-14", "observability": { "enabled": true, "head_sampling_rate": 1, }, "ai": { "binding": "AI" }, "workflows": [ { "name": "workflows-starter", "binding": "MY_WORKFLOW", "class_name": "MyWorkflow" } ], "r2_buckets": [ { "bucket_name": "workflow-demo", "binding": "workflow_demo_bucket" } ], "d1_databases": [ { "binding": "DB", "database_name": "workflows-demo-d1", "database_id": "66e4fbe9-06ac-4548-abba-2dc42088e13a" } ]} "$schema" = "node_modules/wrangler/config-schema.json"name = "workflows-waitforevent"main = "src/index.ts"compatibility_date = "2025-04-14" [observability]enabled = truehead_sampling_rate = 1 [ai]binding = "AI" [[workflows]]name = "workflows-starter"binding = "MY_WORKFLOW"class_name = "MyWorkflow" [[r2_buckets]]bucket_name = "workflow-demo"binding = "workflow_demo_bucket" [[d1_databases]]binding = "DB"database_name = "workflows-demo-d1"database_id = "66e4fbe9-06ac-4548-abba-2dc42088e13a" For access to the codebase, deployment instructions, and reference architecture, please visit the [GitHub repository ↗](https://github.com/cloudflare/docs-examples/tree/main/workflows/waitForEvent) . This resource provides all the necessary tools and information to effectively implement the Workflow and Next.js frontend application. Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Get started · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/get-started/#_top) Copy page Get started =========== * [Build your first Workflow](https://developers.cloudflare.com/workflows/get-started/guide/) * [Build a Durable AI Agent](https://developers.cloudflare.com/workflows/get-started/durable-agents/) Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Your Privacy Choices Back to top --- # Examples · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/examples/#_top) Copy page Examples ======== Explore the following examples for Workflows. [Pay cart and send invoice\ \ Send invoice when shopping cart is checked out and paid for](https://developers.cloudflare.com/workflows/examples/send-invoices/) [Export and save D1 database\ \ Send invoice when shopping cart is checked out and paid for](https://developers.cloudflare.com/workflows/examples/backup-d1/) [Integrate Workflows with Twilio\ \ Integrate Workflows with Twilio. Learn how to receive and send text messages and phone calls via APIs and Webhooks.](https://developers.cloudflare.com/workflows/examples/twilio/) [Human-in-the-Loop Image Tagging with waitForEvent\ \ Human-in-the-loop Workflow with waitForEvent API](https://developers.cloudflare.com/workflows/examples/wait-for-event/) Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Overview · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workers/runtime-apis/bindings/workflows/#_top) Copy page Cloudflare Workflows ==================== Build durable multi-step applications on Cloudflare Workers with Workflows. Available on Free and Paid plans With Workflows, you can build applications that chain together multiple steps, automatically retry failed tasks, and persist state for minutes, hours, or even weeks - with no infrastructure to manage. Use Workflows to build reliable AI applications, process data pipelines, manage user lifecycle with automated emails and trial expirations, and implement human-in-the-loop approval systems. **Workflows give you:** * Durable multi-step execution without timeouts * The ability to pause for external events or approvals * Automatic retries and error handling * Built-in observability and debugging Example ------- [](https://developers.cloudflare.com/workers/runtime-apis/bindings/workflows/#example) An image processing workflow that fetches from R2, generates an AI description, waits for approval, then publishes: 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); return await object.arrayBuffer(); }); const description = await step.do('generate description', async () => { const imageArray = Array.from(new Uint8Array(imageData)); return await this.env.AI.run('@cf/llava-hf/llava-1.5-7b-hf', { image: imageArray, prompt: 'Describe this image in one sentence', max_tokens: 50, }); }); await step.waitForEvent('await approval', { event: 'approved', timeout: '24 hours', }); await step.do('publish', async () => { await this.env.BUCKET.put(`public/${event.params.imageKey}`, imageData); }); }} [Get started](https://developers.cloudflare.com/workflows/get-started/guide/) [Browse the examples](https://developers.cloudflare.com/workflows/examples/) * * * Features -------- [](https://developers.cloudflare.com/workers/runtime-apis/bindings/workflows/#features) ### Durable step execution Break complex operations into durable steps with automatic retries and error handling. [Learn about steps](https://developers.cloudflare.com/workflows/build/workers-api/) ### Sleep and scheduling Pause workflows for seconds, hours, or days with `step.sleep()` and `step.sleepUntil()`. [Add delays](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/) ### Wait for external events Wait for webhooks, user input, or external system responses before continuing execution. [Handle events](https://developers.cloudflare.com/workflows/build/events-and-parameters/) ### Workflow lifecycle management Trigger, pause, resume, and terminate workflow instances programmatically or via API. [Manage instances](https://developers.cloudflare.com/workflows/build/trigger-workflows/) * * * Related products ---------------- [](https://developers.cloudflare.com/workers/runtime-apis/bindings/workflows/#related-products) **[Workers](https://developers.cloudflare.com/workers/) ** Build serverless applications and deploy instantly across the globe for exceptional performance, reliability, and scale. **[Pages](https://developers.cloudflare.com/pages/) ** Deploy dynamic front-end applications in record time. * * * More resources -------------- [](https://developers.cloudflare.com/workers/runtime-apis/bindings/workflows/#more-resources) [Pricing](https://developers.cloudflare.com/workflows/reference/pricing/) Learn more about how Workflows is priced. [Limits](https://developers.cloudflare.com/workflows/reference/limits/) Learn more about Workflow limits, and how to work within them. [Storage options](https://developers.cloudflare.com/workers/platform/storage-options/) Learn more about the storage and database options you can build on with Workers. [Developer Discord](https://discord.cloudflare.com/) Connect with the Workers community on Discord to ask questions, show what you are building, and discuss the platform with other developers. [@CloudflareDev](https://x.com/cloudflaredev) Follow @CloudflareDev on Twitter to learn about product announcements, and what is new in Cloudflare Developer Platform. Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings --- # Metrics and analytics · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/observability/metrics-analytics/#_top) Copy page Metrics and analytics ===================== Workflows expose metrics that allow you to inspect and measure Workflow execution, error rates, steps, and total duration across each (and all) of your Workflows. The metrics displayed in the [Cloudflare dashboard ↗](https://dash.cloudflare.com/) charts are queried from Cloudflare’s [GraphQL Analytics API](https://developers.cloudflare.com/analytics/graphql-api/) . You can access the metrics [programmatically](https://developers.cloudflare.com/workflows/observability/metrics-analytics/#query-via-the-graphql-api) via GraphQL or HTTP client. Metrics ------- [](https://developers.cloudflare.com/workflows/observability/metrics-analytics/#metrics) Workflows currently export the below metrics within the `workflowsAdaptiveGroups` GraphQL dataset. | Metric | GraphQL Field Name | Description | | --- | --- | --- | | Read Queries (qps) | `readQueries` | The number of read queries issued against a database. This is the raw number of read queries, and is not used for billing. | Metrics can be queried (and are retained) for the past 31 days. ### Labels and dimensions [](https://developers.cloudflare.com/workflows/observability/metrics-analytics/#labels-and-dimensions) The `workflowsAdaptiveGroups` dataset provides the following dimensions for filtering and grouping query results: * `workflowName` - Workflow name - e.g. `my-workflow` * `instanceId` - Instance ID * `stepName` - Step name * `eventType` - Event type (see [event types](https://developers.cloudflare.com/workflows/observability/metrics-analytics/#event-types) ) * `stepCount` - Step number within a given instance * `date` - The date when the Workflow was triggered * `datetimeFifteenMinutes` - The date and time truncated to fifteen minutes * `datetimeFiveMinutes` - The date and time truncated to five minutes * `datetimeHour` - The date and time truncated to the hour * `datetimeMinute` - The date and time truncated to the minute ### Event types [](https://developers.cloudflare.com/workflows/observability/metrics-analytics/#event-types) The `eventType` metric allows you to filter (or groupBy) Workflows and steps based on their last observed status. The possible values for `eventType` are documented below: #### Workflows-level status labels [](https://developers.cloudflare.com/workflows/observability/metrics-analytics/#workflows-level-status-labels) * `WORKFLOW_QUEUED` - the Workflow is queued, but not currently running. This can happen when you are at the [concurrency limit](https://developers.cloudflare.com/workflows/reference/limits/) and new instances are waiting for currently running instances to complete. * `WORKFLOW_START` - the Workflow has started and is running. * `WORKFLOW_SUCCESS` - the Workflow finished without errors. * `WORKFLOW_FAILURE` - the Workflow failed due to errors (exhausting retries, errors thrown, etc). * `WORKFLOW_TERMINATED` - the Workflow was explicitly terminated. #### Step-level status labels [](https://developers.cloudflare.com/workflows/observability/metrics-analytics/#step-level-status-labels) * `STEP_START` - the step has started and is running. * `STEP_SUCCESS` - the step finished without errors. * `STEP_FAILURE` - the step failed due to an error. * `SLEEP_START` - the step is sleeping. * `SLEEP_COMPLETE` - the step last finished sleeping. * `ATTEMPT_START` - a step is retrying. * `ATTEMPT_SUCCESS` - the retry succeeded. * `ATTEMPT_FAILURE` - the retry attempt failed. View metrics in the dashboard ----------------------------- [](https://developers.cloudflare.com/workflows/observability/metrics-analytics/#view-metrics-in-the-dashboard) Per-Workflow and instance analytics for Workflows are available in the Cloudflare dashboard. To view current and historical metrics for a database: 1. In the Cloudflare dashboard, go to the **Workflows** page. [Go to **Workflows**](https://dash.cloudflare.com/?to=/:account/workers/workflows) 2. Select a Workflow to view its metrics. You can optionally select a time window to query. This defaults to the last 24 hours. Query via the GraphQL API ------------------------- [](https://developers.cloudflare.com/workflows/observability/metrics-analytics/#query-via-the-graphql-api) You can programmatically query analytics for your Workflows via the [GraphQL Analytics API](https://developers.cloudflare.com/analytics/graphql-api/) . This API queries the same datasets as the Cloudflare dashboard, and supports GraphQL [introspection](https://developers.cloudflare.com/analytics/graphql-api/features/discovery/introspection/) . Workflows GraphQL datasets require an `accountTag` filter with your Cloudflare account ID, and includes the `workflowsAdaptiveGroups` dataset. ### Examples [](https://developers.cloudflare.com/workflows/observability/metrics-analytics/#examples) To query the count (number of workflow invocations) and sum of `wallTime` for a given `$workflowName` between `$datetimeStart` and `$datetimeEnd`, grouping by `date`: query WorkflowInvocationsExample( $accountTag: string! $datetimeStart: Time $datetimeEnd: Time $workflowName: string) { viewer { accounts(filter: { accountTag: $accountTag }) { wallTime: workflowsAdaptiveGroups( limit: 10000 filter: { datetimeHour_geq: $datetimeStart datetimeHour_leq: $datetimeEnd workflowName: $workflowName } orderBy: [count_DESC] ) { count sum { wallTime } dimensions { date: datetimeHour } } } }} [Run in GraphQL API Explorer](https://graphql.cloudflare.com/explorer?query=I4VwpgTgngBA6gewgawGYBsEHcCSA7ANwQGMBDAFwEsE8BnAUQA9SBbAB3TAAoAoGGACSlixBCDzkAKqQDmALhi1yESnhkBCPoIAmFMFRZgAyuVIRyCyZUNaBu8vuth6ebZae2sSNJiwA5VjAFJRU1HgBKGABvLQJKMCxIaK1+YVFxclouVEp0BwgFKJg0sQlpeUESjPKYAF9ImP4mmCxSdHQrQwUvFAxsWgBBXTYqAjAAcQgxNiyU5ph0a0oLGABGAAZN9bnmnLzIQp35+0dDAAkxCAB9GTBgBTs9A2NTcyPmk+eLkGvOe50nk4XNp3k0ej5sAEuoJwX1-IFQbVQUhtJAAEJQBQAbXSEiuABF6EYAMIAXSODVBuPIoNoIBYyXm81a7U6YERoO0Tjo1DojKZTROCk+Tm+EA5TKRzSldR4tSAA&variables=N4IghgxhD2CuB2AXAKmA5iAXCAggYTwHkBVAOWQH0BJAERABoQATMRAU0QEsBbNgZURgAToiwgATAAZxANgC0kgIxzZyKZnHjMkgOwAtBs1YcebAKLwmYqbIXLxO5NI1bdBxgHdoQgNYAzABtoD1IwXjEAJTMABQAZfDMKAHUqZAAJCj5kCKpSAHEQAF8gA) Here we are doing the same for `wallTime`, `instanceRuns` and `stepCount` in the same query: query WorkflowInvocationsExample2( $accountTag: string! $datetimeStart: Time $datetimeEnd: Time $workflowName: string) { viewer { accounts(filter: { accountTag: $accountTag }) { instanceRuns: workflowsAdaptiveGroups( limit: 10000 filter: { datetimeHour_geq: $datetimeStart datetimeHour_leq: $datetimeEnd workflowName: $workflowName eventType: "WORKFLOW_START" } orderBy: [count_DESC] ) { count dimensions { date: datetimeHour } } stepCount: workflowsAdaptiveGroups( limit: 10000 filter: { datetimeHour_geq: $datetimeStart datetimeHour_leq: $datetimeEnd workflowName: $workflowName eventType: "WORKFLOW_START" } orderBy: [count_DESC] ) { count dimensions { date: datetimeHour } } wallTime: workflowsAdaptiveGroups( limit: 10000 filter: { datetimeHour_geq: $datetimeStart datetimeHour_leq: $datetimeEnd workflowName: $workflowName } orderBy: [count_DESC] ) { count sum { wallTime } dimensions { date: datetimeHour } } } }} [Run in GraphQL API Explorer](https://graphql.cloudflare.com/explorer?query=I4VwpgTgngBA6gewgawGYBsEHcCSA7ANwQGMBDAFwEsE8BnAUQA9SBbAB3TACYAKAKBgwAJKWLEEIPOQAqpAOYAuGLXIRKeOQEIBwgCYUwVFmADK5UhHJLplYzqH7yh22Hp5d1l-axI0mLAByrGBKKmoafACUMADeOgSUYFiQsTqCouKS5LQ8qJToThBKMTAZElKyisJlWZUwAL7RcYItMOoqpHjEYABKkrRKPigY2LQAgvpsVARgAOIQEmw5aa0w6LaUVjAAjAAM+7srrXkFkMVHq47OxgASEhAA+nJgwEoOBkam5pYXrVefdxAj04rz0Hxcbl0vxaQz82CCxjesJGgWC0MEYBmFSgbBCMAARHAAPI9ADSADEADJEuAPEzSMY9aT46H1aFIXSQABCUCUAG1MlIHgARegmADCAF0Lk1oYLyNDdC46NQ6KlVpcDEp-i5ARBWRc2RqVGA2OLyltkf5xpNpnMFiAlvwNYJ1ixNko9gdoSdCucXS0dbd7k8Xm8g18LAqA4II3qHiDw+DjJD0TArfDgkjfCiEWA05iwNjcUpCSSKdTafTGcyDQGOdzeTABRaRWKpTL1S75YrlbRVbQuwGrtrk2A9XXVkbVlhSOh0DZEemc9aJqQppQZvNFssA26PTsDocA76zkOXXGQ89Qe8nJ8zFG05egQmw2C7xD3GmM6il0If3mk4ag2EA8vy8pthK0oarKAY9gGtAgCw54arO86LvmAbThqSrGCqNCDs0MYjjAz76lhhorNObL1EAA&variables=N4IghgxhD2CuB2AXAKmA5iAXCAggYTwHkBVAOWQH0BJAERABoQATMRAU0QEsBbNgZURgAToiwgATAAZxANgC0kgIxzZyKZnHjMkgOwAtBs1YcebAKLwmYqbIXLxO5NI1bdBxgHdoQgNYAzABtoD1IwXjEAJTMABQAZfDMKAHUqZAAJCj5kCKpSAHEQAF8gA) Here lets query `workflowsAdaptive` for raw data about `$instanceId` between `$datetimeStart` and `$datetimeEnd`: query WorkflowsAdaptiveExample( $accountTag: string! $datetimeStart: Time $datetimeEnd: Time $instanceId: string) { viewer { accounts(filter: { accountTag: $accountTag }) { workflowsAdaptive( limit: 100 filter: { datetime_geq: $datetimeStart datetime_leq: $datetimeEnd instanceId: $instanceId } orderBy: [datetime_ASC] ) { datetime eventType workflowName instanceId stepCount wallTime } } }} [Run in GraphQL API Explorer](https://graphql.cloudflare.com/explorer?query=I4VwpgTgngBA6gewgawGYBsEHcDOBBAEwEMAHAFwEsA3MAUQA8iBbE9MACgCgYYASIgMYCEIAHZkAKkQDmALhg4yECqOkBCbn2JkwlJmADKZIhDLyJFfZt7bdluqILn71lYqKiBYAJJOFSlWlOAEoYAG9NKgowLEhwzR5BYTEyHHZUCnQdCHkwmCSRcSk5PgKU4pgAX1CInjqYLCQ0TFxCUkoaLnr69EsKMxgARgAGYYTujKzIXPHumFs9MAB9aTBgeRsiHUWjEzJZ7oX7JbZ1rS27fVpHA-q3Y08fP157jy9fW6rPpAJIACEoPIANpHfRLPAGADCAF0DjVPqCwJ8wDQilASEi5jxGigMNgAHLMTFY16PD5Y-xgEiQwr7ClYIjodAWKxzSrjdk8dmVIA&variables=N4IghgxhD2CuB2AXAKmA5iAXCAggYTwHkBVAOWQH0BJAERABoQATMRAU0QEsBbNgZURgAToiwgATAAZxANgC0kgIxzZyKZnHjMkgOwAtBs1YcebAKLwmYqbIXLxO5NI1bdBxp3gBnQfAhsqK2wAJTMABQAZfDMKAHUqZAAJajoAXyA) #### GraphQL query variables [](https://developers.cloudflare.com/workflows/observability/metrics-analytics/#graphql-query-variables) Example values for the query variables: { "accountTag": "fedfa729a5b0ecfd623bca1f9000f0a22", "datetimeStart": "2024-10-20T00:00:00Z", "datetimeEnd": "2024-10-29T00:00:00Z", "workflowName": "shoppingCart", "instanceId": "ecc48200-11c4-22a3-b05f-88a3c1c1db81"} Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Trigger Workflows · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/build/trigger-workflows/#_top) Copy page Trigger Workflows ================= You can trigger Workflows both programmatically and via the Workflows APIs, including: 1. With [Workers](https://developers.cloudflare.com/workers) via HTTP requests in a `fetch` handler, or bindings from a `queue` or `scheduled` handler 2. Using the [Workflows REST API](https://developers.cloudflare.com/api/resources/workflows/methods/list/) 3. Via the [wrangler CLI](https://developers.cloudflare.com/workers/wrangler/commands/#workflows) in your terminal Workers API (Bindings) ---------------------- [](https://developers.cloudflare.com/workflows/build/trigger-workflows/#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: * Directly over HTTP via the [`fetch`](https://developers.cloudflare.com/workers/runtime-apis/handlers/fetch/) handler * From a [Queue consumer](https://developers.cloudflare.com/queues/configuration/javascript-apis/#consumer) inside a `queue` handler * From a [Cron Trigger](https://developers.cloudflare.com/workers/configuration/cron-triggers/) inside a `scheduled` handler * Within a [Durable Object](https://developers.cloudflare.com/durable-objects/) To bind to a Workflow from your Workers code, you need to define a [binding](https://developers.cloudflare.com/workers/wrangler/configuration/) to a specific Workflow. For example, to bind to the Workflow defined in the [get started guide](https://developers.cloudflare.com/workflows/get-started/guide/) , you would configure the [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) with the below: * [wrangler.jsonc](https://developers.cloudflare.com/workflows/build/trigger-workflows/#tab-panel-4809) * [wrangler.toml](https://developers.cloudflare.com/workflows/build/trigger-workflows/#tab-panel-4810) { "$schema": "./node_modules/wrangler/config-schema.json", "name": "workflows-tutorial", "main": "src/index.ts", "compatibility_date": "2024-10-22", "workflows": [ { "name": "workflows-tutorial", "binding": "MY_WORKFLOW", "class_name": "MyWorkflow" } ]} name = "workflows-tutorial"main = "src/index.ts"compatibility_date = "2024-10-22" [[workflows]]# The name of the Workflowname = "workflows-tutorial"# The binding name, which must be a valid JavaScript variable name. This will# be how you call (run) your Workflow from your other Workers handlers or# scripts.binding = "MY_WORKFLOW"# Must match the class defined in your code that extends the Workflow classclass_name = "MyWorkflow" 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). 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 * Creating (triggering) a new Workflow instance * Returning the status of a given instance ID interface Env { MY_WORKFLOW: Workflow;} export default { async fetch(req: Request, env: Env) { // Get instanceId from query parameters const instanceId = new URL(req.url).searchParams.get("instanceId"); // If an ?instanceId= query parameter is provided, fetch the status // of an existing Workflow by its ID. if (instanceId) { let instance = await env.MY_WORKFLOW.get(instanceId); return Response.json({ status: await instance.status(), }); } // Else, create a new instance of our Workflow, passing in any (optional) // params and return the ID. const newId = crypto.randomUUID(); let instance = await env.MY_WORKFLOW.create({ id: newId }); return Response.json({ id: instance.id, details: await instance.status(), }); },}; ### Inspect a Workflow's status [](https://developers.cloudflare.com/workflows/build/trigger-workflows/#inspect-a-workflows-status) You can inspect the status of any running Workflow instance by calling `status` against a specific instance ID. This allows you to programmatically inspect whether an instance is queued (waiting to be scheduled), actively running, paused, or errored. let instance = await env.MY_WORKFLOW.get("abc-123");let status = await instance.status(); // Returns an InstanceStatus The possible values of status are as follows: status: | "queued" // means that instance is waiting to be started (see concurrency limits) | "running" | "paused" | "errored" | "terminated" // user terminated the instance while it was running | "complete" | "waiting" // instance is hibernating and waiting for sleep or event to finish | "waitingForPause" // instance is finishing the current work to pause | "unknown"; error?: { name: string, message: string }; output?: unknown; ### Explicitly pause a Workflow [](https://developers.cloudflare.com/workflows/build/trigger-workflows/#explicitly-pause-a-workflow) You can explicitly pause a Workflow instance (and later resume it) by calling `pause` against a specific instance ID. let instance = await env.MY_WORKFLOW.get("abc-123");await instance.pause(); // Returns Promise ### Resume a Workflow [](https://developers.cloudflare.com/workflows/build/trigger-workflows/#resume-a-workflow) You can resume a paused Workflow instance by calling `resume` against a specific instance ID. let instance = await env.MY_WORKFLOW.get("abc-123");await instance.resume(); // Returns Promise Calling `resume` on an instance that is not currently paused will have no effect. ### Stop a Workflow [](https://developers.cloudflare.com/workflows/build/trigger-workflows/#stop-a-workflow) You can stop/terminate a Workflow instance by calling `terminate` against a specific instance ID. let instance = await env.MY_WORKFLOW.get("abc-123");await instance.terminate(); // Returns Promise Once stopped/terminated, the Workflow instance _cannot_ be resumed. ### Restart a Workflow [](https://developers.cloudflare.com/workflows/build/trigger-workflows/#restart-a-workflow) let instance = await env.MY_WORKFLOW.get("abc-123");await instance.restart(); // Returns Promise Restarting an instance will immediately cancel any in-progress steps, erase any intermediate state, and treat the Workflow as if it was run for the first time. ### Trigger a Workflow from another Workflow [](https://developers.cloudflare.com/workflows/build/trigger-workflows/#trigger-a-workflow-from-another-workflow) You can create a new Workflow instance from within a step of another Workflow. The parent Workflow will not block waiting for the child Workflow to complete — it continues execution immediately after the child instance is successfully created. * [JavaScript](https://developers.cloudflare.com/workflows/build/trigger-workflows/#tab-panel-4811) * [TypeScript](https://developers.cloudflare.com/workflows/build/trigger-workflows/#tab-panel-4812) export class ParentWorkflow extends WorkflowEntrypoint { async run(event, step) { // Perform initial work const result = await step.do("initial processing", async () => { // ... processing logic return { fileKey: "output.pdf" }; }); // Trigger a child workflow for additional processing const childInstance = await step.do("trigger child workflow", async () => { return await this.env.CHILD_WORKFLOW.create({ id: `child-${event.instanceId}`, params: { fileKey: result.fileKey }, }); }); // Parent continues immediately - not blocked by child workflow await step.do("continue with other work", async () => { console.log(`Started child workflow: ${childInstance.id}`); // This runs right away, regardless of child workflow status }); }} export class ParentWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // Perform initial work const result = await step.do("initial processing", async () => { // ... processing logic return { fileKey: "output.pdf" }; }); // Trigger a child workflow for additional processing const childInstance = await step.do("trigger child workflow", async () => { return await this.env.CHILD_WORKFLOW.create({ id: `child-${event.instanceId}`, params: { fileKey: result.fileKey }, }); }); // Parent continues immediately - not blocked by child workflow await step.do("continue with other work", async () => { console.log(`Started child workflow: ${childInstance.id}`); // This runs right away, regardless of child workflow status }); }} If the child Workflow fails to start, the step will fail and be retried according to your retry configuration. Once the child instance is successfully created, it runs independently from the parent. REST API (HTTP) --------------- [](https://developers.cloudflare.com/workflows/build/trigger-workflows/#rest-api-http) Refer to the [Workflows REST API documentation](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/methods/create/) . Command line (CLI) ------------------ [](https://developers.cloudflare.com/workflows/build/trigger-workflows/#command-line-cli) Refer to the [CLI quick start](https://developers.cloudflare.com/workflows/get-started/guide/) to learn more about how to manage and trigger Workflows via the command-line. Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Your Privacy Choices Back to top --- # Platform · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/reference/#_top) Copy page Platform ======== * [Pricing](https://developers.cloudflare.com/workflows/reference/pricing/) * [Limits](https://developers.cloudflare.com/workflows/reference/limits/) * [Event subscriptions](https://developers.cloudflare.com/workflows/reference/event-subscriptions/) * [Glossary](https://developers.cloudflare.com/workflows/reference/glossary/) * [Wrangler commands](https://developers.cloudflare.com/workflows/reference/wrangler-commands/) * [Changelog](https://developers.cloudflare.com/workflows/reference/changelog/) Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Python Workers API · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/python/python-workers-api/#_top) Copy page Python Workers API ================== This guide covers the Python Workflows SDK, with instructions on how to build and create workflows using Python. WorkflowEntrypoint ------------------ [](https://developers.cloudflare.com/workflows/python/python-workers-api/#workflowentrypoint) The `WorkflowEntrypoint` is the main entrypoint for a Python workflow. It extends the `WorkflowEntrypoint` class, and implements the `run` method. from workers import WorkflowEntrypoint class MyWorkflow(WorkflowEntrypoint): def run(self, event, step): # steps here WorkflowStep ------------ [](https://developers.cloudflare.com/workflows/python/python-workers-api/#workflowstep) * `step.do(name, depends=[], concurrent=False, config=None)` is a decorator that allows you to define a step in a workflow. * `name` - the name of the step. * `depends` - an optional list of steps that must complete before this step can run. See [DAG Workflows](https://developers.cloudflare.com/workflows/python/dag) . * `concurrent` - an optional boolean that indicates whether this step can run concurrently with other steps. * `config` - an optional [`WorkflowStepConfig`](https://developers.cloudflare.com/workflows/build/workers-api/#workflowstepconfig) for configuring [step specific retry behaviour](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/) . This is passed as a Python dictionary and then type translated into a `WorkflowStepConfig` object. from workers import WorkflowEntrypoint class MyWorkflow(WorkflowEntrypoint): async def run(self, event, step): @step.do("my first step") async def my_first_step(): # do some work return "Hello World!" await my_first_step() Note that the decorator doesn't make the call to the step, it just returns a callable that can be used to invoke the step. You have to call the callable to make the step run. When returning state from a step, you must make sure that the returned value is serializable. Since steps run through an FFI layer, the returned value gets type translated via [FFI. ↗](https://pyodide.org/en/stable/usage/api/python-api/ffi.html#pyodide.ffi.to_js) Refer to [Pyodide's documentation ↗](https://pyodide.org/en/stable/usage/type-conversions.html#type-translations-pyproxy-to-js) regarding type conversions for more information. * `step.sleep(name, duration)` * `name` - the name of the step. * `duration` - the duration to sleep until, in either seconds or as a `WorkflowDuration` compatible string. async def run(self, event, step): await step.sleep("my-sleep-step", "10 seconds") * `step.sleep_until(name, timestamp)` * `name` - the name of the step. * `timestamp` - a `datetime.datetime` object or seconds from the Unix epoch to sleep the workflow instance until. async def run(self, event, step): await step.sleep_until("my-sleep-step", datetime.datetime.now() + datetime.timedelta(seconds=10)) * `step.wait_for_event(name, event_type, timeout="24 hours")` * `name` - the name of the step. * `event_type` - the type of event to wait for. * `timeout` - the timeout for the `wait_for_event` call. The default timeout is 24 hours. async def run(self, event, step): await step.wait_for_event("my-wait-for-event-step", "my-event-type") ### `event` parameter [](https://developers.cloudflare.com/workflows/python/python-workers-api/#event-parameter) The `event` parameter is a dictionary that contains the payload passed to the workflow instance, along with other metadata: * `payload` - the payload passed to the workflow instance. * `timestamp` - the timestamp that the workflow was triggered. * `instanceId` - the ID of the current workflow instance. * `workflowName` - the name of the workflow. Error Handling -------------- [](https://developers.cloudflare.com/workflows/python/python-workers-api/#error-handling) Workflows semantics allow users to catch exceptions that get thrown to the top level. Catching specific exceptions within an `except` block may not work, as some Python errors will not be re-instantiated into the same type of error when they are passed through the RPC layer. async def run(self, event, step): async def try_step(fn): try: return await fn() except Exception as e: print(f"Successfully caught {type(e).__name__}: {e}") @step.do("my_failing") async def my_failing(): print("Executing my_failing") raise TypeError("Intentional error in my_failing") await try_step(my_failing) ### NonRetryableError [](https://developers.cloudflare.com/workflows/python/python-workers-api/#nonretryableerror) The Python Workflows SDK provides a `NonRetryableError` class that can be used to signal that a step should not be retried. from workers.workflows import NonRetryableError raise NonRetryableError(message) Configure a workflow instance ----------------------------- [](https://developers.cloudflare.com/workflows/python/python-workers-api/#configure-a-workflow-instance) You can bind a step to a specific retry policy by passing a `WorkflowStepConfig` object to the `config` parameter of the `step.do` decorator. With Python Workflows, you need to make sure that your `dict` respects the [`WorkflowStepConfig`](https://developers.cloudflare.com/workflows/build/workers-api/#workflowstepconfig) type. class DemoWorkflowClass(WorkflowEntrypoint): async def run(self, event, step): @step.do('step-name', config={"retries": {"limit": 1, "delay": "10 seconds"}}) async def first_step(): # do some work pass ### Create an instance via binding [](https://developers.cloudflare.com/workflows/python/python-workers-api/#create-an-instance-via-binding) Note that `env` is a JavaScript object exposed to the Python script via [JsProxy ↗](https://pyodide.org/en/stable/usage/api/python-api/ffi.html#pyodide.ffi.JsProxy) . You can access the binding like you would on a JavaScript worker. Refer to the [Workflow binding documentation](https://developers.cloudflare.com/workflows/build/workers-api/#workflow) to learn more about the methods available. Let's consider the previous binding called `MY_WORKFLOW`. Here's how you would create a new instance: class Default(WorkerEntrypoint): async def fetch(self, request): instance = await self.env.MY_WORKFLOW.create() return Response.json({"status": "success"}) Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Glossary · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/reference/glossary/#_top) Copy page Glossary ======== Review the definitions for terms used across Cloudflare's Workflows documentation. | Term | Definition | | --- | --- | | Durable Execution | "Durable Execution" is a programming model that allows applications to execute reliably, automatically persist state, retry, and be resistant to errors caused by API, network or even machine/infrastructure failures. Cloudflare Workflows provide a way to build and deploy applications that align with this model. | | Event | The event that triggered the Workflow instance. A `WorkflowEvent` may contain optional parameters (data) that a Workflow can operate on. | | instance | A specific instance (running, paused, errored) of a Workflow. A Workflow can have a potentially infinite number of instances. | | step | A step is self-contained, individually retriable component of a Workflow. Steps may emit (optional) state that allows a Workflow to persist and continue from that step, even if a Workflow fails due to a network or infrastructure issue. A Workflow can have one or more steps up to the [step limit](https://developers.cloudflare.com/workflows/reference/limits/)
. | | Workflow | The named Workflow definition, associated with a single Workers script. | Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Observability · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/observability/#_top) Copy page Observability ============= * [Metrics and analytics](https://developers.cloudflare.com/workflows/observability/metrics-analytics/) Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Pricing · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/reference/pricing/#_top) Copy page Pricing ======= Workflows pricing is identical to [Workers Standard pricing](https://developers.cloudflare.com/workers/platform/pricing/#workers) and are billed on three dimensions: * **CPU time**: the total amount of compute (measured in milliseconds) consumed by a given Workflow. * **Requests** (invocations): the number of Workflow invocations. [Subrequests](https://developers.cloudflare.com/workers/platform/limits/#subrequests) made from a Workflow do not incur additional request costs. * **Storage**: the total amount of storage (measured in GB) persisted by your Workflows. A Workflow that is waiting on a response to an API call, paused as a result of calling `step.sleep`, or otherwise idle, does not incur CPU time. ### Workflows Pricing [](https://developers.cloudflare.com/workflows/reference/pricing/#workflows-pricing) | Unit | Workers Free | Workers Paid | | --- | --- | --- | | Requests (millions) | 100,000 per day ([shared with Workers requests](https://developers.cloudflare.com/workers/platform/pricing/#workers) | 10 million included per month + $0.30 per additional million | | CPU time (ms) | 10 milliseconds of CPU time per invocation | 30 million CPU milliseconds included per month + $0.02 per additional million CPU milliseconds | | Storage (GB-mo) | 1GB | 1GB included per month + $0.20/ GB-month | ### Storage Usage [](https://developers.cloudflare.com/workflows/reference/pricing/#storage-usage) Storage is billed using gigabyte-month (GB-month) as the billing metric, identical to [Durable Objects SQL storage](https://developers.cloudflare.com/durable-objects/platform/pricing/#sqlite-storage-backend) . A GB-month is calculated by averaging the peak storage per day over a billing period (30 days). * Storage is calculated across all instances, and includes running, errored, sleeping and completed instances. * By default, instance state is retained for [3 days on the Free plan](https://developers.cloudflare.com/workflows/reference/limits/) and [7 days on the Paid plan](https://developers.cloudflare.com/workflows/reference/limits/) . * When creating a Workflow instance, you can set a shorter state retention period if you do not need to retain state for errored or completed Workflows. * Deleting instances via the [Workers API](https://developers.cloudflare.com/workflows/build/workers-api/) , [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/commands/#workflows) , REST API, or dashboard will free up storage. Note that it may take a few minutes for storage limits to update. An instance that attempts to store state when your have reached the storage limit on the Free plan will cause an error to be thrown. Frequently Asked Questions -------------------------- [](https://developers.cloudflare.com/workflows/reference/pricing/#frequently-asked-questions) Frequently asked questions related to Workflows pricing: ### Are there additional costs for Workflows? [](https://developers.cloudflare.com/workflows/reference/pricing/#are-there-additional-costs-for-workflows) No. Workflows are priced based on the same compute (CPU time), requests (invocations) as Workers, as well as storage (state from a Workflow). ### Are Workflows available on the [Workers Free](https://developers.cloudflare.com/workers/platform/pricing/#workers) plan? [](https://developers.cloudflare.com/workflows/reference/pricing/#are-workflows-available-on-the-workers-free-plan) Yes. ### What is a Workflow invocation? [](https://developers.cloudflare.com/workflows/reference/pricing/#what-is-a-workflow-invocation) A Workflow invocation is when you trigger a new Workflow instance: for example, via the [Workers API](https://developers.cloudflare.com/workflows/build/workers-api/) , wrangler CLI, or REST API. Steps within a Workflow are not invocations. ### How do Workflows show up on my bill? [](https://developers.cloudflare.com/workflows/reference/pricing/#how-do-workflows-show-up-on-my-bill) Workflows are billed as Workers, and share the same CPU time and request SKUs. ### Are there any limits to Workflows? [](https://developers.cloudflare.com/workflows/reference/pricing/#are-there-any-limits-to-workflows) Refer to the published [limits](https://developers.cloudflare.com/workflows/reference/limits/) documentation. Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Event subscriptions · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/reference/event-subscriptions/#_top) Copy page Event subscriptions =================== [Event subscriptions](https://developers.cloudflare.com/queues/event-subscriptions/) allow you to receive messages when events occur across your Cloudflare account. Cloudflare products (e.g., [KV](https://developers.cloudflare.com/kv/) , [Workers AI](https://developers.cloudflare.com/workers-ai/) , [Workers](https://developers.cloudflare.com/workers/) ) can publish structured events to a [queue](https://developers.cloudflare.com/queues/) , which you can then consume with Workers or [HTTP pull consumers](https://developers.cloudflare.com/queues/configuration/pull-consumers/) to build custom workflows, integrations, or logic. For more information on [Event Subscriptions](https://developers.cloudflare.com/queues/event-subscriptions/) , refer to the [management guide](https://developers.cloudflare.com/queues/event-subscriptions/manage-event-subscriptions/) . Available Workflows events -------------------------- [](https://developers.cloudflare.com/workflows/reference/event-subscriptions/#available-workflows-events) #### `instance.queued` [](https://developers.cloudflare.com/workflows/reference/event-subscriptions/#instancequeued) Triggered when an instance was created and is awaiting execution. **Example:** { "type": "cf.workflows.workflow.instance.queued", "source": { "type": "workflows.workflow", "workflowName": "my-workflow" }, "payload": { "versionId": "v1", "instanceId": "inst-12345678-90ab-cdef-1234-567890abcdef" }, "metadata": { "accountId": "f9f79265f388666de8122cfb508d7776", "eventSubscriptionId": "1830c4bb612e43c3af7f4cada31fbf3f", "eventSchemaVersion": 1, "eventTimestamp": "2025-05-01T02:48:57.132Z" }} #### `instance.started` [](https://developers.cloudflare.com/workflows/reference/event-subscriptions/#instancestarted) Triggered when an instance starts or resumes execution. **Example:** { "type": "cf.workflows.workflow.instance.started", "source": { "type": "workflows.workflow", "workflowName": "my-workflow" }, "payload": { "versionId": "v1", "instanceId": "inst-12345678-90ab-cdef-1234-567890abcdef" }, "metadata": { "accountId": "f9f79265f388666de8122cfb508d7776", "eventSubscriptionId": "1830c4bb612e43c3af7f4cada31fbf3f", "eventSchemaVersion": 1, "eventTimestamp": "2025-05-01T02:48:57.132Z" }} #### `instance.paused` [](https://developers.cloudflare.com/workflows/reference/event-subscriptions/#instancepaused) Triggered when an instance pauses execution. **Example:** { "type": "cf.workflows.workflow.instance.paused", "source": { "type": "workflows.workflow", "workflowName": "my-workflow" }, "payload": { "versionId": "v1", "instanceId": "inst-12345678-90ab-cdef-1234-567890abcdef" }, "metadata": { "accountId": "f9f79265f388666de8122cfb508d7776", "eventSubscriptionId": "1830c4bb612e43c3af7f4cada31fbf3f", "eventSchemaVersion": 1, "eventTimestamp": "2025-05-01T02:48:57.132Z" }} #### `instance.errored` [](https://developers.cloudflare.com/workflows/reference/event-subscriptions/#instanceerrored) Triggered when an instance step throws an error. **Example:** { "type": "cf.workflows.workflow.instance.errored", "source": { "type": "workflows.workflow", "workflowName": "my-workflow" }, "payload": { "versionId": "v1", "instanceId": "inst-12345678-90ab-cdef-1234-567890abcdef" }, "metadata": { "accountId": "f9f79265f388666de8122cfb508d7776", "eventSubscriptionId": "1830c4bb612e43c3af7f4cada31fbf3f", "eventSchemaVersion": 1, "eventTimestamp": "2025-05-01T02:48:57.132Z" }} #### `instance.terminated` [](https://developers.cloudflare.com/workflows/reference/event-subscriptions/#instanceterminated) Triggered when an instance is manually terminated. **Example:** { "type": "cf.workflows.workflow.instance.terminated", "source": { "type": "workflows.workflow", "workflowName": "my-workflow" }, "payload": { "versionId": "v1", "instanceId": "inst-12345678-90ab-cdef-1234-567890abcdef" }, "metadata": { "accountId": "f9f79265f388666de8122cfb508d7776", "eventSubscriptionId": "1830c4bb612e43c3af7f4cada31fbf3f", "eventSchemaVersion": 1, "eventTimestamp": "2025-05-01T02:48:57.132Z" }} #### `instance.completed` [](https://developers.cloudflare.com/workflows/reference/event-subscriptions/#instancecompleted) Triggered when an instance finishes execution successfully. **Example:** { "type": "cf.workflows.workflow.instance.completed", "source": { "type": "workflows.workflow", "workflowName": "my-workflow" }, "payload": { "versionId": "v1", "instanceId": "inst-12345678-90ab-cdef-1234-567890abcdef" }, "metadata": { "accountId": "f9f79265f388666de8122cfb508d7776", "eventSubscriptionId": "1830c4bb612e43c3af7f4cada31fbf3f", "eventSchemaVersion": 1, "eventTimestamp": "2025-05-01T02:48:57.132Z" }} Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Limits · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/reference/limits/#_top) Copy page Limits ====== Limits that apply to authoring, deploying, and running Workflows are detailed below. Many limits are inherited from those applied to Workers scripts and as documented in the [Workers limits](https://developers.cloudflare.com/workers/platform/limits/) documentation. | Feature | Workers Free | Workers Paid | | --- | --- | --- | | Workflow class definitions per script | 3MB max script size per [Worker size limits](https://developers.cloudflare.com/workers/platform/limits/#account-plan-limits) | 10MB max script size per [Worker size limits](https://developers.cloudflare.com/workers/platform/limits/#account-plan-limits) | | Total scripts per account | 100 | 500 (shared with [Worker script limits](https://developers.cloudflare.com/workers/platform/limits/#account-plan-limits) | | Compute time per step 1 | 10 ms | 30 seconds (default) / configurable to 5 minutes of [active CPU time](https://developers.cloudflare.com/workers/platform/limits/#cpu-time) | | Duration (wall clock) per step 1 | Unlimited | Unlimited - for example, waiting on network I/O calls or querying a database | | Maximum persisted state per step | 1MiB (2^20 bytes) | 1MiB (2^20 bytes) | | Maximum event [payload size](https://developers.cloudflare.com/workflows/build/events-and-parameters/) | 1MiB (2^20 bytes) | 1MiB (2^20 bytes) | | Maximum state that can be persisted per Workflow instance | 100MB | 1GB | | Maximum `step.sleep` duration | 365 days (1 year) | 365 days (1 year) | | Maximum steps per Workflow 2 | 1024 | 1024 | | Maximum Workflow executions | 100,000 per day [shared with Workers daily limit](https://developers.cloudflare.com/workers/platform/limits/#worker-limits) | Unlimited | | Concurrent Workflow instances (executions) per account 3 | 100 | 10,000 | | Maximum Workflow instance creation rate 4 | 100 per second 5 | 100 per second 5 | | Maximum number of [queued instances](https://developers.cloudflare.com/workflows/observability/metrics-analytics/#event-types) | 100,000 | 1,000,000 | | Retention limit for completed Workflow instance state | 3 days | 30 days 6 | | Maximum length of a Workflow name 7 | 64 characters | 64 characters | | Maximum length of a Workflow instance ID 7 | 100 characters | 100 characters | | Maximum number of subrequests per Workflow instance | 50/request | 1000/request | ### `waiting` instances do not count towards instance concurrency limits [](https://developers.cloudflare.com/workflows/reference/limits/#waiting-instances-do-not-count-towards-instance-concurrency-limits) Instances that are in a `waiting` state — either sleeping via `step.sleep`, waiting for a retry, or waiting for an event via `step.waitForEvent` — do **not** count towards concurrency limits. This means you can have millions of Workflow instances sleeping or waiting for events simultaneously, as only actively `running` instances count toward the 10,000 concurrent instance limit. However, if there are 10,000 concurrent instances actively running, an instance that has been in a `waiting` state will be queued instead of resuming immediately. When an instance transitions from `running` to `waiting`, other `queued` instances will be scheduled (usually the oldest queued instance, on a best-effort basis). This state transition may not occur if the wait duration is very short. For example, consider a Workflow that does some work, waits for 30 days, and then continues with more work: import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent,} from "cloudflare:workers"; type Env = { MY_WORKFLOW: Workflow;}; export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { const apiResponse = await step.do("initial work", async () => { let resp = await fetch("https://api.cloudflare.com/client/v4/ips"); return await resp.json(); }); await step.sleep("wait 30 days", "30 days"); await step.do( "make a call to write that could maybe, just might, fail", { retries: { limit: 5, delay: "5 second", backoff: "exponential", }, timeout: "15 minutes", }, async () => { if (Math.random() > 0.5) { throw new Error("API call to $STORAGE_SYSTEM failed"); } }, ); }} 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. ### Increasing Workflow CPU limits [](https://developers.cloudflare.com/workflows/reference/limits/#increasing-workflow-cpu-limits) Workflows are Worker scripts, and share the same [per invocation CPU limits](https://developers.cloudflare.com/workers/platform/limits/#worker-limits) as any Workers do. Note that CPU time is active processing time: not time spent waiting on network requests, storage calls, or other general I/O, which don't count towards your CPU time or Workflows compute consumption. By default, the maximum CPU time per Workflow invocation is set to 30 seconds, but can be increased for all invocations associated with a Workflow definition by setting `limits.cpu_ms` in your Wrangler configuration: * [wrangler.jsonc](https://developers.cloudflare.com/workflows/reference/limits/#tab-panel-4843) * [wrangler.toml](https://developers.cloudflare.com/workflows/reference/limits/#tab-panel-4844) { // ...rest of your configuration... "limits": { "cpu_ms": 300000, // 300,000 milliseconds = 5 minutes }, // ...rest of your configuration...} [limits]cpu_ms = 300_000 To learn more about CPU time and limits, [review the Workers documentation](https://developers.cloudflare.com/workers/platform/limits/#cpu-time) . Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Pay cart and send invoice · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/examples/send-invoices/#_top) Copy page Pay cart and send invoice ========================= Send invoice when shopping cart is checked out and paid for In this example, we implement a Workflow for an e-commerce website that is triggered every time a shopping cart is created. Once a Workflow instance is triggered, it starts polling a [D1](https://developers.cloudflare.com/d1) database for the cart ID until it has been checked out. Once the shopping cart is checked out, we proceed to process the payment with an external provider doing a fetch POST. Finally, assuming everything goes well, we try to send an email using [Email Workers](https://developers.cloudflare.com/email-routing/email-workers/) with the invoice to the customer. As you can see, Workflows handles all the different service responses and failures; it will retry D1 until the cart is checked out, retry the payment processor if it fails for some reason, and retry sending the email with the invoice if it can't. The developer doesn't have to care about any of that logic, and the workflow can run for hours, handling all the possible conditions until it is completed. This is a simplified example of processing a shopping cart. We would assume more steps and additional logic in a real-life scenario, but this example gives you a good idea of what you can do with Workflows. import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent,} from "cloudflare:workers";import { EmailMessage } from "cloudflare:email";import { createMimeMessage } from "mimetext"; // We are using Email Routing to send emails out and D1 for our cart databasetype Env = { CART_WORKFLOW: Workflow; SEND_EMAIL: any; DB: any;}; // Workflow parameters: we expect a cartIdtype Params = { cartId: string;}; // Adjust this to your Cloudflare zone using Email Routingconst merchantEmail = "merchant@example.com"; // Uses mimetext npm to generate Emailconst genEmail = (email: string, amount: number) => { const msg = createMimeMessage(); msg.setSender({ name: "Pet shop", addr: merchantEmail }); msg.setRecipient(email); msg.setSubject("You invoice"); msg.addMessage({ contentType: "text/plain", data: `Your invoice for ${amount} has been paid. Your products will be shipped shortly.`, }); return new EmailMessage(merchantEmail, email, msg.asRaw());}; // Workflow logicexport class cartInvoicesWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { await step.sleep("sleep for a while", "10 seconds"); // Retrieve the cart from the D1 database // if the cart hasn't been checked out yet retry every 2 minutes, 10 times, otherwise give up const cart = await step.do( "retrieve cart", { retries: { limit: 10, delay: 2000 * 60, backoff: "constant", }, timeout: "30 seconds", }, async () => { const { results } = await this.env.DB.prepare( `SELECT * FROM cart WHERE id = ?`, ) .bind(event.payload.cartId) .run(); // should return { checkedOut: true, amount: 250 , account: { email: "celsomartinho@gmail.com" }}; if (results[0].checkedOut === false) { throw new Error("cart hasn't been checked out yet"); } return results[0]; }, ); // Proceed to payment, retry 10 times every minute or give up const payment = await step.do( "payment", { retries: { limit: 10, delay: 1000 * 60, backoff: "constant", }, timeout: "30 seconds", }, async () => { let resp = await fetch("https://payment-processor.example.com/", { method: "POST", headers: { "Content-Type": "application/json; charset=utf-8", }, body: JSON.stringify({ amount: cart.amount }), }); if (!resp.ok) { throw new Error("payment has failed"); } return { success: true, amount: cart.amount }; }, ); // Send invoice to the customer, retry 10 times every 5 minutes or give up // Requires that cart.account.email has previously been validated in Email Routing, // See https://developers.cloudflare.com/email-routing/email-workers/ await step.do( "send invoice", { retries: { limit: 10, delay: 5000 * 60, backoff: "constant", }, timeout: "30 seconds", }, async () => { const message = genEmail(cart.account.email, payment.amount); try { await this.env.SEND_EMAIL.send(message); } catch (e) { throw new Error("failed to send invoice"); } }, ); }} // Default page for admin// Remove in production export default { async fetch(req: Request, env: Env): Promise { let url = new URL(req.url); let id = new URL(req.url).searchParams.get("instanceId"); // Get the status of an existing instance, if provided if (id) { let instance = await env.CART_WORKFLOW.get(id); return Response.json({ status: await instance.status(), }); } if (url.pathname.startsWith("/new")) { let instance = await env.CART_WORKFLOW.create({ params: { cartId: "123", }, }); return Response.json({ id: instance.id, details: await instance.status(), }); } return new Response( `new instance or add ?instanceId=...`, { headers: { "content-type": "text/html;charset=UTF-8", }, }, ); },}; Here's a minimal package.json: { "devDependencies": { "wrangler": "^3.83.0" }, "dependencies": { "mimetext": "^3.0.24" }} And finally [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) : * [wrangler.jsonc](https://developers.cloudflare.com/workflows/examples/send-invoices/#tab-panel-4821) * [wrangler.toml](https://developers.cloudflare.com/workflows/examples/send-invoices/#tab-panel-4822) { "$schema": "./node_modules/wrangler/config-schema.json", "name": "cart-invoices", "main": "src/index.ts", "compatibility_date": "2024-10-22", "compatibility_flags": [ "nodejs_compat" ], "workflows": [ { "name": "cart-invoices-workflow", "binding": "CART_WORKFLOW", "class_name": "cartInvoicesWorkflow" } ], "send_email": [ { "name": "SEND_EMAIL" } ]} name = "cart-invoices"main = "src/index.ts"compatibility_date = "2024-10-22"compatibility_flags = ["nodejs_compat" ] [[workflows]]name = "cart-invoices-workflow"binding = "CART_WORKFLOW"class_name = "cartInvoicesWorkflow" [[send_email]]name = "SEND_EMAIL" If you're using TypeScript, run [`wrangler types`](https://developers.cloudflare.com/workers/wrangler/commands/#types) whenever you modify your Wrangler configuration file. This generates types for the `env` object based on your bindings, as well as [runtime types](https://developers.cloudflare.com/workers/languages/typescript/) . Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Rules of Workflows · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#_top) Copy page Rules of Workflows ================== A Workflow contains one or more steps. Each step is a self-contained, individually retriable component of a Workflow. Steps may emit (optional) state that allows a Workflow to persist and continue from that step, even if a Workflow fails due to a network or infrastructure issue. This is a small guidebook on how to build more resilient and correct Workflows. ### Ensure API/Binding calls are idempotent [](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#ensure-apibinding-calls-are-idempotent) Because a step might be retried multiple times, your steps should (ideally) be idempotent. For context, idempotency is a logical property where the operation (in this case a step), can be applied multiple times without changing the result beyond the initial application. As an example, let us assume you have a Workflow that charges your customers, and you really do not want to charge them twice by accident. Before charging them, you should check if they were already charged: * [JavaScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4783) * [TypeScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4784) export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { const customer_id = 123456; // ✅ Good: Non-idempotent API/Binding calls are always done **after** checking if the operation is // still needed. await step.do( `charge ${customer_id} for its monthly subscription`, async () => { // API call to check if customer was already charged const subscription = await fetch( `https://payment.processor/subscriptions/${customer_id}`, ).then((res) => res.json()); // return early if the customer was already charged, this can happen if the destination service dies // in the middle of the request but still commits it, or if the Workflows Engine restarts. if (subscription.charged) { return; } // non-idempotent call, this operation can fail and retry but still commit in the payment // processor - which means that, on retry, it would mischarge the customer again if the above checks // were not in place. return await fetch( `https://payment.processor/subscriptions/${customer_id}`, { method: "POST", body: JSON.stringify({ amount: 10.0 }), }, ); }, ); }} export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { const customer_id = 123456; // ✅ Good: Non-idempotent API/Binding calls are always done **after** checking if the operation is // still needed. await step.do( `charge ${customer_id} for its monthly subscription`, async () => { // API call to check if customer was already charged const subscription = await fetch( `https://payment.processor/subscriptions/${customer_id}`, ).then((res) => res.json()); // return early if the customer was already charged, this can happen if the destination service dies // in the middle of the request but still commits it, or if the Workflows Engine restarts. if (subscription.charged) { return; } // non-idempotent call, this operation can fail and retry but still commit in the payment // processor - which means that, on retry, it would mischarge the customer again if the above checks // were not in place. return await fetch( `https://payment.processor/subscriptions/${customer_id}`, { method: "POST", body: JSON.stringify({ amount: 10.0 }), }, ); }, ); }} ### Make your steps granular [](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#make-your-steps-granular) Steps should be as self-contained as possible. This allows your own logic to be more durable in case of failures in third-party APIs, network errors, and so on. You can also think of it as a transaction, or a unit of work. * ✅ Minimize the number of API/binding calls per step (unless you need multiple calls to prove idempotency). * [JavaScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4781) * [TypeScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4782) export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // ✅ Good: Unrelated API/Binding calls are self-contained, so that in case one of them fails // it can retry them individually. It also has an extra advantage: you can control retry or // timeout policies for each granular step - you might not to want to overload http.cat in // case of it being down. const httpCat = await step.do("get cutest cat from KV", async () => { return await env.KV.get("cutest-http-cat"); }); const image = await step.do("fetch cat image from http.cat", async () => { return await fetch(`https://http.cat/${httpCat}`); }); }} export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // ✅ Good: Unrelated API/Binding calls are self-contained, so that in case one of them fails // it can retry them individually. It also has an extra advantage: you can control retry or // timeout policies for each granular step - you might not to want to overload http.cat in // case of it being down. const httpCat = await step.do("get cutest cat from KV", async () => { return await env.KV.get("cutest-http-cat"); }); const image = await step.do("fetch cat image from http.cat", async () => { return await fetch(`https://http.cat/${httpCat}`); }); }} Otherwise, your entire Workflow might not be as durable as you might think, and you may encounter some undefined behaviour. You can avoid them by following the rules below: * 🔴 Do not encapsulate your entire logic in one single step. * 🔴 Do not call separate services in the same step (unless you need it to prove idempotency). * 🔴 Do not make too many service calls in the same step (unless you need it to prove idempotency). * 🔴 Do not do too much CPU-intensive work inside a single step - sometimes the engine may have to restart, and it will start over from the beginning of that step. * [JavaScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4779) * [TypeScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4780) export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // 🔴 Bad: you are calling two separate services from within the same step. This might cause // some extra calls to the first service in case the second one fails, and in some cases, makes // the step non-idempotent altogether const image = await step.do("get cutest cat from KV", async () => { const httpCat = await env.KV.get("cutest-http-cat"); return fetch(`https://http.cat/${httpCat}`); }); }} export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // 🔴 Bad: you are calling two separate services from within the same step. This might cause // some extra calls to the first service in case the second one fails, and in some cases, makes // the step non-idempotent altogether const image = await step.do("get cutest cat from KV", async () => { const httpCat = await env.KV.get("cutest-http-cat"); return fetch(`https://http.cat/${httpCat}`); }); }} ### Do not rely on state outside of a step [](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#do-not-rely-on-state-outside-of-a-step) Workflows may hibernate and lose all in-memory state. This will happen when engine detects that there is no pending work and can hibernate until it needs to wake-up (because of a sleep, retry, or event). This means that you should not store state outside of a step: * [JavaScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4793) * [TypeScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4794) function getRandomInt(min, max) { const minCeiled = Math.ceil(min); const maxFloored = Math.floor(max); return Math.floor(Math.random() * (maxFloored - minCeiled) + minCeiled); // The maximum is exclusive and the minimum is inclusive} export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // 🔴 Bad: `imageList` will be not persisted across engine's lifetimes. Which means that after hibernation, // `imageList` will be empty again, even though the following two steps have already ran. const imageList = []; await step.do("get first cutest cat from KV", async () => { const httpCat = await env.KV.get("cutest-http-cat-1"); imageList.append(httpCat); }); await step.do("get second cutest cat from KV", async () => { const httpCat = await env.KV.get("cutest-http-cat-2"); imageList.append(httpCat); }); // A long sleep can (and probably will) hibernate the engine which means that the first engine lifetime ends here await step.sleep("💤💤💤💤", "3 hours"); // When this runs, it will be on the second engine lifetime - which means `imageList` will be empty. await step.do( "choose a random cat from the list and download it", async () => { const randomCat = imageList.at(getRandomInt(0, imageList.length)); // this will fail since `randomCat` is undefined because `imageList` is empty return await fetch(`https://http.cat/${randomCat}`); }, ); }} function getRandomInt(min, max) { const minCeiled = Math.ceil(min); const maxFloored = Math.floor(max); return Math.floor(Math.random() * (maxFloored - minCeiled) + minCeiled); // The maximum is exclusive and the minimum is inclusive} export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // 🔴 Bad: `imageList` will be not persisted across engine's lifetimes. Which means that after hibernation, // `imageList` will be empty again, even though the following two steps have already ran. const imageList: string[] = []; await step.do("get first cutest cat from KV", async () => { const httpCat = await env.KV.get("cutest-http-cat-1"); imageList.append(httpCat); }); await step.do("get second cutest cat from KV", async () => { const httpCat = await env.KV.get("cutest-http-cat-2"); imageList.append(httpCat); }); // A long sleep can (and probably will) hibernate the engine which means that the first engine lifetime ends here await step.sleep("💤💤💤💤", "3 hours"); // When this runs, it will be on the second engine lifetime - which means `imageList` will be empty. await step.do( "choose a random cat from the list and download it", async () => { const randomCat = imageList.at(getRandomInt(0, imageList.length)); // this will fail since `randomCat` is undefined because `imageList` is empty return await fetch(`https://http.cat/${randomCat}`); }, ); }} Instead, you should build top-level state exclusively comprised of `step.do` returns: * [JavaScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4791) * [TypeScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4792) function getRandomInt(min, max) { const minCeiled = Math.ceil(min); const maxFloored = Math.floor(max); return Math.floor(Math.random() * (maxFloored - minCeiled) + minCeiled); // The maximum is exclusive and the minimum is inclusive} export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // ✅ Good: imageList state is exclusively comprised of step returns - this means that in the event of // multiple engine lifetimes, imageList will be built accordingly const imageList = await Promise.all([ step.do("get first cutest cat from KV", async () => { return await env.KV.get("cutest-http-cat-1"); }),\ step.do("get second cutest cat from KV", async () => { return await env.KV.get("cutest-http-cat-2"); }), ]); // A long sleep can (and probably will) hibernate the engine which means that the first engine lifetime ends here await step.sleep("💤💤💤💤", "3 hours"); // When this runs, it will be on the second engine lifetime - but this time, imageList will contain // the two most cutest cats await step.do( "choose a random cat from the list and download it", async () => { const randomCat = imageList.at(getRandomInt(0, imageList.length)); // this will eventually succeed since `randomCat` is defined return await fetch(`https://http.cat/${randomCat}`); }, ); }} function getRandomInt(min, max) { const minCeiled = Math.ceil(min); const maxFloored = Math.floor(max); return Math.floor(Math.random() * (maxFloored - minCeiled) + minCeiled); // The maximum is exclusive and the minimum is inclusive} export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // ✅ Good: imageList state is exclusively comprised of step returns - this means that in the event of // multiple engine lifetimes, imageList will be built accordingly const imageList: string[] = await Promise.all([ step.do("get first cutest cat from KV", async () => { return await env.KV.get("cutest-http-cat-1"); }),\ step.do("get second cutest cat from KV", async () => { return await env.KV.get("cutest-http-cat-2"); }), ]); // A long sleep can (and probably will) hibernate the engine which means that the first engine lifetime ends here await step.sleep("💤💤💤💤", "3 hours"); // When this runs, it will be on the second engine lifetime - but this time, imageList will contain // the two most cutest cats await step.do( "choose a random cat from the list and download it", async () => { const randomCat = imageList.at(getRandomInt(0, imageList.length)); // this will eventually succeed since `randomCat` is defined return await fetch(`https://http.cat/${randomCat}`); }, ); }} ### Avoid doing side effects outside of a `step.do` [](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#avoid-doing-side-effects-outside-of-a-stepdo) It is not recommended to write code with any side effects outside of steps, unless you would like it to be repeated, because the Workflow engine may restart while an instance is running. If the engine restarts, the step logic will be preserved, but logic outside of the steps may be duplicated. For example, a `console.log()` outside of workflow steps may cause the logs to print twice when the engine restarts. However, logic involving non-serializable resources, like a database connection, should be executed outside of steps. Operations ouside of a `step.do` might be repeated more than once, due to the nature of the Workflows' instance lifecycle. * [JavaScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4805) * [TypeScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4806) export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // 🔴 Bad: creating instances outside of steps // This might get called more than once creating more instances than expected const myNewInstance = await this.env.ANOTHER_WORKFLOW.create(); // 🔴 Bad: using non-deterministic functions outside of steps // this will produce different results if the instance has to restart, different runs of the same instance // might go through different paths const myRandom = Math.random(); if (myRandom > 0) { // do some stuff } // ⚠️ Warning: This log may happen many times console.log("This might be logged more than once"); await step.do("do some stuff and have a log for when it runs", async () => { // do some stuff // this log will only appear once console.log("successfully did stuff"); }); // ✅ Good: wrap non-deterministic function in a step // after running successfully will not run again const myRandom = await step.do("create a random number", async () => { return Math.random(); }); // ✅ Good: calls that have no side effects can be done outside of steps const db = createDBConnection(this.env.DB_URL, this.env.DB_TOKEN); // ✅ Good: run funtions with side effects inside of a step // after running successfully will not run again const myNewInstance = await step.do( "good step that returns state", async () => { const myNewInstance = await this.env.ANOTHER_WORKFLOW.create(); return myNewInstance; }, ); }} export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // 🔴 Bad: creating instances outside of steps // This might get called more than once creating more instances than expected const myNewInstance = await this.env.ANOTHER_WORKFLOW.create(); // 🔴 Bad: using non-deterministic functions outside of steps // this will produce different results if the instance has to restart, different runs of the same instance // might go through different paths const myRandom = Math.random(); if (myRandom > 0) { // do some stuff } // ⚠️ Warning: This log may happen many times console.log("This might be logged more than once"); await step.do("do some stuff and have a log for when it runs", async () => { // do some stuff // this log will only appear once console.log("successfully did stuff"); }); // ✅ Good: wrap non-deterministic function in a step // after running successfully will not run again const myRandom = await step.do("create a random number", async () => { return Math.random(); }); // ✅ Good: calls that have no side effects can be done outside of steps const db = createDBConnection(this.env.DB_URL, this.env.DB_TOKEN); // ✅ Good: run funtions with side effects inside of a step // after running successfully will not run again const myNewInstance = await step.do( "good step that returns state", async () => { const myNewInstance = await this.env.ANOTHER_WORKFLOW.create(); return myNewInstance; }, ); }} ### Do not mutate your incoming events [](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#do-not-mutate-your-incoming-events) The `event` passed to your Workflow's `run` method is immutable: changes you make to the event are not persisted across steps and/or Workflow restarts. * [JavaScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4789) * [TypeScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4790) export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // 🔴 Bad: Mutating the event // This will not be persisted across steps and `event.payload` will // take on its original value. await step.do("bad step that mutates the incoming event", async () => { let userData = await env.KV.get(event.payload.user); event.payload = userData; }); // ✅ Good: persist data by returning it as state from your step // Use that state in subsequent steps let userData = await step.do("good step that returns state", async () => { return await env.KV.get(event.payload.user); }); let someOtherData = await step.do( "following step that uses that state", async () => { // Access to userData here // Will always be the same if this step is retried }, ); }} interface MyEvent { user: string; data: string;} export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // 🔴 Bad: Mutating the event // This will not be persisted across steps and `event.payload` will // take on its original value. await step.do("bad step that mutates the incoming event", async () => { let userData = await env.KV.get(event.payload.user); event.payload = userData; }); // ✅ Good: persist data by returning it as state from your step // Use that state in subsequent steps let userData = await step.do("good step that returns state", async () => { return await env.KV.get(event.payload.user); }); let someOtherData = await step.do( "following step that uses that state", async () => { // Access to userData here // Will always be the same if this step is retried }, ); }} ### Name steps deterministically [](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#name-steps-deterministically) Steps should be named deterministically (that is, not using the current date/time, randomness, etc). This ensures that their state is cached, and prevents the step from being rerun unnecessarily. Step names act as the "cache key" in your Workflow. * [JavaScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4797) * [TypeScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4798) export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // 🔴 Bad: Naming the step non-deterministically prevents it from being cached // This will cause the step to be re-run if subsequent steps fail. await step.do(`step #1 running at: ${Date.now()}`, async () => { let userData = await env.KV.get(event.payload.user); // Do not mutate event.payload event.payload = userData; }); // ✅ Good: give steps a deterministic name. // Return dynamic values in your state, or log them instead. let state = await step.do("fetch user data from KV", async () => { let userData = await env.KV.get(event.payload.user); console.log(`fetched at ${Date.now}`); return userData; }); // ✅ Good: steps that are dynamically named are constructed in a deterministic way. // In this case, `catList` is a step output, which is stable, and `catList` is // traversed in a deterministic fashion (no shuffles or random accesses) so, // it's fine to dynamically name steps (e.g: create a step per list entry). let catList = await step.do("get cat list from KV", async () => { return await env.KV.get("cat-list"); }); for (const cat of catList) { await step.do(`get cat: ${cat}`, async () => { return await env.KV.get(cat); }); } }} export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // 🔴 Bad: Naming the step non-deterministically prevents it from being cached // This will cause the step to be re-run if subsequent steps fail. await step.do(`step #1 running at: ${Date.now()}`, async () => { let userData = await env.KV.get(event.payload.user); // Do not mutate event.payload event.payload = userData; }); // ✅ Good: give steps a deterministic name. // Return dynamic values in your state, or log them instead. let state = await step.do("fetch user data from KV", async () => { let userData = await env.KV.get(event.payload.user); console.log(`fetched at ${Date.now}`); return userData; }); // ✅ Good: steps that are dynamically named are constructed in a deterministic way. // In this case, `catList` is a step output, which is stable, and `catList` is // traversed in a deterministic fashion (no shuffles or random accesses) so, // it's fine to dynamically name steps (e.g: create a step per list entry). let catList = await step.do("get cat list from KV", async () => { return await env.KV.get("cat-list"); }); for (const cat of catList) { await step.do(`get cat: ${cat}`, async () => { return await env.KV.get(cat); }); } }} ### Take care with `Promise.race()` and `Promise.any()` [](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#take-care-with-promiserace-and-promiseany) Workflows allows the usage steps within the `Promise.race()` or `Promise.any()` methods as a way to achieve concurrent steps execution. However, some considerations must be taken. Due to the nature of Workflows' instance lifecycle, and given that a step inside a Promise will run until it finishes, the step that is returned during the first passage may not be the actual cached step, as [steps are cached by their names](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#name-steps-deterministically) . * [JavaScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4785) * [TypeScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4786) // helper sleep methodconst sleep = (ms) => new Promise((r) => setTimeout(r, ms)); export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // 🔴 Bad: The `Promise.race` is not surrounded by a `step.do`, which may cause undeterministic caching behavior. const race_return = await Promise.race([ step.do("Promise first race", async () => { await sleep(1000); return "first"; }), step.do("Promise second race", async () => { return "second"; }), ]); await step.sleep("Sleep step", "2 hours"); return await step.do("Another step", async () => { // This step will return `first`, even though the `Promise.race` first returned `second`. return race_return; }); }} // helper sleep methodconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // 🔴 Bad: The `Promise.race` is not surrounded by a `step.do`, which may cause undeterministic caching behavior. const race_return = await Promise.race([ step.do("Promise first race", async () => { await sleep(1000); return "first"; }), step.do("Promise second race", async () => { return "second"; }), ]); await step.sleep("Sleep step", "2 hours"); return await step.do("Another step", async () => { // This step will return `first`, even though the `Promise.race` first returned `second`. return race_return; }); }} To ensure consistency, we suggest to surround the `Promise.race()` or `Promise.any()` within a `step.do()`, as this will ensure caching consistency across multiple passages. * [JavaScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4795) * [TypeScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4796) // helper sleep methodconst sleep = (ms) => new Promise((r) => setTimeout(r, ms)); export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // ✅ Good: The `Promise.race` is surrounded by a `step.do`, ensuring deterministic caching behavior. const race_return = await step.do("Promise step", async () => { return await Promise.race([ step.do("Promise first race", async () => { await sleep(1000); return "first"; }), step.do("Promise second race", async () => { return "second"; }), ]); }); await step.sleep("Sleep step", "2 hours"); return await step.do("Another step", async () => { // This step will return `second` because the `Promise.race` was surround by the `step.do` method. return race_return; }); }} // helper sleep methodconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // ✅ Good: The `Promise.race` is surrounded by a `step.do`, ensuring deterministic caching behavior. const race_return = await step.do("Promise step", async () => { return await Promise.race([ step.do("Promise first race", async () => { await sleep(1000); return "first"; }), step.do("Promise second race", async () => { return "second"; }), ]); }); await step.sleep("Sleep step", "2 hours"); return await step.do("Another step", async () => { // This step will return `second` because the `Promise.race` was surround by the `step.do` method. return race_return; }); }} ### Instance IDs are unique [](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#instance-ids-are-unique) Workflow [instance IDs](https://developers.cloudflare.com/workflows/build/workers-api/#workflowinstance) are unique per Workflow. The ID is the unique identifier that associates logs, metrics, state and status of a run to a specific instance, even after completion. Allowing ID re-use would make it hard to understand if a Workflow instance ID referred to an instance that run yesterday, last week or today. It would also present a problem if you wanted to run multiple different Workflow instances with different [input parameters](https://developers.cloudflare.com/workflows/build/events-and-parameters/) for the same user ID, as you would immediately need to determine a new ID mapping. If you need to associate multiple instances with a specific user, merchant or other "customer" ID in your system, consider using a composite ID or using randomly generated IDs and storing the mapping in a database like [D1](https://developers.cloudflare.com/d1/) . * [JavaScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4799) * [TypeScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4800) // This is in the same file as your Workflow definitionexport default { async fetch(req, env) { // 🔴 Bad: Use an ID that isn't unique across future Workflow invocations let userId = getUserId(req); // Returns the userId let badInstance = await env.MY_WORKFLOW.create({ id: userId, params: payload, }); // ✅ Good: use an ID that is unique // e.g. a transaction ID, order ID, or task ID are good options let instanceId = getTransactionId(); // e.g. assuming transaction IDs are unique // or: compose a composite ID and store it in your database // so that you can track all instances associated with a specific user or merchant. instanceId = `${getUserId(req)}-${crypto.randomUUID().slice(0, 6)}`; let { result } = await addNewInstanceToDB(userId, instanceId); let goodInstance = await env.MY_WORKFLOW.create({ id: instanceId, params: payload, }); return Response.json({ id: goodInstance.id, details: await goodInstance.status(), }); },}; // This is in the same file as your Workflow definitionexport default { async fetch(req: Request, env: Env): Promise { // 🔴 Bad: Use an ID that isn't unique across future Workflow invocations let userId = getUserId(req); // Returns the userId let badInstance = await env.MY_WORKFLOW.create({ id: userId, params: payload, }); // ✅ Good: use an ID that is unique // e.g. a transaction ID, order ID, or task ID are good options let instanceId = getTransactionId(); // e.g. assuming transaction IDs are unique // or: compose a composite ID and store it in your database // so that you can track all instances associated with a specific user or merchant. instanceId = `${getUserId(req)}-${crypto.randomUUID().slice(0, 6)}`; let { result } = await addNewInstanceToDB(userId, instanceId); let goodInstance = await env.MY_WORKFLOW.create({ id: instanceId, params: payload, }); return Response.json({ id: goodInstance.id, details: await goodInstance.status(), }); },}; ### `await` your steps [](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#await-your-steps) When calling `step.do` or `step.sleep`, use `await` to avoid introducing bugs and race conditions into your Workflow code. If you don't call `await step.do` or `await step.sleep`, you create a dangling Promise. This occurs when a Promise is created but not properly `await`ed, leading to potential bugs and race conditions. This happens when you do not use the `await` keyword or fail to chain `.then()` methods to handle the result of a Promise. For example, calling `fetch(GITHUB_URL)` without awaiting its response will cause subsequent code to execute immediately, regardless of whether the fetch completed. This can cause issues like premature logging, exceptions being swallowed (and not terminating the Workflow), and lost return values (state). * [JavaScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4787) * [TypeScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4788) export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // 🔴 Bad: The step isn't await'ed, and any state or errors is swallowed before it returns. const issues = step.do(`fetch issues from GitHub`, async () => { // The step will return before this call is done let issues = await getIssues(event.payload.repoName); return issues; }); // ✅ Good: The step is correctly await'ed. const issues = await step.do(`fetch issues from GitHub`, async () => { let issues = await getIssues(event.payload.repoName); return issues; }); // Rest of your Workflow goes here! }} export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // 🔴 Bad: The step isn't await'ed, and any state or errors is swallowed before it returns. const issues = step.do(`fetch issues from GitHub`, async () => { // The step will return before this call is done let issues = await getIssues(event.payload.repoName); return issues; }); // ✅ Good: The step is correctly await'ed. const issues = await step.do(`fetch issues from GitHub`, async () => { let issues = await getIssues(event.payload.repoName); return issues; }); // Rest of your Workflow goes here! }} ### Use conditional logic carefully [](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#use-conditional-logic-carefully) You can use `if` statements, loops, and other control flow outside of steps. However, conditions must be based on **deterministic values** — either values from `event.payload` or return values from previous steps. Non-deterministic conditions (such as `Math.random()` or `Date.now()`) outside of steps can cause unexpected behavior if the Workflow restarts. * [JavaScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4807) * [TypeScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4808) export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { const config = await step.do("fetch config", async () => { return await this.env.KV.get("feature-flags", { type: "json" }); }); // ✅ Good: Condition based on step output (deterministic) if (config.enableEmailNotifications) { await step.do("send email", async () => { // Send email logic }); } // ✅ Good: Condition based on event payload (deterministic) if (event.payload.userType === "premium") { await step.do("premium processing", async () => { // Premium-only logic }); } // 🔴 Bad: Condition based on non-deterministic value outside a step // This could behave differently if the Workflow restarts if (Math.random() > 0.5) { await step.do("maybe do something", async () => {}); } // ✅ Good: Wrap non-deterministic values in a step const shouldProcess = await step.do("decide randomly", async () => { return Math.random() > 0.5; }); if (shouldProcess) { await step.do("conditionally do something", async () => {}); } }} export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { const config = await step.do("fetch config", async () => { return await this.env.KV.get("feature-flags", { type: "json" }); }); // ✅ Good: Condition based on step output (deterministic) if (config.enableEmailNotifications) { await step.do("send email", async () => { // Send email logic }); } // ✅ Good: Condition based on event payload (deterministic) if (event.payload.userType === "premium") { await step.do("premium processing", async () => { // Premium-only logic }); } // 🔴 Bad: Condition based on non-deterministic value outside a step // This could behave differently if the Workflow restarts if (Math.random() > 0.5) { await step.do("maybe do something", async () => {}); } // ✅ Good: Wrap non-deterministic values in a step const shouldProcess = await step.do("decide randomly", async () => { return Math.random() > 0.5; }); if (shouldProcess) { await step.do("conditionally do something", async () => {}); } }} ### Batch multiple Workflow invocations [](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#batch-multiple-workflow-invocations) When creating multiple Workflow instances, use the [`createBatch`](https://developers.cloudflare.com/workflows/build/workers-api/#createBatch) method to batch the invocations together. This allows you to create multiple Workflow instances in a single request, which will reduce the number of requests made to the Workflows API. However, each individual instance in the batch will still count towards the [creation rate limit](https://developers.cloudflare.com/workflows/reference/limits/) . * [JavaScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4801) * [TypeScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4802) export default { async fetch(req, env) { let instances = [ { id: "user1", params: { name: "John" } }, { id: "user2", params: { name: "Jane" } }, { id: "user3", params: { name: "Alice" } }, { id: "user4", params: { name: "Bob" } }, ]; // 🔴 Bad: Create them one by one, which is more likely to hit creation rate limits. for (let instance of instances) { await env.MY_WORKFLOW.create({ id: instance.id, params: instance.params, }); } // ✅ Good: Batch calls together // This improves throughput. let instances = await env.MY_WORKFLOW.createBatch(instances); return Response.json({ instances }); },}; export default { async fetch(req: Request, env: Env): Promise { let instances = [ { id: "user1", params: { name: "John" } }, { id: "user2", params: { name: "Jane" } }, { id: "user3", params: { name: "Alice" } }, { id: "user4", params: { name: "Bob" } }, ]; // 🔴 Bad: Create them one by one, which is more likely to hit creation rate limits. for (let instance of instances) { await env.MY_WORKFLOW.create({ id: instance.id, params: instance.params, }); } // ✅ Good: Batch calls together // This improves throughput. let instances = await env.MY_WORKFLOW.createBatch(instances); return Response.json({ instances }); },}; ### Limit timeouts to 30 minutes or less [](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#limit-timeouts-to-30-minutes-or-less) When setting a [WorkflowStep timeout](https://developers.cloudflare.com/workflows/build/workers-api/#workflowstep) , ensure that its duration is 30 minutes or less. If your use case requires a timeout greater than 30 minutes, consider using `step.waitForEvent()` instead. ### Keep step return values under 1 MiB [](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#keep-step-return-values-under-1-mib) Each step can persist up to 1 MiB (2^20 bytes) of state. If your step returns data exceeding this limit, the step will fail. This is a common issue when fetching large API responses or processing large files. To work around this limit, store large data externally (for example, in R2 or KV) and return only a reference: * [JavaScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4803) * [TypeScript](https://developers.cloudflare.com/workflows/build/rules-of-workflows/#tab-panel-4804) export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // 🔴 Bad: Returning a large response that may exceed 1 MiB const largeData = await step.do("fetch large dataset", async () => { const response = await fetch("https://api.example.com/large-dataset"); return await response.json(); // Could exceed 1 MiB }); // ✅ Good: Store large data externally and return a reference const dataRef = await step.do("fetch and store large dataset", async () => { const response = await fetch("https://api.example.com/large-dataset"); const data = await response.json(); // Store in R2 and return a reference await this.env.MY_BUCKET.put("dataset-123", JSON.stringify(data)); return { key: "dataset-123" }; }); // Retrieve the data in a later step when needed const data = await step.do("process dataset", async () => { const stored = await this.env.MY_BUCKET.get(dataRef.key); return processData(await stored.json()); }); }} export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // 🔴 Bad: Returning a large response that may exceed 1 MiB const largeData = await step.do("fetch large dataset", async () => { const response = await fetch("https://api.example.com/large-dataset"); return await response.json(); // Could exceed 1 MiB }); // ✅ Good: Store large data externally and return a reference const dataRef = await step.do("fetch and store large dataset", async () => { const response = await fetch("https://api.example.com/large-dataset"); const data = await response.json(); // Store in R2 and return a reference await this.env.MY_BUCKET.put("dataset-123", JSON.stringify(data)); return { key: "dataset-123" }; }); // Retrieve the data in a later step when needed const data = await step.do("process dataset", async () => { const stored = await this.env.MY_BUCKET.get(dataRef.key); return processData(await stored.json()); }); }} Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Build your first Workflow · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/get-started/guide/#_top) Copy page 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](https://developers.cloudflare.com/r2/) , automate generation of [Workers AI](https://developers.cloudflare.com/workers-ai/) embeddings into a [Vectorize](https://developers.cloudflare.com/vectorize/) vector database, or to trigger user lifecycle emails using [Email Service](https://developers.cloudflare.com/email-routing/) . In this guide, you will create and deploy a Workflow that fetches data, pauses, and processes results. Quick start ----------- [](https://developers.cloudflare.com/workflows/get-started/guide/#quick-start) If you want to skip the steps and pull down the complete Workflow we are building in this guide, run: 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 ------------- [](https://developers.cloudflare.com/workflows/get-started/guide/#prerequisites) 1. Sign up for a [Cloudflare account ↗](https://dash.cloudflare.com/sign-up/workers-and-pages) . 2. Install [`Node.js` ↗](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) . Node.js version manager Use a Node version manager like [Volta ↗](https://volta.sh/) or [nvm ↗](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node.js versions. [Wrangler](https://developers.cloudflare.com/workers/wrangler/install-and-update/) , discussed later in this guide, requires a Node version of `16.17.0` or later. 1\. Create a new Worker project ------------------------------- [](https://developers.cloudflare.com/workflows/get-started/guide/#1-create-a-new-worker-project) 1. Open a terminal and run the `create cloudflare` (C3) CLI tool to create your Worker project: * [npm](https://developers.cloudflare.com/workflows/get-started/guide/#tab-panel-4834) * [yarn](https://developers.cloudflare.com/workflows/get-started/guide/#tab-panel-4835) * [pnpm](https://developers.cloudflare.com/workflows/get-started/guide/#tab-panel-4836) npm create cloudflare@latest -- my-workflow yarn create cloudflare my-workflow pnpm 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: cd my-workflow What files did C3 create? In your project directory, C3 will have generated the following: * `wrangler.jsonc`: Your [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/#sample-wrangler-configuration) . * `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 ----------------------- [](https://developers.cloudflare.com/workflows/get-started/guide/#2-write-your-workflow) 1. Create a new file `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 { async run(event: WorkflowEvent, 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(); }); 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](https://developers.cloudflare.com/workflows/build/events-and-parameters/) so that events that trigger our Workflow are typed. The [`step`](https://developers.cloudflare.com/workflows/build/workers-api/#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](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/) to `step.do()` to customize how failures are handled. See the [full step API](https://developers.cloudflare.com/workflows/build/workers-api/#step) 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](https://developers.cloudflare.com/workflows/build/rules-of-workflows/) . 3\. Configure your Workflow --------------------------- [](https://developers.cloudflare.com/workflows/get-started/guide/#3-configure-your-workflow) 1. Open `wrangler.jsonc`, which is your [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) for your Workers project and your Workflow, and add the `workflows` configuration: * [wrangler.jsonc](https://developers.cloudflare.com/workflows/get-started/guide/#tab-panel-4837) * [wrangler.toml](https://developers.cloudflare.com/workflows/get-started/guide/#tab-panel-4838) { "$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" } ]} "$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](https://developers.cloudflare.com/workers/runtime-apis/bindings/) (such as [KV](https://developers.cloudflare.com/kv/) , [R2](https://developers.cloudflare.com/r2/) , or [D1](https://developers.cloudflare.com/d1/) ) via `this.env` within your Workflow. For more information on bindings within Workers, refer to [Bindings (env)](https://developers.cloudflare.com/workers/runtime-apis/bindings/) . 2. Now, generate types for your bindings: 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 ------------------ [](https://developers.cloudflare.com/workflows/get-started/guide/#4-write-your-api) Now, you'll need a place to call your Workflow. 1. Replace `src/index.ts` with a [fetch handler](https://developers.cloudflare.com/workers/runtime-apis/handlers/fetch/) to start and check Workflow instances: export { MyWorkflow } from "./workflow"; export default { async fetch(request: Request, env: Env): Promise { 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; 5\. Develop locally ------------------- [](https://developers.cloudflare.com/workflows/get-started/guide/#5-develop-locally) 1. Start a local development server: npx wrangler dev 2. To start a Workflow instance, open a new terminal window and run: curl http://localhost:8787 An `instanceId` will be automatically generated: { "instanceId": "abc-123-def" } 3. Check the status using the returned `instanceId`: 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 ------------------------ [](https://developers.cloudflare.com/workflows/get-started/guide/#6-deploy-your-workflow) 1. Deploy your Workflow: npx wrangler deploy Test in production using the same curl commands against your deployed URL. You can also [trigger a workflow instance](https://developers.cloudflare.com/workflows/build/trigger-workflows/) in production via Workers, Wrangler, or the Cloudflare dashboard. Once deployed, you can also inspect Workflow instances with the CLI: 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 ---------- [](https://developers.cloudflare.com/workflows/get-started/guide/#learn-more) [Events and parameters](https://developers.cloudflare.com/workflows/build/events-and-parameters/) Pass data to Workflows and pause for external events with waitForEvent. [Sleeping and retrying](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/) Configure retry behavior and sleep patterns. [Workers API](https://developers.cloudflare.com/workflows/build/workers-api/) Explore the full Workflows API for programmatic control. [Rules of Workflows](https://developers.cloudflare.com/workflows/build/rules-of-workflows/) Understand the programming model and best practices. Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Export and save D1 database · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/examples/backup-d1/#_top) Copy page Export and save D1 database =========================== Export a D1 database into R2 storage with Workflows In this example, we implement a Workflow periodically triggered by a [Cron Trigger](https://developers.cloudflare.com/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](https://developers.cloudflare.com/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. As shown in this example, Workflows handles both the responses and failures, thereby removing the burden from the developer. Workflows retries the following steps: * API calls until it gets a successful response * Fetching the backup from the URL provided * Saving the file to [R2](https://developers.cloudflare.com/r2) The Workflow can run until the backup file is ready, handling all of the possible conditions until it is completed. This example provides simplified steps for backing up a [D1](https://developers.cloudflare.com/d1) database to help you understand the possibilities of Workflows. In every step, it uses the [default](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying) sleeping and retrying configuration. In a real-world scenario, more steps and additional logic would likely be needed. import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent,} from "cloudflare:workers"; // We are using R2 to store the D1 backuptype Env = { BACKUP_WORKFLOW: Workflow; D1_REST_API_TOKEN: string; BACKUP_BUCKET: R2Bucket;}; // Workflow parameters: we expect accountId and databaseIdtype Params = { accountId: string; databaseId: string;}; // Workflow logicexport class backupWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { const { accountId, databaseId } = event.payload; const url = `https://api.cloudflare.com/client/v4/accounts/${accountId}/d1/database/${databaseId}/export`; const method = "POST"; const headers = new Headers(); headers.append("Content-Type", "application/json"); headers.append("Authorization", `Bearer ${this.env.D1_REST_API_TOKEN}`); const bookmark = await step.do( `Starting backup for ${databaseId}`, async () => { const payload = { output_format: "polling" }; const res = await fetch(url, { method, headers, body: JSON.stringify(payload), }); const { result } = (await res.json()) as any; // If we don't get `at_bookmark` we throw to retry the step if (!result?.at_bookmark) throw new Error("Missing `at_bookmark`"); return result.at_bookmark; }, ); await step.do("Check backup status and store it on R2", async () => { const payload = { current_bookmark: bookmark }; const res = await fetch(url, { method, headers, body: JSON.stringify(payload), }); const { result } = (await res.json()) as any; // The endpoint sends `signed_url` when the backup is ready to download. // If we don't get `signed_url` we throw to retry the step. if (!result?.signed_url) throw new Error("Missing `signed_url`"); const dumpResponse = await fetch(result.signed_url); if (!dumpResponse.ok) throw new Error("Failed to fetch dump file"); // Finally, stream the file directly to R2 await this.env.BACKUP_BUCKET.put(result.filename, dumpResponse.body); }); }} 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}`); },}; Here is a minimal package.json: { "devDependencies": { "wrangler": "^3.99.0" }} Here is a [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) : * [wrangler.jsonc](https://developers.cloudflare.com/workflows/examples/backup-d1/#tab-panel-4823) * [wrangler.toml](https://developers.cloudflare.com/workflows/examples/backup-d1/#tab-panel-4824) { "$schema": "./node_modules/wrangler/config-schema.json", "name": "backup-d1", "main": "src/index.ts", "compatibility_date": "2024-12-27", "compatibility_flags": [ "nodejs_compat" ], "workflows": [ { "name": "backup-workflow", "binding": "BACKUP_WORKFLOW", "class_name": "backupWorkflow" } ], "r2_buckets": [ { "binding": "BACKUP_BUCKET", "bucket_name": "d1-backups" } ], "triggers": { "crons": [ "0 0 * * *" ] }} name = "backup-d1"main = "src/index.ts"compatibility_date = "2024-12-27"compatibility_flags = [ "nodejs_compat" ] [[workflows]]name = "backup-workflow"binding = "BACKUP_WORKFLOW"class_name = "backupWorkflow" [[r2_buckets]]binding = "BACKUP_BUCKET"bucket_name = "d1-backups" [triggers]crons = [ "0 0 * * *" ] Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Build a Durable AI Agent · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/get-started/durable-agents/#_top) Copy page Build a Durable AI Agent ======================== In this guide, you will build an AI agent that researches GitHub repositories. Give it a task like "Compare open-source LLM projects" and it will: 1. Search GitHub for relevant repositories 2. Fetch details about each one (stars, forks, activity) 3. Analyze and compare them 4. Return a recommendation Each LLM call and tool call becomes a step — a self-contained, individually retriable unit of work. If any step fails, Workflows retries it automatically. If the entire Workflow crashes mid-task, it resumes from the last successful step. | Challenge | Solution with Workflows | | --- | --- | | Long-running agent loops | Durable execution that survives any interruption | | Unreliable LLM and API calls | Automatic retry with independent checkpoints | | Waiting for human approval | `waitForEvent()` pauses for hours or days | | Polling for job completion | `step.sleep()` between checks without consuming resources | This guide uses the Anthropic SDK, but the same patterns apply to any LLM SDK (OpenAI, Google AI, Mistral, etc.). Quick start ----------- [](https://developers.cloudflare.com/workflows/get-started/durable-agents/#quick-start) If you want to skip the steps and pull down the complete agent, utilizing [AI Gateway](https://developers.cloudflare.com/ai-gateway) , run the following command: npm create cloudflare@latest -- --template cloudflare/docs-examples/workflows/durableAgent Use this option if you are familiar with Cloudflare Workflows or want to explore the code first. Follow the steps below to learn how to build a durable AI agent from scratch. Prerequisites ------------- [](https://developers.cloudflare.com/workflows/get-started/durable-agents/#prerequisites) 1. Sign up for a [Cloudflare account ↗](https://dash.cloudflare.com/sign-up/workers-and-pages) . 2. Install [`Node.js` ↗](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) . Node.js version manager Use a Node version manager like [Volta ↗](https://volta.sh/) or [nvm ↗](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node.js versions. [Wrangler](https://developers.cloudflare.com/workers/wrangler/install-and-update/) , discussed later in this guide, requires a Node version of `16.17.0` or later. You will also need an [Anthropic API key ↗](https://platform.claude.com/settings/keys) for LLM calls. New accounts include free credits. 1\. Create a new Worker project ------------------------------- [](https://developers.cloudflare.com/workflows/get-started/durable-agents/#1-create-a-new-worker-project) 1. Create a new Worker project by running the following command: * [npm](https://developers.cloudflare.com/workflows/get-started/durable-agents/#tab-panel-4829) * [yarn](https://developers.cloudflare.com/workflows/get-started/durable-agents/#tab-panel-4830) * [pnpm](https://developers.cloudflare.com/workflows/get-started/durable-agents/#tab-panel-4831) npm create cloudflare@latest -- durable-ai-agent yarn create cloudflare durable-ai-agent pnpm create cloudflare@latest durable-ai-agent 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 project: cd durable-ai-agent 3. Install dependencies: npm install @anthropic-ai/sdk 2\. Define your tools --------------------- [](https://developers.cloudflare.com/workflows/get-started/durable-agents/#2-define-your-tools) Tools are functions the LLM can call to interact with external systems. You define the schema (what inputs the tool accepts) and the implementation (what it does). The LLM decides when to use each tool based on the task. 1. Create `src/tools.ts` with two complementary tools: export interface SearchReposInput { query: string; limit?: number;} export interface GetRepoInput { owner: string; repo: string;} interface GitHubSearchResponse { items: Array<{ full_name: string; stargazers_count: number }>;} interface GitHubRepoResponse { full_name: string; description: string; stargazers_count: number; forks_count: number; open_issues_count: number; language: string; license: { name: string } | null; updated_at: string;} export const searchReposTool = { name: "search_repos" as const, description: "Search GitHub repositories by keyword. Returns top results. Use get_repo for details.", input_schema: { type: "object" as const, properties: { query: { type: "string", description: "Search query (e.g., 'typescript orm')", }, limit: { type: "number", description: "Max results (default 5)" }, }, required: ["query"], }, run: async (input: SearchReposInput): Promise => { const response = await fetch( `https://api.github.com/search/repositories?q=${encodeURIComponent(input.query)}&sort=stars&per_page=${input.limit ?? 5}`, { headers: { Accept: "application/vnd.github+json", "User-Agent": "DurableAgent/1.0", }, }, ); if (!response.ok) return `Search failed: ${response.status}`; const data = await response.json(); return JSON.stringify( data.items.map((r) => ({ name: r.full_name, stars: r.stargazers_count })), ); },}; export const getRepoTool = { name: "get_repo" as const, description: "Get detailed info about a GitHub repository including stars, forks, and description.", input_schema: { type: "object" as const, properties: { owner: { type: "string", description: "Repository owner (e.g., 'cloudflare')", }, repo: { type: "string", description: "Repository name (e.g., 'workers-sdk')", }, }, required: ["owner", "repo"], }, run: async (input: GetRepoInput): Promise => { const response = await fetch( `https://api.github.com/repos/${input.owner}/${input.repo}`, { headers: { Accept: "application/vnd.github+json", "User-Agent": "DurableAgent/1.0", }, }, ); if (!response.ok) return `Repo not found: ${input.owner}/${input.repo}`; const data = await response.json(); return JSON.stringify({ name: data.full_name, description: data.description, stars: data.stargazers_count, forks: data.forks_count, issues: data.open_issues_count, language: data.language, license: data.license?.name ?? "None", updated: data.updated_at, }); },}; export const tools = [searchReposTool, getRepoTool]; These tools complement each other: `search_repos` finds repositories, and `get_repo` fetches details about specific ones. 3\. Write your agent Workflow ----------------------------- [](https://developers.cloudflare.com/workflows/get-started/durable-agents/#3-write-your-agent-workflow) A Workflow extends `WorkflowEntrypoint` and implements a `run` method. * The [`step`](https://developers.cloudflare.com/workflows/build/workers-api/#step) object provides methods to define durable steps. * `step.do(name, callback)` executes code and persists the result. If the Workflow is interrupted, it resumes from the last successful step. For a gentler introduction, refer to [Build your first Workflow](https://developers.cloudflare.com/workflows/get-started/guide/) . The agent loop sends messages to the LLM, executes any tool calls, and repeats until the task is complete. Each LLM call and tool execution is wrapped in `step.do()` for durability. 1. Create `src/workflow.ts`: import { WorkflowEntrypoint, WorkflowStep } from "cloudflare:workers";import type { WorkflowEvent } from "cloudflare:workers";import Anthropic from "@anthropic-ai/sdk";import { tools, searchReposTool, getRepoTool, type SearchReposInput, type GetRepoInput,} from "./tools"; type Params = { task: string }; export class AgentWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { const client = new Anthropic({ apiKey: this.env.ANTHROPIC_API_KEY }); const messages: Anthropic.MessageParam[] = [ { role: "user", content: event.payload.task }, ]; const toolDefinitions = tools.map(({ run, ...rest }) => rest); // Durable agent loop - each turn is checkpointed for (let turn = 0; turn < 10; turn++) { const response = (await step.do( `llm-turn-${turn}`, { retries: { limit: 3, delay: "10 seconds", backoff: "exponential" } }, async () => { const msg = await client.messages.create({ model: "claude-sonnet-4-5-20250929", max_tokens: 4096, tools: toolDefinitions, messages, }); // Serialize for Workflow state return JSON.parse(JSON.stringify(msg)); }, )) as Anthropic.Message; if (!response || !response.content) continue; messages.push({ role: "assistant", content: response.content }); if (response.stop_reason === "end_turn") { const textBlock = response.content.find( (b): b is Anthropic.TextBlock => b.type === "text", ); return { status: "complete", turns: turn + 1, result: textBlock?.text ?? null, }; } const toolResults: Anthropic.ToolResultBlockParam[] = []; for (const block of response.content) { if (block.type !== "tool_use") continue; const result = await step.do( `tool-${turn}-${block.id}`, { retries: { limit: 2, delay: "5 seconds" } }, async () => { switch (block.name) { case "search_repos": return searchReposTool.run(block.input as SearchReposInput); case "get_repo": return getRepoTool.run(block.input as GetRepoInput); default: return `Unknown tool: ${block.name}`; } }, ); toolResults.push({ type: "tool_result", tool_use_id: block.id, content: result, }); } messages.push({ role: "user", content: toolResults }); } return { status: "max_turns_reached", turns: 10 }; }} Why separate steps for LLM and tools? Each `step.do()` creates a checkpoint. If your Workflow crashes or the Worker restarts: * **After LLM step**: The response is persisted. On resume, it skips the LLM call and moves to tool execution. * **After tool step**: The result is persisted. If a later tool fails, earlier tools do not re-run. This is especially important for: * **LLM calls**: Expensive and slow, should not repeat unnecessarily * **External APIs**: May have rate limits or side effects * **Idempotency**: Some tools (like sending emails) should not run twice 4\. Configure your Workflow --------------------------- [](https://developers.cloudflare.com/workflows/get-started/durable-agents/#4-configure-your-workflow) 1. Open `wrangler.jsonc` and add the `workflow` configuration: * [wrangler.jsonc](https://developers.cloudflare.com/workflows/get-started/durable-agents/#tab-panel-4832) * [wrangler.toml](https://developers.cloudflare.com/workflows/get-started/durable-agents/#tab-panel-4833) { "$schema": "node_modules/wrangler/config-schema.json", "name": "durable-ai-agent", "main": "src/index.ts", "compatibility_date": "2025-01-01", "observability": { "enabled": true }, "workflows": [ { "name": "agent-workflow", "binding": "AGENT_WORKFLOW", "class_name": "AgentWorkflow" } ]} "$schema" = "node_modules/wrangler/config-schema.json"name = "durable-ai-agent"main = "src/index.ts"compatibility_date = "2025-01-01" [observability]enabled = true [[workflows]]name = "agent-workflow"binding = "AGENT_WORKFLOW"class_name = "AgentWorkflow" 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.AGENT_WORKFLOW`). 2. Generate types for your bindings: npx wrangler types This creates a `worker-configuration.d.ts` file with the `Env` type that includes your `AGENT_WORKFLOW` binding. 5\. Write your API ------------------ [](https://developers.cloudflare.com/workflows/get-started/durable-agents/#5-write-your-api) The Worker exposes an HTTP API to start new agent instances and check their status. Each instance runs independently and can be polled for results. 1. Replace `src/index.ts`: export { AgentWorkflow } from "./workflow"; export default { async fetch(request: Request, env: Env): Promise { const url = new URL(request.url); const instanceId = url.searchParams.get("instanceId"); if (instanceId) { const instance = await env.AGENT_WORKFLOW.get(instanceId); const status = await instance.status(); return Response.json({ status: status.status, output: status.output, }); } if (request.method === "POST") { const { task } = await request.json<{ task: string }>(); const instance = await env.AGENT_WORKFLOW.create({ params: { task }, }); return Response.json({ instanceId: instance.id }); } return new Response("POST a task to start an agent", { status: 400 }); },} satisfies ExportedHandler; 6\. Develop locally ------------------- [](https://developers.cloudflare.com/workflows/get-started/durable-agents/#6-develop-locally) 1. Create a [`.env` file](https://developers.cloudflare.com/workers/wrangler/environments/#secrets-in-local-development) for local development: ANTHROPIC_API_KEY=your-api-key-here 2. Start the dev server: npx wrangler dev 3. Start an agent that searches and compares repositories: curl -X POST http://localhost:8787 \ -H "Content-Type: application/json" \ -d '{"task": "Compare open-source LLM projects"}' { "instanceId": "abc-123-def" } 4. Check progress (may take a few seconds to complete): curl "http://localhost:8787?instanceId=abc-123-def" The agent will search for repositories, fetch details, and return a comparison. 7\. Deploy ---------- [](https://developers.cloudflare.com/workflows/get-started/durable-agents/#7-deploy) 1. Deploy the Worker: npx wrangler deploy 2. Add your API key as a secret: npx wrangler secret put ANTHROPIC_API_KEY 3. Start an agent on your deployed Worker: curl -X POST https://durable-ai-agent..workers.dev \ -H "Content-Type: application/json" \ -d '{"task": "Compare open-source LLM projects"}' 4. Inspect agent runs with the CLI: npx wrangler workflows instances describe agent-workflow latest This shows every step the agent took, including LLM calls, tool executions, timing, and any retries. You can also view this in the Cloudflare dashboard under **agent-workflow**. [Go to **Workflows**](https://dash.cloudflare.com/?to=/:account/workers/workflows) Adding real-time updates with Agents SDK ---------------------------------------- [](https://developers.cloudflare.com/workflows/get-started/durable-agents/#adding-real-time-updates-with-agents-sdk) The polling approach works well for simple use cases, but for real-time UIs you can combine Workflows with the [Agents SDK](https://developers.cloudflare.com/agents/) . The pattern is as follows: 1. Agent handles WebSocket connections and client state 2. Workflow runs the durable agent loop and pushes updates to the Agent 3. Agent broadcasts state changes to all connected clients In your Workflow, push updates to the Agent: // agentId passed via workflow paramsconst agent = this.env.RESEARCH_AGENT.get( this.env.RESEARCH_AGENT.idFromName(agentId),);await agent.updateProgress({ status: "searching", message: "Found 5 repositories...",}); In your Agent, receive updates and broadcast to clients: import { Agent } from "agents"; export class ResearchAgent extends Agent { async updateProgress(progress: { status: string; message: string }) { this.setState({ ...this.state, ...progress }); // pushes to all connected clients }} Clients use `useAgent()` to subscribe to state changes: import { useAgent } from "agents/react"; const [state, setState] = useState(initialState); useAgent({ agent: "research-agent", onStateUpdate: (newState) => setState(newState),});// state updates in real-time as the Workflow progresses This gives you durable execution (Workflows) with real-time UI updates (Agents SDK). For a complete example with a React UI, refer to the [durableAgent template ↗](https://github.com/cloudflare/docs-examples/tree/main/workflows/durableAgent) . Learn more ---------- [](https://developers.cloudflare.com/workflows/get-started/durable-agents/#learn-more) [Events and parameters](https://developers.cloudflare.com/workflows/build/events-and-parameters/) Pass data to Workflows and pause for external events with waitForEvent. [Sleeping and retrying](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/) Configure retry behavior and sleep patterns. [Workers API](https://developers.cloudflare.com/workflows/build/workers-api/) Explore the full Workflows API for programmatic control. [Agents SDK](https://developers.cloudflare.com/agents/) For interactive agents with real-time chat and WebSocket connections. Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Wrangler commands · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#_top) Copy page Wrangler commands ================= `workflows list` ---------------- [](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#workflows-list) List Workflows associated to account * [npm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4845) * [pnpm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4846) * [yarn](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4847) npx wrangler workflows list pnpm wrangler workflows list yarn wrangler workflows list * `--page` number default: 1 Show a sepecific page from the listing, can configure page size using "per-page" * `--per-page` number Configure the maximum number of workflows to show per page Global flags * `--v` boolean alias: --version Show version number * `--cwd` string Run as if Wrangler was started in the specified directory instead of the current working directory * `--config` string alias: --c Path to Wrangler configuration file * `--env` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files * `--env-file` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files * `--experimental-provision` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning * `--experimental-auto-create` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources `workflows describe` -------------------- [](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#workflows-describe) Describe Workflow resource * [npm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4848) * [pnpm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4849) * [yarn](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4850) npx wrangler workflows describe [NAME] pnpm wrangler workflows describe [NAME] yarn wrangler workflows describe [NAME] * `[NAME]` string required Name of the workflow Global flags * `--v` boolean alias: --version Show version number * `--cwd` string Run as if Wrangler was started in the specified directory instead of the current working directory * `--config` string alias: --c Path to Wrangler configuration file * `--env` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files * `--env-file` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files * `--experimental-provision` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning * `--experimental-auto-create` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources `workflows delete` ------------------ [](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#workflows-delete) Delete workflow - when deleting a workflow, it will also delete it's own instances * [npm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4851) * [pnpm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4852) * [yarn](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4853) npx wrangler workflows delete [NAME] pnpm wrangler workflows delete [NAME] yarn wrangler workflows delete [NAME] * `[NAME]` string required Name of the workflow Global flags * `--v` boolean alias: --version Show version number * `--cwd` string Run as if Wrangler was started in the specified directory instead of the current working directory * `--config` string alias: --c Path to Wrangler configuration file * `--env` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files * `--env-file` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files * `--experimental-provision` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning * `--experimental-auto-create` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources `workflows trigger` ------------------- [](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#workflows-trigger) Trigger a workflow, creating a new instance. Can optionally take a JSON string to pass a parameter into the workflow instance * [npm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4854) * [pnpm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4855) * [yarn](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4856) npx wrangler workflows trigger [NAME] [PARAMS] pnpm wrangler workflows trigger [NAME] [PARAMS] yarn wrangler workflows trigger [NAME] [PARAMS] * `[NAME]` string required Name of the workflow * `[PARAMS]` string default: Params for the workflow instance, encoded as a JSON string * `--id` string Custom instance ID, if not provided it will default to a random UUIDv4 Global flags * `--v` boolean alias: --version Show version number * `--cwd` string Run as if Wrangler was started in the specified directory instead of the current working directory * `--config` string alias: --c Path to Wrangler configuration file * `--env` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files * `--env-file` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files * `--experimental-provision` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning * `--experimental-auto-create` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources `workflows instances list` -------------------------- [](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#workflows-instances-list) Instance related commands (list, describe, terminate, pause, resume) * [npm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4857) * [pnpm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4858) * [yarn](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4859) npx wrangler workflows instances list [NAME] pnpm wrangler workflows instances list [NAME] yarn wrangler workflows instances list [NAME] * `[NAME]` string required Name of the workflow * `--reverse` boolean default: false Reverse order of the instances table * `--status` string Filters list by instance status (can be one of: queued, running, paused, errored, terminated, complete) * `--page` number default: 1 Show a sepecific page from the listing, can configure page size using "per-page" * `--per-page` number Configure the maximum number of instances to show per page Global flags * `--v` boolean alias: --version Show version number * `--cwd` string Run as if Wrangler was started in the specified directory instead of the current working directory * `--config` string alias: --c Path to Wrangler configuration file * `--env` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files * `--env-file` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files * `--experimental-provision` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning * `--experimental-auto-create` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources `workflows instances describe` ------------------------------ [](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#workflows-instances-describe) Describe a workflow instance - see its logs, retries and errors * [npm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4860) * [pnpm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4861) * [yarn](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4862) npx wrangler workflows instances describe [NAME] [ID] pnpm wrangler workflows instances describe [NAME] [ID] yarn wrangler workflows instances describe [NAME] [ID] * `[NAME]` string required Name of the workflow * `[ID]` string default: latest ID of the instance - instead of an UUID you can type 'latest' to get the latest instance and describe it * `--step-output` boolean default: true Don't output the step output since it might clutter the terminal * `--truncate-output-limit` number default: 5000 Truncate step output after x characters Global flags * `--v` boolean alias: --version Show version number * `--cwd` string Run as if Wrangler was started in the specified directory instead of the current working directory * `--config` string alias: --c Path to Wrangler configuration file * `--env` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files * `--env-file` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files * `--experimental-provision` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning * `--experimental-auto-create` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources `workflows instances send-event` -------------------------------- [](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#workflows-instances-send-event) Send an event to a workflow instance * [npm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4863) * [pnpm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4864) * [yarn](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4865) npx wrangler workflows instances send-event [NAME] [ID] pnpm wrangler workflows instances send-event [NAME] [ID] yarn wrangler workflows instances send-event [NAME] [ID] * `[NAME]` string required Name of the workflow * `[ID]` string required ID of the instance - instead of an UUID you can type 'latest' to get the latest instance and send an event to it * `--type` string required Type of the workflow event * `--payload` string default: {} JSON string for the workflow event (e.g., '{"key": "value"}') Global flags * `--v` boolean alias: --version Show version number * `--cwd` string Run as if Wrangler was started in the specified directory instead of the current working directory * `--config` string alias: --c Path to Wrangler configuration file * `--env` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files * `--env-file` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files * `--experimental-provision` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning * `--experimental-auto-create` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources `workflows instances terminate` ------------------------------- [](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#workflows-instances-terminate) Terminate a workflow instance * [npm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4866) * [pnpm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4867) * [yarn](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4868) npx wrangler workflows instances terminate [NAME] [ID] pnpm wrangler workflows instances terminate [NAME] [ID] yarn wrangler workflows instances terminate [NAME] [ID] * `[NAME]` string required Name of the workflow * `[ID]` string required ID of the instance - instead of an UUID you can type 'latest' to get the latest instance and describe it Global flags * `--v` boolean alias: --version Show version number * `--cwd` string Run as if Wrangler was started in the specified directory instead of the current working directory * `--config` string alias: --c Path to Wrangler configuration file * `--env` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files * `--env-file` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files * `--experimental-provision` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning * `--experimental-auto-create` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources `workflows instances restart` ----------------------------- [](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#workflows-instances-restart) Restart a workflow instance * [npm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4869) * [pnpm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4870) * [yarn](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4871) npx wrangler workflows instances restart [NAME] [ID] pnpm wrangler workflows instances restart [NAME] [ID] yarn wrangler workflows instances restart [NAME] [ID] * `[NAME]` string required Name of the workflow * `[ID]` string required ID of the instance - instead of an UUID you can type 'latest' to get the latest instance and describe it Global flags * `--v` boolean alias: --version Show version number * `--cwd` string Run as if Wrangler was started in the specified directory instead of the current working directory * `--config` string alias: --c Path to Wrangler configuration file * `--env` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files * `--env-file` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files * `--experimental-provision` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning * `--experimental-auto-create` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources `workflows instances pause` --------------------------- [](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#workflows-instances-pause) Pause a workflow instance * [npm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4872) * [pnpm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4873) * [yarn](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4874) npx wrangler workflows instances pause [NAME] [ID] pnpm wrangler workflows instances pause [NAME] [ID] yarn wrangler workflows instances pause [NAME] [ID] * `[NAME]` string required Name of the workflow * `[ID]` string required ID of the instance - instead of an UUID you can type 'latest' to get the latest instance and pause it Global flags * `--v` boolean alias: --version Show version number * `--cwd` string Run as if Wrangler was started in the specified directory instead of the current working directory * `--config` string alias: --c Path to Wrangler configuration file * `--env` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files * `--env-file` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files * `--experimental-provision` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning * `--experimental-auto-create` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources `workflows instances resume` ---------------------------- [](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#workflows-instances-resume) Resume a workflow instance * [npm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4875) * [pnpm](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4876) * [yarn](https://developers.cloudflare.com/workflows/reference/wrangler-commands/#tab-panel-4877) npx wrangler workflows instances resume [NAME] [ID] pnpm wrangler workflows instances resume [NAME] [ID] yarn wrangler workflows instances resume [NAME] [ID] * `[NAME]` string required Name of the workflow * `[ID]` string required ID of the instance - instead of an UUID you can type 'latest' to get the latest instance and resume it Global flags * `--v` boolean alias: --version Show version number * `--cwd` string Run as if Wrangler was started in the specified directory instead of the current working directory * `--config` string alias: --c Path to Wrangler configuration file * `--env` string alias: --e Environment to use for operations, and for selecting .env and .dev.vars files * `--env-file` string Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files * `--experimental-provision` boolean aliases: --x-provision default: true Experimental: Enable automatic resource provisioning * `--experimental-auto-create` boolean alias: --x-auto-create default: true Automatically provision draft bindings with new resources Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Changelog · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/reference/changelog/#_top) Copy page Changelog ========= [Subscribe to RSS](https://developers.cloudflare.com/workflows/reference/changelog/index.xml) 2025-09-12 ---------- [](https://developers.cloudflare.com/workflows/reference/changelog/#2025-09-12) **Test Workflows locally** Workflows can now be tested with new test APIs available in the "cloudflare:test" module. More information available in the Vitest integration [docs](https://developers.cloudflare.com/workers/testing/vitest-integration/test-apis/#workflows) . 2025-08-22 ---------- [](https://developers.cloudflare.com/workflows/reference/changelog/#2025-08-22) **Python Workflows is now open beta** [Python Workflows](https://developers.cloudflare.com/workflows/python/) is now in open beta, and available to any developer a free or paid Workers plan. More information available in the [changelog](https://developers.cloudflare.com/changelog/2025-08-22-workflows-python-beta/) . 2025-05-07 ---------- [](https://developers.cloudflare.com/workflows/reference/changelog/#2025-05-07) **Search for specific Workflows** With this release, you can search Workflows by name via API. 2025-04-29 ---------- [](https://developers.cloudflare.com/workflows/reference/changelog/#2025-04-29) **Workflow deletion and more** Workflows can now be deleted (from the Dashboard/UI or via API), and the maximum length limit for event types and instance IDs was increased to 100 characters. Also, this release fixes a bug where a delay of `0` in step config retries would fail. 2025-04-07 ---------- [](https://developers.cloudflare.com/workflows/reference/changelog/#2025-04-07) **Workflows is now Generally Available** Workflows is now Generally Available (or "GA"). This release includes the following new features: * A new `waitForEvent` API that allows a Workflow to wait for an event to occur before continuing execution. * Increased concurrency: you can run up to 4,500 Workflow instances concurrently — and this will continue to grow. * Improved observability, including new CPU time metrics that allow you to better understand which Workflow instances are consuming the most resources and/or contributing to your bill. * Support for vitest for testing Workflows locally and in CI/CD pipelines. More information available in the [changelog](https://developers.cloudflare.com/changelog/2025-04-07-workflows-ga/) . 2025-02-25 ---------- [](https://developers.cloudflare.com/workflows/reference/changelog/#2025-02-25) **Concurrent Workflow instances limits increased** Workflows now supports up to 4,500 concurrent (running) instances, up from the previous limit of 100. More information available in the [changelog](https://developers.cloudflare.com/changelog/2025-02-25-workflows-concurrency-increased/) . 2025-02-11 ---------- [](https://developers.cloudflare.com/workflows/reference/changelog/#2025-02-11) **Behavior improvements** Improved Workflows execution that prevents Workflows instances from getting stuck, and allows stuck instances to become unstuck. Also, improved the reliability of Workflows step retry counts, and improved Instance ID validation. 2025-01-23 ---------- [](https://developers.cloudflare.com/workflows/reference/changelog/#2025-01-23) **Major bugfixes and improvements** With this release, some bug were fixed: * `event.timestamp` is now `Date`, fixing a regression. * Fixed issue where instances without metadata were not terminated as expected. Also, this release makes Workflows execution more reliable for accounts with high loads. 2025-01-09 ---------- [](https://developers.cloudflare.com/workflows/reference/changelog/#2025-01-09) **Improved Wrangler local dev experience for steps' output, matching production** Previously, in local dev, the output field would return the list of successful steps outputs in the workflow. This is not expected behavior compared to production workflows (where the output is the actual return of the run function). This release aligns the local dev output field behavior with the production behavior. 2024-12-19 ---------- [](https://developers.cloudflare.com/workflows/reference/changelog/#2024-12-19) **Better instance control, improved queued logic, and step limit increased** Workflows can now be terminated and pause instances from a queued state and the ID of an instance is now exposed via the `WorkflowEvent` parameter. Also, the mechanism to queue instances was improved to force miss-behaved queued instances to be automatically errored. Workflows now allow you to define up to 1024 steps in a single Workflow definition, up from the previous limit of 512. This limit will continue to increase during the course of the open beta. 2024-12-09 ---------- [](https://developers.cloudflare.com/workflows/reference/changelog/#2024-12-09) **New queue instances logic** Introduction of a new mechanism to queue instances, which will prevent instances from getting stuck on queued status forever. 2024-11-30 ---------- [](https://developers.cloudflare.com/workflows/reference/changelog/#2024-11-30) **Step limit increased** Workflows now allow you to define up to 512 steps in a single Workflow definition, up from the previous limit of 256. This limit will continue to increase during the course of the open beta. If you have Workflows that need more steps, we recommend delegating additional work to other Workflows by [triggering a new Workflow](https://developers.cloudflare.com/workflows/build/trigger-workflows/) from within a step and passing any state as [parameters to that Workflow instance](https://developers.cloudflare.com/workflows/build/events-and-parameters/) . 2024-11-21 ---------- [](https://developers.cloudflare.com/workflows/reference/changelog/#2024-11-21) **Fixed create instance API in Workers bindings** You can now call `create()` without any arguments when using the [Workers API](https://developers.cloudflare.com/workflows/build/workers-api/#create) for Workflows. Workflows will automatically generate the ID of the Workflow on your behalf. This addresses a bug that caused calls to `create()` to fail when provided with no arguments. 2024-11-20 ---------- [](https://developers.cloudflare.com/workflows/reference/changelog/#2024-11-20) **Multiple Workflows in local development now supported** Local development with `wrangler dev` now correctly supports multiple Workflow definitions per script. There is no change to production Workflows, where multiple Workflow definitions per Worker script was already supported. 2024-10-23 ---------- [](https://developers.cloudflare.com/workflows/reference/changelog/#2024-10-23) **Workflows is now in public beta!** Workflows, a new product for building reliable, multi-step workflows using Cloudflare Workers, is now in public beta. The public beta is available to any user with a [free or paid Workers plan](https://developers.cloudflare.com/workers/platform/pricing/) . A Workflow allows you to define multiple, independent steps that encapsulate errors, automatically retry, persist state, and can run for seconds, minutes, hours or even days. A Workflow can be useful for post-processing data from R2 buckets before querying it, automating a Workers AI RAG pipeline, or managing user signup flows and lifecycle emails. You can learn more about Workflows in [our announcement blog](https://blog.cloudflare.com/building-workflows-durable-execution-on-workers/) , or start building in our [get started guide](https://developers.cloudflare.com/workflows/get-started/guide/) . Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Cloudflare API | Workflows › List All Workflows [![Logo](https://developers.cloudflare.com/logo.svg)Cloudflare API](https://developers.cloudflare.com/api/) HTTP [Docs](https://developers.cloudflare.com/) SearchCTRL+K API Reference [Overview](https://developers.cloudflare.com/api/) Account & User Management [Abuse Reports](https://developers.cloudflare.com/api/resources/abuse_reports/) [Accounts](https://developers.cloudflare.com/api/resources/accounts/) [Alerting](https://developers.cloudflare.com/api/resources/alerting/) [Audit Logs](https://developers.cloudflare.com/api/resources/audit_logs/) [Billing](https://developers.cloudflare.com/api/resources/billing/) [IAM](https://developers.cloudflare.com/api/resources/iam/) [Memberships](https://developers.cloudflare.com/api/resources/memberships/) [Resource Sharing](https://developers.cloudflare.com/api/resources/resource_sharing/) [User](https://developers.cloudflare.com/api/resources/user/) [Custom Pages](https://developers.cloudflare.com/api/resources/custom_pages/) AI [AI](https://developers.cloudflare.com/api/resources/ai/) [AI Gateway](https://developers.cloudflare.com/api/resources/ai_gateway/) [AI Search](https://developers.cloudflare.com/api/resources/ai_search/) [Vectorize](https://developers.cloudflare.com/api/resources/vectorize/) Certificate Management [ACM](https://developers.cloudflare.com/api/resources/acm/) [Certificate Authorities](https://developers.cloudflare.com/api/resources/certificate_authorities/) [Client Certificates](https://developers.cloudflare.com/api/resources/client_certificates/) [Custom Certificates](https://developers.cloudflare.com/api/resources/custom_certificates/) [Custom Hostnames](https://developers.cloudflare.com/api/resources/custom_hostnames/) [DCV Delegation](https://developers.cloudflare.com/api/resources/dcv_delegation/) [Hostnames](https://developers.cloudflare.com/api/resources/hostnames/) [Keyless Certificates](https://developers.cloudflare.com/api/resources/keyless_certificates/) [MTLS Certificates](https://developers.cloudflare.com/api/resources/mtls_certificates/) [Origin CA Certificates](https://developers.cloudflare.com/api/resources/origin_ca_certificates/) [Origin Post Quantum Encryption](https://developers.cloudflare.com/api/resources/origin_post_quantum_encryption/) [Origin TLS Client Auth](https://developers.cloudflare.com/api/resources/origin_tls_client_auth/) [SSL](https://developers.cloudflare.com/api/resources/ssl/) Cloudflare One [Email Security](https://developers.cloudflare.com/api/resources/email_security/) [Zero Trust](https://developers.cloudflare.com/api/resources/zero_trust/) DNS [Account Custom Nameservers](https://developers.cloudflare.com/api/resources/custom_nameservers/) [DNS](https://developers.cloudflare.com/api/resources/dns/) [DNS Firewall](https://developers.cloudflare.com/api/resources/dns_firewall/) Domain/Zone Management [Registrar](https://developers.cloudflare.com/api/resources/registrar/) [Zones](https://developers.cloudflare.com/api/resources/zones/) IP Addresses [Addressing](https://developers.cloudflare.com/api/resources/addressing/) [IPs](https://developers.cloudflare.com/api/resources/ips/) Media [Calls](https://developers.cloudflare.com/api/resources/calls/) [Images](https://developers.cloudflare.com/api/resources/images/) [Stream](https://developers.cloudflare.com/api/resources/stream/) [Realtime Kit](https://developers.cloudflare.com/api/resources/realtime_kit/) Networking [Magic Cloud Networking](https://developers.cloudflare.com/api/resources/magic_cloud_networking/) [Magic Network Monitoring](https://developers.cloudflare.com/api/resources/magic_network_monitoring/) [Magic Transit](https://developers.cloudflare.com/api/resources/magic_transit/) [Network Interconnects](https://developers.cloudflare.com/api/resources/network_interconnects/) Observability [Diagnostics](https://developers.cloudflare.com/api/resources/diagnostics/) [Healthchecks](https://developers.cloudflare.com/api/resources/healthchecks/) [Logpush](https://developers.cloudflare.com/api/resources/logpush/) [Logs](https://developers.cloudflare.com/api/resources/logs/) [RUM](https://developers.cloudflare.com/api/resources/rum/) [Security Center](https://developers.cloudflare.com/api/resources/security_center/) Radar [Radar](https://developers.cloudflare.com/api/resources/radar/) Routing & Performance [Argo](https://developers.cloudflare.com/api/resources/argo/) [Cache](https://developers.cloudflare.com/api/resources/cache/) [Email Routing](https://developers.cloudflare.com/api/resources/email_routing/) [Load Balancers](https://developers.cloudflare.com/api/resources/load_balancers/) [Smart Shield](https://developers.cloudflare.com/api/resources/smart_shield/) [Spectrum](https://developers.cloudflare.com/api/resources/spectrum/) [Speed](https://developers.cloudflare.com/api/resources/speed/) [Waiting Rooms](https://developers.cloudflare.com/api/resources/waiting_rooms/) [Web3](https://developers.cloudflare.com/api/resources/web3/) Rules [Cloud Connector](https://developers.cloudflare.com/api/resources/cloud_connector/) [Managed Transforms](https://developers.cloudflare.com/api/resources/managed_transforms/) [Page Rules](https://developers.cloudflare.com/api/resources/page_rules/) [Rules Lists](https://developers.cloudflare.com/api/resources/rules/) [Rules Trace](https://developers.cloudflare.com/api/resources/request_tracers/) [Rulesets](https://developers.cloudflare.com/api/resources/rulesets/) [Snippets](https://developers.cloudflare.com/api/resources/snippets/) [URL Normalization](https://developers.cloudflare.com/api/resources/url_normalization/) Security [API Gateway](https://developers.cloudflare.com/api/resources/api_gateway/) [Schema Validation](https://developers.cloudflare.com/api/resources/schema_validation/) [Bot Management](https://developers.cloudflare.com/api/resources/bot_management/) [Botnet Feed](https://developers.cloudflare.com/api/resources/botnet_feed/) [Content Scanning](https://developers.cloudflare.com/api/resources/content_scanning/) [Filters](https://developers.cloudflare.com/api/resources/filters/) [Firewall](https://developers.cloudflare.com/api/resources/firewall/) [Fraud](https://developers.cloudflare.com/api/resources/fraud/) [Leaked Credential Checks](https://developers.cloudflare.com/api/resources/leaked_credential_checks/) [Page Shield](https://developers.cloudflare.com/api/resources/page_shield/) [Rate Limits](https://developers.cloudflare.com/api/resources/rate_limits/) [Security TXT](https://developers.cloudflare.com/api/resources/security_txt/) [Token Validation](https://developers.cloudflare.com/api/resources/token_validation/) [Turnstile](https://developers.cloudflare.com/api/resources/turnstile/) Storage & Databases [D1](https://developers.cloudflare.com/api/resources/d1/) [Hyperdrive](https://developers.cloudflare.com/api/resources/hyperdrive/) [KV](https://developers.cloudflare.com/api/resources/kv/) [Queues](https://developers.cloudflare.com/api/resources/queues/) [R2](https://developers.cloudflare.com/api/resources/r2/) [R2 Data Catalog](https://developers.cloudflare.com/api/resources/r2_data_catalog/) [Secrets Store](https://developers.cloudflare.com/api/resources/secrets_store/) [Pipelines](https://developers.cloudflare.com/api/resources/pipelines/) Threat Intelligence [Brand Protection](https://developers.cloudflare.com/api/resources/brand_protection/) [Cloudforce One](https://developers.cloudflare.com/api/resources/cloudforce_one/) [Intel](https://developers.cloudflare.com/api/resources/intel/) [URL Scanner](https://developers.cloudflare.com/api/resources/url_scanner/) Workers & Pages [Browser Rendering](https://developers.cloudflare.com/api/resources/browser_rendering/) [Durable Objects](https://developers.cloudflare.com/api/resources/durable_objects/) [Pages](https://developers.cloudflare.com/api/resources/pages/) [Workers](https://developers.cloudflare.com/api/resources/workers/) [Workers Builds](https://developers.cloudflare.com/api/resources/workers_builds/) [Workers For Platforms](https://developers.cloudflare.com/api/resources/workers_for_platforms/) [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [List all Workflows](https://developers.cloudflare.com/api/resources/workflows/methods/list/) [Get Workflow details](https://developers.cloudflare.com/api/resources/workflows/methods/get/) [Create/modify Workflow](https://developers.cloudflare.com/api/resources/workflows/methods/update/) [Deletes a Workflow](https://developers.cloudflare.com/api/resources/workflows/methods/delete/) * * * [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) * * * [Versions](https://developers.cloudflare.com/api/resources/workflows/subresources/versions/) Shared [Shared](https://developers.cloudflare.com/api/resources/$shared/) [Organizations](https://developers.cloudflare.com/api/resources/organizations/) [Connectivity](https://developers.cloudflare.com/api/resources/connectivity/) [Zaraz](https://developers.cloudflare.com/api/resources/zaraz/) [AutoRAG](https://developers.cloudflare.com/api/resources/autorag/) ### Workflows workflows Methods List All Workflows -> V4PagePaginationArray<{ id, class\_name, created\_on, 5 more... }\> get/accounts/{account\_id}/workflows List all Workflows Security API Token The preferred authorization scheme for interacting with the Cloudflare API. [Create a token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) . Example: Authorization: Bearer Sn3lZJTBX6kkg7OdcBUAxOO963GEIyGQqnFTOFYY Accepted Permissions (at least one required) Workers Tail Read Workers Scripts Write Workers Scripts Read path Parameters account\_id: string query Parameters page: number Optional (minimum: 1, default: 1) per\_page: number Optional (maximum: 100, minimum: 1, default: 10) search: string Optional (maxLength: 64, minLength: 1) Allows filtering workflows\` name. Response fields errors: Array<{ code, message }\> messages: Array<{ code, message }\> result: Array<{ id, class\_name, created\_on, 5 more... }\> success: true result\_info: { count, per\_page, total\_count, 2 more... } Optional Request example cURL curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workflows \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" 200Example { "errors": [\ {\ "code": 0,\ "message": "message"\ }\ ], "messages": [\ {\ "code": 0,\ "message": "message"\ }\ ], "result": [\ {\ "id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",\ "class_name": "class_name",\ "created_on": "2019-12-27T18:11:19.117Z",\ "instances": {\ "complete": 0,\ "errored": 0,\ "paused": 0,\ "queued": 0,\ "running": 0,\ "terminated": 0,\ "waiting": 0,\ "waitingForPause": 0\ },\ "modified_on": "2019-12-27T18:11:19.117Z",\ "name": "x",\ "script_name": "script_name",\ "triggered_on": "2019-12-27T18:11:19.117Z"\ }\ ], "success": true, "result_info": { "count": 0, "per_page": 0, "total_count": 0, "cursor": "cursor", "page": 0 } } Get Workflow Details -> Envelope<{ id, class\_name, created\_on, 5 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name} Get Workflow details Create Modify Workflow -> Envelope<{ id, class\_name, created\_on, 7 more... }\> put/accounts/{account\_id}/workflows/{workflow\_name} Create/modify Workflow Deletes A Workflow -> Envelope<{ status, success }\> delete/accounts/{account\_id}/workflows/{workflow\_name} Deletes a Workflow. This only deletes the Workflow and does not delete or modify any Worker associated to this Workflow or bounded to it. [Workflows](https://developers.cloudflare.com/api/resources/workflows/) ### Instances workflows.instances Methods List Of Workflow Instances -> V4PagePaginationArray<{ id, created\_on, ended\_on, 5 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/instances List of workflow instances Get Logs And Status From Instance -> Envelope<{ end, error, output, 8 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id} Get logs and status from instance Create A New Workflow Instance -> Envelope<{ id, status, version\_id, 1 more... }\> post/accounts/{account\_id}/workflows/{workflow\_name}/instances Create a new workflow instance Batch Create New Workflow Instances -> SinglePage<{ id, status, version\_id, 1 more... }\> post/accounts/{account\_id}/workflows/{workflow\_name}/instances/batch Batch create new Workflow instances [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) ### Events workflows.instances.events Methods Send Event To Instance -> Envelope post/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id}/events/{event\_type} Send event to instance [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) ### Status workflows.instances.status Methods Change Status Of Instance -> Envelope<{ status, timestamp }\> patch/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id}/status Change status of instance [Workflows](https://developers.cloudflare.com/api/resources/workflows/) ### Versions workflows.versions Methods List Deployed Workflow Versions -> V4PagePaginationArray<{ id, class\_name, created\_on, 2 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/versions List deployed Workflow versions Get Workflow Version Details -> Envelope<{ id, class\_name, created\_on, 2 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/versions/{version\_id} Get Workflow version details --- # Workers API · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/build/workers-api/#_top) Copy page Workers API =========== This guide details the Workflows API within Cloudflare Workers, including methods, types, and usage examples. WorkflowEntrypoint ------------------ [](https://developers.cloudflare.com/workflows/build/workers-api/#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 { async run(event: WorkflowEvent, step: WorkflowStep) { // Steps here }} ### run [](https://developers.cloudflare.com/workflows/build/workers-api/#run) * `run(event: WorkflowEvent, step: WorkflowStep): Promise` * `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](https://developers.cloudflare.com/workflows/build/workers-api/#instancestatus) , [REST API](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/subresources/status/) 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 { async run(event: WorkflowEvent, 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 ↗](https://www.typescriptlang.org/docs/handbook/2/generics.html#working-with-generic-type-variables) that allows you to provide a type for the `payload` property within the `WorkflowEvent`. Refer to the [events and parameters](https://developers.cloudflare.com/workflows/build/events-and-parameters/) documentation for how to handle events within your Workflow code. Finally, any JS control-flow primitive (if conditions, loops, try-catches, promises, etc) can be used to manage steps inside the `run` method. WorkflowEvent ------------- [](https://developers.cloudflare.com/workflows/build/workers-api/#workflowevent) export type WorkflowEvent = { payload: Readonly; timestamp: Date; instanceId: string;}; * The `WorkflowEvent` is the first argument to a Workflow's `run` method, and includes an optional `payload` parameter and a `timestamp` property. * `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. Refer to the [events and parameters](https://developers.cloudflare.com/workflows/build/events-and-parameters/) documentation for how to handle events within your Workflow code. WorkflowStep ------------ [](https://developers.cloudflare.com/workflows/build/workers-api/#workflowstep) ### step [](https://developers.cloudflare.com/workflows/build/workers-api/#step) * `step.do(name: string, callback: (): RpcSerializable): Promise` * `step.do(name: string, config?: WorkflowStepConfig, callback: (): RpcSerializable): Promise` * `name` - the name of the step, up to 256 characters. * `config` (optional) - an optional `WorkflowStepConfig` for configuring [step specific retry behaviour](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/) . * `callback` - an asynchronous function that optionally returns serializable state for the Workflow to persist. * `step.sleep(name: string, duration: WorkflowDuration): Promise` * `name` - the name of the step. * `duration` - the duration to sleep until, in either seconds or as a `WorkflowDuration` compatible string. * Refer to the [documentation on sleeping and retrying](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/) to learn more about how Workflows are retried. * `step.sleepUntil(name: string, timestamp: Date | number): Promise` * `name` - the name of the step. * `timestamp` - a JavaScript `Date` object or seconds from the Unix epoch to sleep the Workflow instance until. * `step.waitForEvent(name: string, options: ): Promise` * `name` - the name of the step. * `options` - an object with properties for `type` (up to 100 characters 1), 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. * [JavaScript](https://developers.cloudflare.com/workflows/build/workers-api/#tab-panel-4817) * [TypeScript](https://developers.cloudflare.com/workflows/build/workers-api/#tab-panel-4818) export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) { // Other steps in your Workflow let event = await step.waitForEvent( "receive invoice paid webhook from Stripe", { type: "stripe-webhook", timeout: "1 hour" }, ); // Rest of your Workflow }} export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // Other steps in your Workflow let event = await step.waitForEvent( "receive invoice paid webhook from Stripe", { type: "stripe-webhook", timeout: "1 hour" }, ); // Rest of your Workflow }} Review the documentation on [events and parameters](https://developers.cloudflare.com/workflows/build/events-and-parameters/) to learn how to send events to a running Workflow instance. WorkflowStepConfig ------------------ [](https://developers.cloudflare.com/workflows/build/workers-api/#workflowstepconfig) export type WorkflowStepConfig = { retries?: { limit: number; delay: string | number; 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. Refer to the [documentation on sleeping and retrying](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/) to learn more about how Workflows are retried. NonRetryableError ----------------- [](https://developers.cloudflare.com/workflows/build/workers-api/#nonretryableerror) * `throw new NonRetryableError(message: string, name string optional)`: NonRetryableError * When thrown inside [`step.do()`](https://developers.cloudflare.com/workflows/build/workers-api/#step) , this error stops step retries, propagating the error to the top level (the [run](https://developers.cloudflare.com/workflows/build/workers-api/#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](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/) to learn more about how Workflows steps are retried. Call Workflows from Workers --------------------------- [](https://developers.cloudflare.com/workflows/build/workers-api/#call-workflows-from-workers) Workflows exposes an API directly to your Workers scripts via the [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/#what-is-a-binding) 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: * [wrangler.jsonc](https://developers.cloudflare.com/workflows/build/workers-api/#tab-panel-4813) * [wrangler.toml](https://developers.cloudflare.com/workflows/build/workers-api/#tab-panel-4814) { "$schema": "./node_modules/wrangler/config-schema.json", "name": "workflows-starter", "main": "src/index.ts", "compatibility_date": "2024-10-22", "workflows": [ { "name": "workflows-starter", "binding": "MY_WORKFLOW", "class_name": "MyWorkflow" } ]} #:schema node_modules/wrangler/config-schema.jsonname = "workflows-starter"main = "src/index.ts"compatibility_date = "2024-10-22" [[workflows]]# name of your workflowname = "workflows-starter"# binding name env.MY_WORKFLOWbinding = "MY_WORKFLOW"# this is class that extends the Workflow class in src/index.tsclass_name = "MyWorkflow" ### Bind from Pages [](https://developers.cloudflare.com/workflows/build/workers-api/#bind-from-pages) You can bind and trigger Workflows from [Pages Functions](https://developers.cloudflare.com/pages/functions/) by deploying a Workers project with your Workflow definition and then invoking that Worker using [service bindings](https://developers.cloudflare.com/pages/functions/bindings/#service-bindings) or a standard `fetch()` call. Visit the documentation on [calling Workflows from Pages](https://developers.cloudflare.com/workflows/build/call-workflows-from-pages/) for examples. ### Cross-script calls [](https://developers.cloudflare.com/workflows/build/workers-api/#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](https://developers.cloudflare.com/workers/wrangler/configuration/) would resemble the following: * [wrangler.jsonc](https://developers.cloudflare.com/workflows/build/workers-api/#tab-panel-4815) * [wrangler.toml](https://developers.cloudflare.com/workflows/build/workers-api/#tab-panel-4816) { "$schema": "./node_modules/wrangler/config-schema.json", "name": "web-api-worker", "main": "src/index.ts", "compatibility_date": "2024-10-22", "workflows": [ { "name": "billing-workflow", "binding": "MY_WORKFLOW", "class_name": "MyWorkflow", "script_name": "billing-worker" } ]} #:schema node_modules/wrangler/config-schema.jsonname = "web-api-worker"main = "src/index.ts"compatibility_date = "2024-10-22" [[workflows]]# name of your workflowname = "billing-workflow"# binding name env.MY_WORKFLOWbinding = "MY_WORKFLOW"# this is class that extends the Workflow class in src/index.tsclass_name = "MyWorkflow"# the script name where the Workflow is defined.# required if the Workflow is defined in another script.script_name = "billing-worker" If you're using TypeScript, run [`wrangler types`](https://developers.cloudflare.com/workers/wrangler/commands/#types) whenever you modify your Wrangler configuration file. This generates types for the `env` object based on your bindings, as well as [runtime types](https://developers.cloudflare.com/workers/languages/typescript/) . Workflow -------- [](https://developers.cloudflare.com/workflows/build/workers-api/#workflow) 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`](https://developers.cloudflare.com/workers/wrangler/commands/#types) . 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 [](https://developers.cloudflare.com/workflows/build/workers-api/#create) Create (trigger) a new instance of the given Workflow. * `create(options?: WorkflowInstanceCreateOptions): Promise` * `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 1). 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`](https://developers.cloudflare.com/workflows/build/events-and-parameters/) . // Create a new Workflow instance with your own ID and pass params to the Workflow instancelet 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](https://developers.cloudflare.com/workflows/reference/limits/) . To re-run a workflow with the same ID, you can [`restart`](https://developers.cloudflare.com/workflows/build/trigger-workflows/#restart-a-workflow) the existing instance. 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](https://developers.cloudflare.com/workflows/build/workers-api/#workflow) . Note that this does _not_ propagate type information into the Workflow itself, as TypeScript types are a build-time construct. To provide the type of an incoming `WorkflowEvent`, refer to the [TypeScript and type parameters](https://developers.cloudflare.com/workflows/build/events-and-parameters/#typescript-and-type-parameters) section of the Workflows documentation. 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;} 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 [](https://developers.cloudflare.com/workflows/build/workers-api/#createbatch) Create (trigger) a batch of new instance 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](https://developers.cloudflare.com/workflows/reference/limits/) . * `createBatch(batch: WorkflowInstanceCreateOptions[]): Promise` * `batch` - list of Options to pass when creating an instance, including a user-provided ID and payload parameters. Each element of the `batch` list is expected to include both `id` and `params` properties: // Create a new batch of 3 Workflow instances, each with its own ID and pass params to the Workflow instancesconst listOfInstances = [ { id: "id-abc123", params: { hello: "world-0" } }, { id: "id-def456", params: { hello: "world-1" } }, { id: "id-ghi789", params: { hello: "world-2" } },];let instances = await env.MY_WORKFLOW.createBatch(listOfInstances); Returns an array of `WorkflowInstance`. Unlike [`create`](https://developers.cloudflare.com/workflows/build/workers-api/#create) , this operation is idempotent and will not fail if an ID is already in use. If an existing instance with the same ID is still within its [retention limit](https://developers.cloudflare.com/workflows/reference/limits/) , it will be skipped and excluded from the returned array. ### get [](https://developers.cloudflare.com/workflows/build/workers-api/#get) Get a specific Workflow instance by ID. * `get(id: string): Promise`\- `id` - the ID of the Workflow instance. Returns a `WorkflowInstance`. Throws an exception if the instance ID does not exist. // Fetch an existing Workflow instance by ID:try { let instance = await env.MY_WORKFLOW.get(id); return Response.json({ id: instance.id, details: await instance.status(), });} catch (e: any) { // Handle errors // .get will throw an exception if the ID doesn't exist or is invalid. const msg = `failed to get instance ${id}: ${e.message}`; console.error(msg); return Response.json({ error: msg }, { status: 400 });} WorkflowInstanceCreateOptions ----------------------------- [](https://developers.cloudflare.com/workflows/build/workers-api/#workflowinstancecreateoptions) Optional properties to pass when creating an instance. interface WorkflowInstanceCreateOptions { /** * An id for your Workflow instance. Must be unique within the Workflow. */ id?: string; /** * The event payload the Workflow instance is triggered with */ params?: unknown;} WorkflowInstance ---------------- [](https://developers.cloudflare.com/workflows/build/workers-api/#workflowinstance) Represents a specific instance of a Workflow, and provides methods to manage the instance. declare abstract class WorkflowInstance { public id: string; /** * Pause the instance. */ public pause(): Promise; /** * Resume the instance. If it is already running, an error will be thrown. */ public resume(): Promise; /** * Terminate the instance. If it is errored, terminated or complete, an error will be thrown. */ public terminate(): Promise; /** * Restart the instance. */ public restart(): Promise; /** * Returns the current status of the instance. */ public status(): Promise;} ### id [](https://developers.cloudflare.com/workflows/build/workers-api/#id) Return the id of a Workflow. * `id: string` ### status [](https://developers.cloudflare.com/workflows/build/workers-api/#status) Return the status of a running Workflow instance. * `status(): Promise` ### pause [](https://developers.cloudflare.com/workflows/build/workers-api/#pause) Pause a running Workflow instance. * `pause(): Promise` ### resume [](https://developers.cloudflare.com/workflows/build/workers-api/#resume) Resume a paused Workflow instance. * `resume(): Promise` ### restart [](https://developers.cloudflare.com/workflows/build/workers-api/#restart) Restart a Workflow instance. * `restart(): Promise` ### terminate [](https://developers.cloudflare.com/workflows/build/workers-api/#terminate) Terminate a Workflow instance. * `terminate(): Promise` ### sendEvent [](https://developers.cloudflare.com/workflows/build/workers-api/#sendevent) [Send an event](https://developers.cloudflare.com/workflows/build/events-and-parameters/) to a running Workflow instance. * `sendEvent(): Promise` * `options` - the event `type` (up to 100 characters 1) and `payload` to send to the Workflow instance. The `type` must match the `type` in the corresponding `waitForEvent` call in your Workflow. Return `void` on success; throws an exception if the Workflow is not running or is an errored state. * [JavaScript](https://developers.cloudflare.com/workflows/build/workers-api/#tab-panel-4819) * [TypeScript](https://developers.cloudflare.com/workflows/build/workers-api/#tab-panel-4820) export default { async fetch(req, env) { const instanceId = new URL(req.url).searchParams.get("instanceId"); const webhookPayload = await req.json(); let instance = await env.MY_WORKFLOW.get(instanceId); // Send our event, with `type` matching the event type defined in // our step.waitForEvent call await instance.sendEvent({ type: "stripe-webhook", payload: webhookPayload, }); return Response.json({ status: await instance.status(), }); },}; export default { async fetch(req: Request, env: Env) { const instanceId = new URL(req.url).searchParams.get("instanceId"); const webhookPayload = await req.json(); let instance = await env.MY_WORKFLOW.get(instanceId); // Send our event, with `type` matching the event type defined in // our step.waitForEvent call await instance.sendEvent({ type: "stripe-webhook", payload: webhookPayload, }); return Response.json({ status: await instance.status(), }); },}; You can call `sendEvent` multiple times, setting the value of the `type` property to match the specific `waitForEvent` calls in your Workflow. This allows you to wait for multiple events at once, or use `Promise.race` to wait for multiple events and allow the first event to progress the Workflow. ### InstanceStatus [](https://developers.cloudflare.com/workflows/build/workers-api/#instancestatus) Details the status of a Workflow instance. type InstanceStatus = { status: | "queued" // means that instance is waiting to be started (see concurrency limits) | "running" | "paused" | "errored" | "terminated" // user terminated the instance while it was running | "complete" | "waiting" // instance is hibernating and waiting for sleep or event to finish | "waitingForPause" // instance is finishing the current work to pause | "unknown"; error?: { name: string, message: string }; output?: unknown;}; Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Cookie Settings Back to top --- # Integrate Workflows with Twilio · Cloudflare Workflows docs [Skip to content](https://developers.cloudflare.com/workflows/examples/twilio/#_top) Copy page Integrate Workflows with Twilio =============================== Integrate Workflows with Twilio. Learn how to receive and send text messages and phone calls via APIs and Webhooks. Using the following [repository ↗](https://github.com/craigsdennis/twilio-cloudflare-workflow) , learn how to integrate Cloudflare Workflows with Twilio, a popular cloud communications platform that enables developers to integrate messaging, voice, video, and authentication features into applications via APIs. By the end of the video tutorial, you will become familiarized with the process of setting up Cloudflare Workflows to seamlessly interact with Twilio's APIs, enabling you to build interesting communication features directly into your applications. Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Your Privacy Choices Back to top --- # Changelog | Workflows Changelog | WorkflowsUpdates to Workflowshttps://developers.cloudflare.com/workflows/reference/changelogWorkflows - Test Workflows locallyhttps://developers.cloudflare.com/workflows/reference/changelog/#test-workflows-locallyhttps://developers.cloudflare.com/workflows/reference/changelog/#test-workflows-locally

Workflows can now be tested with new test APIs available in the "cloudflare:test" module.

More information available in the Vitest integration docs.

Fri, 12 Sep 2025 00:00:00 GMTWorkflows - Python Workflows is now open betahttps://developers.cloudflare.com/workflows/reference/changelog/#python-workflows-is-now-open-betahttps://developers.cloudflare.com/workflows/reference/changelog/#python-workflows-is-now-open-beta

Python Workflows is now in open beta, and available to any developer a free or paid Workers plan.

More information available in the changelog.

Fri, 22 Aug 2025 00:00:00 GMTWorkflows - Search for specific Workflowshttps://developers.cloudflare.com/workflows/reference/changelog/#search-for-specific-workflowshttps://developers.cloudflare.com/workflows/reference/changelog/#search-for-specific-workflows

With this release, you can search Workflows by name via API.

Wed, 07 May 2025 00:00:00 GMTWorkflows - Workflow deletion and morehttps://developers.cloudflare.com/workflows/reference/changelog/#workflow-deletion-and-morehttps://developers.cloudflare.com/workflows/reference/changelog/#workflow-deletion-and-more

Workflows can now be deleted (from the Dashboard/UI or via API), and the maximum length limit for event types and instance IDs was increased to 100 characters.

Also, this release fixes a bug where a delay of 0 in step config retries would fail.

Tue, 29 Apr 2025 00:00:00 GMTWorkflows - Workflows is now Generally Availablehttps://developers.cloudflare.com/workflows/reference/changelog/#workflows-is-now-generally-availablehttps://developers.cloudflare.com/workflows/reference/changelog/#workflows-is-now-generally-available

Workflows is now Generally Available (or "GA").

This release includes the following new features:

  • A new waitForEvent API that allows a Workflow to wait for an event to occur before continuing execution.
  • Increased concurrency: you can run up to 4,500 Workflow instances concurrently — and this will continue to grow.
  • Improved observability, including new CPU time metrics that allow you to better understand which Workflow instances are consuming the most resources and/or contributing to your bill.
  • Support for vitest for testing Workflows locally and in CI/CD pipelines.

More information available in the changelog.

Mon, 07 Apr 2025 00:00:00 GMTWorkflows - Concurrent Workflow instances limits increasedhttps://developers.cloudflare.com/workflows/reference/changelog/#concurrent-workflow-instances-limits-increasedhttps://developers.cloudflare.com/workflows/reference/changelog/#concurrent-workflow-instances-limits-increased

Workflows now supports up to 4,500 concurrent (running) instances, up from the previous limit of 100.

More information available in the changelog.

Tue, 25 Feb 2025 00:00:00 GMTWorkflows - Behavior improvementshttps://developers.cloudflare.com/workflows/reference/changelog/#behavior-improvementshttps://developers.cloudflare.com/workflows/reference/changelog/#behavior-improvements

Improved Workflows execution that prevents Workflows instances from getting stuck, and allows stuck instances to become unstuck.

Also, improved the reliability of Workflows step retry counts, and improved Instance ID validation.

Tue, 11 Feb 2025 00:00:00 GMTWorkflows - Major bugfixes and improvementshttps://developers.cloudflare.com/workflows/reference/changelog/#major-bugfixes-and-improvementshttps://developers.cloudflare.com/workflows/reference/changelog/#major-bugfixes-and-improvements

With this release, some bug were fixed:

  • event.timestamp is now Date, fixing a regression.
  • Fixed issue where instances without metadata were not terminated as expected.

Also, this release makes Workflows execution more reliable for accounts with high loads.

Thu, 23 Jan 2025 00:00:00 GMTWorkflows - Improved Wrangler local dev experience for steps' output, matching productionhttps://developers.cloudflare.com/workflows/reference/changelog/#improved-wrangler-local-dev-experience-for-steps-output-matching-productionhttps://developers.cloudflare.com/workflows/reference/changelog/#improved-wrangler-local-dev-experience-for-steps-output-matching-production

Previously, in local dev, the output field would return the list of successful steps outputs in the workflow. This is not expected behavior compared to production workflows (where the output is the actual return of the run function).

This release aligns the local dev output field behavior with the production behavior.

Thu, 09 Jan 2025 00:00:00 GMTWorkflows - Better instance control, improved queued logic, and step limit increasedhttps://developers.cloudflare.com/workflows/reference/changelog/#better-instance-control-improved-queued-logic-and-step-limit-increasedhttps://developers.cloudflare.com/workflows/reference/changelog/#better-instance-control-improved-queued-logic-and-step-limit-increased

Workflows can now be terminated and pause instances from a queued state and the ID of an instance is now exposed via the WorkflowEvent parameter.

Also, the mechanism to queue instances was improved to force miss-behaved queued instances to be automatically errored.

Workflows now allow you to define up to 1024 steps in a single Workflow definition, up from the previous limit of 512. This limit will continue to increase during the course of the open beta.

Thu, 19 Dec 2024 00:00:00 GMTWorkflows - New queue instances logichttps://developers.cloudflare.com/workflows/reference/changelog/#new-queue-instances-logichttps://developers.cloudflare.com/workflows/reference/changelog/#new-queue-instances-logic

Introduction of a new mechanism to queue instances, which will prevent instances from getting stuck on queued status forever.

Mon, 09 Dec 2024 00:00:00 GMTWorkflows - Step limit increasedhttps://developers.cloudflare.com/workflows/reference/changelog/#step-limit-increasedhttps://developers.cloudflare.com/workflows/reference/changelog/#step-limit-increased

Workflows now allow you to define up to 512 steps in a single Workflow definition, up from the previous limit of 256. This limit will continue to increase during the course of the open beta.

If you have Workflows that need more steps, we recommend delegating additional work to other Workflows by triggering a new Workflow from within a step and passing any state as parameters to that Workflow instance.

Sat, 30 Nov 2024 00:00:00 GMTWorkflows - Fixed create instance API in Workers bindingshttps://developers.cloudflare.com/workflows/reference/changelog/#fixed-create-instance-api-in-workers-bindingshttps://developers.cloudflare.com/workflows/reference/changelog/#fixed-create-instance-api-in-workers-bindings

You can now call create() without any arguments when using the Workers API for Workflows. Workflows will automatically generate the ID of the Workflow on your behalf.

This addresses a bug that caused calls to create() to fail when provided with no arguments.

Thu, 21 Nov 2024 00:00:00 GMTWorkflows - Multiple Workflows in local development now supportedhttps://developers.cloudflare.com/workflows/reference/changelog/#multiple-workflows-in-local-development-now-supportedhttps://developers.cloudflare.com/workflows/reference/changelog/#multiple-workflows-in-local-development-now-supported

Local development with wrangler dev now correctly supports multiple Workflow definitions per script.

There is no change to production Workflows, where multiple Workflow definitions per Worker script was already supported.

Wed, 20 Nov 2024 00:00:00 GMTWorkflows - Workflows is now in public beta!https://developers.cloudflare.com/workflows/reference/changelog/#workflows-is-now-in-public-betahttps://developers.cloudflare.com/workflows/reference/changelog/#workflows-is-now-in-public-beta

Workflows, a new product for building reliable, multi-step workflows using Cloudflare Workers, is now in public beta. The public beta is available to any user with a free or paid Workers plan.

A Workflow allows you to define multiple, independent steps that encapsulate errors, automatically retry, persist state, and can run for seconds, minutes, hours or even days. A Workflow can be useful for post-processing data from R2 buckets before querying it, automating a Workers AI RAG pipeline, or managing user signup flows and lifecycle emails.

You can learn more about Workflows in our announcement blog, or start building in our get started guide.

Wed, 23 Oct 2024 00:00:00 GMT Changelog | Workflows Updates to Workflows https://developers.cloudflare.com/workflows/reference/changelog Workflows - Test Workflows locally https://developers.cloudflare.com/workflows/reference/changelog/#test-workflows-locally https://developers.cloudflare.com/workflows/reference/changelog/#test-workflows-locally

Workflows can now be tested with new test APIs available in the "cloudflare:test" module.

More information available in the Vitest integration docs.

Fri, 12 Sep 2025 00:00:00 GMT ...
Workflows - Python Workflows is now open beta https://developers.cloudflare.com/workflows/reference/changelog/#python-workflows-is-now-open-beta https://developers.cloudflare.com/workflows/reference/changelog/#python-workflows-is-now-open-beta

Python Workflows is now in open beta, and available to any developer a free or paid Workers plan.

More information available in the changelog.

Fri, 22 Aug 2025 00:00:00 GMT ...
Workflows - Search for specific Workflows https://developers.cloudflare.com/workflows/reference/changelog/#search-for-specific-workflows https://developers.cloudflare.com/workflows/reference/changelog/#search-for-specific-workflows

With this release, you can search Workflows by name via API.

Wed, 07 May 2025 00:00:00 GMT ...
Workflows - Workflow deletion and more https://developers.cloudflare.com/workflows/reference/changelog/#workflow-deletion-and-more https://developers.cloudflare.com/workflows/reference/changelog/#workflow-deletion-and-more

Workflows can now be deleted (from the Dashboard/UI or via API), and the maximum length limit for event types and instance IDs was increased to 100 characters.

Also, this release fixes a bug where a delay of 0 in step config retries would fail.

Tue, 29 Apr 2025 00:00:00 GMT ...
Workflows - Workflows is now Generally Available https://developers.cloudflare.com/workflows/reference/changelog/#workflows-is-now-generally-available https://developers.cloudflare.com/workflows/reference/changelog/#workflows-is-now-generally-available

Workflows is now Generally Available (or "GA").

This release includes the following new features:

  • A new waitForEvent API that allows a Workflow to wait for an event to occur before continuing execution.
  • Increased concurrency: you can run up to 4,500 Workflow instances concurrently — and this will continue to grow.
  • Improved observability, including new CPU time metrics that allow you to better understand which Workflow instances are consuming the most resources and/or contributing to your bill.
  • Support for vitest for testing Workflows locally and in CI/CD pipelines.

More information available in the changelog.

Mon, 07 Apr 2025 00:00:00 GMT ...
Workflows - Concurrent Workflow instances limits increased https://developers.cloudflare.com/workflows/reference/changelog/#concurrent-workflow-instances-limits-increased https://developers.cloudflare.com/workflows/reference/changelog/#concurrent-workflow-instances-limits-increased

Workflows now supports up to 4,500 concurrent (running) instances, up from the previous limit of 100.

More information available in the changelog.

Tue, 25 Feb 2025 00:00:00 GMT ...
Workflows - Behavior improvements https://developers.cloudflare.com/workflows/reference/changelog/#behavior-improvements https://developers.cloudflare.com/workflows/reference/changelog/#behavior-improvements

Improved Workflows execution that prevents Workflows instances from getting stuck, and allows stuck instances to become unstuck.

Also, improved the reliability of Workflows step retry counts, and improved Instance ID validation.

Tue, 11 Feb 2025 00:00:00 GMT ...
Workflows - Major bugfixes and improvements https://developers.cloudflare.com/workflows/reference/changelog/#major-bugfixes-and-improvements https://developers.cloudflare.com/workflows/reference/changelog/#major-bugfixes-and-improvements

With this release, some bug were fixed:

  • event.timestamp is now Date, fixing a regression.
  • Fixed issue where instances without metadata were not terminated as expected.

Also, this release makes Workflows execution more reliable for accounts with high loads.

Thu, 23 Jan 2025 00:00:00 GMT ...
Workflows - Improved Wrangler local dev experience for steps' output, matching production https://developers.cloudflare.com/workflows/reference/changelog/#improved-wrangler-local-dev-experience-for-steps-output-matching-production https://developers.cloudflare.com/workflows/reference/changelog/#improved-wrangler-local-dev-experience-for-steps-output-matching-production

Previously, in local dev, the output field would return the list of successful steps outputs in the workflow. This is not expected behavior compared to production workflows (where the output is the actual return of the run function).

This release aligns the local dev output field behavior with the production behavior.

Thu, 09 Jan 2025 00:00:00 GMT ...
Workflows - Better instance control, improved queued logic, and step limit increased https://developers.cloudflare.com/workflows/reference/changelog/#better-instance-control-improved-queued-logic-and-step-limit-increased https://developers.cloudflare.com/workflows/reference/changelog/#better-instance-control-improved-queued-logic-and-step-limit-increased

Workflows can now be terminated and pause instances from a queued state and the ID of an instance is now exposed via the WorkflowEvent parameter.

Also, the mechanism to queue instances was improved to force miss-behaved queued instances to be automatically errored.

Workflows now allow you to define up to 1024 steps in a single Workflow definition, up from the previous limit of 512. This limit will continue to increase during the course of the open beta.

Thu, 19 Dec 2024 00:00:00 GMT ...
Workflows - New queue instances logic https://developers.cloudflare.com/workflows/reference/changelog/#new-queue-instances-logic https://developers.cloudflare.com/workflows/reference/changelog/#new-queue-instances-logic

Introduction of a new mechanism to queue instances, which will prevent instances from getting stuck on queued status forever.

Mon, 09 Dec 2024 00:00:00 GMT ...
Workflows - Step limit increased https://developers.cloudflare.com/workflows/reference/changelog/#step-limit-increased https://developers.cloudflare.com/workflows/reference/changelog/#step-limit-increased

Workflows now allow you to define up to 512 steps in a single Workflow definition, up from the previous limit of 256. This limit will continue to increase during the course of the open beta.

If you have Workflows that need more steps, we recommend delegating additional work to other Workflows by triggering a new Workflow from within a step and passing any state as parameters to that Workflow instance.

Sat, 30 Nov 2024 00:00:00 GMT ...
Workflows - Fixed create instance API in Workers bindings https://developers.cloudflare.com/workflows/reference/changelog/#fixed-create-instance-api-in-workers-bindings https://developers.cloudflare.com/workflows/reference/changelog/#fixed-create-instance-api-in-workers-bindings

You can now call create() without any arguments when using the Workers API for Workflows. Workflows will automatically generate the ID of the Workflow on your behalf.

This addresses a bug that caused calls to create() to fail when provided with no arguments.

Thu, 21 Nov 2024 00:00:00 GMT ...
Workflows - Multiple Workflows in local development now supported https://developers.cloudflare.com/workflows/reference/changelog/#multiple-workflows-in-local-development-now-supported https://developers.cloudflare.com/workflows/reference/changelog/#multiple-workflows-in-local-development-now-supported

Local development with wrangler dev now correctly supports multiple Workflow definitions per script.

There is no change to production Workflows, where multiple Workflow definitions per Worker script was already supported.

Wed, 20 Nov 2024 00:00:00 GMT ...
Workflows - Workflows is now in public beta! https://developers.cloudflare.com/workflows/reference/changelog/#workflows-is-now-in-public-beta https://developers.cloudflare.com/workflows/reference/changelog/#workflows-is-now-in-public-beta

Workflows, a new product for building reliable, multi-step workflows using Cloudflare Workers, is now in public beta. The public beta is available to any user with a free or paid Workers plan.

A Workflow allows you to define multiple, independent steps that encapsulate errors, automatically retry, persist state, and can run for seconds, minutes, hours or even days. A Workflow can be useful for post-processing data from R2 buckets before querying it, automating a Workers AI RAG pipeline, or managing user signup flows and lifecycle emails.

You can learn more about Workflows in our announcement blog, or start building in our get started guide.

Wed, 23 Oct 2024 00:00:00 GMT ...
...
...
--- # Cloudflare API | Workflows [![Logo](https://developers.cloudflare.com/logo.svg)Cloudflare API](https://developers.cloudflare.com/api/) HTTP [Docs](https://developers.cloudflare.com/) SearchCTRL+K API Reference [Overview](https://developers.cloudflare.com/api/) Account & User Management [Abuse Reports](https://developers.cloudflare.com/api/resources/abuse_reports/) [Accounts](https://developers.cloudflare.com/api/resources/accounts/) [Alerting](https://developers.cloudflare.com/api/resources/alerting/) [Audit Logs](https://developers.cloudflare.com/api/resources/audit_logs/) [Billing](https://developers.cloudflare.com/api/resources/billing/) [IAM](https://developers.cloudflare.com/api/resources/iam/) [Memberships](https://developers.cloudflare.com/api/resources/memberships/) [Resource Sharing](https://developers.cloudflare.com/api/resources/resource_sharing/) [User](https://developers.cloudflare.com/api/resources/user/) [Custom Pages](https://developers.cloudflare.com/api/resources/custom_pages/) AI [AI](https://developers.cloudflare.com/api/resources/ai/) [AI Gateway](https://developers.cloudflare.com/api/resources/ai_gateway/) [AI Search](https://developers.cloudflare.com/api/resources/ai_search/) [Vectorize](https://developers.cloudflare.com/api/resources/vectorize/) Certificate Management [ACM](https://developers.cloudflare.com/api/resources/acm/) [Certificate Authorities](https://developers.cloudflare.com/api/resources/certificate_authorities/) [Client Certificates](https://developers.cloudflare.com/api/resources/client_certificates/) [Custom Certificates](https://developers.cloudflare.com/api/resources/custom_certificates/) [Custom Hostnames](https://developers.cloudflare.com/api/resources/custom_hostnames/) [DCV Delegation](https://developers.cloudflare.com/api/resources/dcv_delegation/) [Hostnames](https://developers.cloudflare.com/api/resources/hostnames/) [Keyless Certificates](https://developers.cloudflare.com/api/resources/keyless_certificates/) [MTLS Certificates](https://developers.cloudflare.com/api/resources/mtls_certificates/) [Origin CA Certificates](https://developers.cloudflare.com/api/resources/origin_ca_certificates/) [Origin Post Quantum Encryption](https://developers.cloudflare.com/api/resources/origin_post_quantum_encryption/) [Origin TLS Client Auth](https://developers.cloudflare.com/api/resources/origin_tls_client_auth/) [SSL](https://developers.cloudflare.com/api/resources/ssl/) Cloudflare One [Email Security](https://developers.cloudflare.com/api/resources/email_security/) [Zero Trust](https://developers.cloudflare.com/api/resources/zero_trust/) DNS [Account Custom Nameservers](https://developers.cloudflare.com/api/resources/custom_nameservers/) [DNS](https://developers.cloudflare.com/api/resources/dns/) [DNS Firewall](https://developers.cloudflare.com/api/resources/dns_firewall/) Domain/Zone Management [Registrar](https://developers.cloudflare.com/api/resources/registrar/) [Zones](https://developers.cloudflare.com/api/resources/zones/) IP Addresses [Addressing](https://developers.cloudflare.com/api/resources/addressing/) [IPs](https://developers.cloudflare.com/api/resources/ips/) Media [Calls](https://developers.cloudflare.com/api/resources/calls/) [Images](https://developers.cloudflare.com/api/resources/images/) [Stream](https://developers.cloudflare.com/api/resources/stream/) [Realtime Kit](https://developers.cloudflare.com/api/resources/realtime_kit/) Networking [Magic Cloud Networking](https://developers.cloudflare.com/api/resources/magic_cloud_networking/) [Magic Network Monitoring](https://developers.cloudflare.com/api/resources/magic_network_monitoring/) [Magic Transit](https://developers.cloudflare.com/api/resources/magic_transit/) [Network Interconnects](https://developers.cloudflare.com/api/resources/network_interconnects/) Observability [Diagnostics](https://developers.cloudflare.com/api/resources/diagnostics/) [Healthchecks](https://developers.cloudflare.com/api/resources/healthchecks/) [Logpush](https://developers.cloudflare.com/api/resources/logpush/) [Logs](https://developers.cloudflare.com/api/resources/logs/) [RUM](https://developers.cloudflare.com/api/resources/rum/) [Security Center](https://developers.cloudflare.com/api/resources/security_center/) Radar [Radar](https://developers.cloudflare.com/api/resources/radar/) Routing & Performance [Argo](https://developers.cloudflare.com/api/resources/argo/) [Cache](https://developers.cloudflare.com/api/resources/cache/) [Email Routing](https://developers.cloudflare.com/api/resources/email_routing/) [Load Balancers](https://developers.cloudflare.com/api/resources/load_balancers/) [Smart Shield](https://developers.cloudflare.com/api/resources/smart_shield/) [Spectrum](https://developers.cloudflare.com/api/resources/spectrum/) [Speed](https://developers.cloudflare.com/api/resources/speed/) [Waiting Rooms](https://developers.cloudflare.com/api/resources/waiting_rooms/) [Web3](https://developers.cloudflare.com/api/resources/web3/) Rules [Cloud Connector](https://developers.cloudflare.com/api/resources/cloud_connector/) [Managed Transforms](https://developers.cloudflare.com/api/resources/managed_transforms/) [Page Rules](https://developers.cloudflare.com/api/resources/page_rules/) [Rules Lists](https://developers.cloudflare.com/api/resources/rules/) [Rules Trace](https://developers.cloudflare.com/api/resources/request_tracers/) [Rulesets](https://developers.cloudflare.com/api/resources/rulesets/) [Snippets](https://developers.cloudflare.com/api/resources/snippets/) [URL Normalization](https://developers.cloudflare.com/api/resources/url_normalization/) Security [API Gateway](https://developers.cloudflare.com/api/resources/api_gateway/) [Schema Validation](https://developers.cloudflare.com/api/resources/schema_validation/) [Bot Management](https://developers.cloudflare.com/api/resources/bot_management/) [Botnet Feed](https://developers.cloudflare.com/api/resources/botnet_feed/) [Content Scanning](https://developers.cloudflare.com/api/resources/content_scanning/) [Filters](https://developers.cloudflare.com/api/resources/filters/) [Firewall](https://developers.cloudflare.com/api/resources/firewall/) [Fraud](https://developers.cloudflare.com/api/resources/fraud/) [Leaked Credential Checks](https://developers.cloudflare.com/api/resources/leaked_credential_checks/) [Page Shield](https://developers.cloudflare.com/api/resources/page_shield/) [Rate Limits](https://developers.cloudflare.com/api/resources/rate_limits/) [Security TXT](https://developers.cloudflare.com/api/resources/security_txt/) [Token Validation](https://developers.cloudflare.com/api/resources/token_validation/) [Turnstile](https://developers.cloudflare.com/api/resources/turnstile/) Storage & Databases [D1](https://developers.cloudflare.com/api/resources/d1/) [Hyperdrive](https://developers.cloudflare.com/api/resources/hyperdrive/) [KV](https://developers.cloudflare.com/api/resources/kv/) [Queues](https://developers.cloudflare.com/api/resources/queues/) [R2](https://developers.cloudflare.com/api/resources/r2/) [R2 Data Catalog](https://developers.cloudflare.com/api/resources/r2_data_catalog/) [Secrets Store](https://developers.cloudflare.com/api/resources/secrets_store/) [Pipelines](https://developers.cloudflare.com/api/resources/pipelines/) Threat Intelligence [Brand Protection](https://developers.cloudflare.com/api/resources/brand_protection/) [Cloudforce One](https://developers.cloudflare.com/api/resources/cloudforce_one/) [Intel](https://developers.cloudflare.com/api/resources/intel/) [URL Scanner](https://developers.cloudflare.com/api/resources/url_scanner/) Workers & Pages [Browser Rendering](https://developers.cloudflare.com/api/resources/browser_rendering/) [Durable Objects](https://developers.cloudflare.com/api/resources/durable_objects/) [Pages](https://developers.cloudflare.com/api/resources/pages/) [Workers](https://developers.cloudflare.com/api/resources/workers/) [Workers Builds](https://developers.cloudflare.com/api/resources/workers_builds/) [Workers For Platforms](https://developers.cloudflare.com/api/resources/workers_for_platforms/) [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [List all Workflows](https://developers.cloudflare.com/api/resources/workflows/methods/list/) [Get Workflow details](https://developers.cloudflare.com/api/resources/workflows/methods/get/) [Create/modify Workflow](https://developers.cloudflare.com/api/resources/workflows/methods/update/) [Deletes a Workflow](https://developers.cloudflare.com/api/resources/workflows/methods/delete/) * * * [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) * * * [Versions](https://developers.cloudflare.com/api/resources/workflows/subresources/versions/) Shared [Shared](https://developers.cloudflare.com/api/resources/$shared/) [Organizations](https://developers.cloudflare.com/api/resources/organizations/) [Connectivity](https://developers.cloudflare.com/api/resources/connectivity/) [Zaraz](https://developers.cloudflare.com/api/resources/zaraz/) [AutoRAG](https://developers.cloudflare.com/api/resources/autorag/) ### Workflows workflows Methods List All Workflows -> V4PagePaginationArray<{ id, class\_name, created\_on, 5 more... }\> get/accounts/{account\_id}/workflows List all Workflows Get Workflow Details -> Envelope<{ id, class\_name, created\_on, 5 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name} Get Workflow details Create Modify Workflow -> Envelope<{ id, class\_name, created\_on, 7 more... }\> put/accounts/{account\_id}/workflows/{workflow\_name} Create/modify Workflow Deletes A Workflow -> Envelope<{ status, success }\> delete/accounts/{account\_id}/workflows/{workflow\_name} Deletes a Workflow. This only deletes the Workflow and does not delete or modify any Worker associated to this Workflow or bounded to it. [Workflows](https://developers.cloudflare.com/api/resources/workflows/) ### Instances workflows.instances Methods List Of Workflow Instances -> V4PagePaginationArray<{ id, created\_on, ended\_on, 5 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/instances List of workflow instances Get Logs And Status From Instance -> Envelope<{ end, error, output, 8 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id} Get logs and status from instance Create A New Workflow Instance -> Envelope<{ id, status, version\_id, 1 more... }\> post/accounts/{account\_id}/workflows/{workflow\_name}/instances Create a new workflow instance Batch Create New Workflow Instances -> SinglePage<{ id, status, version\_id, 1 more... }\> post/accounts/{account\_id}/workflows/{workflow\_name}/instances/batch Batch create new Workflow instances [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) ### Events workflows.instances.events Methods Send Event To Instance -> Envelope post/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id}/events/{event\_type} Send event to instance [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) ### Status workflows.instances.status Methods Change Status Of Instance -> Envelope<{ status, timestamp }\> patch/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id}/status Change status of instance [Workflows](https://developers.cloudflare.com/api/resources/workflows/) ### Versions workflows.versions Methods List Deployed Workflow Versions -> V4PagePaginationArray<{ id, class\_name, created\_on, 2 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/versions List deployed Workflow versions Get Workflow Version Details -> Envelope<{ id, class\_name, created\_on, 2 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/versions/{version\_id} Get Workflow version details ![](https://id.rlcdn.com/464526.gif)![](https://cdn.bizible.com/ipv?_biz_r=&_biz_h=-1719903975&_biz_u=f46c0880560b4b259156cf970db64b17&_biz_l=https%3A%2F%2Fdevelopers.cloudflare.com%2Fapi%2Fresources%2Fworkflows%2F&_biz_t=1769490482381&_biz_i=Cloudflare%20API%20%7C%20Workflows&_biz_n=2&rnd=867286&cdn_o=a&_biz_z=1769490482382) --- # Cloudflare API | Workflows › Create Modify Workflow [![Logo](https://developers.cloudflare.com/logo.svg)Cloudflare API](https://developers.cloudflare.com/api/) HTTP [Docs](https://developers.cloudflare.com/) SearchCTRL+K API Reference [Overview](https://developers.cloudflare.com/api/) Account & User Management [Abuse Reports](https://developers.cloudflare.com/api/resources/abuse_reports/) [Accounts](https://developers.cloudflare.com/api/resources/accounts/) [Alerting](https://developers.cloudflare.com/api/resources/alerting/) [Audit Logs](https://developers.cloudflare.com/api/resources/audit_logs/) [Billing](https://developers.cloudflare.com/api/resources/billing/) [IAM](https://developers.cloudflare.com/api/resources/iam/) [Memberships](https://developers.cloudflare.com/api/resources/memberships/) [Resource Sharing](https://developers.cloudflare.com/api/resources/resource_sharing/) [User](https://developers.cloudflare.com/api/resources/user/) [Custom Pages](https://developers.cloudflare.com/api/resources/custom_pages/) AI [AI](https://developers.cloudflare.com/api/resources/ai/) [AI Gateway](https://developers.cloudflare.com/api/resources/ai_gateway/) [AI Search](https://developers.cloudflare.com/api/resources/ai_search/) [Vectorize](https://developers.cloudflare.com/api/resources/vectorize/) Certificate Management [ACM](https://developers.cloudflare.com/api/resources/acm/) [Certificate Authorities](https://developers.cloudflare.com/api/resources/certificate_authorities/) [Client Certificates](https://developers.cloudflare.com/api/resources/client_certificates/) [Custom Certificates](https://developers.cloudflare.com/api/resources/custom_certificates/) [Custom Hostnames](https://developers.cloudflare.com/api/resources/custom_hostnames/) [DCV Delegation](https://developers.cloudflare.com/api/resources/dcv_delegation/) [Hostnames](https://developers.cloudflare.com/api/resources/hostnames/) [Keyless Certificates](https://developers.cloudflare.com/api/resources/keyless_certificates/) [MTLS Certificates](https://developers.cloudflare.com/api/resources/mtls_certificates/) [Origin CA Certificates](https://developers.cloudflare.com/api/resources/origin_ca_certificates/) [Origin Post Quantum Encryption](https://developers.cloudflare.com/api/resources/origin_post_quantum_encryption/) [Origin TLS Client Auth](https://developers.cloudflare.com/api/resources/origin_tls_client_auth/) [SSL](https://developers.cloudflare.com/api/resources/ssl/) Cloudflare One [Email Security](https://developers.cloudflare.com/api/resources/email_security/) [Zero Trust](https://developers.cloudflare.com/api/resources/zero_trust/) DNS [Account Custom Nameservers](https://developers.cloudflare.com/api/resources/custom_nameservers/) [DNS](https://developers.cloudflare.com/api/resources/dns/) [DNS Firewall](https://developers.cloudflare.com/api/resources/dns_firewall/) Domain/Zone Management [Registrar](https://developers.cloudflare.com/api/resources/registrar/) [Zones](https://developers.cloudflare.com/api/resources/zones/) IP Addresses [Addressing](https://developers.cloudflare.com/api/resources/addressing/) [IPs](https://developers.cloudflare.com/api/resources/ips/) Media [Calls](https://developers.cloudflare.com/api/resources/calls/) [Images](https://developers.cloudflare.com/api/resources/images/) [Stream](https://developers.cloudflare.com/api/resources/stream/) [Realtime Kit](https://developers.cloudflare.com/api/resources/realtime_kit/) Networking [Magic Cloud Networking](https://developers.cloudflare.com/api/resources/magic_cloud_networking/) [Magic Network Monitoring](https://developers.cloudflare.com/api/resources/magic_network_monitoring/) [Magic Transit](https://developers.cloudflare.com/api/resources/magic_transit/) [Network Interconnects](https://developers.cloudflare.com/api/resources/network_interconnects/) Observability [Diagnostics](https://developers.cloudflare.com/api/resources/diagnostics/) [Healthchecks](https://developers.cloudflare.com/api/resources/healthchecks/) [Logpush](https://developers.cloudflare.com/api/resources/logpush/) [Logs](https://developers.cloudflare.com/api/resources/logs/) [RUM](https://developers.cloudflare.com/api/resources/rum/) [Security Center](https://developers.cloudflare.com/api/resources/security_center/) Radar [Radar](https://developers.cloudflare.com/api/resources/radar/) Routing & Performance [Argo](https://developers.cloudflare.com/api/resources/argo/) [Cache](https://developers.cloudflare.com/api/resources/cache/) [Email Routing](https://developers.cloudflare.com/api/resources/email_routing/) [Load Balancers](https://developers.cloudflare.com/api/resources/load_balancers/) [Smart Shield](https://developers.cloudflare.com/api/resources/smart_shield/) [Spectrum](https://developers.cloudflare.com/api/resources/spectrum/) [Speed](https://developers.cloudflare.com/api/resources/speed/) [Waiting Rooms](https://developers.cloudflare.com/api/resources/waiting_rooms/) [Web3](https://developers.cloudflare.com/api/resources/web3/) Rules [Cloud Connector](https://developers.cloudflare.com/api/resources/cloud_connector/) [Managed Transforms](https://developers.cloudflare.com/api/resources/managed_transforms/) [Page Rules](https://developers.cloudflare.com/api/resources/page_rules/) [Rules Lists](https://developers.cloudflare.com/api/resources/rules/) [Rules Trace](https://developers.cloudflare.com/api/resources/request_tracers/) [Rulesets](https://developers.cloudflare.com/api/resources/rulesets/) [Snippets](https://developers.cloudflare.com/api/resources/snippets/) [URL Normalization](https://developers.cloudflare.com/api/resources/url_normalization/) Security [API Gateway](https://developers.cloudflare.com/api/resources/api_gateway/) [Schema Validation](https://developers.cloudflare.com/api/resources/schema_validation/) [Bot Management](https://developers.cloudflare.com/api/resources/bot_management/) [Botnet Feed](https://developers.cloudflare.com/api/resources/botnet_feed/) [Content Scanning](https://developers.cloudflare.com/api/resources/content_scanning/) [Filters](https://developers.cloudflare.com/api/resources/filters/) [Firewall](https://developers.cloudflare.com/api/resources/firewall/) [Fraud](https://developers.cloudflare.com/api/resources/fraud/) [Leaked Credential Checks](https://developers.cloudflare.com/api/resources/leaked_credential_checks/) [Page Shield](https://developers.cloudflare.com/api/resources/page_shield/) [Rate Limits](https://developers.cloudflare.com/api/resources/rate_limits/) [Security TXT](https://developers.cloudflare.com/api/resources/security_txt/) [Token Validation](https://developers.cloudflare.com/api/resources/token_validation/) [Turnstile](https://developers.cloudflare.com/api/resources/turnstile/) Storage & Databases [D1](https://developers.cloudflare.com/api/resources/d1/) [Hyperdrive](https://developers.cloudflare.com/api/resources/hyperdrive/) [KV](https://developers.cloudflare.com/api/resources/kv/) [Queues](https://developers.cloudflare.com/api/resources/queues/) [R2](https://developers.cloudflare.com/api/resources/r2/) [R2 Data Catalog](https://developers.cloudflare.com/api/resources/r2_data_catalog/) [Secrets Store](https://developers.cloudflare.com/api/resources/secrets_store/) [Pipelines](https://developers.cloudflare.com/api/resources/pipelines/) Threat Intelligence [Brand Protection](https://developers.cloudflare.com/api/resources/brand_protection/) [Cloudforce One](https://developers.cloudflare.com/api/resources/cloudforce_one/) [Intel](https://developers.cloudflare.com/api/resources/intel/) [URL Scanner](https://developers.cloudflare.com/api/resources/url_scanner/) Workers & Pages [Browser Rendering](https://developers.cloudflare.com/api/resources/browser_rendering/) [Durable Objects](https://developers.cloudflare.com/api/resources/durable_objects/) [Pages](https://developers.cloudflare.com/api/resources/pages/) [Workers](https://developers.cloudflare.com/api/resources/workers/) [Workers Builds](https://developers.cloudflare.com/api/resources/workers_builds/) [Workers For Platforms](https://developers.cloudflare.com/api/resources/workers_for_platforms/) [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [List all Workflows](https://developers.cloudflare.com/api/resources/workflows/methods/list/) [Get Workflow details](https://developers.cloudflare.com/api/resources/workflows/methods/get/) [Create/modify Workflow](https://developers.cloudflare.com/api/resources/workflows/methods/update/) [Deletes a Workflow](https://developers.cloudflare.com/api/resources/workflows/methods/delete/) * * * [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) * * * [Versions](https://developers.cloudflare.com/api/resources/workflows/subresources/versions/) Shared [Shared](https://developers.cloudflare.com/api/resources/$shared/) [Organizations](https://developers.cloudflare.com/api/resources/organizations/) [Connectivity](https://developers.cloudflare.com/api/resources/connectivity/) [Zaraz](https://developers.cloudflare.com/api/resources/zaraz/) [AutoRAG](https://developers.cloudflare.com/api/resources/autorag/) ### Workflows workflows Methods List All Workflows -> V4PagePaginationArray<{ id, class\_name, created\_on, 5 more... }\> get/accounts/{account\_id}/workflows List all Workflows Get Workflow Details -> Envelope<{ id, class\_name, created\_on, 5 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name} Get Workflow details Create Modify Workflow -> Envelope<{ id, class\_name, created\_on, 7 more... }\> put/accounts/{account\_id}/workflows/{workflow\_name} Create/modify Workflow Security API Token The preferred authorization scheme for interacting with the Cloudflare API. [Create a token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) . Example: Authorization: Bearer Sn3lZJTBX6kkg7OdcBUAxOO963GEIyGQqnFTOFYY Accepted Permissions (at least one required) Workers Scripts Write path Parameters account\_id: string workflow\_name: string (maxLength: 64, minLength: 1) Body parameters class\_name: string (maxLength: 255, minLength: 1) script\_name: string (maxLength: 255, minLength: 1) Response fields errors: Array<{ code, message }\> messages: Array<{ code, message }\> result: { id, class\_name, created\_on, 7 more... } success: true result\_info: { count, per\_page, total\_count, 2 more... } Optional Request example cURL curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workflows/$WORKFLOW_NAME \ -X PUT \ -H 'Content-Type: application/json' \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ -d '{ "class_name": "x", "script_name": "x" }' 200Example { "errors": [\ {\ "code": 0,\ "message": "message"\ }\ ], "messages": [\ {\ "code": 0,\ "message": "message"\ }\ ], "result": { "id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", "class_name": "class_name", "created_on": "2019-12-27T18:11:19.117Z", "is_deleted": 0, "modified_on": "2019-12-27T18:11:19.117Z", "name": "x", "script_name": "script_name", "terminator_running": 0, "triggered_on": "2019-12-27T18:11:19.117Z", "version_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e" }, "success": true, "result_info": { "count": 0, "per_page": 0, "total_count": 0, "cursor": "cursor", "page": 0 } } Deletes A Workflow -> Envelope<{ status, success }\> delete/accounts/{account\_id}/workflows/{workflow\_name} Deletes a Workflow. This only deletes the Workflow and does not delete or modify any Worker associated to this Workflow or bounded to it. [Workflows](https://developers.cloudflare.com/api/resources/workflows/) ### Instances workflows.instances Methods List Of Workflow Instances -> V4PagePaginationArray<{ id, created\_on, ended\_on, 5 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/instances List of workflow instances Get Logs And Status From Instance -> Envelope<{ end, error, output, 8 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id} Get logs and status from instance Create A New Workflow Instance -> Envelope<{ id, status, version\_id, 1 more... }\> post/accounts/{account\_id}/workflows/{workflow\_name}/instances Create a new workflow instance Batch Create New Workflow Instances -> SinglePage<{ id, status, version\_id, 1 more... }\> post/accounts/{account\_id}/workflows/{workflow\_name}/instances/batch Batch create new Workflow instances [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) ### Events workflows.instances.events Methods Send Event To Instance -> Envelope post/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id}/events/{event\_type} Send event to instance [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) ### Status workflows.instances.status Methods Change Status Of Instance -> Envelope<{ status, timestamp }\> patch/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id}/status Change status of instance [Workflows](https://developers.cloudflare.com/api/resources/workflows/) ### Versions workflows.versions Methods List Deployed Workflow Versions -> V4PagePaginationArray<{ id, class\_name, created\_on, 2 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/versions List deployed Workflow versions Get Workflow Version Details -> Envelope<{ id, class\_name, created\_on, 2 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/versions/{version\_id} Get Workflow version details ![](https://id.rlcdn.com/464526.gif)![](https://cdn.bizible.com/ipv?_biz_r=&_biz_h=-1719903975&_biz_u=f46c0880560b4b259156cf970db64b17&_biz_l=https%3A%2F%2Fdevelopers.cloudflare.com%2Fapi%2Fresources%2Fworkflows%2F&_biz_t=1769490482381&_biz_i=Cloudflare%20API%20%7C%20Workflows&_biz_n=2&rnd=867286&cdn_o=a&_biz_z=1769490482556)![](https://cdn.bizible.com/ipv?_biz_r=&_biz_h=-1719903975&_biz_u=f46c0880560b4b259156cf970db64b17&_biz_l=https%3A%2F%2Fdevelopers.cloudflare.com%2Fapi%2Fresources%2Fworkflows%2Fmethods%2Fupdate%2F&_biz_t=1769490482555&_biz_i=Cloudflare%20API%20%7C%20Workflows%20%E2%80%BA%20Create%20Modify%20Workflow&_biz_n=3&rnd=308207&cdn_o=a&_biz_z=1769490482556) --- # Cloudflare API | Workflows › Get Workflow Details [![Logo](https://developers.cloudflare.com/logo.svg)Cloudflare API](https://developers.cloudflare.com/api/) HTTP [Docs](https://developers.cloudflare.com/) SearchCTRL+K API Reference [Overview](https://developers.cloudflare.com/api/) Account & User Management [Abuse Reports](https://developers.cloudflare.com/api/resources/abuse_reports/) [Accounts](https://developers.cloudflare.com/api/resources/accounts/) [Alerting](https://developers.cloudflare.com/api/resources/alerting/) [Audit Logs](https://developers.cloudflare.com/api/resources/audit_logs/) [Billing](https://developers.cloudflare.com/api/resources/billing/) [IAM](https://developers.cloudflare.com/api/resources/iam/) [Memberships](https://developers.cloudflare.com/api/resources/memberships/) [Resource Sharing](https://developers.cloudflare.com/api/resources/resource_sharing/) [User](https://developers.cloudflare.com/api/resources/user/) [Custom Pages](https://developers.cloudflare.com/api/resources/custom_pages/) AI [AI](https://developers.cloudflare.com/api/resources/ai/) [AI Gateway](https://developers.cloudflare.com/api/resources/ai_gateway/) [AI Search](https://developers.cloudflare.com/api/resources/ai_search/) [Vectorize](https://developers.cloudflare.com/api/resources/vectorize/) Certificate Management [ACM](https://developers.cloudflare.com/api/resources/acm/) [Certificate Authorities](https://developers.cloudflare.com/api/resources/certificate_authorities/) [Client Certificates](https://developers.cloudflare.com/api/resources/client_certificates/) [Custom Certificates](https://developers.cloudflare.com/api/resources/custom_certificates/) [Custom Hostnames](https://developers.cloudflare.com/api/resources/custom_hostnames/) [DCV Delegation](https://developers.cloudflare.com/api/resources/dcv_delegation/) [Hostnames](https://developers.cloudflare.com/api/resources/hostnames/) [Keyless Certificates](https://developers.cloudflare.com/api/resources/keyless_certificates/) [MTLS Certificates](https://developers.cloudflare.com/api/resources/mtls_certificates/) [Origin CA Certificates](https://developers.cloudflare.com/api/resources/origin_ca_certificates/) [Origin Post Quantum Encryption](https://developers.cloudflare.com/api/resources/origin_post_quantum_encryption/) [Origin TLS Client Auth](https://developers.cloudflare.com/api/resources/origin_tls_client_auth/) [SSL](https://developers.cloudflare.com/api/resources/ssl/) Cloudflare One [Email Security](https://developers.cloudflare.com/api/resources/email_security/) [Zero Trust](https://developers.cloudflare.com/api/resources/zero_trust/) DNS [Account Custom Nameservers](https://developers.cloudflare.com/api/resources/custom_nameservers/) [DNS](https://developers.cloudflare.com/api/resources/dns/) [DNS Firewall](https://developers.cloudflare.com/api/resources/dns_firewall/) Domain/Zone Management [Registrar](https://developers.cloudflare.com/api/resources/registrar/) [Zones](https://developers.cloudflare.com/api/resources/zones/) IP Addresses [Addressing](https://developers.cloudflare.com/api/resources/addressing/) [IPs](https://developers.cloudflare.com/api/resources/ips/) Media [Calls](https://developers.cloudflare.com/api/resources/calls/) [Images](https://developers.cloudflare.com/api/resources/images/) [Stream](https://developers.cloudflare.com/api/resources/stream/) [Realtime Kit](https://developers.cloudflare.com/api/resources/realtime_kit/) Networking [Magic Cloud Networking](https://developers.cloudflare.com/api/resources/magic_cloud_networking/) [Magic Network Monitoring](https://developers.cloudflare.com/api/resources/magic_network_monitoring/) [Magic Transit](https://developers.cloudflare.com/api/resources/magic_transit/) [Network Interconnects](https://developers.cloudflare.com/api/resources/network_interconnects/) Observability [Diagnostics](https://developers.cloudflare.com/api/resources/diagnostics/) [Healthchecks](https://developers.cloudflare.com/api/resources/healthchecks/) [Logpush](https://developers.cloudflare.com/api/resources/logpush/) [Logs](https://developers.cloudflare.com/api/resources/logs/) [RUM](https://developers.cloudflare.com/api/resources/rum/) [Security Center](https://developers.cloudflare.com/api/resources/security_center/) Radar [Radar](https://developers.cloudflare.com/api/resources/radar/) Routing & Performance [Argo](https://developers.cloudflare.com/api/resources/argo/) [Cache](https://developers.cloudflare.com/api/resources/cache/) [Email Routing](https://developers.cloudflare.com/api/resources/email_routing/) [Load Balancers](https://developers.cloudflare.com/api/resources/load_balancers/) [Smart Shield](https://developers.cloudflare.com/api/resources/smart_shield/) [Spectrum](https://developers.cloudflare.com/api/resources/spectrum/) [Speed](https://developers.cloudflare.com/api/resources/speed/) [Waiting Rooms](https://developers.cloudflare.com/api/resources/waiting_rooms/) [Web3](https://developers.cloudflare.com/api/resources/web3/) Rules [Cloud Connector](https://developers.cloudflare.com/api/resources/cloud_connector/) [Managed Transforms](https://developers.cloudflare.com/api/resources/managed_transforms/) [Page Rules](https://developers.cloudflare.com/api/resources/page_rules/) [Rules Lists](https://developers.cloudflare.com/api/resources/rules/) [Rules Trace](https://developers.cloudflare.com/api/resources/request_tracers/) [Rulesets](https://developers.cloudflare.com/api/resources/rulesets/) [Snippets](https://developers.cloudflare.com/api/resources/snippets/) [URL Normalization](https://developers.cloudflare.com/api/resources/url_normalization/) Security [API Gateway](https://developers.cloudflare.com/api/resources/api_gateway/) [Schema Validation](https://developers.cloudflare.com/api/resources/schema_validation/) [Bot Management](https://developers.cloudflare.com/api/resources/bot_management/) [Botnet Feed](https://developers.cloudflare.com/api/resources/botnet_feed/) [Content Scanning](https://developers.cloudflare.com/api/resources/content_scanning/) [Filters](https://developers.cloudflare.com/api/resources/filters/) [Firewall](https://developers.cloudflare.com/api/resources/firewall/) [Fraud](https://developers.cloudflare.com/api/resources/fraud/) [Leaked Credential Checks](https://developers.cloudflare.com/api/resources/leaked_credential_checks/) [Page Shield](https://developers.cloudflare.com/api/resources/page_shield/) [Rate Limits](https://developers.cloudflare.com/api/resources/rate_limits/) [Security TXT](https://developers.cloudflare.com/api/resources/security_txt/) [Token Validation](https://developers.cloudflare.com/api/resources/token_validation/) [Turnstile](https://developers.cloudflare.com/api/resources/turnstile/) Storage & Databases [D1](https://developers.cloudflare.com/api/resources/d1/) [Hyperdrive](https://developers.cloudflare.com/api/resources/hyperdrive/) [KV](https://developers.cloudflare.com/api/resources/kv/) [Queues](https://developers.cloudflare.com/api/resources/queues/) [R2](https://developers.cloudflare.com/api/resources/r2/) [R2 Data Catalog](https://developers.cloudflare.com/api/resources/r2_data_catalog/) [Secrets Store](https://developers.cloudflare.com/api/resources/secrets_store/) [Pipelines](https://developers.cloudflare.com/api/resources/pipelines/) Threat Intelligence [Brand Protection](https://developers.cloudflare.com/api/resources/brand_protection/) [Cloudforce One](https://developers.cloudflare.com/api/resources/cloudforce_one/) [Intel](https://developers.cloudflare.com/api/resources/intel/) [URL Scanner](https://developers.cloudflare.com/api/resources/url_scanner/) Workers & Pages [Browser Rendering](https://developers.cloudflare.com/api/resources/browser_rendering/) [Durable Objects](https://developers.cloudflare.com/api/resources/durable_objects/) [Pages](https://developers.cloudflare.com/api/resources/pages/) [Workers](https://developers.cloudflare.com/api/resources/workers/) [Workers Builds](https://developers.cloudflare.com/api/resources/workers_builds/) [Workers For Platforms](https://developers.cloudflare.com/api/resources/workers_for_platforms/) [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [List all Workflows](https://developers.cloudflare.com/api/resources/workflows/methods/list/) [Get Workflow details](https://developers.cloudflare.com/api/resources/workflows/methods/get/) [Create/modify Workflow](https://developers.cloudflare.com/api/resources/workflows/methods/update/) [Deletes a Workflow](https://developers.cloudflare.com/api/resources/workflows/methods/delete/) * * * [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) * * * [Versions](https://developers.cloudflare.com/api/resources/workflows/subresources/versions/) Shared [Shared](https://developers.cloudflare.com/api/resources/$shared/) [Organizations](https://developers.cloudflare.com/api/resources/organizations/) [Connectivity](https://developers.cloudflare.com/api/resources/connectivity/) [Zaraz](https://developers.cloudflare.com/api/resources/zaraz/) [AutoRAG](https://developers.cloudflare.com/api/resources/autorag/) ### Workflows workflows Methods List All Workflows -> V4PagePaginationArray<{ id, class\_name, created\_on, 5 more... }\> get/accounts/{account\_id}/workflows List all Workflows Get Workflow Details -> Envelope<{ id, class\_name, created\_on, 5 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name} Get Workflow details Security API Token The preferred authorization scheme for interacting with the Cloudflare API. [Create a token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) . Example: Authorization: Bearer Sn3lZJTBX6kkg7OdcBUAxOO963GEIyGQqnFTOFYY Accepted Permissions (at least one required) Workers Tail Read Workers Scripts Write Workers Scripts Read path Parameters account\_id: string workflow\_name: string (maxLength: 64, minLength: 1) Response fields errors: Array<{ code, message }\> messages: Array<{ code, message }\> result: { id, class\_name, created\_on, 5 more... } success: true result\_info: { count, per\_page, total\_count, 2 more... } Optional Request example cURL curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workflows/$WORKFLOW_NAME \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" 200Example { "errors": [\ {\ "code": 0,\ "message": "message"\ }\ ], "messages": [\ {\ "code": 0,\ "message": "message"\ }\ ], "result": { "id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", "class_name": "class_name", "created_on": "2019-12-27T18:11:19.117Z", "instances": { "complete": 0, "errored": 0, "paused": 0, "queued": 0, "running": 0, "terminated": 0, "waiting": 0, "waitingForPause": 0 }, "modified_on": "2019-12-27T18:11:19.117Z", "name": "x", "script_name": "script_name", "triggered_on": "2019-12-27T18:11:19.117Z" }, "success": true, "result_info": { "count": 0, "per_page": 0, "total_count": 0, "cursor": "cursor", "page": 0 } } Create Modify Workflow -> Envelope<{ id, class\_name, created\_on, 7 more... }\> put/accounts/{account\_id}/workflows/{workflow\_name} Create/modify Workflow Deletes A Workflow -> Envelope<{ status, success }\> delete/accounts/{account\_id}/workflows/{workflow\_name} Deletes a Workflow. This only deletes the Workflow and does not delete or modify any Worker associated to this Workflow or bounded to it. [Workflows](https://developers.cloudflare.com/api/resources/workflows/) ### Instances workflows.instances Methods List Of Workflow Instances -> V4PagePaginationArray<{ id, created\_on, ended\_on, 5 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/instances List of workflow instances Get Logs And Status From Instance -> Envelope<{ end, error, output, 8 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id} Get logs and status from instance Create A New Workflow Instance -> Envelope<{ id, status, version\_id, 1 more... }\> post/accounts/{account\_id}/workflows/{workflow\_name}/instances Create a new workflow instance Batch Create New Workflow Instances -> SinglePage<{ id, status, version\_id, 1 more... }\> post/accounts/{account\_id}/workflows/{workflow\_name}/instances/batch Batch create new Workflow instances [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) ### Events workflows.instances.events Methods Send Event To Instance -> Envelope post/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id}/events/{event\_type} Send event to instance [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) ### Status workflows.instances.status Methods Change Status Of Instance -> Envelope<{ status, timestamp }\> patch/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id}/status Change status of instance [Workflows](https://developers.cloudflare.com/api/resources/workflows/) ### Versions workflows.versions Methods List Deployed Workflow Versions -> V4PagePaginationArray<{ id, class\_name, created\_on, 2 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/versions List deployed Workflow versions Get Workflow Version Details -> Envelope<{ id, class\_name, created\_on, 2 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/versions/{version\_id} Get Workflow version details ![](https://cdn.bizible.com/ipv?_biz_r=&_biz_h=-1719903975&_biz_u=f46c0880560b4b259156cf970db64b17&_biz_l=https%3A%2F%2Fdevelopers.cloudflare.com%2Fapi%2Fresources%2Fworkflows%2F&_biz_t=1769490482381&_biz_i=Cloudflare%20API%20%7C%20Workflows&_biz_n=2&rnd=867286&cdn_o=a&_biz_z=1769490482656)![](https://cdn.bizible.com/ipv?_biz_r=&_biz_h=-1719903975&_biz_u=f46c0880560b4b259156cf970db64b17&_biz_l=https%3A%2F%2Fdevelopers.cloudflare.com%2Fapi%2Fresources%2Fworkflows%2Fmethods%2Fupdate%2F&_biz_t=1769490482555&_biz_i=Cloudflare%20API%20%7C%20Workflows%20%E2%80%BA%20Create%20Modify%20Workflow&_biz_n=3&rnd=308207&cdn_o=a&_biz_z=1769490482656)![](https://cdn.bizible.com/ipv?_biz_r=&_biz_h=-1719903975&_biz_u=f46c0880560b4b259156cf970db64b17&_biz_l=https%3A%2F%2Fdevelopers.cloudflare.com%2Fapi%2Fresources%2Fworkflows%2Fmethods%2Fget%2F&_biz_t=1769490482655&_biz_i=Cloudflare%20API%20%7C%20Workflows%20%E2%80%BA%20Get%20Workflow%20Details&_biz_n=4&rnd=745257&cdn_o=a&_biz_z=1769490482656)![](https://id.rlcdn.com/464526.gif) --- # Cloudflare API | Workflows › Versions [![Logo](https://developers.cloudflare.com/logo.svg)Cloudflare API](https://developers.cloudflare.com/api/) HTTP [Docs](https://developers.cloudflare.com/) SearchCTRL+K API Reference [Overview](https://developers.cloudflare.com/api/) Account & User Management [Abuse Reports](https://developers.cloudflare.com/api/resources/abuse_reports/) [Accounts](https://developers.cloudflare.com/api/resources/accounts/) [Alerting](https://developers.cloudflare.com/api/resources/alerting/) [Audit Logs](https://developers.cloudflare.com/api/resources/audit_logs/) [Billing](https://developers.cloudflare.com/api/resources/billing/) [IAM](https://developers.cloudflare.com/api/resources/iam/) [Memberships](https://developers.cloudflare.com/api/resources/memberships/) [Resource Sharing](https://developers.cloudflare.com/api/resources/resource_sharing/) [User](https://developers.cloudflare.com/api/resources/user/) [Custom Pages](https://developers.cloudflare.com/api/resources/custom_pages/) AI [AI](https://developers.cloudflare.com/api/resources/ai/) [AI Gateway](https://developers.cloudflare.com/api/resources/ai_gateway/) [AI Search](https://developers.cloudflare.com/api/resources/ai_search/) [Vectorize](https://developers.cloudflare.com/api/resources/vectorize/) Certificate Management [ACM](https://developers.cloudflare.com/api/resources/acm/) [Certificate Authorities](https://developers.cloudflare.com/api/resources/certificate_authorities/) [Client Certificates](https://developers.cloudflare.com/api/resources/client_certificates/) [Custom Certificates](https://developers.cloudflare.com/api/resources/custom_certificates/) [Custom Hostnames](https://developers.cloudflare.com/api/resources/custom_hostnames/) [DCV Delegation](https://developers.cloudflare.com/api/resources/dcv_delegation/) [Hostnames](https://developers.cloudflare.com/api/resources/hostnames/) [Keyless Certificates](https://developers.cloudflare.com/api/resources/keyless_certificates/) [MTLS Certificates](https://developers.cloudflare.com/api/resources/mtls_certificates/) [Origin CA Certificates](https://developers.cloudflare.com/api/resources/origin_ca_certificates/) [Origin Post Quantum Encryption](https://developers.cloudflare.com/api/resources/origin_post_quantum_encryption/) [Origin TLS Client Auth](https://developers.cloudflare.com/api/resources/origin_tls_client_auth/) [SSL](https://developers.cloudflare.com/api/resources/ssl/) Cloudflare One [Email Security](https://developers.cloudflare.com/api/resources/email_security/) [Zero Trust](https://developers.cloudflare.com/api/resources/zero_trust/) DNS [Account Custom Nameservers](https://developers.cloudflare.com/api/resources/custom_nameservers/) [DNS](https://developers.cloudflare.com/api/resources/dns/) [DNS Firewall](https://developers.cloudflare.com/api/resources/dns_firewall/) Domain/Zone Management [Registrar](https://developers.cloudflare.com/api/resources/registrar/) [Zones](https://developers.cloudflare.com/api/resources/zones/) IP Addresses [Addressing](https://developers.cloudflare.com/api/resources/addressing/) [IPs](https://developers.cloudflare.com/api/resources/ips/) Media [Calls](https://developers.cloudflare.com/api/resources/calls/) [Images](https://developers.cloudflare.com/api/resources/images/) [Stream](https://developers.cloudflare.com/api/resources/stream/) [Realtime Kit](https://developers.cloudflare.com/api/resources/realtime_kit/) Networking [Magic Cloud Networking](https://developers.cloudflare.com/api/resources/magic_cloud_networking/) [Magic Network Monitoring](https://developers.cloudflare.com/api/resources/magic_network_monitoring/) [Magic Transit](https://developers.cloudflare.com/api/resources/magic_transit/) [Network Interconnects](https://developers.cloudflare.com/api/resources/network_interconnects/) Observability [Diagnostics](https://developers.cloudflare.com/api/resources/diagnostics/) [Healthchecks](https://developers.cloudflare.com/api/resources/healthchecks/) [Logpush](https://developers.cloudflare.com/api/resources/logpush/) [Logs](https://developers.cloudflare.com/api/resources/logs/) [RUM](https://developers.cloudflare.com/api/resources/rum/) [Security Center](https://developers.cloudflare.com/api/resources/security_center/) Radar [Radar](https://developers.cloudflare.com/api/resources/radar/) Routing & Performance [Argo](https://developers.cloudflare.com/api/resources/argo/) [Cache](https://developers.cloudflare.com/api/resources/cache/) [Email Routing](https://developers.cloudflare.com/api/resources/email_routing/) [Load Balancers](https://developers.cloudflare.com/api/resources/load_balancers/) [Smart Shield](https://developers.cloudflare.com/api/resources/smart_shield/) [Spectrum](https://developers.cloudflare.com/api/resources/spectrum/) [Speed](https://developers.cloudflare.com/api/resources/speed/) [Waiting Rooms](https://developers.cloudflare.com/api/resources/waiting_rooms/) [Web3](https://developers.cloudflare.com/api/resources/web3/) Rules [Cloud Connector](https://developers.cloudflare.com/api/resources/cloud_connector/) [Managed Transforms](https://developers.cloudflare.com/api/resources/managed_transforms/) [Page Rules](https://developers.cloudflare.com/api/resources/page_rules/) [Rules Lists](https://developers.cloudflare.com/api/resources/rules/) [Rules Trace](https://developers.cloudflare.com/api/resources/request_tracers/) [Rulesets](https://developers.cloudflare.com/api/resources/rulesets/) [Snippets](https://developers.cloudflare.com/api/resources/snippets/) [URL Normalization](https://developers.cloudflare.com/api/resources/url_normalization/) Security [API Gateway](https://developers.cloudflare.com/api/resources/api_gateway/) [Schema Validation](https://developers.cloudflare.com/api/resources/schema_validation/) [Bot Management](https://developers.cloudflare.com/api/resources/bot_management/) [Botnet Feed](https://developers.cloudflare.com/api/resources/botnet_feed/) [Content Scanning](https://developers.cloudflare.com/api/resources/content_scanning/) [Filters](https://developers.cloudflare.com/api/resources/filters/) [Firewall](https://developers.cloudflare.com/api/resources/firewall/) [Fraud](https://developers.cloudflare.com/api/resources/fraud/) [Leaked Credential Checks](https://developers.cloudflare.com/api/resources/leaked_credential_checks/) [Page Shield](https://developers.cloudflare.com/api/resources/page_shield/) [Rate Limits](https://developers.cloudflare.com/api/resources/rate_limits/) [Security TXT](https://developers.cloudflare.com/api/resources/security_txt/) [Token Validation](https://developers.cloudflare.com/api/resources/token_validation/) [Turnstile](https://developers.cloudflare.com/api/resources/turnstile/) Storage & Databases [D1](https://developers.cloudflare.com/api/resources/d1/) [Hyperdrive](https://developers.cloudflare.com/api/resources/hyperdrive/) [KV](https://developers.cloudflare.com/api/resources/kv/) [Queues](https://developers.cloudflare.com/api/resources/queues/) [R2](https://developers.cloudflare.com/api/resources/r2/) [R2 Data Catalog](https://developers.cloudflare.com/api/resources/r2_data_catalog/) [Secrets Store](https://developers.cloudflare.com/api/resources/secrets_store/) [Pipelines](https://developers.cloudflare.com/api/resources/pipelines/) Threat Intelligence [Brand Protection](https://developers.cloudflare.com/api/resources/brand_protection/) [Cloudforce One](https://developers.cloudflare.com/api/resources/cloudforce_one/) [Intel](https://developers.cloudflare.com/api/resources/intel/) [URL Scanner](https://developers.cloudflare.com/api/resources/url_scanner/) Workers & Pages [Browser Rendering](https://developers.cloudflare.com/api/resources/browser_rendering/) [Durable Objects](https://developers.cloudflare.com/api/resources/durable_objects/) [Pages](https://developers.cloudflare.com/api/resources/pages/) [Workers](https://developers.cloudflare.com/api/resources/workers/) [Workers Builds](https://developers.cloudflare.com/api/resources/workers_builds/) [Workers For Platforms](https://developers.cloudflare.com/api/resources/workers_for_platforms/) [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [List all Workflows](https://developers.cloudflare.com/api/resources/workflows/methods/list/) [Get Workflow details](https://developers.cloudflare.com/api/resources/workflows/methods/get/) [Create/modify Workflow](https://developers.cloudflare.com/api/resources/workflows/methods/update/) [Deletes a Workflow](https://developers.cloudflare.com/api/resources/workflows/methods/delete/) * * * [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) * * * [Versions](https://developers.cloudflare.com/api/resources/workflows/subresources/versions/) [List deployed Workflow versions](https://developers.cloudflare.com/api/resources/workflows/subresources/versions/methods/list/) [Get Workflow version details](https://developers.cloudflare.com/api/resources/workflows/subresources/versions/methods/get/) Shared [Shared](https://developers.cloudflare.com/api/resources/$shared/) [Organizations](https://developers.cloudflare.com/api/resources/organizations/) [Connectivity](https://developers.cloudflare.com/api/resources/connectivity/) [Zaraz](https://developers.cloudflare.com/api/resources/zaraz/) [AutoRAG](https://developers.cloudflare.com/api/resources/autorag/) ### Workflows workflows Methods List All Workflows -> V4PagePaginationArray<{ id, class\_name, created\_on, 5 more... }\> get/accounts/{account\_id}/workflows List all Workflows Get Workflow Details -> Envelope<{ id, class\_name, created\_on, 5 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name} Get Workflow details Create Modify Workflow -> Envelope<{ id, class\_name, created\_on, 7 more... }\> put/accounts/{account\_id}/workflows/{workflow\_name} Create/modify Workflow Deletes A Workflow -> Envelope<{ status, success }\> delete/accounts/{account\_id}/workflows/{workflow\_name} Deletes a Workflow. This only deletes the Workflow and does not delete or modify any Worker associated to this Workflow or bounded to it. [Workflows](https://developers.cloudflare.com/api/resources/workflows/) ### Instances workflows.instances Methods List Of Workflow Instances -> V4PagePaginationArray<{ id, created\_on, ended\_on, 5 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/instances List of workflow instances Get Logs And Status From Instance -> Envelope<{ end, error, output, 8 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id} Get logs and status from instance Create A New Workflow Instance -> Envelope<{ id, status, version\_id, 1 more... }\> post/accounts/{account\_id}/workflows/{workflow\_name}/instances Create a new workflow instance Batch Create New Workflow Instances -> SinglePage<{ id, status, version\_id, 1 more... }\> post/accounts/{account\_id}/workflows/{workflow\_name}/instances/batch Batch create new Workflow instances [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) ### Events workflows.instances.events Methods Send Event To Instance -> Envelope post/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id}/events/{event\_type} Send event to instance [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) ### Status workflows.instances.status Methods Change Status Of Instance -> Envelope<{ status, timestamp }\> patch/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id}/status Change status of instance [Workflows](https://developers.cloudflare.com/api/resources/workflows/) ### Versions workflows.versions Methods List Deployed Workflow Versions -> V4PagePaginationArray<{ id, class\_name, created\_on, 2 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/versions List deployed Workflow versions Get Workflow Version Details -> Envelope<{ id, class\_name, created\_on, 2 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/versions/{version\_id} Get Workflow version details ![](https://id.rlcdn.com/464526.gif)![](https://cdn.bizible.com/ipv?_biz_r=&_biz_h=-1719903975&_biz_u=f46c0880560b4b259156cf970db64b17&_biz_l=https%3A%2F%2Fdevelopers.cloudflare.com%2Fapi%2Fresources%2Fworkflows%2F&_biz_t=1769490482381&_biz_i=Cloudflare%20API%20%7C%20Workflows&_biz_n=2&rnd=867286&cdn_o=a&_biz_z=1769490482668)![](https://cdn.bizible.com/ipv?_biz_r=&_biz_h=-1719903975&_biz_u=f46c0880560b4b259156cf970db64b17&_biz_l=https%3A%2F%2Fdevelopers.cloudflare.com%2Fapi%2Fresources%2Fworkflows%2Fmethods%2Fupdate%2F&_biz_t=1769490482555&_biz_i=Cloudflare%20API%20%7C%20Workflows%20%E2%80%BA%20Create%20Modify%20Workflow&_biz_n=3&rnd=308207&cdn_o=a&_biz_z=1769490482669)![](https://cdn.bizible.com/ipv?_biz_r=&_biz_h=-1719903975&_biz_u=f46c0880560b4b259156cf970db64b17&_biz_l=https%3A%2F%2Fdevelopers.cloudflare.com%2Fapi%2Fresources%2Fworkflows%2Fmethods%2Fget%2F&_biz_t=1769490482655&_biz_i=Cloudflare%20API%20%7C%20Workflows%20%E2%80%BA%20Get%20Workflow%20Details&_biz_n=4&rnd=745257&cdn_o=a&_biz_z=1769490482669)![](https://cdn.bizible.com/ipv?_biz_r=&_biz_h=-1719903975&_biz_u=f46c0880560b4b259156cf970db64b17&_biz_l=https%3A%2F%2Fdevelopers.cloudflare.com%2Fapi%2Fresources%2Fworkflows%2Fsubresources%2Fversions%2F&_biz_t=1769490482668&_biz_i=Cloudflare%20API%20%7C%20Workflows%20%E2%80%BA%20Versions&_biz_n=5&rnd=177829&cdn_o=a&_biz_z=1769490482669) --- # Cloudflare API | Workflows › Deletes A Workflow [![Logo](https://developers.cloudflare.com/logo.svg)Cloudflare API](https://developers.cloudflare.com/api/) HTTP [Docs](https://developers.cloudflare.com/) SearchCTRL+K API Reference [Overview](https://developers.cloudflare.com/api/) Account & User Management [Abuse Reports](https://developers.cloudflare.com/api/resources/abuse_reports/) [Accounts](https://developers.cloudflare.com/api/resources/accounts/) [Alerting](https://developers.cloudflare.com/api/resources/alerting/) [Audit Logs](https://developers.cloudflare.com/api/resources/audit_logs/) [Billing](https://developers.cloudflare.com/api/resources/billing/) [IAM](https://developers.cloudflare.com/api/resources/iam/) [Memberships](https://developers.cloudflare.com/api/resources/memberships/) [Resource Sharing](https://developers.cloudflare.com/api/resources/resource_sharing/) [User](https://developers.cloudflare.com/api/resources/user/) [Custom Pages](https://developers.cloudflare.com/api/resources/custom_pages/) AI [AI](https://developers.cloudflare.com/api/resources/ai/) [AI Gateway](https://developers.cloudflare.com/api/resources/ai_gateway/) [AI Search](https://developers.cloudflare.com/api/resources/ai_search/) [Vectorize](https://developers.cloudflare.com/api/resources/vectorize/) Certificate Management [ACM](https://developers.cloudflare.com/api/resources/acm/) [Certificate Authorities](https://developers.cloudflare.com/api/resources/certificate_authorities/) [Client Certificates](https://developers.cloudflare.com/api/resources/client_certificates/) [Custom Certificates](https://developers.cloudflare.com/api/resources/custom_certificates/) [Custom Hostnames](https://developers.cloudflare.com/api/resources/custom_hostnames/) [DCV Delegation](https://developers.cloudflare.com/api/resources/dcv_delegation/) [Hostnames](https://developers.cloudflare.com/api/resources/hostnames/) [Keyless Certificates](https://developers.cloudflare.com/api/resources/keyless_certificates/) [MTLS Certificates](https://developers.cloudflare.com/api/resources/mtls_certificates/) [Origin CA Certificates](https://developers.cloudflare.com/api/resources/origin_ca_certificates/) [Origin Post Quantum Encryption](https://developers.cloudflare.com/api/resources/origin_post_quantum_encryption/) [Origin TLS Client Auth](https://developers.cloudflare.com/api/resources/origin_tls_client_auth/) [SSL](https://developers.cloudflare.com/api/resources/ssl/) Cloudflare One [Email Security](https://developers.cloudflare.com/api/resources/email_security/) [Zero Trust](https://developers.cloudflare.com/api/resources/zero_trust/) DNS [Account Custom Nameservers](https://developers.cloudflare.com/api/resources/custom_nameservers/) [DNS](https://developers.cloudflare.com/api/resources/dns/) [DNS Firewall](https://developers.cloudflare.com/api/resources/dns_firewall/) Domain/Zone Management [Registrar](https://developers.cloudflare.com/api/resources/registrar/) [Zones](https://developers.cloudflare.com/api/resources/zones/) IP Addresses [Addressing](https://developers.cloudflare.com/api/resources/addressing/) [IPs](https://developers.cloudflare.com/api/resources/ips/) Media [Calls](https://developers.cloudflare.com/api/resources/calls/) [Images](https://developers.cloudflare.com/api/resources/images/) [Stream](https://developers.cloudflare.com/api/resources/stream/) [Realtime Kit](https://developers.cloudflare.com/api/resources/realtime_kit/) Networking [Magic Cloud Networking](https://developers.cloudflare.com/api/resources/magic_cloud_networking/) [Magic Network Monitoring](https://developers.cloudflare.com/api/resources/magic_network_monitoring/) [Magic Transit](https://developers.cloudflare.com/api/resources/magic_transit/) [Network Interconnects](https://developers.cloudflare.com/api/resources/network_interconnects/) Observability [Diagnostics](https://developers.cloudflare.com/api/resources/diagnostics/) [Healthchecks](https://developers.cloudflare.com/api/resources/healthchecks/) [Logpush](https://developers.cloudflare.com/api/resources/logpush/) [Logs](https://developers.cloudflare.com/api/resources/logs/) [RUM](https://developers.cloudflare.com/api/resources/rum/) [Security Center](https://developers.cloudflare.com/api/resources/security_center/) Radar [Radar](https://developers.cloudflare.com/api/resources/radar/) Routing & Performance [Argo](https://developers.cloudflare.com/api/resources/argo/) [Cache](https://developers.cloudflare.com/api/resources/cache/) [Email Routing](https://developers.cloudflare.com/api/resources/email_routing/) [Load Balancers](https://developers.cloudflare.com/api/resources/load_balancers/) [Smart Shield](https://developers.cloudflare.com/api/resources/smart_shield/) [Spectrum](https://developers.cloudflare.com/api/resources/spectrum/) [Speed](https://developers.cloudflare.com/api/resources/speed/) [Waiting Rooms](https://developers.cloudflare.com/api/resources/waiting_rooms/) [Web3](https://developers.cloudflare.com/api/resources/web3/) Rules [Cloud Connector](https://developers.cloudflare.com/api/resources/cloud_connector/) [Managed Transforms](https://developers.cloudflare.com/api/resources/managed_transforms/) [Page Rules](https://developers.cloudflare.com/api/resources/page_rules/) [Rules Lists](https://developers.cloudflare.com/api/resources/rules/) [Rules Trace](https://developers.cloudflare.com/api/resources/request_tracers/) [Rulesets](https://developers.cloudflare.com/api/resources/rulesets/) [Snippets](https://developers.cloudflare.com/api/resources/snippets/) [URL Normalization](https://developers.cloudflare.com/api/resources/url_normalization/) Security [API Gateway](https://developers.cloudflare.com/api/resources/api_gateway/) [Schema Validation](https://developers.cloudflare.com/api/resources/schema_validation/) [Bot Management](https://developers.cloudflare.com/api/resources/bot_management/) [Botnet Feed](https://developers.cloudflare.com/api/resources/botnet_feed/) [Content Scanning](https://developers.cloudflare.com/api/resources/content_scanning/) [Filters](https://developers.cloudflare.com/api/resources/filters/) [Firewall](https://developers.cloudflare.com/api/resources/firewall/) [Fraud](https://developers.cloudflare.com/api/resources/fraud/) [Leaked Credential Checks](https://developers.cloudflare.com/api/resources/leaked_credential_checks/) [Page Shield](https://developers.cloudflare.com/api/resources/page_shield/) [Rate Limits](https://developers.cloudflare.com/api/resources/rate_limits/) [Security TXT](https://developers.cloudflare.com/api/resources/security_txt/) [Token Validation](https://developers.cloudflare.com/api/resources/token_validation/) [Turnstile](https://developers.cloudflare.com/api/resources/turnstile/) Storage & Databases [D1](https://developers.cloudflare.com/api/resources/d1/) [Hyperdrive](https://developers.cloudflare.com/api/resources/hyperdrive/) [KV](https://developers.cloudflare.com/api/resources/kv/) [Queues](https://developers.cloudflare.com/api/resources/queues/) [R2](https://developers.cloudflare.com/api/resources/r2/) [R2 Data Catalog](https://developers.cloudflare.com/api/resources/r2_data_catalog/) [Secrets Store](https://developers.cloudflare.com/api/resources/secrets_store/) [Pipelines](https://developers.cloudflare.com/api/resources/pipelines/) Threat Intelligence [Brand Protection](https://developers.cloudflare.com/api/resources/brand_protection/) [Cloudforce One](https://developers.cloudflare.com/api/resources/cloudforce_one/) [Intel](https://developers.cloudflare.com/api/resources/intel/) [URL Scanner](https://developers.cloudflare.com/api/resources/url_scanner/) Workers & Pages [Browser Rendering](https://developers.cloudflare.com/api/resources/browser_rendering/) [Durable Objects](https://developers.cloudflare.com/api/resources/durable_objects/) [Pages](https://developers.cloudflare.com/api/resources/pages/) [Workers](https://developers.cloudflare.com/api/resources/workers/) [Workers Builds](https://developers.cloudflare.com/api/resources/workers_builds/) [Workers For Platforms](https://developers.cloudflare.com/api/resources/workers_for_platforms/) [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [List all Workflows](https://developers.cloudflare.com/api/resources/workflows/methods/list/) [Get Workflow details](https://developers.cloudflare.com/api/resources/workflows/methods/get/) [Create/modify Workflow](https://developers.cloudflare.com/api/resources/workflows/methods/update/) [Deletes a Workflow](https://developers.cloudflare.com/api/resources/workflows/methods/delete/) * * * [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) * * * [Versions](https://developers.cloudflare.com/api/resources/workflows/subresources/versions/) Shared [Shared](https://developers.cloudflare.com/api/resources/$shared/) [Organizations](https://developers.cloudflare.com/api/resources/organizations/) [Connectivity](https://developers.cloudflare.com/api/resources/connectivity/) [Zaraz](https://developers.cloudflare.com/api/resources/zaraz/) [AutoRAG](https://developers.cloudflare.com/api/resources/autorag/) ### Workflows workflows Methods List All Workflows -> V4PagePaginationArray<{ id, class\_name, created\_on, 5 more... }\> get/accounts/{account\_id}/workflows List all Workflows Get Workflow Details -> Envelope<{ id, class\_name, created\_on, 5 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name} Get Workflow details Create Modify Workflow -> Envelope<{ id, class\_name, created\_on, 7 more... }\> put/accounts/{account\_id}/workflows/{workflow\_name} Create/modify Workflow Deletes A Workflow -> Envelope<{ status, success }\> delete/accounts/{account\_id}/workflows/{workflow\_name} Deletes a Workflow. This only deletes the Workflow and does not delete or modify any Worker associated to this Workflow or bounded to it. Security API Token The preferred authorization scheme for interacting with the Cloudflare API. [Create a token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) . Example: Authorization: Bearer Sn3lZJTBX6kkg7OdcBUAxOO963GEIyGQqnFTOFYY Accepted Permissions (at least one required) Workers Scripts Write path Parameters account\_id: string workflow\_name: string (maxLength: 64, minLength: 1) Response fields errors: Array<{ code, message }\> messages: Array<{ code, message }\> result: { status, success } success: true result\_info: { count, per\_page, total\_count, 2 more... } Optional Request example cURL curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workflows/$WORKFLOW_NAME \ -X DELETE \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" 200Example { "errors": [\ {\ "code": 0,\ "message": "message"\ }\ ], "messages": [\ {\ "code": 0,\ "message": "message"\ }\ ], "result": { "status": "ok", "success": true }, "success": true, "result_info": { "count": 0, "per_page": 0, "total_count": 0, "cursor": "cursor", "page": 0 } } [Workflows](https://developers.cloudflare.com/api/resources/workflows/) ### Instances workflows.instances Methods List Of Workflow Instances -> V4PagePaginationArray<{ id, created\_on, ended\_on, 5 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/instances List of workflow instances Get Logs And Status From Instance -> Envelope<{ end, error, output, 8 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id} Get logs and status from instance Create A New Workflow Instance -> Envelope<{ id, status, version\_id, 1 more... }\> post/accounts/{account\_id}/workflows/{workflow\_name}/instances Create a new workflow instance Batch Create New Workflow Instances -> SinglePage<{ id, status, version\_id, 1 more... }\> post/accounts/{account\_id}/workflows/{workflow\_name}/instances/batch Batch create new Workflow instances [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) ### Events workflows.instances.events Methods Send Event To Instance -> Envelope post/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id}/events/{event\_type} Send event to instance [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) ### Status workflows.instances.status Methods Change Status Of Instance -> Envelope<{ status, timestamp }\> patch/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id}/status Change status of instance [Workflows](https://developers.cloudflare.com/api/resources/workflows/) ### Versions workflows.versions Methods List Deployed Workflow Versions -> V4PagePaginationArray<{ id, class\_name, created\_on, 2 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/versions List deployed Workflow versions Get Workflow Version Details -> Envelope<{ id, class\_name, created\_on, 2 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/versions/{version\_id} Get Workflow version details ![](https://id.rlcdn.com/464526.gif)![](https://cdn.bizible.com/ipv?_biz_r=&_biz_h=-1719903975&_biz_u=f46c0880560b4b259156cf970db64b17&_biz_l=https%3A%2F%2Fdevelopers.cloudflare.com%2Fapi%2Fresources%2Fworkflows%2Fmethods%2Fupdate%2F&_biz_t=1769490482555&_biz_i=Cloudflare%20API%20%7C%20Workflows%20%E2%80%BA%20Create%20Modify%20Workflow&_biz_n=3&rnd=308207&cdn_o=a&_biz_z=1769490482675)![](https://cdn.bizible.com/ipv?_biz_r=&_biz_h=-1719903975&_biz_u=f46c0880560b4b259156cf970db64b17&_biz_l=https%3A%2F%2Fdevelopers.cloudflare.com%2Fapi%2Fresources%2Fworkflows%2Fmethods%2Fget%2F&_biz_t=1769490482655&_biz_i=Cloudflare%20API%20%7C%20Workflows%20%E2%80%BA%20Get%20Workflow%20Details&_biz_n=4&rnd=745257&cdn_o=a&_biz_z=1769490482676)![](https://cdn.bizible.com/ipv?_biz_r=&_biz_h=-1719903975&_biz_u=f46c0880560b4b259156cf970db64b17&_biz_l=https%3A%2F%2Fdevelopers.cloudflare.com%2Fapi%2Fresources%2Fworkflows%2Fsubresources%2Fversions%2F&_biz_t=1769490482668&_biz_i=Cloudflare%20API%20%7C%20Workflows%20%E2%80%BA%20Versions&_biz_n=5&rnd=177829&cdn_o=a&_biz_z=1769490482676)![](https://cdn.bizible.com/ipv?_biz_r=&_biz_h=-1719903975&_biz_u=f46c0880560b4b259156cf970db64b17&_biz_l=https%3A%2F%2Fdevelopers.cloudflare.com%2Fapi%2Fresources%2Fworkflows%2Fmethods%2Fdelete%2F&_biz_t=1769490482675&_biz_i=Cloudflare%20API%20%7C%20Workflows%20%E2%80%BA%20Deletes%20A%20Workflow&_biz_n=6&rnd=18348&cdn_o=a&_biz_z=1769490482676) --- # Cloudflare API | Workflows › Instances [![Logo](https://developers.cloudflare.com/logo.svg)Cloudflare API](https://developers.cloudflare.com/api/) HTTP [Docs](https://developers.cloudflare.com/) SearchCTRL+K API Reference [Overview](https://developers.cloudflare.com/api/) Account & User Management [Abuse Reports](https://developers.cloudflare.com/api/resources/abuse_reports/) [Accounts](https://developers.cloudflare.com/api/resources/accounts/) [Alerting](https://developers.cloudflare.com/api/resources/alerting/) [Audit Logs](https://developers.cloudflare.com/api/resources/audit_logs/) [Billing](https://developers.cloudflare.com/api/resources/billing/) [IAM](https://developers.cloudflare.com/api/resources/iam/) [Memberships](https://developers.cloudflare.com/api/resources/memberships/) [Resource Sharing](https://developers.cloudflare.com/api/resources/resource_sharing/) [User](https://developers.cloudflare.com/api/resources/user/) [Custom Pages](https://developers.cloudflare.com/api/resources/custom_pages/) AI [AI](https://developers.cloudflare.com/api/resources/ai/) [AI Gateway](https://developers.cloudflare.com/api/resources/ai_gateway/) [AI Search](https://developers.cloudflare.com/api/resources/ai_search/) [Vectorize](https://developers.cloudflare.com/api/resources/vectorize/) Certificate Management [ACM](https://developers.cloudflare.com/api/resources/acm/) [Certificate Authorities](https://developers.cloudflare.com/api/resources/certificate_authorities/) [Client Certificates](https://developers.cloudflare.com/api/resources/client_certificates/) [Custom Certificates](https://developers.cloudflare.com/api/resources/custom_certificates/) [Custom Hostnames](https://developers.cloudflare.com/api/resources/custom_hostnames/) [DCV Delegation](https://developers.cloudflare.com/api/resources/dcv_delegation/) [Hostnames](https://developers.cloudflare.com/api/resources/hostnames/) [Keyless Certificates](https://developers.cloudflare.com/api/resources/keyless_certificates/) [MTLS Certificates](https://developers.cloudflare.com/api/resources/mtls_certificates/) [Origin CA Certificates](https://developers.cloudflare.com/api/resources/origin_ca_certificates/) [Origin Post Quantum Encryption](https://developers.cloudflare.com/api/resources/origin_post_quantum_encryption/) [Origin TLS Client Auth](https://developers.cloudflare.com/api/resources/origin_tls_client_auth/) [SSL](https://developers.cloudflare.com/api/resources/ssl/) Cloudflare One [Email Security](https://developers.cloudflare.com/api/resources/email_security/) [Zero Trust](https://developers.cloudflare.com/api/resources/zero_trust/) DNS [Account Custom Nameservers](https://developers.cloudflare.com/api/resources/custom_nameservers/) [DNS](https://developers.cloudflare.com/api/resources/dns/) [DNS Firewall](https://developers.cloudflare.com/api/resources/dns_firewall/) Domain/Zone Management [Registrar](https://developers.cloudflare.com/api/resources/registrar/) [Zones](https://developers.cloudflare.com/api/resources/zones/) IP Addresses [Addressing](https://developers.cloudflare.com/api/resources/addressing/) [IPs](https://developers.cloudflare.com/api/resources/ips/) Media [Calls](https://developers.cloudflare.com/api/resources/calls/) [Images](https://developers.cloudflare.com/api/resources/images/) [Stream](https://developers.cloudflare.com/api/resources/stream/) [Realtime Kit](https://developers.cloudflare.com/api/resources/realtime_kit/) Networking [Magic Cloud Networking](https://developers.cloudflare.com/api/resources/magic_cloud_networking/) [Magic Network Monitoring](https://developers.cloudflare.com/api/resources/magic_network_monitoring/) [Magic Transit](https://developers.cloudflare.com/api/resources/magic_transit/) [Network Interconnects](https://developers.cloudflare.com/api/resources/network_interconnects/) Observability [Diagnostics](https://developers.cloudflare.com/api/resources/diagnostics/) [Healthchecks](https://developers.cloudflare.com/api/resources/healthchecks/) [Logpush](https://developers.cloudflare.com/api/resources/logpush/) [Logs](https://developers.cloudflare.com/api/resources/logs/) [RUM](https://developers.cloudflare.com/api/resources/rum/) [Security Center](https://developers.cloudflare.com/api/resources/security_center/) Radar [Radar](https://developers.cloudflare.com/api/resources/radar/) Routing & Performance [Argo](https://developers.cloudflare.com/api/resources/argo/) [Cache](https://developers.cloudflare.com/api/resources/cache/) [Email Routing](https://developers.cloudflare.com/api/resources/email_routing/) [Load Balancers](https://developers.cloudflare.com/api/resources/load_balancers/) [Smart Shield](https://developers.cloudflare.com/api/resources/smart_shield/) [Spectrum](https://developers.cloudflare.com/api/resources/spectrum/) [Speed](https://developers.cloudflare.com/api/resources/speed/) [Waiting Rooms](https://developers.cloudflare.com/api/resources/waiting_rooms/) [Web3](https://developers.cloudflare.com/api/resources/web3/) Rules [Cloud Connector](https://developers.cloudflare.com/api/resources/cloud_connector/) [Managed Transforms](https://developers.cloudflare.com/api/resources/managed_transforms/) [Page Rules](https://developers.cloudflare.com/api/resources/page_rules/) [Rules Lists](https://developers.cloudflare.com/api/resources/rules/) [Rules Trace](https://developers.cloudflare.com/api/resources/request_tracers/) [Rulesets](https://developers.cloudflare.com/api/resources/rulesets/) [Snippets](https://developers.cloudflare.com/api/resources/snippets/) [URL Normalization](https://developers.cloudflare.com/api/resources/url_normalization/) Security [API Gateway](https://developers.cloudflare.com/api/resources/api_gateway/) [Schema Validation](https://developers.cloudflare.com/api/resources/schema_validation/) [Bot Management](https://developers.cloudflare.com/api/resources/bot_management/) [Botnet Feed](https://developers.cloudflare.com/api/resources/botnet_feed/) [Content Scanning](https://developers.cloudflare.com/api/resources/content_scanning/) [Filters](https://developers.cloudflare.com/api/resources/filters/) [Firewall](https://developers.cloudflare.com/api/resources/firewall/) [Fraud](https://developers.cloudflare.com/api/resources/fraud/) [Leaked Credential Checks](https://developers.cloudflare.com/api/resources/leaked_credential_checks/) [Page Shield](https://developers.cloudflare.com/api/resources/page_shield/) [Rate Limits](https://developers.cloudflare.com/api/resources/rate_limits/) [Security TXT](https://developers.cloudflare.com/api/resources/security_txt/) [Token Validation](https://developers.cloudflare.com/api/resources/token_validation/) [Turnstile](https://developers.cloudflare.com/api/resources/turnstile/) Storage & Databases [D1](https://developers.cloudflare.com/api/resources/d1/) [Hyperdrive](https://developers.cloudflare.com/api/resources/hyperdrive/) [KV](https://developers.cloudflare.com/api/resources/kv/) [Queues](https://developers.cloudflare.com/api/resources/queues/) [R2](https://developers.cloudflare.com/api/resources/r2/) [R2 Data Catalog](https://developers.cloudflare.com/api/resources/r2_data_catalog/) [Secrets Store](https://developers.cloudflare.com/api/resources/secrets_store/) [Pipelines](https://developers.cloudflare.com/api/resources/pipelines/) Threat Intelligence [Brand Protection](https://developers.cloudflare.com/api/resources/brand_protection/) [Cloudforce One](https://developers.cloudflare.com/api/resources/cloudforce_one/) [Intel](https://developers.cloudflare.com/api/resources/intel/) [URL Scanner](https://developers.cloudflare.com/api/resources/url_scanner/) Workers & Pages [Browser Rendering](https://developers.cloudflare.com/api/resources/browser_rendering/) [Durable Objects](https://developers.cloudflare.com/api/resources/durable_objects/) [Pages](https://developers.cloudflare.com/api/resources/pages/) [Workers](https://developers.cloudflare.com/api/resources/workers/) [Workers Builds](https://developers.cloudflare.com/api/resources/workers_builds/) [Workers For Platforms](https://developers.cloudflare.com/api/resources/workers_for_platforms/) [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [List all Workflows](https://developers.cloudflare.com/api/resources/workflows/methods/list/) [Get Workflow details](https://developers.cloudflare.com/api/resources/workflows/methods/get/) [Create/modify Workflow](https://developers.cloudflare.com/api/resources/workflows/methods/update/) [Deletes a Workflow](https://developers.cloudflare.com/api/resources/workflows/methods/delete/) * * * [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) [List of workflow instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/methods/list/) [Get logs and status from instance](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/methods/get/) [Create a new workflow instance](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/methods/create/) [Batch create new Workflow instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/methods/bulk/) * * * [Events](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/subresources/events/) * * * [Status](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/subresources/status/) * * * [Versions](https://developers.cloudflare.com/api/resources/workflows/subresources/versions/) Shared [Shared](https://developers.cloudflare.com/api/resources/$shared/) [Organizations](https://developers.cloudflare.com/api/resources/organizations/) [Connectivity](https://developers.cloudflare.com/api/resources/connectivity/) [Zaraz](https://developers.cloudflare.com/api/resources/zaraz/) [AutoRAG](https://developers.cloudflare.com/api/resources/autorag/) ### Workflows workflows Methods List All Workflows -> V4PagePaginationArray<{ id, class\_name, created\_on, 5 more... }\> get/accounts/{account\_id}/workflows List all Workflows Get Workflow Details -> Envelope<{ id, class\_name, created\_on, 5 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name} Get Workflow details Create Modify Workflow -> Envelope<{ id, class\_name, created\_on, 7 more... }\> put/accounts/{account\_id}/workflows/{workflow\_name} Create/modify Workflow Deletes A Workflow -> Envelope<{ status, success }\> delete/accounts/{account\_id}/workflows/{workflow\_name} Deletes a Workflow. This only deletes the Workflow and does not delete or modify any Worker associated to this Workflow or bounded to it. [Workflows](https://developers.cloudflare.com/api/resources/workflows/) ### Instances workflows.instances Methods List Of Workflow Instances -> V4PagePaginationArray<{ id, created\_on, ended\_on, 5 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/instances List of workflow instances Get Logs And Status From Instance -> Envelope<{ end, error, output, 8 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id} Get logs and status from instance Create A New Workflow Instance -> Envelope<{ id, status, version\_id, 1 more... }\> post/accounts/{account\_id}/workflows/{workflow\_name}/instances Create a new workflow instance Batch Create New Workflow Instances -> SinglePage<{ id, status, version\_id, 1 more... }\> post/accounts/{account\_id}/workflows/{workflow\_name}/instances/batch Batch create new Workflow instances [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) ### Events workflows.instances.events Methods Send Event To Instance -> Envelope post/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id}/events/{event\_type} Send event to instance [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) ### Status workflows.instances.status Methods Change Status Of Instance -> Envelope<{ status, timestamp }\> patch/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id}/status Change status of instance [Workflows](https://developers.cloudflare.com/api/resources/workflows/) ### Versions workflows.versions Methods List Deployed Workflow Versions -> V4PagePaginationArray<{ id, class\_name, created\_on, 2 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/versions List deployed Workflow versions Get Workflow Version Details -> Envelope<{ id, class\_name, created\_on, 2 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/versions/{version\_id} Get Workflow version details ![](https://id.rlcdn.com/464526.gif)![](https://cdn.bizible.com/ipv?_biz_r=&_biz_h=-1719903975&_biz_u=f46c0880560b4b259156cf970db64b17&_biz_l=https%3A%2F%2Fdevelopers.cloudflare.com%2Fapi%2Fresources%2Fworkflows%2Fsubresources%2Finstances%2F&_biz_t=1769490483003&_biz_i=Cloudflare%20API%20%7C%20Workflows%20%E2%80%BA%20Instances&_biz_n=7&rnd=156191&cdn_o=a&_biz_z=1769490483003) --- # Agents · Cloudflare Agents docs [Skip to content](https://developers.cloudflare.com/workflows/examples/agents/#_top) Copy page Build Agents on Cloudflare ========================== The Agents SDK enables you to build and deploy AI-powered agents that can autonomously perform tasks, communicate with clients in real time, call AI models, persist state, schedule tasks, run asynchronous workflows, browse the web, query data from your database, support human-in-the-loop interactions, and [a lot more](https://developers.cloudflare.com/agents/api-reference/) . ### Ship your first Agent [](https://developers.cloudflare.com/workflows/examples/agents/#ship-your-first-agent) To use the Agent starter template and create your first Agent with the Agents SDK: 1. Create a new project: npx create-cloudflare@latest --template cloudflare/agents-starter 2. Install dependencies: npm install 3. Set up your environment: Create a `.dev.vars` file: OPENAI_API_KEY=your_openai_api_key 4. Run locally: npm start 5. Deploy: npm run deploy Head to the guide on [building a chat agent](https://developers.cloudflare.com/agents/getting-started/build-a-chat-agent) to learn how the starter project is built and how to use it as a foundation for your own agents. If you're already building on [Workers](https://developers.cloudflare.com/workers/) , you can install the `agents` package directly into an existing project: npm i agents And then define your first Agent by creating a class that extends the `Agent` class: * [JavaScript](https://developers.cloudflare.com/workflows/examples/agents/#tab-panel-723) * [TypeScript](https://developers.cloudflare.com/workflows/examples/agents/#tab-panel-724) import { Agent } from "agents"; export class MyAgent extends Agent { // Define methods on the Agent: // https://developers.cloudflare.com/agents/api-reference/agents-api/ // // Every Agent has built in state via this.setState and this.sql // Built-in scheduling via this.schedule // Agents support WebSockets, HTTP requests, state synchronization and // can run for seconds, minutes or hours: as long as the tasks need.} import { Agent } from "agents"; export class MyAgent extends Agent { // Define methods on the Agent: // https://developers.cloudflare.com/agents/api-reference/agents-api/ // // Every Agent has built in state via this.setState and this.sql // Built-in scheduling via this.schedule // Agents support WebSockets, HTTP requests, state synchronization and // can run for seconds, minutes or hours: as long as the tasks need.} Lastly, add the [Durable Objects](https://developers.cloudflare.com/durable-objects/) binding to your wrangler file: * [wrangler.jsonc](https://developers.cloudflare.com/workflows/examples/agents/#tab-panel-721) * [wrangler.toml](https://developers.cloudflare.com/workflows/examples/agents/#tab-panel-722) { "$schema": "./node_modules/wrangler/config-schema.json", "durable_objects": { "bindings": [ { "name": "MyAgent", "class_name": "MyAgent" } ] }, "migrations": [ { "tag": "v1", "new_sqlite_classes": [ "MyAgent" ] } ]} [[durable_objects.bindings]]name = "MyAgent"class_name = "MyAgent" [[migrations]]tag = "v1"new_sqlite_classes = ["MyAgent"] Dive into the [Agent SDK reference](https://developers.cloudflare.com/agents/api-reference/agents-api/) to learn more about how to use the Agents SDK package and defining an `Agent`. ### Why build agents on Cloudflare? [](https://developers.cloudflare.com/workflows/examples/agents/#why-build-agents-on-cloudflare) We built the Agents SDK with a few things in mind: * **Batteries (state) included**: Agents come with [built-in state management](https://developers.cloudflare.com/agents/api-reference/store-and-sync-state/) , with the ability to automatically sync state between an Agent and clients, trigger events on state changes, and read+write to each Agent's SQL database. * **Communicative**: You can connect to an Agent via [WebSockets](https://developers.cloudflare.com/agents/api-reference/websockets/) and stream updates back to client in real-time. Handle a long-running response from a reasoning model, the results of an [asynchronous workflow](https://developers.cloudflare.com/agents/api-reference/run-workflows/) , or build a chat app that builds on the `useAgent` hook included in the Agents SDK. * **Extensible**: Agents are code. Use the [AI models](https://developers.cloudflare.com/agents/api-reference/using-ai-models/) you want, bring-your-own headless browser service, pull data from your database hosted in another cloud, add your own methods to your Agent and call them. Agents built with Agents SDK can be deployed directly to Cloudflare and run on top of [Durable Objects](https://developers.cloudflare.com/durable-objects/) — which you can think of as stateful micro-servers that can scale to tens of millions — and are able to run wherever they need to. Run your Agents close to a user for low-latency interactivity, close to your data for throughput, and/or anywhere in between. * * * ### Build on the Cloudflare Platform [](https://developers.cloudflare.com/workflows/examples/agents/#build-on-the-cloudflare-platform) **[Workers](https://developers.cloudflare.com/workers/) ** Build serverless applications and deploy instantly across the globe for exceptional performance, reliability, and scale. **[AI Gateway](https://developers.cloudflare.com/ai-gateway/) ** Observe and control your AI applications with caching, rate limiting, request retries, model fallback, and more. **[Vectorize](https://developers.cloudflare.com/vectorize/) ** Build full-stack AI applications with Vectorize, Cloudflare’s vector database. Adding Vectorize enables you to perform tasks such as semantic search, recommendations, anomaly detection or can be used to provide context and memory to an LLM. **[Workers AI](https://developers.cloudflare.com/workers-ai/) ** Run machine learning models, powered by serverless GPUs, on Cloudflare's global network. **[Workflows](https://developers.cloudflare.com/workflows/) ** Build stateful agents that guarantee executions, including automatic retries, persistent state that runs for minutes, hours, days, or weeks. Was this helpful? ----------------- * **Resources** * [API](https://developers.cloudflare.com/api/) * [New to Cloudflare?](https://developers.cloudflare.com/fundamentals/) * [Directory](https://developers.cloudflare.com/directory/) * [Sponsorships](https://developers.cloudflare.com/sponsorships/) * [Open Source](https://github.com/cloudflare) * **Support** * [Help Center](https://support.cloudflare.com/) * [System Status](https://www.cloudflarestatus.com/) * [Compliance](https://www.cloudflare.com/trust-hub/compliance-resources/) * [GDPR](https://www.cloudflare.com/trust-hub/gdpr/) * **Company** * [cloudflare.com](https://www.cloudflare.com/) * [Our team](https://www.cloudflare.com/people/) * [Careers](https://www.cloudflare.com/careers/) * **Tools** * [Cloudflare Radar](https://radar.cloudflare.com/) * [Speed Test](https://speed.cloudflare.com/) * [Is BGP Safe Yet?](https://isbgpsafeyet.com/) * [RPKI Toolkit](https://rpki.cloudflare.com/) * [Certificate Transparency](https://ct.cloudflare.com/) * **Community** * [X](https://x.com/cloudflare) * [Discord](http://discord.cloudflare.com/) * [YouTube](https://www.youtube.com/cloudflare) * [GitHub](https://github.com/cloudflare/cloudflare-docs) * © 2026 Cloudflare, Inc. * [Privacy Policy](https://www.cloudflare.com/privacypolicy/) * [Terms of Use](https://www.cloudflare.com/website-terms/) * [Report Security Issues](https://www.cloudflare.com/disclosure/) * [Trademark](https://www.cloudflare.com/trademark/) * ![privacy options](https://developers.cloudflare.com/_astro/privacyoptions.BWXSiJOZ_22PXh4.svg) Your Privacy Choices Back to top ![](https://id.rlcdn.com/464526.gif)![](https://cdn.bizible.com/ipv?_biz_r=https%3A%2F%2Fdevelopers.cloudflare.com%2Fworkflows%2Fexamples%2Fagents%2F&_biz_h=-1719903975&_biz_u=f46c0880560b4b259156cf970db64b17&_biz_l=https%3A%2F%2Fdevelopers.cloudflare.com%2Fagents%2F&_biz_t=1769490483894&_biz_i=Agents%20%C2%B7%20Cloudflare%20Agents%20docs&_biz_n=8&rnd=611353&cdn_o=a&_biz_z=1769490483895) --- # Cloudflare API | Workflows › List All Workflows [![Logo](https://developers.cloudflare.com/logo.svg)Cloudflare API](https://developers.cloudflare.com/api/) HTTP [Docs](https://developers.cloudflare.com/) SearchCTRL+K API Reference [Overview](https://developers.cloudflare.com/api/) Account & User Management [Abuse Reports](https://developers.cloudflare.com/api/resources/abuse_reports/) [Accounts](https://developers.cloudflare.com/api/resources/accounts/) [Alerting](https://developers.cloudflare.com/api/resources/alerting/) [Audit Logs](https://developers.cloudflare.com/api/resources/audit_logs/) [Billing](https://developers.cloudflare.com/api/resources/billing/) [IAM](https://developers.cloudflare.com/api/resources/iam/) [Memberships](https://developers.cloudflare.com/api/resources/memberships/) [Resource Sharing](https://developers.cloudflare.com/api/resources/resource_sharing/) [User](https://developers.cloudflare.com/api/resources/user/) [Custom Pages](https://developers.cloudflare.com/api/resources/custom_pages/) AI [AI](https://developers.cloudflare.com/api/resources/ai/) [AI Gateway](https://developers.cloudflare.com/api/resources/ai_gateway/) [AI Search](https://developers.cloudflare.com/api/resources/ai_search/) [Vectorize](https://developers.cloudflare.com/api/resources/vectorize/) Certificate Management [ACM](https://developers.cloudflare.com/api/resources/acm/) [Certificate Authorities](https://developers.cloudflare.com/api/resources/certificate_authorities/) [Client Certificates](https://developers.cloudflare.com/api/resources/client_certificates/) [Custom Certificates](https://developers.cloudflare.com/api/resources/custom_certificates/) [Custom Hostnames](https://developers.cloudflare.com/api/resources/custom_hostnames/) [DCV Delegation](https://developers.cloudflare.com/api/resources/dcv_delegation/) [Hostnames](https://developers.cloudflare.com/api/resources/hostnames/) [Keyless Certificates](https://developers.cloudflare.com/api/resources/keyless_certificates/) [MTLS Certificates](https://developers.cloudflare.com/api/resources/mtls_certificates/) [Origin CA Certificates](https://developers.cloudflare.com/api/resources/origin_ca_certificates/) [Origin Post Quantum Encryption](https://developers.cloudflare.com/api/resources/origin_post_quantum_encryption/) [Origin TLS Client Auth](https://developers.cloudflare.com/api/resources/origin_tls_client_auth/) [SSL](https://developers.cloudflare.com/api/resources/ssl/) Cloudflare One [Email Security](https://developers.cloudflare.com/api/resources/email_security/) [Zero Trust](https://developers.cloudflare.com/api/resources/zero_trust/) DNS [Account Custom Nameservers](https://developers.cloudflare.com/api/resources/custom_nameservers/) [DNS](https://developers.cloudflare.com/api/resources/dns/) [DNS Firewall](https://developers.cloudflare.com/api/resources/dns_firewall/) Domain/Zone Management [Registrar](https://developers.cloudflare.com/api/resources/registrar/) [Zones](https://developers.cloudflare.com/api/resources/zones/) IP Addresses [Addressing](https://developers.cloudflare.com/api/resources/addressing/) [IPs](https://developers.cloudflare.com/api/resources/ips/) Media [Calls](https://developers.cloudflare.com/api/resources/calls/) [Images](https://developers.cloudflare.com/api/resources/images/) [Stream](https://developers.cloudflare.com/api/resources/stream/) [Realtime Kit](https://developers.cloudflare.com/api/resources/realtime_kit/) Networking [Magic Cloud Networking](https://developers.cloudflare.com/api/resources/magic_cloud_networking/) [Magic Network Monitoring](https://developers.cloudflare.com/api/resources/magic_network_monitoring/) [Magic Transit](https://developers.cloudflare.com/api/resources/magic_transit/) [Network Interconnects](https://developers.cloudflare.com/api/resources/network_interconnects/) Observability [Diagnostics](https://developers.cloudflare.com/api/resources/diagnostics/) [Healthchecks](https://developers.cloudflare.com/api/resources/healthchecks/) [Logpush](https://developers.cloudflare.com/api/resources/logpush/) [Logs](https://developers.cloudflare.com/api/resources/logs/) [RUM](https://developers.cloudflare.com/api/resources/rum/) [Security Center](https://developers.cloudflare.com/api/resources/security_center/) Radar [Radar](https://developers.cloudflare.com/api/resources/radar/) Routing & Performance [Argo](https://developers.cloudflare.com/api/resources/argo/) [Cache](https://developers.cloudflare.com/api/resources/cache/) [Email Routing](https://developers.cloudflare.com/api/resources/email_routing/) [Load Balancers](https://developers.cloudflare.com/api/resources/load_balancers/) [Smart Shield](https://developers.cloudflare.com/api/resources/smart_shield/) [Spectrum](https://developers.cloudflare.com/api/resources/spectrum/) [Speed](https://developers.cloudflare.com/api/resources/speed/) [Waiting Rooms](https://developers.cloudflare.com/api/resources/waiting_rooms/) [Web3](https://developers.cloudflare.com/api/resources/web3/) Rules [Cloud Connector](https://developers.cloudflare.com/api/resources/cloud_connector/) [Managed Transforms](https://developers.cloudflare.com/api/resources/managed_transforms/) [Page Rules](https://developers.cloudflare.com/api/resources/page_rules/) [Rules Lists](https://developers.cloudflare.com/api/resources/rules/) [Rules Trace](https://developers.cloudflare.com/api/resources/request_tracers/) [Rulesets](https://developers.cloudflare.com/api/resources/rulesets/) [Snippets](https://developers.cloudflare.com/api/resources/snippets/) [URL Normalization](https://developers.cloudflare.com/api/resources/url_normalization/) Security [API Gateway](https://developers.cloudflare.com/api/resources/api_gateway/) [Schema Validation](https://developers.cloudflare.com/api/resources/schema_validation/) [Bot Management](https://developers.cloudflare.com/api/resources/bot_management/) [Botnet Feed](https://developers.cloudflare.com/api/resources/botnet_feed/) [Content Scanning](https://developers.cloudflare.com/api/resources/content_scanning/) [Filters](https://developers.cloudflare.com/api/resources/filters/) [Firewall](https://developers.cloudflare.com/api/resources/firewall/) [Fraud](https://developers.cloudflare.com/api/resources/fraud/) [Leaked Credential Checks](https://developers.cloudflare.com/api/resources/leaked_credential_checks/) [Page Shield](https://developers.cloudflare.com/api/resources/page_shield/) [Rate Limits](https://developers.cloudflare.com/api/resources/rate_limits/) [Security TXT](https://developers.cloudflare.com/api/resources/security_txt/) [Token Validation](https://developers.cloudflare.com/api/resources/token_validation/) [Turnstile](https://developers.cloudflare.com/api/resources/turnstile/) Storage & Databases [D1](https://developers.cloudflare.com/api/resources/d1/) [Hyperdrive](https://developers.cloudflare.com/api/resources/hyperdrive/) [KV](https://developers.cloudflare.com/api/resources/kv/) [Queues](https://developers.cloudflare.com/api/resources/queues/) [R2](https://developers.cloudflare.com/api/resources/r2/) [R2 Data Catalog](https://developers.cloudflare.com/api/resources/r2_data_catalog/) [Secrets Store](https://developers.cloudflare.com/api/resources/secrets_store/) [Pipelines](https://developers.cloudflare.com/api/resources/pipelines/) Threat Intelligence [Brand Protection](https://developers.cloudflare.com/api/resources/brand_protection/) [Cloudforce One](https://developers.cloudflare.com/api/resources/cloudforce_one/) [Intel](https://developers.cloudflare.com/api/resources/intel/) [URL Scanner](https://developers.cloudflare.com/api/resources/url_scanner/) Workers & Pages [Browser Rendering](https://developers.cloudflare.com/api/resources/browser_rendering/) [Durable Objects](https://developers.cloudflare.com/api/resources/durable_objects/) [Pages](https://developers.cloudflare.com/api/resources/pages/) [Workers](https://developers.cloudflare.com/api/resources/workers/) [Workers Builds](https://developers.cloudflare.com/api/resources/workers_builds/) [Workers For Platforms](https://developers.cloudflare.com/api/resources/workers_for_platforms/) [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [List all Workflows](https://developers.cloudflare.com/api/resources/workflows/methods/list/) [Get Workflow details](https://developers.cloudflare.com/api/resources/workflows/methods/get/) [Create/modify Workflow](https://developers.cloudflare.com/api/resources/workflows/methods/update/) [Deletes a Workflow](https://developers.cloudflare.com/api/resources/workflows/methods/delete/) * * * [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) * * * [Versions](https://developers.cloudflare.com/api/resources/workflows/subresources/versions/) Shared [Shared](https://developers.cloudflare.com/api/resources/$shared/) [Organizations](https://developers.cloudflare.com/api/resources/organizations/) [Connectivity](https://developers.cloudflare.com/api/resources/connectivity/) [Zaraz](https://developers.cloudflare.com/api/resources/zaraz/) [AutoRAG](https://developers.cloudflare.com/api/resources/autorag/) ### Workflows workflows Methods List All Workflows -> V4PagePaginationArray<{ id, class\_name, created\_on, 5 more... }\> get/accounts/{account\_id}/workflows List all Workflows Security API Token The preferred authorization scheme for interacting with the Cloudflare API. [Create a token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) . Example: Authorization: Bearer Sn3lZJTBX6kkg7OdcBUAxOO963GEIyGQqnFTOFYY Accepted Permissions (at least one required) Workers Tail Read Workers Scripts Write Workers Scripts Read path Parameters account\_id: string query Parameters page: number Optional (minimum: 1, default: 1) per\_page: number Optional (maximum: 100, minimum: 1, default: 10) search: string Optional (maxLength: 64, minLength: 1) Allows filtering workflows\` name. Response fields errors: Array<{ code, message }\> messages: Array<{ code, message }\> result: Array<{ id, class\_name, created\_on, 5 more... }\> success: true result\_info: { count, per\_page, total\_count, 2 more... } Optional Request example cURL curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workflows \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" 200Example { "errors": [\ {\ "code": 0,\ "message": "message"\ }\ ], "messages": [\ {\ "code": 0,\ "message": "message"\ }\ ], "result": [\ {\ "id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",\ "class_name": "class_name",\ "created_on": "2019-12-27T18:11:19.117Z",\ "instances": {\ "complete": 0,\ "errored": 0,\ "paused": 0,\ "queued": 0,\ "running": 0,\ "terminated": 0,\ "waiting": 0,\ "waitingForPause": 0\ },\ "modified_on": "2019-12-27T18:11:19.117Z",\ "name": "x",\ "script_name": "script_name",\ "triggered_on": "2019-12-27T18:11:19.117Z"\ }\ ], "success": true, "result_info": { "count": 0, "per_page": 0, "total_count": 0, "cursor": "cursor", "page": 0 } } Get Workflow Details -> Envelope<{ id, class\_name, created\_on, 5 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name} Get Workflow details Create Modify Workflow -> Envelope<{ id, class\_name, created\_on, 7 more... }\> put/accounts/{account\_id}/workflows/{workflow\_name} Create/modify Workflow Deletes A Workflow -> Envelope<{ status, success }\> delete/accounts/{account\_id}/workflows/{workflow\_name} Deletes a Workflow. This only deletes the Workflow and does not delete or modify any Worker associated to this Workflow or bounded to it. [Workflows](https://developers.cloudflare.com/api/resources/workflows/) ### Instances workflows.instances Methods List Of Workflow Instances -> V4PagePaginationArray<{ id, created\_on, ended\_on, 5 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/instances List of workflow instances Get Logs And Status From Instance -> Envelope<{ end, error, output, 8 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id} Get logs and status from instance Create A New Workflow Instance -> Envelope<{ id, status, version\_id, 1 more... }\> post/accounts/{account\_id}/workflows/{workflow\_name}/instances Create a new workflow instance Batch Create New Workflow Instances -> SinglePage<{ id, status, version\_id, 1 more... }\> post/accounts/{account\_id}/workflows/{workflow\_name}/instances/batch Batch create new Workflow instances [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) ### Events workflows.instances.events Methods Send Event To Instance -> Envelope post/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id}/events/{event\_type} Send event to instance [Workflows](https://developers.cloudflare.com/api/resources/workflows/) [Instances](https://developers.cloudflare.com/api/resources/workflows/subresources/instances/) ### Status workflows.instances.status Methods Change Status Of Instance -> Envelope<{ status, timestamp }\> patch/accounts/{account\_id}/workflows/{workflow\_name}/instances/{instance\_id}/status Change status of instance [Workflows](https://developers.cloudflare.com/api/resources/workflows/) ### Versions workflows.versions Methods List Deployed Workflow Versions -> V4PagePaginationArray<{ id, class\_name, created\_on, 2 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/versions List deployed Workflow versions Get Workflow Version Details -> Envelope<{ id, class\_name, created\_on, 2 more... }\> get/accounts/{account\_id}/workflows/{workflow\_name}/versions/{version\_id} Get Workflow version details ![](https://cdn.bizible.com/ipv?_biz_r=https%3A%2F%2Fdevelopers.cloudflare.com%2Fworkflows%2Fworkflows-api%2F&_biz_h=-1719903975&_biz_u=f46c0880560b4b259156cf970db64b17&_biz_l=https%3A%2F%2Fdevelopers.cloudflare.com%2Fapi%2Fresources%2Fworkflows%2Fmethods%2Flist%2F&_biz_t=1769490484016&_biz_i=Cloudflare%20API%20%7C%20Workflows%20%E2%80%BA%20List%20All%20Workflows&_biz_n=9&rnd=679602&cdn_o=a&_biz_z=1769490484016)![](https://id.rlcdn.com/464526.gif) ---