Skip to content

Changelog

New updates and improvements at Cloudflare.

Workflows
hero image
  1. All wrangler workflows commands now accept a --local flag to target a Workflow running in a local wrangler dev session instead of the production API.

    You can now manage the full Workflow lifecycle locally, including triggering Workflows, listing instances, pausing, resuming, restarting, terminating, and sending events:

    Terminal window
    npx wrangler workflows list --local
    npx wrangler workflows trigger my-workflow --local
    npx wrangler workflows instances list my-workflow --local
    npx wrangler workflows instances pause my-workflow <INSTANCE_ID> --local
    npx wrangler workflows instances send-event my-workflow <INSTANCE_ID> --type my-event --local

    All commands also accept --port to target a specific wrangler dev session (defaults to 8787).

    For more information, refer to Workflows local development.

  1. Workflow instance methods pause(), resume(), restart(), and terminate() are now available in local development when using wrangler dev.

    You can now test the full Workflow instance lifecycle locally:

    TypeScript
    const instance = await env.MY_WORKFLOW.create({
    id: "my-instance-id",
    });
    await instance.pause(); // pauses a running workflow instance
    await instance.resume(); // resumes a paused instance
    await instance.restart(); // restarts the instance from the beginning
    await instance.terminate(); // terminates the instance immediately
  1. Cloudflare Workflows allows you to configure specific retry logic for each step in your workflow execution. Now, you can access which retry attempt is currently executing for calls to step.do():

    TypeScript
    await step.do("my-step", async (ctx) => {
    // ctx.attempt is 1 on first try, 2 on first retry, etc.
    console.log(`Attempt ${ctx.attempt}`);
    });

    You can use the step context for improved logging & observability, progressive backoff, or conditional logic in your workflow definition.

    Note that the current attempt number is 1-indexed. For more information on retry behavior, refer to Sleeping and Retrying.

  1. Each Workflow on Workers Paid now supports 10,000 steps by default, configurable up to 25,000 steps in your wrangler.jsonc file:

    {
    "workflows": [
    {
    "name": "my-workflow",
    "binding": "MY_WORKFLOW",
    "class_name": "MyWorkflow",
    "limits": {
    "steps": 25000
    }
    }
    ]
    }

    Previously, each instance was limited to 1,024 steps. Now, Workflows can support more complex, long-running executions without the additional complexity of recursive or child workflow calls.

    Note that the maximum persisted state limit per Workflow instance remains 100 MB for Workers Free and 1 GB for Workers Paid. Refer to Workflows limits for more information.

  1. Cloudflare Workflows now automatically generates visual diagrams from your code

    Your Workflow is parsed to provide a visual map of the Workflow structure, allowing you to:

    • Understand how steps connect and execute
    • Visualize loops and nested logic
    • Follow branching paths for conditional logic
    Example diagram

    You can collapse loops and nested logic to see the high-level flow, or expand them to see every step.

    Workflow diagrams are available in beta for all JavaScript and TypeScript Workflows. Find your Workflows in the Cloudflare dashboard to see their diagrams.

  1. The latest release of the Agents SDK brings first-class support for Cloudflare Workflows, synchronous state management, and new scheduling capabilities.

    Cloudflare Workflows integration

    Agents excel at real-time communication and state management. Workflows excel at durable execution. Together, they enable powerful patterns where Agents handle WebSocket connections while Workflows handle long-running tasks, retries, and human-in-the-loop flows.

    Use the new AgentWorkflow class to define workflows with typed access to your Agent:

    JavaScript
    import { AgentWorkflow } from "agents/workflows";
    export class ProcessingWorkflow extends AgentWorkflow {
    async run(event, step) {
    // Call Agent methods via RPC
    await this.agent.updateStatus(event.payload.taskId, "processing");
    // Non-durable: progress reporting to clients
    await this.reportProgress({ step: "process", percent: 0.5 });
    this.broadcastToClients({ type: "update", taskId: event.payload.taskId });
    // Durable via step: idempotent, won't repeat on retry
    await step.mergeAgentState({ taskProgress: 0.5 });
    const result = await step.do("process", async () => {
    return processData(event.payload.data);
    });
    await step.reportComplete(result);
    return result;
    }
    }

    Start workflows from your Agent with runWorkflow() and handle lifecycle events:

    JavaScript
    export class MyAgent extends Agent {
    async startTask(taskId, data) {
    const instanceId = await this.runWorkflow("PROCESSING_WORKFLOW", {
    taskId,
    data,
    });
    return { instanceId };
    }
    async onWorkflowProgress(workflowName, instanceId, progress) {
    this.broadcast(JSON.stringify({ type: "progress", progress }));
    }
    async onWorkflowComplete(workflowName, instanceId, result) {
    console.log(`Workflow ${instanceId} completed`);
    }
    async onWorkflowError(workflowName, instanceId, error) {
    console.error(`Workflow ${instanceId} failed:`, error);
    }
    }

    Key workflow methods on your Agent:

    • runWorkflow(workflowName, params, options?) — Start a workflow with optional metadata
    • getWorkflow(workflowId) / getWorkflows(criteria?) — Query workflows with cursor-based pagination
    • approveWorkflow(workflowId) / rejectWorkflow(workflowId) — Human-in-the-loop approval flows
    • pauseWorkflow(), resumeWorkflow(), terminateWorkflow() — Workflow control

    Synchronous setState()

    State updates are now synchronous with a new validateStateChange() validation hook:

    JavaScript
    export class MyAgent extends Agent {
    validateStateChange(oldState, newState) {
    // Return false to reject the change
    if (newState.count < 0) return false;
    // Return modified state to transform
    return { ...newState, lastUpdated: Date.now() };
    }
    }

    scheduleEvery() for recurring tasks

    The new scheduleEvery() method enables fixed-interval recurring tasks with built-in overlap prevention:

    JavaScript
    // Run every 5 minutes
    await this.scheduleEvery("syncData", 5 * 60 * 1000, { source: "api" });

    Callable system improvements

    • Client-side RPC timeout — Set timeouts on callable method invocations
    • StreamingResponse.error(message) — Graceful stream error signaling
    • getCallableMethods() — Introspection API for discovering callable methods
    • Connection close handling — Pending calls are automatically rejected on disconnect
    JavaScript
    await agent.call("method", [args], {
    timeout: 5000,
    stream: { onChunk, onDone, onError },
    });

    Email and routing enhancements

    Secure email reply routing — Email replies are now secured with HMAC-SHA256 signed headers, preventing unauthorized routing of emails to agent instances.

    Routing improvements:

    • basePath option to bypass default URL construction for custom routing
    • Server-sent identity — Agents send name and agent type on connect
    • New onIdentity and onIdentityChange callbacks on the client
    JavaScript
    const agent = useAgent({
    basePath: "user",
    onIdentity: (name, agentType) => console.log(`Connected to ${name}`),
    });

    Upgrade

    To update to the latest version:

    Terminal window
    npm i agents@latest

    For the complete Workflows API reference and patterns, see Run Workflows.

  1. We've raised the Cloudflare Workflows account-level limits for all accounts on the Workers paid plan:

    • Instance creation rate increased from 100 workflow instances per 10 seconds to 100 instances per second
    • Concurrency limit increased from 4,500 to 10,000 workflow instances per account

    These increases mean you can create new instances up to 10x faster, and have more workflow instances concurrently executing. To learn more and get started with Workflows, refer to the getting started guide.

    If your application requires a higher limit, fill out the Limit Increase Request Form or contact your account team. Please refer to Workflows pricing for more information.

  1. You can now build Workflows using Python. With Python Workflows, you get automatic retries, state persistence, and the ability to run multi-step operations that can span minutes, hours, or weeks using Python’s familiar syntax and the Python Workers runtime.

    Python Workflows use the same step-based execution model as JavaScript Workflows, but with Python syntax and access to Python’s ecosystem. Python Workflows also enable DAG (Directed Acyclic Graph) workflows, where you can define complex dependencies between steps using the depends parameter.

    Here’s a simple example:

    Python
    from workers import Response, WorkflowEntrypoint
    class PythonWorkflowStarter(WorkflowEntrypoint):
    async def run(self, event, step):
    @step.do("my first step")
    async def my_first_step():
    # do some work
    return "Hello Python!"
    await my_first_step()
    await step.sleep("my-sleep-step", "10 seconds")
    @step.do("my second step")
    async def my_second_step():
    # do some more work
    return "Hello again!"
    await my_second_step()
    class Default(WorkerEntrypoint):
    async def fetch(self, request):
    await self.env.MY_WORKFLOW.create()
    return Response("Hello Workflow creation!")

    Python Workflows support the same core capabilities as JavaScript Workflows, including sleep scheduling, event-driven workflows, and built-in error handling with configurable retry policies.

    To learn more and get started, refer to Python Workflows documentation.

  1. AI is supercharging app development for everyone, but we need a safe way to run untrusted, LLM-written code. We’re introducing Sandboxes, which let your Worker run actual processes in a secure, container-based environment.

    TypeScript
    import { getSandbox } from "@cloudflare/sandbox";
    export { Sandbox } from "@cloudflare/sandbox";
    export default {
    async fetch(request: Request, env: Env) {
    const sandbox = getSandbox(env.Sandbox, "my-sandbox");
    return sandbox.exec("ls", ["-la"]);
    },
    };

    Methods

    • exec(command: string, args: string[], options?: { stream?: boolean }):Execute a command in the sandbox.
    • gitCheckout(repoUrl: string, options: { branch?: string; targetDir?: string; stream?: boolean }): Checkout a git repository in the sandbox.
    • mkdir(path: string, options: { recursive?: boolean; stream?: boolean }): Create a directory in the sandbox.
    • writeFile(path: string, content: string, options: { encoding?: string; stream?: boolean }): Write content to a file in the sandbox.
    • readFile(path: string, options: { encoding?: string; stream?: boolean }): Read content from a file in the sandbox.
    • deleteFile(path: string, options?: { stream?: boolean }): Delete a file from the sandbox.
    • renameFile(oldPath: string, newPath: string, options?: { stream?: boolean }): Rename a file in the sandbox.
    • moveFile(sourcePath: string, destinationPath: string, options?: { stream?: boolean }): Move a file from one location to another in the sandbox.
    • ping(): Ping the sandbox.

    Sandboxes are still experimental. We're using them to explore how isolated, container-like workloads might scale on Cloudflare — and to help define the developer experience around them.

    You can try it today from your Worker, with just a few lines of code. Let us know what you build.

  1. Workflows is now Generally Available (or "GA"): in short, it's ready for production workloads. Alongside marking Workflows as GA, we've introduced a number of changes during the beta period, including:

    • 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.

    Workflows also supports the new increased CPU limits that apply to Workers, allowing you to run more CPU-intensive tasks (up to 5 minutes of CPU time per instance), not including the time spent waiting on network calls, AI models, or other I/O bound tasks.

    Human-in-the-loop

    The new step.waitForEvent API allows a Workflow instance to wait on events and data, enabling human-in-the-the-loop interactions, such as approving or rejecting a request, directly handling webhooks from other systems, or pushing event data to a Workflow while it's running.

    Because Workflows are just code, you can conditionally execute code based on the result of a waitForEvent call, and/or call waitForEvent multiple times in a single Workflow based on what the Workflow needs.

    For example, if you wanted to implement a human-in-the-loop approval process, you could use waitForEvent to wait for a user to approve or reject a request, and then conditionally execute code based on the result.

    JavaScript
    import {
    WorkflowEntrypoint,
    WorkflowStep,
    WorkflowEvent,
    } from "cloudflare:workers";
    export class MyWorkflow extends WorkflowEntrypoint {
    async run(event, step) {
    // Other steps in your Workflow
    let stripeEvent = await step.waitForEvent(
    "receive invoice paid webhook from Stripe",
    { type: "stripe-webhook", timeout: "1 hour" },
    );
    // Rest of your Workflow
    }
    }

    You can then send a Workflow an event from an external service via HTTP or from within a Worker using the Workers API for Workflows:

    JavaScript
    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(),
    });
    },
    };

    Read the GA announcement blog to learn more about what landed as part of the Workflows GA.

  1. Workflows now supports up to 4,500 concurrent (running) instances, up from the previous limit of 100. This limit will continue to increase during the Workflows open beta. This increase applies to all users on the Workers Paid plan, and takes effect immediately.

    Review the Workflows limits documentation and/or dive into the get started guide to start building on Workflows.

  1. We've added an example prompt to help you get started with building AI agents and applications on Cloudflare Workers, including Workflows, Durable Objects, and Workers KV.

    You can use this prompt with your favorite AI model, including Claude 3.5 Sonnet, OpenAI's o3-mini, Gemini 2.0 Flash, or Llama 3.3 on Workers AI. Models with large context windows will allow you to paste the prompt directly: provide your own prompt within the <user_prompt></user_prompt> tags.

    Terminal window
    {paste_prompt_here}
    <user_prompt>
    user: Build an AI agent using Cloudflare Workflows. The Workflow should run when a new GitHub issue is opened on a specific project with the label 'help' or 'bug', and attempt to help the user troubleshoot the issue by calling the OpenAI API with the issue title and description, and a clear, structured prompt that asks the model to suggest 1-3 possible solutions to the issue. Any code snippets should be formatted in Markdown code blocks. Documentation and sources should be referenced at the bottom of the response. The agent should then post the response to the GitHub issue. The agent should run as the provided GitHub bot account.
    </user_prompt>

    This prompt is still experimental, but we encourage you to try it out and provide feedback.

  1. Workflows (beta) now allows you to define up to 1024 steps. sleep steps do not count against this limit.

    We've also added:

    • instanceId as property to the WorkflowEvent type, allowing you to retrieve the current instance ID from within a running Workflow instance
    • Improved queueing logic for Workflow instances beyond the current maximum concurrent instances, reducing the cases where instances are stuck in the queued state.
    • Support for pause and resume for Workflow instances in a queued state.

    We're continuing to work on increases to the number of concurrent Workflow instances, steps, and support for a new waitForEvent API over the coming weeks.

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

    Workflows allow you to build multi-step applications that can automatically retry, persist state and run for minutes, hours, days, or weeks. Workflows introduces a programming model that makes it easier to build reliable, long-running tasks, observe as they progress, and programmatically trigger instances based on events across your services.

    Get started

    You can get started with Workflows by following our get started guide and/or using npm create cloudflare to pull down the starter project:

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

    You can open the src/index.ts file, extend it, and use wrangler deploy to deploy your first Workflow. From there, you can: