Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions src/content/changelog/workflows/2025-04-07-workflows-ga.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
title: Workflows is now Generally Available
description: Workflows is now GA - ship Workflows that you can rely on in production.
- workflows
- workers
date: 2025-04-07T13:00:00Z
hidden: true
---

import { Render, PackageManagers, TypeScriptExample } from "~/components"

[Workflows](/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](/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](/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.

<TypeScriptExample>

```ts
import { Workflow, WorkflowEvent } from "cloudflare:workflows";

export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
// Other steps in your Workflow
let event = await step.waitForEvent<IncomingStripeWebhook>("receive invoice paid webhook from Stripe", { type: "stripe-webhook", timeout: "1 hour" })
// Rest of your Workflow
}
}
```

</TypeScriptExample>

You can then send a Workflow an event from an external service via HTTP or from within a Worker using the [Workers API](/workflows/build/workers-api/) for Workflows:

<TypeScriptExample>

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

</TypeScriptExample>

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.
72 changes: 70 additions & 2 deletions src/content/docs/workflows/build/events-and-parameters.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ When a Workflow is triggered, it can receive an optional event. This event can i

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 parameters to a Workflow
## Pass data to a Workflow

You can pass parameters to a Workflow in two ways:
You can pass parameters to a Workflow in three ways:

* As an optional argument to the `create` method on a [Workflow binding](/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.

Expand All @@ -29,6 +30,8 @@ Store state durably by returning it from your `step.do` callbacks.

:::

<TypeScriptExample>

```ts
export default {
async fetch(req: Request, env: Env) {
Expand All @@ -51,6 +54,8 @@ export default {
};
```

</TypeScriptExample>

To pass parameters via the `wrangler` command-line interface, pass a JSON string as the second parameter to the `workflows trigger` sub-command:

```sh
Expand All @@ -60,6 +65,69 @@ npx wrangler@latest workflows trigger workflows-starter '{"some":"data"}'
🚀 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](/workflows/build/workers-api/): call `instance.sendEvent` to send events to specific workflow instances.
2. Using the REST API (HTTP API)'s [Events endpoint](/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`, which is used to match the corresponding `type` when sending an event to a Workflow instance.

For example, to wait for billing webhook:

<TypeScriptExample>

```ts
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
// Other steps in your Workflow
let event = await step.waitForEvent<IncomingStripeWebhook>("receive invoice paid webhook from Stripe", { type: "stripe-webhook", timeout: "1 hour" })
// Rest of your Workflow
}
}
```

</TypeScriptExample>

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.

### Sending events to running workflows

Workflow instances that are waiting on events using the `waitForEvent` API can be sent events using the `instance.sendEvent` API:

<TypeScriptExample>

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

</TypeScriptExample>

* Similar to the [`waitForEvent`](#wait-for-events) example in this guide, the `type` property 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.
* Events can also be sent using the REST API (HTTP API)'s [Events endpoint](/api/resources/workflows/subresources/instances/subresources/events/methods/create/).

## 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:
Expand Down
69 changes: 60 additions & 9 deletions src/content/docs/workflows/build/workers-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ sidebar:

---

import { MetaInfo, Render, Type, WranglerConfig } from "~/components";
import { MetaInfo, Render, Type, TypeScriptExample, WranglerConfig } from "~/components";

This guide details the Workflows API within Cloudflare Workers, including methods, types, and usage examples.

Expand Down Expand Up @@ -84,7 +84,7 @@ When returning state from a `step`, ensure that the object you return is _serial

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

:::

Expand All @@ -107,6 +107,27 @@ More information about the limits imposed on Workflow can be found in the [Workf

:::

* <code>step.waitForEvent(name: string, options: ): Promise&lt;void&gt;</code>

* `name` - the name of the step.
* `options` - an object with properties for `type`, 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.

<TypeScriptExample>

```ts
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
// Other steps in your Workflow
let event = await step.waitForEvent<IncomingStripeWebhook>("receive invoice paid webhook from Stripe", { type: "stripe-webhook", timeout: "1 hour" })
// Rest of your Workflow
}
}
```

</TypeScriptExample>

Review the documentation on [events and parameters](/workflows/build/events-and-parameters/) to learn how to send events to a running Workflow instance.

## WorkflowStepConfig

```ts
Expand All @@ -133,12 +154,6 @@ Refer to the [documentation on sleeping and retrying](/workflows/build/sleeping-

## Call Workflows from Workers

:::note[Workflows beta]

Workflows currently requires you to bind to a Workflow via the [Wrangler configuration file](/workers/wrangler/configuration/), and does not yet support bindings via the Workers dashboard.

:::

Workflows exposes an API directly to your Workers scripts via the [bindings](/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.
Expand Down Expand Up @@ -286,7 +301,7 @@ This is useful when you are scheduling multiple instances at once. A call to `cr

* `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 the
Each element of the `batch` list is expected to the

```ts
// Create a new batch of 3 Workflow instances, each with its own ID and pass params to the Workflow instances
Expand Down Expand Up @@ -410,6 +425,42 @@ Terminate a Workflow instance.

* <code>terminate(): Promise&lt;void&gt;</code>

### sendEvent

[Send an event](/workflows/build/events-and-parameters/) to a running Workflow instance.

* <code>sendEvent(): Promise&lt;void&gt;</code>

* `options` - the event `type` 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.

<TypeScriptExample>

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

</TypeScriptExample>

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.
Expand Down
8 changes: 0 additions & 8 deletions src/content/docs/workflows/get-started/cli-quick-start.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,6 @@ sidebar:

import { Render, PackageManagers, WranglerConfig } from "~/components"

:::note

Workflows is in **public beta**, and any developer with a [free or paid Workers plan](/workers/platform/pricing/#workers) can start using Workflows immediately.

To learn more about Workflows and how it works, read [the beta announcement blog](https://blog.cloudflare.com/building-workflows-durable-execution-on-workers).

:::

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](/r2/), automate generation of [Workers AI](/workers-ai/) embeddings into a [Vectorize](/vectorize/) vector database, or to trigger user lifecycle emails using your favorite email API.
Expand Down
8 changes: 0 additions & 8 deletions src/content/docs/workflows/get-started/guide.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,6 @@ sidebar:

import { Render, PackageManagers, WranglerConfig } from "~/components"

:::note

Workflows is in **public beta**, and any developer with a [free or paid Workers plan](/workers/platform/pricing/#workers) can start using Workflows immediately.

To learn more about Workflows and how it works, read [the beta announcement blog](https://blog.cloudflare.com/building-workflows-durable-execution-on-workers).

:::

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](/r2/), automate generation of [Workers AI](/workers-ai/) embeddings into a [Vectorize](/vectorize/) vector database, or to trigger user lifecycle emails using your favorite email API.
Expand Down
2 changes: 0 additions & 2 deletions src/content/docs/workflows/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ type: overview
pcx_content_type: overview
sidebar:
order: 1
badge:
text: Beta
head:
- tag: title
content: Overview
Expand Down
12 changes: 5 additions & 7 deletions src/content/docs/workflows/reference/limits.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,15 @@ Many limits are inherited from those applied to Workers scripts and as documente
| Maximum event [payload size](/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 length of a Workflow ID [^4] | 64 characters | 64 characters |
| Maximum `step.sleep` duration | 365 days (1 year) [^1] | 365 days (1 year) [^1] |
| Maximum steps per Workflow [^5] | 1024 [^1] | 1024 [^1] |
| Maximum `step.sleep` duration | 365 days (1 year) | 365 days (1 year) |
| Maximum steps per Workflow [^5] | 1024 | 1024 |
| Maximum Workflow executions | 100,000 per day [shared with Workers daily limit](/workers/platform/limits/#worker-limits) | Unlimited |
| Concurrent Workflow instances (executions) per account | 25 | 4500 [^1] |
| Maximum Workflow instance creation rate | 100 per 10 seconds [^1][^6] | 100 per 10 seconds [^1][^6] |
| Maximum number of [queued instances](/workflows/observability/metrics-analytics/#event-types) | 10,000 [^1] | 100,000 [^1] |
| Concurrent Workflow instances (executions) per account | 25 | 4500 |
| Maximum Workflow instance creation rate | 100 per 10 seconds [^6] | 100 per 10 seconds [^6] |
| Maximum number of [queued instances](/workflows/observability/metrics-analytics/#event-types) | 10,000 | 100,000 |
| Retention limit for completed Workflow state | 3 days | 30 days [^2] |
| Maximum length of a Workflow ID [^4] | 64 characters | 64 characters |

[^1]: This limit will be reviewed and revised during the open beta for Workflows. Follow the [Workflows changelog](/workflows/reference/changelog/) for updates.

[^2]: Workflow state and logs will be retained for 3 days on the Workers Free plan and for 7 days on the Workers Paid plan.

[^3]: 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.
Expand Down
Loading