Skip to content

Commit 068416d

Browse files
elithrarOxyjun
authored andcommitted
workflows: new docs (#21313)
* workflows: new docs * update * remove beta badge * limits * pricin * waitForEvent more * hidden: true * flesh out examples * changelog done * final * Apply suggestions from code review Co-authored-by: Jun Lee <[email protected]> * ok --------- Co-authored-by: Jun Lee <[email protected]>
1 parent 7d3ff02 commit 068416d

File tree

8 files changed

+238
-38
lines changed

8 files changed

+238
-38
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
---
2+
title: Workflows is now Generally Available
3+
description: Workflows is now GA - ship Workflows that you can rely on in production.
4+
- workflows
5+
- workers
6+
date: 2025-04-07T13:00:00Z
7+
hidden: true
8+
---
9+
10+
import { Render, PackageManagers, TypeScriptExample } from "~/components"
11+
12+
[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:
13+
14+
* A new `waitForEvent` API that allows a Workflow to wait for an event to occur before continuing execution.
15+
* 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.
16+
* 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.
17+
* Support for `vitest` for testing Workflows locally and in CI/CD pipelines.
18+
19+
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.
20+
21+
#### Human-in-the-loop
22+
23+
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.
24+
25+
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.
26+
27+
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.
28+
29+
<TypeScriptExample>
30+
31+
```ts
32+
import { Workflow, WorkflowEvent } from "cloudflare:workflows";
33+
34+
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
35+
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
36+
// Other steps in your Workflow
37+
let event = await step.waitForEvent<IncomingStripeWebhook>("receive invoice paid webhook from Stripe", { type: "stripe-webhook", timeout: "1 hour" })
38+
// Rest of your Workflow
39+
}
40+
}
41+
```
42+
43+
</TypeScriptExample>
44+
45+
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:
46+
47+
<TypeScriptExample>
48+
49+
```ts
50+
export default {
51+
async fetch(req: Request, env: Env) {
52+
const instanceId = new URL(req.url).searchParams.get("instanceId")
53+
const webhookPayload = await req.json<Payload>()
54+
55+
let instance = await env.MY_WORKFLOW.get(instanceId);
56+
// Send our event, with `type` matching the event type defined in
57+
// our step.waitForEvent call
58+
await instance.sendEvent({type: "stripe-webhook", payload: webhookPayload})
59+
60+
return Response.json({
61+
status: await instance.status(),
62+
});
63+
},
64+
};
65+
```
66+
67+
</TypeScriptExample>
68+
69+
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.

src/content/docs/workflows/build/events-and-parameters.mdx

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ When a Workflow is triggered, it can receive an optional event. This event can i
1212

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

15-
## Pass parameters to a Workflow
15+
## Pass data to a Workflow
1616

17-
You can pass parameters to a Workflow in two ways:
17+
You can pass parameters to a Workflow in three ways:
1818

1919
* As an optional argument to the `create` method on a [Workflow binding](/workers/wrangler/commands/#trigger) when triggering a Workflow from a Worker.
2020
* Via the `--params` flag when using the `wrangler` CLI to trigger a Workflow.
21+
* 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.
2122

2223
You can pass any JSON-serializable object as a parameter.
2324

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

3031
:::
3132

33+
<TypeScriptExample>
34+
3235
```ts
3336
export default {
3437
async fetch(req: Request, env: Env) {
@@ -51,6 +54,8 @@ export default {
5154
};
5255
```
5356

57+
</TypeScriptExample>
58+
5459
To pass parameters via the `wrangler` command-line interface, pass a JSON string as the second parameter to the `workflows trigger` sub-command:
5560

5661
```sh
@@ -60,6 +65,69 @@ npx wrangler@latest workflows trigger workflows-starter '{"some":"data"}'
6065
🚀 Workflow instance "57c7913b-8e1d-4a78-a0dd-dce5a0b7aa30" has been queued successfully
6166
```
6267

68+
### Wait for events
69+
70+
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:
71+
72+
1. Via the [Workers API binding](/workflows/build/workers-api/): call `instance.sendEvent` to send events to specific workflow instances.
73+
2. Using the REST API (HTTP API)'s [Events endpoint](/api/resources/workflows/subresources/instances/subresources/events/methods/create/).
74+
75+
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.
76+
77+
Calling `waitForEvent` requires you to specify an `type`, which is used to match the corresponding `type` when sending an event to a Workflow instance.
78+
79+
For example, to wait for billing webhook:
80+
81+
<TypeScriptExample>
82+
83+
```ts
84+
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
85+
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
86+
// Other steps in your Workflow
87+
let event = await step.waitForEvent<IncomingStripeWebhook>("receive invoice paid webhook from Stripe", { type: "stripe-webhook", timeout: "1 hour" })
88+
// Rest of your Workflow
89+
}
90+
}
91+
```
92+
93+
</TypeScriptExample>
94+
95+
The above example:
96+
97+
* Calls `waitForEvent` with a `type` of `stripe-webhook` - the corresponding `sendEvent` call would thus be `await instance.sendEvent({type: "stripe-webhook", payload: webhookPayload})`.
98+
* 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`.
99+
* Continues on with the rest of the Workflow.
100+
101+
### Send events to running workflows
102+
103+
Workflow instances that are waiting on events using the `waitForEvent` API can be sent events using the `instance.sendEvent` API:
104+
105+
<TypeScriptExample>
106+
107+
```ts
108+
export default {
109+
async fetch(req: Request, env: Env) {
110+
const instanceId = new URL(req.url).searchParams.get("instanceId")
111+
const webhookPayload = await req.json<Payload>()
112+
113+
let instance = await env.MY_WORKFLOW.get(instanceId);
114+
// Send our event, with `type` matching the event type defined in
115+
// our step.waitForEvent call
116+
await instance.sendEvent({type: "stripe-webhook", payload: webhookPayload})
117+
118+
return Response.json({
119+
status: await instance.status(),
120+
});
121+
},
122+
};
123+
```
124+
125+
</TypeScriptExample>
126+
127+
* Similar to the [`waitForEvent`](#wait-for-events) example in this guide, the `type` property in our `waitForEvent` and `sendEvent` fields must match.
128+
* To send multiple events to a Workflow that has multiple `waitForEvent` calls, call `sendEvent` with the corresponding `type` property set.
129+
* Events can also be sent using the REST API (HTTP API)'s [Events endpoint](/api/resources/workflows/subresources/instances/subresources/events/methods/create/).
130+
63131
## TypeScript and type parameters
64132

65133
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:

src/content/docs/workflows/build/workers-api.mdx

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ sidebar:
66

77
---
88

9-
import { MetaInfo, Render, Type, WranglerConfig } from "~/components";
9+
import { MetaInfo, Render, Type, TypeScriptExample, WranglerConfig } from "~/components";
1010

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

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

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

87-
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.
87+
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.
8888

8989
:::
9090

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

108108
:::
109109

110+
* <code>step.waitForEvent(name: string, options: ): Promise&lt;void&gt;</code>
111+
112+
* `name` - the name of the step.
113+
* `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.
114+
115+
<TypeScriptExample>
116+
117+
```ts
118+
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
119+
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
120+
// Other steps in your Workflow
121+
let event = await step.waitForEvent<IncomingStripeWebhook>("receive invoice paid webhook from Stripe", { type: "stripe-webhook", timeout: "1 hour" })
122+
// Rest of your Workflow
123+
}
124+
}
125+
```
126+
127+
</TypeScriptExample>
128+
129+
Review the documentation on [events and parameters](/workflows/build/events-and-parameters/) to learn how to send events to a running Workflow instance.
130+
110131
## WorkflowStepConfig
111132

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

134155
## Call Workflows from Workers
135156

136-
:::note[Workflows beta]
137-
138-
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.
139-
140-
:::
141-
142157
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.
143158

144159
You can bind to a Workflow by defining a `[[workflows]]` binding within your Wrangler configuration.
@@ -286,7 +301,7 @@ This is useful when you are scheduling multiple instances at once. A call to `cr
286301

287302
* `batch` - list of Options to pass when creating an instance, including a user-provided ID and payload parameters.
288303

289-
Each element of the `batch` list is expected to the
304+
Each element of the `batch` list is expected to include both `id` and `params` properties:
290305

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

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

428+
### sendEvent
429+
430+
[Send an event](/workflows/build/events-and-parameters/) to a running Workflow instance.
431+
432+
* <code>sendEvent(): Promise&lt;void&gt;</code>
433+
434+
* `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.
435+
436+
Return `void` on success; throws an exception if the Workflow is not running or is an errored state.
437+
438+
<TypeScriptExample>
439+
440+
```ts
441+
export default {
442+
async fetch(req: Request, env: Env) {
443+
const instanceId = new URL(req.url).searchParams.get("instanceId")
444+
const webhookPayload = await req.json<Payload>()
445+
446+
let instance = await env.MY_WORKFLOW.get(instanceId);
447+
// Send our event, with `type` matching the event type defined in
448+
// our step.waitForEvent call
449+
await instance.sendEvent({type: "stripe-webhook", payload: webhookPayload})
450+
451+
return Response.json({
452+
status: await instance.status(),
453+
});
454+
},
455+
};
456+
```
457+
458+
</TypeScriptExample>
459+
460+
You can call `sendEvent` multiple times, setting the value of the `type` property to match the specific `waitForEvent` calls in your Workflow.
461+
462+
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.
463+
413464
### InstanceStatus
414465

415466
Details the status of a Workflow instance.

src/content/docs/workflows/get-started/cli-quick-start.mdx

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,6 @@ sidebar:
99

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

12-
:::note
13-
14-
Workflows is in **public beta**, and any developer with a [free or paid Workers plan](/workers/platform/pricing/#workers) can start using Workflows immediately.
15-
16-
To learn more about Workflows and how it works, read [the beta announcement blog](https://blog.cloudflare.com/building-workflows-durable-execution-on-workers).
17-
18-
:::
19-
2012
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.
2113

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

src/content/docs/workflows/get-started/guide.mdx

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,6 @@ sidebar:
99

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

12-
:::note
13-
14-
Workflows is in **public beta**, and any developer with a [free or paid Workers plan](/workers/platform/pricing/#workers) can start using Workflows immediately.
15-
16-
To learn more about Workflows and how it works, read [the beta announcement blog](https://blog.cloudflare.com/building-workflows-durable-execution-on-workers).
17-
18-
:::
19-
2012
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.
2113

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

src/content/docs/workflows/index.mdx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ type: overview
55
pcx_content_type: overview
66
sidebar:
77
order: 1
8-
badge:
9-
text: Beta
108
head:
119
- tag: title
1210
content: Overview

src/content/docs/workflows/reference/limits.mdx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,15 @@ Many limits are inherited from those applied to Workers scripts and as documente
2222
| Maximum event [payload size](/workflows/build/events-and-parameters/) | 1MiB (2^20 bytes) | 1MiB (2^20 bytes) |
2323
| Maximum state that can be persisted per Workflow instance | 100MB | 1GB |
2424
| Maximum length of a Workflow ID [^4] | 64 characters | 64 characters |
25-
| Maximum `step.sleep` duration | 365 days (1 year) [^1] | 365 days (1 year) [^1] |
26-
| Maximum steps per Workflow [^5] | 1024 [^1] | 1024 [^1] |
25+
| Maximum `step.sleep` duration | 365 days (1 year) | 365 days (1 year) |
26+
| Maximum steps per Workflow [^5] | 1024 | 1024 |
2727
| Maximum Workflow executions | 100,000 per day [shared with Workers daily limit](/workers/platform/limits/#worker-limits) | Unlimited |
28-
| Concurrent Workflow instances (executions) per account | 25 | 4500 [^1] |
29-
| Maximum Workflow instance creation rate | 100 per 10 seconds [^1][^6] | 100 per 10 seconds [^1][^6] |
30-
| Maximum number of [queued instances](/workflows/observability/metrics-analytics/#event-types) | 10,000 [^1] | 100,000 [^1] |
28+
| Concurrent Workflow instances (executions) per account | 25 | 4500 |
29+
| Maximum Workflow instance creation rate | 100 per 10 seconds [^6] | 100 per 10 seconds [^6] |
30+
| Maximum number of [queued instances](/workflows/observability/metrics-analytics/#event-types) | 10,000 | 100,000 |
3131
| Retention limit for completed Workflow state | 3 days | 30 days [^2] |
3232
| Maximum length of a Workflow ID [^4] | 64 characters | 64 characters |
3333

34-
[^1]: This limit will be reviewed and revised during the open beta for Workflows. Follow the [Workflows changelog](/workflows/reference/changelog/) for updates.
35-
3634
[^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.
3735

3836
[^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.

0 commit comments

Comments
 (0)