---
title: Workflows Changelog
image: https://developers.cloudflare.com/cf-twitter-card.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/changelog/llms.txt  
> Use this file to discover all available pages before exploring further.

[Skip to content](#%5Ftop) 

# Changelog

New updates and improvements at Cloudflare.

[ Subscribe to RSS ](https://developers.cloudflare.com/changelog/rss/index.xml) [ View RSS feeds ](https://developers.cloudflare.com/fundamentals/new-features/available-rss-feeds/) 

Workflows

![hero image](https://developers.cloudflare.com/_astro/hero.CVYJHPAd_26AMqX.svg) 

May 01, 2026
1. ### [Run Workflows inside Dynamic Workers with the @cloudflare/dynamic-workflows library](https://developers.cloudflare.com/changelog/post/2026-05-01-dynamic-workflows/)  
[ Workflows ](https://developers.cloudflare.com/workflows/)[ Workers ](https://developers.cloudflare.com/workers/)  
You can now use [@cloudflare/dynamic-workflows ↗](https://github.com/cloudflare/dynamic-workflows) to run a [Workflow](https://developers.cloudflare.com/workflows/) inside a [Dynamic Worker](https://developers.cloudflare.com/dynamic-workers/), ensuring durable execution for code that is loaded at runtime.  
The Worker Loader loads Dynamic Workers on demand, which previously made durability challenging. Even within a Dynamic Worker, a Workflow might sleep for hours or days between steps, and by the time it resumes, the original Dynamic Worker code would no longer be in memory.  
The library solves this by tagging each Workflow instance with metadata that identifies which Dynamic Worker to load — for example, a tenant ID — then reloading the matching Dynamic Worker through the Worker Loader whenever a Workflow awakens.  
Because Dynamic Workers are created on-demand, you do not have to register each Workflow up front or manage them individually. Load the Workflow code in the Dynamic Worker when it is needed, and the Workflows engine handles persistence and retries behind the scenes. Your Workflow code itself is unaffected by the routing and behaves as normal.  
This unlocks patterns where the Workflow code itself is dynamic. For example, this is useful with:  
   * **SaaS platforms** where each tenant defines their own automation, such as onboarding sequences, approval chains, or billing retry logic.  
   * **AI agent frameworks** where agents generate and execute multi-step plans at runtime, surviving restarts and waiting for human approval between tool calls.  
   * **Multi-tenant job systems** where each customer submits their own processing logic and every step persists progress and retries on failure.  
TypeScript  
```  
import {  
  createDynamicWorkflowEntrypoint,  
  DynamicWorkflowBinding,  
  wrapWorkflowBinding,  
  type WorkflowRunner,  
} from "@cloudflare/dynamic-workflows";  
export { DynamicWorkflowBinding };  
interface Env {  
  WORKFLOWS: Workflow;  
  LOADER: WorkerLoader;  
}  
function loadTenant(env: Env, tenantId: string) {  
  return env.LOADER.get(tenantId, async () => ({  
    compatibilityDate: "2026-01-01",  
    mainModule: "index.js",  
    modules: { "index.js": await fetchTenantCode(tenantId) },  
    // The Dynamic Worker uses this exactly like a real Workflow binding;  
    // every create() is tagged with { tenantId } automatically.  
    env: { WORKFLOWS: wrapWorkflowBinding({ tenantId }) },  
  }));  
}  
// The entrypoint name must match `class_name` in the workflows binding of your Wrangler config file.  
export const DynamicWorkflow = createDynamicWorkflowEntrypoint<Env>(  
  async ({ env, metadata }) => {  
    const stub = loadTenant(env, metadata.tenantId as string);  
    return stub.getEntrypoint("TenantWorkflow") as unknown as WorkflowRunner;  
  },  
);  
export default {  
  fetch(request: Request, env: Env) {  
    const tenantId = request.headers.get("x-tenant-id")!;  
    return loadTenant(env, tenantId).getEntrypoint().fetch(request);  
  },  
};  
```  
For a full walkthrough, refer to the [Dynamic Workflows guide](https://developers.cloudflare.com/dynamic-workers/usage/dynamic-workflows/).

Apr 21, 2026
1. ### [Additional step context and ReadableStream support now available in Workflows step.do()](https://developers.cloudflare.com/changelog/post/2026-04-21-step-context-and-readable-streams/)  
[ Workflows ](https://developers.cloudflare.com/workflows/)  
[Workflows](https://developers.cloudflare.com/workflows/) now provides additional context inside `step.do()` callbacks and supports returning `ReadableStream` to handle larger step outputs.  
#### Step context properties  
The `step.do()` callback receives a context object with new properties [alongside](https://developers.cloudflare.com/changelog/post/2026-03-06-step-context-available/) `attempt`:  
   * **`step.name`** — The name passed to `step.do()`  
   * **`step.count`** — How many times a step with that name has been invoked in this instance (1-indexed)  
         * Useful when running the same step in a loop.  
   * **`config`** — The resolved step configuration, including `timeout` and `retries` with defaults applied  
TypeScript  
```  
type ResolvedStepConfig = {  
  retries: {  
    limit: number;  
    delay: WorkflowDelayDuration | number;  
    backoff?: "constant" | "linear" | "exponential";  
  };  
  timeout: WorkflowTimeoutDuration | number;  
};  
type WorkflowStepContext = {  
  step: {  
    name: string;  
    count: number;  
  };  
  attempt: number;  
  config: ResolvedStepConfig;  
};  
```  
#### ReadableStream support in `step.do()`  
Steps can now return a `ReadableStream` directly. Although non-stream step outputs are [limited to 1 MiB](https://developers.cloudflare.com/workflows/reference/limits/), streamed outputs support much larger payloads.  
TypeScript  
```  
const largePayload = await step.do("fetch-large-file", async () => {  
  const object = await env.MY_BUCKET.get("large-file.bin");  
  return object.body;  
});  
```  
Note that streamed outputs are still considered part of the Workflow instance storage limit.

Apr 15, 2026
1. ### [Increased concurrency, creation rate, and queued instance limits for Workflows instances](https://developers.cloudflare.com/changelog/post/2026-04-15-workflows-limits-raised/)  
[ Workflows ](https://developers.cloudflare.com/workflows/)[ Workers ](https://developers.cloudflare.com/workers/)  
[Workflows](https://developers.cloudflare.com/workflows/) limits have been raised to the following:  
| Limit                                                 | Previous               | New                                             |  
| ----------------------------------------------------- | ---------------------- | ----------------------------------------------- |  
| Concurrent instances (running in parallel)            | 10,000                 | 50,000                                          |  
| Instance creation rate (per account)                  | 100/second per account | 300/second per account, 100/second per workflow |  
| Queued instances per Workflow [1](#user-content-fn-1) | 1 million              | 2 million                                       |  
These increases apply to all users on the [Workers Paid plan](https://developers.cloudflare.com/workers/platform/pricing/). Refer to the [Workflows limits documentation](https://developers.cloudflare.com/workflows/reference/limits/) for more details.  
#### Footnotes  
   1. Queued instances are instances that have been created or awoken and are waiting for a concurrency slot. [↩](#user-content-fnref-1)

Apr 01, 2026
1. ### [All Wrangler commands for Workflows now support local development](https://developers.cloudflare.com/changelog/post/2026-04-01-wrangler-workflows-local/)  
[ Workflows ](https://developers.cloudflare.com/workflows/)[ Workers ](https://developers.cloudflare.com/workers/)  
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](https://developers.cloudflare.com/workflows/build/local-development/).

Mar 23, 2026
1. ### [Workflow instances now support pause(), resume(), restart(), and terminate() methods in local development](https://developers.cloudflare.com/changelog/post/2026-03-23-local-dev-instance-methods/)  
[ Workflows ](https://developers.cloudflare.com/workflows/)[ Workers ](https://developers.cloudflare.com/workers/)  
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  
```

Mar 06, 2026
1. ### [Workflow steps now expose retry attempt number via step context](https://developers.cloudflare.com/changelog/post/2026-03-06-step-context-available/)  
[ Workflows ](https://developers.cloudflare.com/workflows/)[ Workers ](https://developers.cloudflare.com/workers/)  
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](https://developers.cloudflare.com/workflows/build/sleeping-and-retrying/).

Mar 03, 2026
1. ### [Workflows step limit increased to 25,000 steps per instance](https://developers.cloudflare.com/changelog/post/2026-03-03-step-limits-to-25k/)  
[ Workflows ](https://developers.cloudflare.com/workflows/)[ Workers ](https://developers.cloudflare.com/workers/)  
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](https://developers.cloudflare.com/workflows/reference/limits/) for more information.

Feb 04, 2026
1. ### [Visualize your Workflows in the Cloudflare dashboard](https://developers.cloudflare.com/changelog/post/2026-02-03-workflows-visualizer/)  
[ Workflows ](https://developers.cloudflare.com/workflows/)[ Workers ](https://developers.cloudflare.com/workers/)  
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](https://developers.cloudflare.com/_astro/2026-02-03-workflows-diagram.BfQAnWL3_Z203oFd.webp)  
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 ↗](https://dash.cloudflare.com/?to=/:account/workers/workflows) to see their diagrams.

Feb 03, 2026
1. ### [Agents SDK v0.3.7: Workflows integration, synchronous state, and scheduleEvery()](https://developers.cloudflare.com/changelog/post/2026-02-03-agents-workflows-integration/)  
[ Agents ](https://developers.cloudflare.com/agents/)[ Workflows ](https://developers.cloudflare.com/workflows/)  
The latest release of the [Agents SDK ↗](https://github.com/cloudflare/agents) brings first-class support for [Cloudflare Workflows](https://developers.cloudflare.com/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 ](#tab-panel-746)  
   * [  TypeScript ](#tab-panel-747)  
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;  
  }  
}  
```  
TypeScript  
```  
import { AgentWorkflow } from "agents/workflows";  
import type { AgentWorkflowEvent, AgentWorkflowStep } from "agents/workflows";  
export class ProcessingWorkflow extends AgentWorkflow<MyAgent, TaskParams> {  
  async run(event: AgentWorkflowEvent<TaskParams>, step: AgentWorkflowStep) {  
    // 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 ](#tab-panel-752)  
   * [  TypeScript ](#tab-panel-753)  
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);  
  }  
}  
```  
TypeScript  
```  
export class MyAgent extends Agent {  
  async startTask(taskId: string, data: string) {  
    const instanceId = await this.runWorkflow("PROCESSING_WORKFLOW", {  
      taskId,  
      data,  
    });  
    return { instanceId };  
  }  
  async onWorkflowProgress(  
    workflowName: string,  
    instanceId: string,  
    progress: unknown,  
  ) {  
    this.broadcast(JSON.stringify({ type: "progress", progress }));  
  }  
  async onWorkflowComplete(  
    workflowName: string,  
    instanceId: string,  
    result?: unknown,  
  ) {  
    console.log(`Workflow ${instanceId} completed`);  
  }  
  async onWorkflowError(  
    workflowName: string,  
    instanceId: string,  
    error: unknown,  
  ) {  
    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 ](#tab-panel-740)  
   * [  TypeScript ](#tab-panel-741)  
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() };  
  }  
}  
```  
TypeScript  
```  
export class MyAgent extends Agent<Env, State> {  
  validateStateChange(oldState: State, newState: State): State | false {  
    // 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 ](#tab-panel-738)  
   * [  TypeScript ](#tab-panel-739)  
JavaScript  
```  
// Run every 5 minutes  
await this.scheduleEvery("syncData", 5 * 60 * 1000, { source: "api" });  
```  
TypeScript  
```  
// 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 ](#tab-panel-742)  
   * [  TypeScript ](#tab-panel-743)  
JavaScript  
```  
await agent.call("method", [args], {  
  timeout: 5000,  
  stream: { onChunk, onDone, onError },  
});  
```  
TypeScript  
```  
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 ](#tab-panel-744)  
   * [  TypeScript ](#tab-panel-745)  
JavaScript  
```  
const agent = useAgent({  
  basePath: "user",  
  onIdentity: (name, agentType) => console.log(`Connected to ${name}`),  
});  
```  
TypeScript  
```  
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](https://developers.cloudflare.com/agents/api-reference/run-workflows/).

Oct 31, 2025
1. ### [Increased Workflows instance and concurrency limits](https://developers.cloudflare.com/changelog/post/2025-10-28-raising-limits/)  
[ Workflows ](https://developers.cloudflare.com/workflows/)[ Workers ](https://developers.cloudflare.com/workers/)  
We've raised the [Cloudflare Workflows](https://developers.cloudflare.com/workflows/) account-level limits for all accounts on the [Workers paid plan](https://developers.cloudflare.com/workers/platform/pricing/):  
   * **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](https://developers.cloudflare.com/workflows/get-started/guide/).  
If your application requires a higher limit, fill out the [Limit Increase Request Form](https://developers.cloudflare.com/workers/platform/limits/) or contact your account team. Please refer to [Workflows pricing](https://developers.cloudflare.com/workflows/reference/pricing/) for more information.

Aug 22, 2025
1. ### [Build durable multi-step applications in Python with Workflows (now in beta)](https://developers.cloudflare.com/changelog/post/2025-08-22-workflows-python-beta/)  
[ Workflows ](https://developers.cloudflare.com/workflows/)[ Workers ](https://developers.cloudflare.com/workers/)  
You can now build [Workflows](https://developers.cloudflare.com/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](https://developers.cloudflare.com/workers/languages/python/) 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](https://developers.cloudflare.com/workflows/python/dag/), 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!")  
```  
Note  
Python Workflows requires a `compatibility_date = "2025-08-01"`, or lower, in your wrangler toml file.  
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](https://developers.cloudflare.com/workflows/python/).

Jun 25, 2025
1. ### [Run AI-generated code on-demand with Code Sandboxes (new)](https://developers.cloudflare.com/changelog/post/2025-06-24-announcing-sandboxes/)  
[ Agents ](https://developers.cloudflare.com/agents/)[ Workers ](https://developers.cloudflare.com/workers/)[ Workflows ](https://developers.cloudflare.com/workflows/)  
AI is supercharging app development for everyone, but we need a safe way to run untrusted, LLM-written code. We’re introducing [Sandboxes ↗](https://www.npmjs.com/package/@cloudflare/sandbox), 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.

Apr 07, 2025
1. ### [Workflows is now Generally Available](https://developers.cloudflare.com/changelog/post/2025-04-07-workflows-ga/)  
[ Workflows ](https://developers.cloudflare.com/workflows/)[ Workers ](https://developers.cloudflare.com/workers/)  
[Workflows](https://developers.cloudflare.com/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](https://developers.cloudflare.com/changelog/2025-02-25-workflows-concurrency-increased/) 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](https://developers.cloudflare.com/changelog/2025-03-25-higher-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 ](#tab-panel-748)  
   * [  TypeScript ](#tab-panel-749)  
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  
  }  
}  
```  
TypeScript  
```  
import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from "cloudflare:workers";  
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {  
  async run(event: WorkflowEvent<Params>, step: WorkflowStep) {  
    // Other steps in your Workflow  
    let stripeEvent = await step.waitForEvent<IncomingStripeWebhook>("receive invoice paid webhook from Stripe", { type: "stripe-webhook", timeout: "1 hour" })  
    // Rest of your Workflow  
  }  
}  
```  
You can then send a Workflow an event from an external service via HTTP or from within a Worker using the [Workers API](https://developers.cloudflare.com/workflows/build/workers-api/) for Workflows:  
   * [  JavaScript ](#tab-panel-750)  
   * [  TypeScript ](#tab-panel-751)  
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(),  
    });  
  },  
};  
```  
TypeScript  
```  
export default {  
  async fetch(req: Request, env: Env) {  
    const instanceId = new URL(req.url).searchParams.get("instanceId")  
    const webhookPayload = await req.json<Payload>()  
    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 ↗](https://blog.cloudflare.com/workflows-is-now-generally-available/) to learn more about what landed as part of the Workflows GA.

Feb 25, 2025
1. ### [Concurrent Workflow instances limits increased.](https://developers.cloudflare.com/changelog/post/2025-02-25-workflows-concurrency-increased/)  
[ Workflows ](https://developers.cloudflare.com/workflows/)  
[Workflows](https://developers.cloudflare.com/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](https://developers.cloudflare.com/workflows/reference/limits) and/or dive into the [get started guide](https://developers.cloudflare.com/workflows/get-started/guide/) to start building on Workflows.

Feb 14, 2025
1. ### [Build AI Agents with Example Prompts](https://developers.cloudflare.com/changelog/post/2025-02-14-example-ai-prompts/)  
[ Agents ](https://developers.cloudflare.com/agents/)[ Workers ](https://developers.cloudflare.com/workers/)[ Workflows ](https://developers.cloudflare.com/workflows/)  
We've added an [example prompt](https://developers.cloudflare.com/workers/get-started/prompting/) to help you get started with building AI agents and applications on Cloudflare [Workers](https://developers.cloudflare.com/workers/), including [Workflows](https://developers.cloudflare.com/workflows/), [Durable Objects](https://developers.cloudflare.com/durable-objects/), and [Workers KV](https://developers.cloudflare.com/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 ↗](https://github.com/cloudflare/cloudflare-docs/issues/new?template=content.edit.yml).

Jan 15, 2025
1. ### [Increased Workflows limits and improved instance queueing.](https://developers.cloudflare.com/changelog/post/2025-01-15-workflows-more-steps/)  
[ Workflows ](https://developers.cloudflare.com/workflows/)  
[Workflows](https://developers.cloudflare.com/workflows/) (beta) now allows you to define up to 1024 [steps](https://developers.cloudflare.com/workflows/build/workers-api/#workflowstep). `sleep` steps do not count against this limit.  
We've also added:  
   * `instanceId` as property to the [WorkflowEvent](https://developers.cloudflare.com/workflows/build/workers-api/#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](https://developers.cloudflare.com/workflows/build/workers-api/#pause) 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.

Oct 24, 2024
1. ### [Workflows is now in open beta](https://developers.cloudflare.com/changelog/post/2024-10-24-workflows-beta/)  
[ Workers ](https://developers.cloudflare.com/workers/)[ Workflows ](https://developers.cloudflare.com/workflows/)  
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](https://developers.cloudflare.com/workflows/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:  
   * Learn the [Workflows API](https://developers.cloudflare.com/workflows/build/workers-api/)  
   * [Trigger Workflows](https://developers.cloudflare.com/workflows/build/trigger-workflows/) via your Workers apps.  
   * Understand the [Rules of Workflows](https://developers.cloudflare.com/workflows/build/rules-of-workflows/) and how to adopt best practices

[Search all changelog entries](https://developers.cloudflare.com/search/?contentType=Changelog+entry) 