workflow 5.0.0-beta.31 → 5.0.0-beta.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/docs/api-reference/index.mdx +1 -1
  2. package/docs/api-reference/workflow-runtime/index.mdx +1 -1
  3. package/docs/api-reference/workflow-runtime/world/analytics.mdx +139 -0
  4. package/docs/api-reference/workflow-runtime/world/index.mdx +5 -2
  5. package/docs/api-reference/workflow-runtime/world/meta.json +1 -1
  6. package/docs/configuration/worlds.mdx +8 -0
  7. package/docs/cookbook/advanced/child-workflows.mdx +4 -0
  8. package/docs/cookbook/advanced/publishing-libraries.mdx +4 -0
  9. package/docs/cookbook/advanced/serializable-steps.mdx +4 -0
  10. package/docs/cookbook/advanced/upgrading-workflows.mdx +4 -0
  11. package/docs/cookbook/agent-patterns/agent-cancellation.mdx +4 -0
  12. package/docs/cookbook/agent-patterns/durable-agent.mdx +4 -0
  13. package/docs/cookbook/agent-patterns/human-in-the-loop.mdx +4 -0
  14. package/docs/cookbook/common-patterns/batching.mdx +4 -0
  15. package/docs/cookbook/common-patterns/idempotency.mdx +4 -0
  16. package/docs/cookbook/common-patterns/rate-limiting.mdx +4 -0
  17. package/docs/cookbook/common-patterns/saga.mdx +4 -0
  18. package/docs/cookbook/common-patterns/scheduling.mdx +4 -0
  19. package/docs/cookbook/common-patterns/sequential-and-parallel.mdx +4 -0
  20. package/docs/cookbook/common-patterns/timeouts.mdx +4 -0
  21. package/docs/cookbook/common-patterns/webhooks.mdx +4 -0
  22. package/docs/cookbook/common-patterns/workflow-composition.mdx +4 -0
  23. package/docs/cookbook/integrations/ai-sdk.mdx +4 -0
  24. package/docs/cookbook/integrations/chat-sdk.mdx +4 -0
  25. package/docs/cookbook/integrations/sandbox.mdx +4 -0
  26. package/docs/deploying/index.mdx +2 -1
  27. package/docs/deploying/meta.json +1 -1
  28. package/docs/deploying/world/local-world.mdx +1 -1
  29. package/docs/deploying/world/meta.json +1 -1
  30. package/docs/deploying/world/postgres-world.mdx +1 -1
  31. package/docs/deploying/world/vercel-world.mdx +65 -4
  32. package/docs/errors/abort-signal-timeout-in-workflow.mdx +4 -0
  33. package/docs/errors/fetch-in-workflow.mdx +4 -0
  34. package/docs/errors/hook-conflict.mdx +4 -0
  35. package/docs/errors/node-js-module-in-workflow.mdx +4 -0
  36. package/docs/errors/serialization-failed.mdx +4 -0
  37. package/docs/errors/start-invalid-workflow-function.mdx +4 -0
  38. package/docs/errors/timeout-in-workflow.mdx +4 -0
  39. package/docs/errors/webhook-response-not-sent.mdx +4 -0
  40. package/docs/getting-started/astro.mdx +4 -0
  41. package/docs/getting-started/express.mdx +4 -0
  42. package/docs/getting-started/fastify.mdx +4 -0
  43. package/docs/getting-started/hono.mdx +4 -0
  44. package/docs/getting-started/nestjs.mdx +4 -0
  45. package/docs/getting-started/next.mdx +4 -0
  46. package/docs/getting-started/nitro.mdx +4 -0
  47. package/docs/getting-started/nuxt.mdx +4 -0
  48. package/docs/getting-started/python.mdx +4 -0
  49. package/docs/getting-started/sveltekit.mdx +4 -0
  50. package/docs/getting-started/tanstack-start.mdx +4 -0
  51. package/docs/getting-started/vite.mdx +4 -0
  52. package/docs/how-it-works/framework-integrations.mdx +7 -2
  53. package/docs/migration-guides/migrating-from-aws-step-functions.mdx +4 -0
  54. package/docs/migration-guides/migrating-from-inngest.mdx +4 -0
  55. package/docs/migration-guides/migrating-from-temporal.mdx +4 -0
  56. package/docs/migration-guides/migrating-from-trigger-dev.mdx +4 -0
  57. package/docs/observability/attributes.mdx +25 -3
  58. package/docs/observability/tracing.mdx +3 -1
  59. package/package.json +11 -10
@@ -18,7 +18,7 @@ All the functions and primitives that come with Workflow SDK by package.
18
18
  API reference for runtime functions from the `workflow/api` package.
19
19
  </Card>
20
20
  <Card title="workflow/runtime" href="/docs/api-reference/workflow-runtime">
21
- Runtime functions for resolving the World instance and the low-level World SDK.
21
+ Runtime functions for resolving the World instance and the low-level World SDK, including storage and analytics queries.
22
22
  </Card>
23
23
  <Card title="workflow/observability" href="/docs/api-reference/workflow-observability">
24
24
  Utilities to hydrate step I/O, parse display names, and decrypt workflow data.
@@ -16,7 +16,7 @@ The runtime package provides low-level access to the workflow runtime — resolv
16
16
  Async: resolve the World instance for storage, queuing, and streaming backends.
17
17
  </Card>
18
18
  <Card href="/docs/api-reference/workflow-runtime/world" title="World SDK">
19
- Low-level API for inspecting runs, steps, events, hooks, streams, and queues.
19
+ Low-level API for inspecting runs, steps, events, hooks, streams, and queues, plus metadata-only analytics with attribute search.
20
20
  </Card>
21
21
  </Cards>
22
22
 
@@ -0,0 +1,139 @@
1
+ ---
2
+ title: Analytics
3
+ description: Metadata-only read APIs for runs, steps, events, hooks, waits, and attributes, backed by the observability pipeline.
4
+ type: reference
5
+ summary: "Interfaces: world.analytics.runs, .attributes, .steps, .events, .hooks, .waits. Metadata-only listings with plan-based lookback windows; filter runs by attribute key=value."
6
+ prerequisites:
7
+ - /docs/api-reference/workflow-runtime/get-world
8
+ related:
9
+ - /docs/api-reference/workflow-runtime/world/storage
10
+ - /docs/observability/attributes
11
+ keywords:
12
+ - world.analytics
13
+ - analytics.runs
14
+ - analytics.attributes
15
+ - attribute filter
16
+ - listValues
17
+ - lookback window
18
+ - observability-upgrade-required
19
+ - pageInfo
20
+ - metadata-only
21
+ ---
22
+
23
+ `world.analytics` is an optional, read-only namespace for observability surfaces — dashboards, CLIs, and admin tools that list large numbers of runs without touching payload data.
24
+
25
+ It differs from [Storage](/docs/api-reference/workflow-runtime/world/storage) in three ways:
26
+
27
+ - **Metadata only.** Results never include run input/output, step data, or hook tokens. There is no `resolveData` option.
28
+ - **Served from the observability pipeline.** On Vercel, queries hit a ClickHouse-backed analytics store instead of the runtime database, so large listings do not compete with workflow execution. Data is ingested asynchronously and may trail the live state by a few seconds.
29
+ - **Plan-bounded lookback.** Listings only cover the observability retention window for your plan (up to 30 days). Every page includes a `pageInfo` block describing the current window, and requesting an older window fails with an `observability-upgrade-required` error.
30
+
31
+ The namespace is optional — worlds that don't implement it (such as the local development world) leave it `undefined`, so feature-detect before use:
32
+
33
+ ```typescript lineNumbers
34
+ import { getWorld } from "workflow/runtime";
35
+
36
+ const world = await getWorld();
37
+ if (world.analytics) { // [!code highlight]
38
+ const page = await world.analytics.runs.list();
39
+ }
40
+ ```
41
+
42
+ ---
43
+
44
+ ## analytics.runs
45
+
46
+ ### runs.list()
47
+
48
+ List runs with metadata, current status, and attributes. Without an explicit time window, the listing defaults to the trailing 24 hours; pass `startTime`/`endTime` to reach older runs within your plan window.
49
+
50
+ ```typescript lineNumbers
51
+ const page = await world.analytics.runs.list({
52
+ workflowName: "orderWorkflow",
53
+ status: "failed",
54
+ attributes: { source: "checkout" }, // [!code highlight]
55
+ pagination: { limit: 50, sortOrder: "desc" },
56
+ });
57
+ ```
58
+
59
+ | Parameter | Type | Description |
60
+ |-----------|------|-------------|
61
+ | `params.workflowName` | `string` | Filter to one workflow |
62
+ | `params.status` | `string` | `pending`, `running`, `completed`, `failed`, or `cancelled` |
63
+ | `params.startTime` / `params.endTime` | `string` | ISO 8601 window; must be provided together |
64
+ | `params.attributes` | `Record<string, string>` | Only return runs whose latest attributes match every pair (up to 8) |
65
+ | `params.pagination` | `PaginationOptions` | Cursor pagination |
66
+
67
+ **Returns:** `PaginatedResponse<AnalyticsRun>` — each run includes `runId`, `status`, `workflowName`, `deploymentId`, `attributes`, and lifecycle timestamps.
68
+
69
+ Attribute matching is latest-write-wins: a run whose attribute moved from `"v1"` to `"v2"` no longer matches `{ key: "v1" }`. Reserved `$`-prefixed keys may be used in filters even though user code cannot write them.
70
+
71
+ ### runs.get()
72
+
73
+ Fetch one run by ID. Point lookups search the full plan window, not just the trailing 24 hours.
74
+
75
+ ```typescript lineNumbers
76
+ const run = await world.analytics.runs.get(runId);
77
+ ```
78
+
79
+ ---
80
+
81
+ ## analytics.attributes
82
+
83
+ Discover which [attributes](/docs/observability/attributes) exist on your runs — for example to build filter dropdowns over arbitrary user-defined keys.
84
+
85
+ ### attributes.list()
86
+
87
+ List the distinct attribute keys observed on runs in the window, ordered alphabetically.
88
+
89
+ ```typescript lineNumbers
90
+ const page = await world.analytics.attributes.list({ // [!code highlight]
91
+ workflowName: "orderWorkflow",
92
+ });
93
+ for (const { key, runCount, lastSeenAt } of page.data) {
94
+ console.log(key, runCount, lastSeenAt);
95
+ }
96
+ ```
97
+
98
+ | Parameter | Type | Description |
99
+ |-----------|------|-------------|
100
+ | `params.workflowName` | `string` | Only count runs of one workflow |
101
+ | `params.startTime` / `params.endTime` | `string` | ISO 8601 window; must be provided together |
102
+ | `params.pagination` | `PaginationOptions` | Cursor pagination |
103
+
104
+ **Returns:** `PaginatedResponse<AnalyticsAttributeKey>` — `{ key, runCount, firstSeenAt, lastSeenAt }`
105
+
106
+ ---
107
+
108
+ ## analytics.steps, analytics.events, analytics.hooks, analytics.waits
109
+
110
+ Run-scoped listings mirroring their [Storage](/docs/api-reference/workflow-runtime/world/storage) counterparts, minus payload data:
111
+
112
+ ```typescript lineNumbers
113
+ const steps = await world.analytics.steps.list({ runId });
114
+ const events = await world.analytics.events.list({ runId, eventType: "step_failed" });
115
+ const related = await world.analytics.events.listByCorrelationId({ correlationId });
116
+ const hooks = await world.analytics.hooks.list({ runId });
117
+ const waits = await world.analytics.waits.list({ runId, status: "waiting" });
118
+ ```
119
+
120
+ Each namespace also has a `get()` for point lookups (`steps.get(runId, stepId)`, `events.get(runId, eventId)`, `hooks.get(hookId)`, `waits.get(runId, waitId)`). Hook listings never include the hook token — resolve it separately through the runtime APIs if you need to deliver a payload.
121
+
122
+ ---
123
+
124
+ ## Lookback windows and pageInfo
125
+
126
+ Every paginated response carries `pageInfo` describing the window the query was allowed to scan:
127
+
128
+ {/* @skip-typecheck: shape illustration, not runnable code */}
129
+ ```typescript
130
+ {
131
+ currentLookbackDays: 2, // what your plan allows today
132
+ maxLookbackDays: 30, // ceiling with Observability Plus
133
+ currentWindowStart: Date,
134
+ maxWindowStart: Date,
135
+ upgradeAvailable: true, // more history exists behind the plan gate
136
+ }
137
+ ```
138
+
139
+ Requests for a window older than `currentWindowStart` fail with an `observability-upgrade-required` error; windows older than `maxWindowStart` return not-found. Use `pageInfo` to size date pickers and to decide whether to surface an upgrade prompt.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: World SDK
3
- description: Low-level API for inspecting and managing workflow runs, steps, events, hooks, streams, and queues.
3
+ description: Low-level API for inspecting and managing workflow runs, steps, events, hooks, streams, and queues, with an analytics namespace for metadata listings and attribute search.
4
4
  type: overview
5
5
  summary: Access workflow infrastructure via await getWorld() for building observability dashboards, admin tools, and custom integrations.
6
6
  prerequisites:
@@ -14,7 +14,7 @@ keywords:
14
14
  - workflow management
15
15
  ---
16
16
 
17
- The World SDK provides direct access to workflow infrastructure — runs, steps, events, hooks, streams, and queues. Use it to build observability dashboards, admin panels, debugging tools, and custom workflow management logic.
17
+ The World SDK provides direct access to workflow infrastructure — runs, steps, events, hooks, streams, and queues — plus a metadata-only [Analytics](/docs/api-reference/workflow-runtime/world/analytics) namespace with attribute discovery and filtering. Use it to build observability dashboards, admin panels, debugging tools, and custom workflow management logic.
18
18
 
19
19
  ```typescript lineNumbers
20
20
  import { getWorld } from "workflow/runtime";
@@ -28,6 +28,9 @@ const world = await getWorld(); // [!code highlight]
28
28
  <Card href="/docs/api-reference/workflow-runtime/world/storage" title="Storage">
29
29
  Query runs, steps, hooks, and the underlying event log.
30
30
  </Card>
31
+ <Card href="/docs/api-reference/workflow-runtime/world/analytics" title="Analytics">
32
+ Metadata-only listings with attribute discovery and filtering, built for observability dashboards.
33
+ </Card>
31
34
  <Card href="/docs/api-reference/workflow-runtime/world/streams" title="Streams">
32
35
  Read, write, and manage real-time data streams for workflow runs.
33
36
  </Card>
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "title": "World SDK",
3
- "pages": ["storage", "streams", "queue"]
3
+ "pages": ["storage", "analytics", "streams", "queue"]
4
4
  }
@@ -196,6 +196,14 @@ Platform-provided values such as `VERCEL_DEPLOYMENT_ID`, `VERCEL_PROJECT_ID`, an
196
196
  - Default: `https://api.vercel.com/v1/workflow`
197
197
  - Workflow API proxy URL for external tooling.
198
198
 
199
+ ### `WORKFLOW_SEQUENTIAL_REPLAYS`
200
+
201
+ - Default: disabled
202
+ - Set `1` to serialize orchestrator (flow) invocations per run: each run's replays get their own queue topic and the flow trigger is generated with `maxConcurrency: 1`. Inline step executions get per-step topics and keep full parallelism.
203
+ - Read at **both build time and runtime** — set it as a project-level environment variable so the generated trigger and the runtime queue routing agree.
204
+ - Also enabled by `WORKFLOW_SAFE_MODE=1` when not set explicitly.
205
+ - Costs: flow-route push deliveries under `maxConcurrency` are billed at 2x units, queue observability sees one topic per run, and deliveries can queue behind the per-run slot — including hook resumes, aborts, and run-timeout enforcement, which are delayed while a replay is in flight. See [Vercel World](/docs/deploying/world/vercel-world#workflow_sequential_replays) for details.
206
+
199
207
  ### `VERCEL_WORKFLOW_SERVER_URL`
200
208
 
201
209
  - Factory option: none
@@ -7,6 +7,10 @@ related:
7
7
  - /docs/api-reference/workflow-api/start
8
8
  ---
9
9
 
10
+ <CopyPrompt
11
+ text="Refactor this workflow to use child workflows. Keep the parent as an exported `&quot;use workflow&quot;` function. Move independent units of durable work into separate exported child workflow functions. From the parent, call `start(childWorkflow, [args])` from `workflow/api` or the documented `startAndWait`/hook pattern where completion must resume the parent. Pass only serializable state to children. For fan-out, start children in parallel with `Promise.all` or bounded batches, collect run IDs, handle partial failures with `Promise.allSettled`, and use `getRun(runId)` when status, cancellation, streams, or return values are needed. Verify child start, completion, failure, and parent resume behavior."
12
+ />
13
+
10
14
  Use child workflows when a single workflow needs to orchestrate many independent units of work. Each child runs as its own workflow with a separate event log, retry boundary, and failure scope -- if one child fails, it doesn't take down the parent or siblings.
11
15
 
12
16
  ## When to use child workflows
@@ -5,6 +5,10 @@ type: guide
5
5
  summary: Learn how to build, export, and test npm packages that ship workflow and step functions — including package.json exports, re-exporting for stable workflow IDs, keeping step I/O clean, and integration testing.
6
6
  ---
7
7
 
8
+ <CopyPrompt
9
+ text="Package these workflow functions as a publishable npm library. Give the package a dedicated workflows entry point (for example `exports[&quot;./workflows&quot;]`) that ships the workflow and step source for the consumer's compiler to process. Keep every workflow and step input and output serializable, and read credentials from environment variables inside steps instead of accepting client instances. Document the consumer re-export requirement: consumers create a file in their `workflows/` directory containing `export * from &quot;<pkg>/workflows&quot;` so their build assigns stable workflow IDs and replay can resolve functions after cold starts. Add an integration test that runs a library workflow end to end from a consumer-style setup. Verify the build, stable IDs across deployments, and replay safety."
10
+ />
11
+
8
12
  import { File, Folder, Files } from "fumadocs-ui/components/files";
9
13
 
10
14
  <Callout>
@@ -9,6 +9,10 @@ related:
9
9
  - /docs/foundations/workflows-and-steps#step-functions
10
10
  ---
11
11
 
12
+ <CopyPrompt
13
+ text="Make this non-serializable dependency usable inside a durable workflow with the step-as-factory pattern. Instead of passing the object (AI SDK model, cloud SDK client) into the workflow, export a factory that returns an async callback marked with &quot;use step&quot; which constructs and returns the object at execution time, for example `export function openai(...args) { return async () => { &quot;use step&quot;; return openaiProvider(...args); }; }`. Pass the factory across the workflow boundary — the compiler serializes the function reference, not the instance — and invoke it inside steps where full Node.js access is available. Keep the factory's constructor arguments serializable. Verify the workflow builds, replays deterministically, and the dependency is only instantiated during step execution."
14
+ />
15
+
12
16
  <Callout>
13
17
  This is an advanced guide. It dives into workflow internals and is not required reading to use workflow.
14
18
  </Callout>
@@ -10,6 +10,10 @@ related:
10
10
  - /docs/foundations/hooks
11
11
  ---
12
12
 
13
+ <CopyPrompt
14
+ text="Add a safe self-upgrade point to this long-running workflow. Identify the loop boundary where no step is mid-side-effect. Define a serializable state object that contains all progress needed to continue. At the boundary, call `start(self, [state], { deploymentId: &quot;latest&quot; })` or the documented replacement workflow with the carried state, then return from the old run. If upgrades should be manual, add a `defineHook()` upgrade signal and resume it from an API route with `resumeHook()` from `workflow/api`. Make the handoff idempotent so retries do not start duplicate successor runs, and verify old-to-new handoff plus duplicate prevention."
15
+ />
16
+
13
17
  Workflows that block on external events for days, weeks, or months can outlive many deployments. **The key is to identify a clean upgrade point in the workflow** — a moment where it's safe to checkpoint state and start fresh — and then call [`start()`](/docs/api-reference/workflow-api/start) with `deploymentId: "latest"` to spawn a new run carrying that state forward. The current run ends; the next run begins on whatever deployment is live at that moment, so shipped fixes apply immediately without ever migrating an in-flight run.
14
18
 
15
19
  <Callout type="info">
@@ -5,6 +5,10 @@ type: guide
5
5
  summary: Cancel a running agent cooperatively with AbortController. A stop hook fires controller.abort(), the signal propagates into the agent step to cancel the model stream, and a data-stopped part is emitted to streaming clients before the workflow returns.
6
6
  ---
7
7
 
8
+ <CopyPrompt
9
+ text="Add cancellation to this durable AI agent. For hard cancellation, expose a server route that receives `runId` and calls `getRun(runId).cancel()` from `workflow/api`. For graceful stop, define `stopHook` with `defineHook()` from `workflow`, create it with a stable token such as the workflow run ID, and race the agent loop against the stop hook using `Promise.race`. Use `getWritable<UIMessageChunk>()` to emit a final stopped/cancelled message before returning. Wire the UI Stop button to the route that resumes the hook or falls back to `getRun(runId).cancel()`. Verify active model/tool work stops, cleanup runs for graceful stop, and stale run IDs are handled."
10
+ />
11
+
8
12
  Cancel a running agent from the outside — for example, a "Stop" button in a chat UI, an admin cancellation endpoint, or a timeout fallback.
9
13
 
10
14
  <Callout type="warn">
@@ -5,6 +5,10 @@ type: guide
5
5
  summary: Build durable, resumable AI agents with AI SDK v7's WorkflowAgent.
6
6
  ---
7
7
 
8
+ <CopyPrompt
9
+ text="Convert this stateless AI SDK agent to a durable agent with AI SDK v7's `WorkflowAgent`. Install/use `@ai-sdk/workflow` and `workflow`. Replace the manual `streamText` loop with `new WorkflowAgent(...)` from `@ai-sdk/workflow`, run inside an exported function with `&quot;use workflow&quot;`. Mark side-effectful tool `execute` functions with `&quot;use step&quot;` so they run as durable workflow steps with retries and observability. Stream `ModelCallStreamPart` chunks with `getWritable()` from `workflow`, convert the run stream to UI message chunks with `createModelCallToUIChunkTransform()` in the API route, and expose a route that starts or reconnects to the run. If migrating from the deprecated `DurableAgent`, follow the migration guide at https://ai-sdk.dev/v7/docs/agents/workflow-agent#migrating-from-durableagent. Verify tool retries, stream output, and resume/reconnect behavior."
10
+ />
11
+
8
12
  ## WorkflowAgent from AI SDK v7
9
13
 
10
14
  Use AI SDK v7's [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) for new durable agent work. It replaces `DurableAgent` and keeps the current agent pattern in the AI SDK package.
@@ -5,6 +5,10 @@ type: guide
5
5
  summary: Use defineHook with the tool call ID to suspend an agent for human approval, with an optional timeout.
6
6
  ---
7
7
 
8
+ <CopyPrompt
9
+ text="Add a human approval gate to this AI workflow. Define a typed hook with `defineHook()` from `workflow` for approval payloads. At the approval point, create the hook once with a stable token, await it inside the `&quot;use workflow&quot;` function, and branch on approved/rejected input. Add a server route that receives the human decision and calls `resumeHook(token, payload)` from `workflow/api`. If the approval should expire, race the hook against `sleep()` from `workflow`. Update the UI to show the pending approval and call the resume route. Verify approve, reject, timeout, and duplicate resume behavior."
10
+ />
11
+
8
12
  <Callout type="warn">
9
13
  This recipe uses the deprecated `DurableAgent` API. For new agents, use AI SDK's [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) and follow the [migration guide](https://ai-sdk.dev/v7/docs/agents/workflow-agent#migrating-from-durableagent). The human-in-the-loop pattern here (hooks, `Promise.race`, approval gating) applies to either API.
10
14
  </Callout>
@@ -5,6 +5,10 @@ type: guide
5
5
  summary: Split items into fixed-size batches, process each batch concurrently with Promise.allSettled, and pace batches with sleep to avoid overloading downstream services.
6
6
  ---
7
7
 
8
+ <CopyPrompt
9
+ text="Implement durable batch processing. Import `sleep` from `workflow`. In an exported &quot;use workflow&quot; function, split the input records into chunks of a fixed `batchSize`. For each batch, call a &quot;use step&quot; helper such as `processRecord(record)` for every record using `Promise.allSettled` so one record failure does not hide the rest. Record successes and failures in a serializable result object. Between batches, `await sleep(&quot;1s&quot;)` or another configured delay to respect downstream rate limits. Make the step idempotent using record IDs or external idempotency keys. Verify all-success, partial-failure, and rate-paced execution paths."
10
+ />
11
+
8
12
  Use batching when you need to process a large list of items in parallel while controlling concurrency. Items are split into fixed-size batches, each batch runs concurrently, and failures in one batch don't affect others.
9
13
 
10
14
  ## When to use this
@@ -5,6 +5,10 @@ type: guide
5
5
  summary: Use step IDs for retry-safe external calls, and use deterministic hook tokens when duplicate requests must route to one active workflow.
6
6
  ---
7
7
 
8
+ <CopyPrompt
9
+ text="Make this workflow's side effects idempotent. For retry-safe external calls, read the deterministic step ID inside the &quot;use step&quot; function with `getStepMetadata()` from `workflow` and pass `stepId` as the idempotency key to the external API (for example Stripe's `Idempotency-Key` header) so step retries deduplicate. For duplicate workflow starts, derive a deterministic hook token from the domain key (for example `order:${orderId}`): in the API route, look up the active hook with `getHookByToken(token)` from `workflow/api` — catching `HookNotFoundError` from `workflow/errors` — and reuse its `runId`, otherwise call `start(...)`; inside the workflow, create the hook with the same token and check `await hook.getConflict()` before duplicate-sensitive work. Verify retried steps deduplicate, duplicate starts reuse the active run, and conflicts are handled."
10
+ />
11
+
8
12
  Use idempotency when a retry or duplicate request should not repeat the underlying work. In Workflow, there are two common patterns: use the step ID for retry-safe external calls, and use hook tokens to coordinate duplicate workflow starts.
9
13
 
10
14
  ## When to use this
@@ -5,6 +5,10 @@ type: guide
5
5
  summary: When an external API returns 429, throw RetryableError with the Retry-After value so the workflow runtime automatically reschedules the step after the specified delay.
6
6
  ---
7
7
 
8
+ <CopyPrompt
9
+ text="Add durable rate-limit handling to external API steps. Import `RetryableError`, `FatalError`, `getStepMetadata`, and `sleep` from `workflow` as needed. In each HTTP-calling &quot;use step&quot; helper, if the response status is 429, read the `Retry-After` header and throw `new RetryableError(&quot;Rate limited&quot;, { retryAfter })`. For transient 5xx/network failures, throw `RetryableError` with an exponential delay based on `getStepMetadata().attempt`. For permanent 4xx failures, throw `FatalError`. If the dependency is completely down, add a workflow-level circuit breaker that `await sleep(&quot;30s&quot;)` before probing again. Make requests idempotent with stable keys, and verify 429, 5xx, fatal 4xx, and circuit-breaker behavior."
10
+ />
11
+
8
12
  Use this pattern when calling external APIs that enforce rate limits. Instead of writing manual retry loops, throw `RetryableError` with a `retryAfter` value and let the workflow runtime handle rescheduling.
9
13
 
10
14
  ## When to use this
@@ -5,6 +5,10 @@ type: guide
5
5
  summary: Run a sequence of steps where each registers a compensation. If any step throws a FatalError, compensations execute in reverse order to restore consistency.
6
6
  ---
7
7
 
8
+ <CopyPrompt
9
+ text="Implement a saga-style workflow. Import `FatalError` from `workflow`. In the exported &quot;use workflow&quot; function, keep a compensation stack such as `const compensations: Array<() => Promise<void>> = []`. For each forward side effect, create a &quot;use step&quot; helper, await it, then push the matching compensation &quot;use step&quot; helper onto the stack. Throw `FatalError` for permanent business failures and normal errors for retryable failures. In the workflow catch block, run compensations in reverse order with `for (const compensate of compensations.reverse()) await compensate()`, then rethrow or return a failed status. Make each compensation idempotent. Verify successful completion, a fatal failure after multiple steps, and a replay/retry during rollback."
10
+ />
11
+
8
12
  Use the saga pattern when a business transaction spans multiple services and you need automatic rollback if any step fails. Each forward step registers a compensation, and on failure the workflow unwinds them in reverse order.
9
13
 
10
14
  ## When to use this
@@ -5,6 +5,10 @@ type: guide
5
5
  summary: Schedule future actions with durable sleep that survives cold starts, and race sleeps against hooks to let external events cancel the workflow early.
6
6
  ---
7
7
 
8
+ <CopyPrompt
9
+ text="Create an interruptible scheduled workflow. Import `defineHook` and `sleep` from `workflow`. Define a cancellation hook such as `export const cancelDrip = defineHook<{ reason?: string }>()`. In an exported workflow function with &quot;use workflow&quot;, create the hook once, send the first message in a &quot;use step&quot; helper, then `await Promise.race([sleep(&quot;2d&quot;).then(() => false), cancelHook.then(() => true)])` before each delayed action. If the hook wins, return a cancelled status; if sleep wins, continue to the next step. Add an API route that calls `cancelDrip.resume(runIdOrToken, { reason })` or `resumeHook()` from `workflow/api`. Verify scheduled delivery, cancellation before sleep completes, and resume after server restart."
10
+ />
11
+
8
12
  Workflow's `sleep()` is durable — it survives cold starts, restarts, and deployments. Combined with `defineHook()` and `Promise.race()`, it becomes the foundation for interruptible scheduled workflows like drip campaigns, reminders, and timed sequences.
9
13
 
10
14
  <Callout type="info">
@@ -9,6 +9,10 @@ related:
9
9
  - /cookbook/common-patterns/scheduling
10
10
  ---
11
11
 
12
+ <CopyPrompt
13
+ text="Compose these workflow steps with standard async/await patterns. In the exported &quot;use workflow&quot; function, chain dependent &quot;use step&quot; calls with sequential `await`; run independent steps concurrently by starting them without `await` and awaiting `Promise.all([...])`; and use `Promise.race([...])` to act on whichever promise settles first. These compose with durable primitives — race a step or a webhook from `createWebhook()` against `sleep()` from `workflow` for deadlines. Keep every step input and output serializable, and remember `Promise.race` does not cancel the losing branch — it keeps running — so side-effectful losers need idempotency keys. Verify sequential ordering, parallel execution, and both race outcomes."
14
+ />
15
+
12
16
  Workflows are written in plain async/await — there's no new control-flow API to learn. Sequential awaits chain steps that depend on each other, `Promise.all` runs independent steps in parallel, and `Promise.race` returns whichever finishes first. These compose with workflow primitives like [`sleep()`](/docs/api-reference/workflow/sleep) and [`createWebhook()`](/docs/api-reference/workflow/create-webhook) since those are also just promises.
13
17
 
14
18
  ## When to use this
@@ -10,6 +10,10 @@ related:
10
10
  - /cookbook/common-patterns/webhooks
11
11
  ---
12
12
 
13
+ <CopyPrompt
14
+ text="Add a deadline to this slow workflow operation. Import `sleep` from `workflow`. In the exported &quot;use workflow&quot; function, race the operation against a durable sleep: `await Promise.race([slowStep(...), deadline(&quot;10m&quot;)])`, where `deadline` is a helper that awaits `sleep(duration)` and returns a sentinel value or throws. Branch on the winner: continue normally on success, run the fallback or compensation path on timeout. Use the same pattern to bound hooks and webhooks. `Promise.race` does not cancel the loser — the underlying step keeps running — so pass an `AbortSignal` into the step for cooperative cancellation and make non-idempotent side effects retry-safe with idempotency keys. Verify the fast path, the timeout path, and side-effect safety when the loser completes late."
15
+ />
16
+
13
17
  A common requirement is bounding how long a workflow waits for something to finish — a slow step, an external webhook, a human approval. Race the operation against a durable `sleep()` with `Promise.race()` — whichever finishes first wins, and the loser keeps running but its result is ignored.
14
18
 
15
19
  ## When to use this
@@ -5,6 +5,10 @@ type: guide
5
5
  summary: Create webhook endpoints that your workflow can await, process incoming requests in steps, and respond to the caller — all within durable workflow context.
6
6
  ---
7
7
 
8
+ <CopyPrompt
9
+ text="Implement durable webhook handling for this workflow. Use `createWebhook()` from `workflow` inside the &quot;use workflow&quot; function when the workflow needs a generated callback URL. Use `createWebhook({ respondWith: &quot;manual&quot; })` when the workflow must validate the request before sending an HTTP response. Await the webhook request, pass the `RequestWithResponse` into a &quot;use step&quot; function for validation and side effects, and call `request.respondWith(Response.json(...))` on every code path. Race the webhook against `sleep()` for deadlines and throw `FatalError` for permanent timeout/failure paths. For fixed public callbacks or large payloads, use `defineHook()` plus `resumeHook()` from `workflow/api` and pass only a token or blob reference into the hook payload. Verify success response, invalid response, timeout, duplicate callback, and large-payload behavior."
10
+ />
11
+
8
12
  Use webhooks when external services push events to your application via HTTP callbacks. The workflow creates a webhook URL, suspends with zero compute cost, and resumes when a request arrives.
9
13
 
10
14
  ## When to use this
@@ -10,6 +10,10 @@ related:
10
10
  - /docs/api-reference/workflow-api/get-run
11
11
  ---
12
12
 
13
+ <CopyPrompt
14
+ text="Compose these workflows. For direct composition, `await` the child workflow function from the parent &quot;use workflow&quot; function — the child's steps flatten into the parent's event log and run as a single run sharing the parent's lifecycle. For independent background work, spawn the child with `start()` from `workflow/api` called directly in the workflow (in v5, `start()` is step-backed and records a deterministic step boundary) and return `run.runId` so callers can track it with `getRun()`. Choose flattening when the parent needs the child's result; choose background spawn when the child should have its own run, retries, and lifetime. Keep all inputs and outputs serializable. Verify flattened execution, background spawn with a separate runId, and replay determinism."
15
+ />
16
+
13
17
  Workflows can call other workflows. Choose between two composition modes depending on whether the parent needs the child's result inline (direct await) or wants to fire the child off as an independent run (background spawn). For massive fan-out with hook-based waiting and partial-failure handling, see [Child Workflows](/cookbook/advanced/child-workflows).
14
18
 
15
19
  ## When to use this
@@ -11,6 +11,10 @@ related:
11
11
  - /docs/api-reference/workflow-ai/durable-agent
12
12
  ---
13
13
 
14
+ <CopyPrompt
15
+ text="Implement the durable AI SDK multi-turn pattern. Use `streamText`, `stepCountIs`, and `createUIMessageStreamResponse` from `ai`; `defineHook`, `getWritable`, and `getWorkflowMetadata` from `workflow`; and `start`/`getRun` from `workflow/api`. Put the model call in a `&quot;use step&quot;` function such as `runTurn(messages)` and pipe `result.toUIMessageStream()` to `getWritable<UIMessageChunk>()` with `{ preventClose: true }`. In the workflow, create one hook with `turnHook.create({ token: workflowRunId })`, loop over turns, and await the hook between user messages. Add an API route that starts a run on first message, stores/returns the run ID in `x-workflow-run-id`, resumes the hook for follow-up messages, reads from `run.getReadable({ startIndex })`, and handles stale run IDs by starting fresh. Wire the client transport to send `runId` with each request and verify first turn, follow-up turn, `/done`, and reconnect behavior."
16
+ />
17
+
14
18
  [AI SDK](https://ai-sdk.dev/) is Vercel's framework-agnostic TypeScript toolkit for building AI-powered apps and agents — unified provider access, streaming, tool calling, structured output, and UI hooks. Workflow SDK complements it by making the multi-turn loop durable: the conversation state, hooks, and per-turn responses survive restarts and timeouts. Note that in this pattern the durability boundary is the entire turn — individual tool calls inside a turn are **not** durable on their own (see [Pitfalls](#tools-are-not-individually-durable) below).
15
19
 
16
20
  For the full AI SDK reference (providers, `streamText`, `generateObject`, `useChat`, tool calling, etc.) see the [AI SDK docs](https://ai-sdk.dev/docs). This page covers the Workflow-specific integration points.
@@ -11,6 +11,10 @@ related:
11
11
  - /docs/api-reference/workflow-api/get-run
12
12
  ---
13
13
 
14
+ <CopyPrompt
15
+ text="Make this Chat SDK bot durable with Workflow SDK. Install/use `workflow`. Create one exported workflow function with &quot;use workflow&quot; per chat thread. Store the Chat SDK thread ID, Workflow run ID, and any serialized conversation state in the project data store. Use `defineHook()` from `workflow` for incoming turns and call `resumeHook()` from `workflow/api` from the Chat SDK webhook or message handler. Put provider calls, database writes, and outbound platform messages in &quot;use step&quot; helper functions. Start a new run with `start(workflowFn, [initialThreadState])` when no run exists, otherwise resume the existing hook. Use `getRun(runId)` for status, cancellation, or stream reads. Verify first message, follow-up message, restart/reconnect, duplicate webhook, and failed-send retry behavior."
16
+ />
17
+
14
18
  [Chat SDK](https://chat-sdk.dev/) is a unified TypeScript SDK for building bots across Slack, Microsoft Teams, Google Chat, Discord, Telegram, GitHub, Linear, and WhatsApp. Write the bot once, deploy to every platform. It handles webhook verification, event normalization, subscriptions, and cross-platform features like cards and modals.
15
19
 
16
20
  Workflow SDK complements it by making bot **sessions** durable. Each conversation thread maps to a long-running workflow run that:
@@ -10,6 +10,10 @@ related:
10
10
  - /docs/cookbook/agent-patterns/durable-agent
11
11
  ---
12
12
 
13
+ <CopyPrompt
14
+ text="Implement a durable Vercel Sandbox-backed coding-agent workflow. Install the Sandbox package used by this project and `workflow`. Create an exported workflow function with &quot;use workflow&quot; that owns the agent session. Put sandbox creation, command execution, snapshot, refresh, and cleanup into helper functions with &quot;use step&quot;. Persist the sandbox ID, snapshot ID, and workflow run ID in the project data store so clients can reconnect. Use `getWritable()` from `workflow` to stream agent progress and command output. Use `sleep()` to hibernate, refresh, or enforce idle timeouts. Add API routes to start a session, reconnect by run ID, and stop/cleanup. Verify first run, reconnect after reload, snapshot restore, timeout, and cleanup behavior."
15
+ />
16
+
13
17
  [Vercel Sandbox](https://vercel.com/docs/sandbox) provides isolated code execution environments. The `@vercel/sandbox` package has first-class support for the Workflow SDK — the `Sandbox` class is serializable, and its methods (`create`, `runCommand`, `stop`, `snapshot`) implicitly run as steps. You can use `Sandbox` directly inside a workflow function without wrapping each call in a separate `"use step"` function.
14
18
 
15
19
  ## Why Workflow + Sandbox
@@ -43,6 +43,7 @@ The Vercel World provides:
43
43
  - **Managed queuing** - Steps are processed reliably with automatic retries
44
44
  - **Automatic scaling** - Workflows scale with your application
45
45
  - **Built-in observability** - View workflow runs in the Vercel dashboard
46
+ - **Multi-region** - Runs are pinned to the region that creates them, keeping workflow data, queuing, and streaming close to your users (requires `workflow` 5.0.0-beta.33 or later)
46
47
 
47
48
  Simply deploy your application:
48
49
 
@@ -53,7 +54,7 @@ vercel deploy
53
54
  <FluidComputeCallout />
54
55
 
55
56
  <Callout>
56
- Learn more about the [Vercel World](/worlds/vercel) and its capabilities.
57
+ Learn more about the [Vercel World](/docs/deploying/world/vercel-world) and its capabilities, including [multi-region](/docs/deploying/world/vercel-world#multi-region).
57
58
  </Callout>
58
59
 
59
60
  ## Self-Hosting & Other Providers
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "title": "Deploying",
3
- "pages": ["...deploying", "building-a-world"]
3
+ "pages": ["...deploying", "world", "building-a-world"]
4
4
  }
@@ -34,7 +34,7 @@ Learn more in the [Observability](/docs/observability) documentation.
34
34
 
35
35
  ## Testing & Compatibility
36
36
 
37
- <WorldTestingPerformance />
37
+ <WorldTestingPerformance worldId="local" />
38
38
 
39
39
  ## Configuration
40
40
 
@@ -1,4 +1,4 @@
1
1
  {
2
- "title": "World",
2
+ "title": "Worlds",
3
3
  "pages": ["local-world", "vercel-world", "postgres-world"]
4
4
  }
@@ -184,7 +184,7 @@ Learn more in the [Observability](/docs/observability) documentation.
184
184
 
185
185
  ## Testing & Compatibility
186
186
 
187
- <WorldTestingPerformance />
187
+ <WorldTestingPerformance worldId="postgres" />
188
188
 
189
189
  ## Configuration
190
190
 
@@ -42,11 +42,55 @@ For complete details on pricing, usage limits, and included allotments on Vercel
42
42
 
43
43
  For self-hosted deployments, use the [Postgres World](/worlds/postgres). For local development, use the [Local World](/worlds/local).
44
44
 
45
- ## Limitations
45
+ ## Multi-region
46
+
47
+ The Vercel World runs in every [Vercel Function region](https://vercel.com/docs/regions). Each workflow run is pinned to a single region at creation time: its stored state, queue dispatch, and streams are all served from that region — no cross-region round trips on the hot path. When your application is deployed in the run's region (the automatic case below), step execution is region-local too.
48
+
49
+ <Callout type="info">
50
+ Multi-region requires `workflow` version **5.0.0-beta.33** or later.
51
+ The 4.x release line does not support region pinning — runs created by
52
+ 4.x always live in `iad1`.
53
+ </Callout>
54
+
55
+ ### Automatic region pinning
56
+
57
+ No configuration is needed. A run is pinned to the region of the function that creates it:
58
+
59
+ - Deploy your app to a single region (via [`regions`](https://vercel.com/docs/project-configuration#regions) in `vercel.json` or the project settings), and every run lives there.
60
+ - Deploy to multiple regions for a globally distributed audience, and each run is pinned to the region that served the user who triggered it — workflow data and streaming stay close to that user.
61
+
62
+ ### Explicit region selection
63
+
64
+ To pin a specific run somewhere else, pass the `region` option to [`start()`](/docs/api-reference/workflow-api/start):
65
+
66
+ ```typescript
67
+ import { start } from "workflow/api";
68
+ import { myWorkflow } from "@/workflows/my-workflow";
69
+
70
+ const run = await start(myWorkflow, [input], { region: "sfo1" });
71
+ ```
72
+
73
+ <Callout type="warn">
74
+ The `region` option controls where the run's **data is stored** and where
75
+ its **queue messages are dispatched from** — it does not deploy your code
76
+ there. Your workflow and step functions execute in the regions your
77
+ application is deployed to. For execution to actually happen in the
78
+ specified region, your app must be deployed there — via
79
+ [`regions`](https://vercel.com/docs/project-configuration#regions) in
80
+ `vercel.json` or the Function Regions setting in your project settings.
81
+ If it isn't, the run's data lives in the requested region but its steps
82
+ execute in the nearest region your app is deployed to.
83
+ </Callout>
84
+
85
+ ### Good to know
46
86
 
47
- - **Single-region deployment** - The backend infrastructure is currently deployed only in `iad1`. Applications in other regions will route workflow requests to `iad1`, which may result in higher latency. For best performance, deploy your Vercel apps using Workflow to `iad1`. Global deployment is planned to colocate the backend closer to your applications.
87
+ - Reads, hook resumes, and stream consumers can come from anywhere the platform routes them to the run's region automatically.
88
+ - Runs created by 4.x SDKs (and any runs that existed before you upgraded) live in `iad1` and are unaffected by an upgrade; there is no migration.
89
+ - **Hook tokens are currently stored in `iad1`** for every run, regardless of the run's region: the token-to-run mapping that powers [`getHookByToken()`](/docs/api-reference/workflow-api/get-hook-by-token) and [`resumeHook()`](/docs/api-reference/workflow-api/resume-hook) lives there so tokens — which carry no region information — can always be resolved. Hook *payloads* are not affected: a received payload is recorded on the run's event log, which lives in the run's region like all other run data. This token placement may become a project-level setting in the future.
90
+
91
+ ## Limitations
48
92
 
49
- - **Data residency** - The Vercel World is currently deployed in the `iad1` region. This means independently of the deployment location of your application, the data for your workflows will be stored in the `iad1` region.
93
+ - **No run migration** - A run's region is fixed at creation. Existing runs cannot be moved to a different region.
50
94
 
51
95
  ## Observability
52
96
 
@@ -85,7 +129,7 @@ Learn more in the [Observability](/docs/observability) documentation.
85
129
 
86
130
  ## Testing & Compatibility
87
131
 
88
- <WorldTestingPerformance />
132
+ <WorldTestingPerformance worldId="vercel" />
89
133
 
90
134
  ## Configuration
91
135
 
@@ -125,6 +169,23 @@ Custom base URL for the Vercel workflow API proxy. Default: `https://api.vercel.
125
169
 
126
170
  Custom workflow-server URL for direct runtime requests, or for the proxy to forward to via `WORKFLOW_VERCEL_BACKEND_URL`. Default: unset; normal deployments should not need this.
127
171
 
172
+ ### `WORKFLOW_SEQUENTIAL_REPLAYS`
173
+
174
+ Set `WORKFLOW_SEQUENTIAL_REPLAYS=1` to guarantee that **at most one orchestrator (flow) invocation runs at a time per workflow run**. This behavior is off by default; without it, the runtime relies on idempotency and the event log to tolerate concurrent flow invocations of the same run. It is also enabled by `WORKFLOW_SAFE_MODE=1` when `WORKFLOW_SEQUENTIAL_REPLAYS` is not set explicitly.
175
+
176
+ When enabled, each run's orchestrator messages are given their own queue topic and the flow trigger is configured with `maxConcurrency: 1`, so [Vercel Queues](https://vercel.com/docs/queues) processes replays for a given run strictly one at a time. Step executions (which ride the flow topic in the combined handler model) get a per-step topic, so steps keep full parallelism.
177
+
178
+ <Callout type="warn">
179
+ This variable is read at **both build time and runtime**, so it must be set as a project-level environment variable that applies to your build and your deployed functions. Setting it for only one will produce an inconsistent configuration. The same applies to framework integrations that write their own queue trigger configuration instead of using `getWorkflowQueueTrigger()` from `@workflow/builders`: they only get the runtime half (per-run topics) unless they also emit `maxConcurrency: 1` on their flow trigger.
180
+
181
+ Enabling sequential replays has a cost. Per [Vercel Queues pricing](https://vercel.com/docs/queues/pricing), push deliveries under `maxConcurrency` are billed at **2x units** for that operation, so every flow-route delivery costs double while this is enabled. It also creates one queue topic per run, which increases the number of distinct queues surfaced in queue observability, and each flow invocation waits for a per-run concurrency slot before delivery, which can add queueing latency. Leave it off unless you specifically need the per-run serialization guarantee.
182
+
183
+
184
+ While a replay holds a run's slot — including time spent executing steps inline — other wake messages for that run (hook resumes, aborts and cancellations, and run-timeout enforcement) wait for the slot. Expect aborts and timeouts to be delayed by up to the duration of the longest single invocation.
185
+
186
+ The guarantee covers messages sent by the Workflow SDK itself. External producers that compute a flow topic name directly (rather than enqueueing through the SDK) still deliver, but bypass the per-run serialization slot.
187
+ </Callout>
188
+
128
189
  ### `VERCEL_QUEUE_MAX_DELAY_SECONDS`
129
190
 
130
191
  Maximum delay, in seconds, that Workflow uses for one Vercel Queues continuation message when implementing `sleep()`. If a workflow sleeps longer than this, the runtime schedules another continuation message when the first one fires, repeating until the sleep's target time is reached. Default: `82800` (23 hours).
@@ -11,6 +11,10 @@ related:
11
11
  - /docs/errors/timeout-in-workflow
12
12
  ---
13
13
 
14
+ <CopyPrompt
15
+ text="Replace `AbortSignal.timeout()` inside workflow functions. Find the failing `AbortSignal.timeout(ms)` call in a `&quot;use workflow&quot;` function. Import `sleep` from `workflow`. Create `const controller = new AbortController()`, pass `controller.signal` into the async work that supports cancellation, and start a deterministic timeout with `void sleep(&quot;10s&quot;).then(() => controller.abort())` or a `Promise.race` between the work and `sleep(...)`. Keep actual fetch/SDK side effects inside `&quot;use step&quot;` helpers when they need Node.js APIs. Treat intentional aborts as non-retryable if appropriate by throwing `FatalError`. Verify the operation succeeds before the timeout, aborts after the timeout, and replays without abort-signal-timeout-in-workflow."
16
+ />
17
+
14
18
  ## Error
15
19
 
16
20
  ```
@@ -9,6 +9,10 @@ related:
9
9
  - /docs/api-reference/workflow/fetch
10
10
  ---
11
11
 
12
+ <CopyPrompt
13
+ text="Fix `fetch` usage inside workflow functions. Search workflow files for direct global `fetch(...)` calls and libraries such as AI SDK calls that use fetch. For simple HTTP calls inside a `&quot;use workflow&quot;` function, import `{ fetch }` from `workflow` and replace the global call. For SDK/client calls that need normal Node.js or provider behavior, move the call into a helper function with `&quot;use step&quot;` and call that step from the workflow. Keep all step inputs and outputs serializable. Verify the workflow starts and replays without the fetch-in-workflow error."
14
+ />
15
+
12
16
  This error occurs when you try to use `fetch()` directly in a workflow function, or when a library (like the AI SDK) tries to call `fetch()` under the hood.
13
17
 
14
18
  ## Error Message
@@ -10,6 +10,10 @@ related:
10
10
  - /docs/api-reference/workflow/define-hook
11
11
  ---
12
12
 
13
+ <CopyPrompt
14
+ text="Fix hook token conflicts. Find every `createHook({ token })` or typed hook creation site. If multiple waits can exist at the same time, include a unique stable discriminator in the token such as `${workflowRunId}:approval:${itemId}` or `${orderId}:${attempt}` instead of reusing one global token. If duplicate work should join an existing run, catch `HookConflictError` from `@workflow/errors`, read the conflicting run ID from the error/result if available, and use `getRun(runId)` plus `resumeHook()` from `workflow/api` to deliver the payload to the active run. Keep token generation deterministic across retries so replay does not create new hook identities. Verify two concurrent runs and a duplicate request no longer throw hook-conflict unexpectedly."
15
+ />
16
+
13
17
  This error occurs when you try to create a hook with a token that is already in use by another active workflow run. Hook tokens must be unique across all running workflows in your project.
14
18
 
15
19
  ## Error Message
@@ -9,6 +9,10 @@ related:
9
9
  - /docs/how-it-works/understanding-directives
10
10
  ---
11
11
 
12
+ <CopyPrompt
13
+ text="Fix Node.js module usage inside workflow functions. Search workflow files for imports or direct usage of Node-only APIs such as `fs`, `path`, `crypto`, `process`, `http`, or SDK clients. Remove those imports from files/functions that execute under `&quot;use workflow&quot;`. Create helper functions with `&quot;use step&quot;` for filesystem, crypto, environment, network, database, or SDK work, and call those helpers from the workflow. Keep the workflow function limited to deterministic orchestration, serializable values, `sleep`, hooks, and step calls. Verify the workflow starts without node-js-module-in-workflow errors."
14
+ />
15
+
12
16
  This error occurs when you try to import or use Node.js core modules (like `fs`, `http`, `crypto`, `path`, etc.) directly inside a workflow function.
13
17
 
14
18
  ## Error Message
@@ -9,6 +9,10 @@ related:
9
9
  - /docs/foundations/workflows-and-steps
10
10
  ---
11
11
 
12
+ <CopyPrompt
13
+ text="Fix Workflow serialization errors. Find the failing `start()` call, workflow argument, step return value, hook payload, or stream chunk. Replace non-serializable values such as class instances, functions, SDK clients, Response/Request objects, streams, database connections, Dates that need custom handling, Maps/Sets, or circular objects with plain JSON-compatible data, IDs, strings, numbers, booleans, arrays, and objects. Recreate runtime-only clients or objects inside `&quot;use step&quot;` helpers instead of passing them through the workflow log. For external resources, pass a stable ID or URL and load the resource inside the step. Add a test or local route call that serializes the same input/output path successfully."
14
+ />
15
+
12
16
  This error occurs when you try to pass non-serializable data between execution boundaries in your workflow. All data passed between workflow functions, step functions, and the workflow runtime must be serializable to persist in the event log.
13
17
 
14
18
  ## Error Message
@@ -11,6 +11,10 @@ related:
11
11
  - /docs/api-reference/workflow-next/with-workflow
12
12
  ---
13
13
 
14
+ <CopyPrompt
15
+ text="Fix `start()` receiving an invalid workflow function. Find the function passed to `start()` from `workflow/api`. Ensure the target function is directly imported, exported from its workflow file, and contains the literal `&quot;use workflow&quot;` directive at the top of the function body. Do not pass wrapper callbacks like `start(async () => workflowFn())`; call `start(workflowFn, [args])`. Verify the framework integration is configured (`withWorkflow()` in Next.js, `workflow()`/`workflowPlugin()` in Vite/Astro/SvelteKit, `workflow/nitro`, `workflow/nuxt`, or `@workflow/nest` as appropriate) and that the workflow file is inside a transformed directory. Add a local route/test that calls `start(workflowFn, args)` and confirms a run is created."
16
+ />
17
+
14
18
  This error occurs when `start()` receives a function that does not have Workflow SDK's generated workflow metadata. In practice, that usually means the function is missing `"use workflow"` or the file was never transformed by your framework integration.
15
19
 
16
20
  ## Error Message
@@ -9,6 +9,10 @@ related:
9
9
  - /docs/api-reference/workflow/sleep
10
10
  ---
11
11
 
12
+ <CopyPrompt
13
+ text="Fix timer usage inside workflow functions. Search `&quot;use workflow&quot;` functions for `setTimeout`, `setInterval`, `timers/promises`, polling loops, or `AbortSignal.timeout()`. Replace workflow delays with `await sleep(&quot;5s&quot;)`, `await sleep(&quot;24h&quot;)`, or `await sleep(date)` from `workflow`. For polling, use a workflow loop that calls a `&quot;use step&quot;` helper to check external state and then `await sleep(...)` between attempts. If a step itself needs a short in-process delay, keep that timer inside the `&quot;use step&quot;` function only. Verify the workflow can replay and resume after the durable sleep."
14
+ />
15
+
12
16
  This error occurs when you try to use `setTimeout()`, `setInterval()`, or related timing functions directly inside a workflow function.
13
17
 
14
18
  ## Error Message
@@ -9,6 +9,10 @@ related:
9
9
  - /docs/api-reference/workflow/create-webhook
10
10
  ---
11
11
 
12
+ <CopyPrompt
13
+ text="Fix webhook-response-not-sent errors. Find `createWebhook({ respondWith: &quot;manual&quot; })` usage. In the workflow, await the webhook request and pass the `RequestWithResponse` into a `&quot;use step&quot;` helper for validation and side effects. In every success, validation failure, catch, and early-return branch, call `await request.respondWith(new Response(...))` or `await request.respondWith(Response.json(...))` exactly once before the webhook completes. If manual control is not needed, remove `respondWith: &quot;manual&quot;` or use a static `Response` option. Add tests or local webhook calls for success, invalid input, and thrown-error paths."
14
+ />
15
+
12
16
  This error occurs when a webhook is configured with `respondWith: "manual"` but the workflow does not send a response using `request.respondWith()` before the webhook execution completes.
13
17
 
14
18
  ## Error Message
@@ -9,6 +9,10 @@ related:
9
9
  - /docs/foundations/workflows-and-steps
10
10
  ---
11
11
 
12
+ <CopyPrompt
13
+ text="In this Astro app, run `npm i workflow`. In `astro.config.mjs`, import `workflow` from `workflow/astro` and add `integrations: [workflow()]`. Add the TypeScript plugin `{ &quot;name&quot;: &quot;workflow&quot; }` to `tsconfig.json` if TypeScript is used. Create `src/workflows/user-signup.ts` exporting `handleUserSignup(email)` with `&quot;use workflow&quot;`, `sleep` from `workflow`, and `&quot;use step&quot;` helpers. Add `src/pages/api/signup.ts` exporting `POST: APIRoute` that reads `{ email }`, calls `start(handleUserSignup, [email])` from `workflow/api`, returns `Response.json`, and sets `prerender = false`. Run `npm run dev`, call `curl -X POST --json '{&quot;email&quot;:&quot;hello@example.com&quot;}' http://localhost:4321/api/signup`, and inspect with `npx workflow inspect runs`."
14
+ />
15
+
12
16
  This guide will walk through setting up your first workflow in an Astro app. Along the way, you'll learn more about the concepts that are fundamental to using the Workflow SDK in your own projects.
13
17
 
14
18
  ---
@@ -9,6 +9,10 @@ related:
9
9
  - /docs/foundations/workflows-and-steps
10
10
  ---
11
11
 
12
+ <CopyPrompt
13
+ text="In this Express app, run `npm i workflow express nitro rollup` and, if using TypeScript, `npm i -D @types/express`. Create `nitro.config.ts` with `modules: [&quot;workflow/nitro&quot;]`, `vercel: { entryFormat: &quot;node&quot; }`, and `routes: { &quot;/**&quot;: { handler: &quot;./src/index.ts&quot;, format: &quot;node&quot; } }`. Add package scripts `dev: &quot;nitro dev&quot;` and `build: &quot;nitro build&quot;`. Create `workflows/user-signup.ts` with `&quot;use workflow&quot;`, `sleep`, and `&quot;use step&quot;` helpers. Add `src/index.ts` with Express JSON middleware, POST `/api/signup`, and `start(handleUserSignup, [email])` from `workflow/api`. Run `npm run dev`, call `curl -X POST --json '{&quot;email&quot;:&quot;hello@example.com&quot;}' http://localhost:3000/api/signup`, and inspect with `npx workflow web` or `npx workflow inspect runs`."
14
+ />
15
+
12
16
  This guide will walk through setting up your first workflow in an Express app. Along the way, you'll learn more about the concepts that are fundamental to using the Workflow SDK in your own projects.
13
17
 
14
18
  ---
@@ -9,6 +9,10 @@ related:
9
9
  - /docs/foundations/workflows-and-steps
10
10
  ---
11
11
 
12
+ <CopyPrompt
13
+ text="In this Fastify app, run `npm i workflow fastify nitro rollup` and, if using TypeScript, `npm i -D @types/node typescript`. Create `nitro.config.ts` with `modules: [&quot;workflow/nitro&quot;]`, `vercel: { entryFormat: &quot;node&quot; }`, and `routes: { &quot;/**&quot;: { handler: &quot;./src/index.ts&quot;, format: &quot;node&quot; } }`. Add package scripts `dev: &quot;nitro dev&quot;` and `build: &quot;nitro build&quot;`. Create `workflows/user-signup.ts` with `&quot;use workflow&quot;`, `sleep`, and `&quot;use step&quot;` helpers. Add `src/index.ts` with a Fastify app, POST `/api/signup`, `start(handleUserSignup, [email])`, `await app.ready()`, and an exported request handler. Run `npm run dev`, call `curl -X POST --json '{&quot;email&quot;:&quot;hello@example.com&quot;}' http://localhost:3000/api/signup`, and inspect with `npx workflow inspect runs --web`."
14
+ />
15
+
12
16
  This guide will walk through setting up your first workflow in a Fastify app. Along the way, you'll learn more about the concepts that are fundamental to using the Workflow SDK in your own projects.
13
17
 
14
18
  ---
@@ -9,6 +9,10 @@ related:
9
9
  - /docs/foundations/workflows-and-steps
10
10
  ---
11
11
 
12
+ <CopyPrompt
13
+ text="In this Hono app, run `npm i workflow nitro rollup`. Create `nitro.config.ts` with `modules: [&quot;workflow/nitro&quot;]` and `routes: { &quot;/**&quot;: &quot;./src/index.ts&quot; }`. Add package scripts `dev: &quot;nitro dev&quot;` and `build: &quot;nitro build&quot;`. Add the TypeScript plugin `{ &quot;name&quot;: &quot;workflow&quot; }` to `tsconfig.json` if TypeScript is used. Create `workflows/user-signup.ts` with `handleUserSignup(email)`, `&quot;use workflow&quot;`, `sleep` from `workflow`, and `&quot;use step&quot;` helpers. Add `src/index.ts` with a Hono app, POST `/api/signup`, `start(handleUserSignup, [email])` from `workflow/api`, and JSON response. Run `npm run dev`, call `curl -X POST --json '{&quot;email&quot;:&quot;hello@example.com&quot;}' http://localhost:3000/api/signup`, and inspect with `npx workflow web` or `npx workflow inspect runs`."
14
+ />
15
+
12
16
  <Steps>
13
17
 
14
18
  <Step>
@@ -9,6 +9,10 @@ related:
9
9
  - /docs/foundations/workflows-and-steps
10
10
  ---
11
11
 
12
+ <CopyPrompt
13
+ text="In this NestJS app, run `npm i workflow @workflow/nest` and `npm i -D @swc/cli @swc/core`. Configure `nest-cli.json` with `compilerOptions.builder: &quot;swc&quot;` and `deleteOutDir: true`. Run `npx @workflow/nest init`, add `.swcrc` to `.gitignore`, and set package scripts `prebuild: &quot;npx @workflow/nest init --force&quot;` and `start:dev: &quot;npx @workflow/nest init --force && nest start --watch&quot;`. Import `WorkflowModule.forRoot()` from `@workflow/nest` in `src/app.module.ts` (use `{ moduleType: &quot;commonjs&quot;, distDir: &quot;dist&quot; }` if compiling CommonJS). Create `src/workflows/user-signup.ts` with `&quot;use workflow&quot;`, `sleep`, and `&quot;use step&quot;` helpers. Add a `POST /signup` controller method that reads `email`, calls `start(handleUserSignup, [email])` from `workflow/api`, and returns JSON. Run `npm run start:dev`, call `curl -X POST --json '{&quot;email&quot;:&quot;hello@example.com&quot;}' http://localhost:3000/signup`, and inspect with `npx workflow web` or `npx workflow inspect runs`."
14
+ />
15
+
12
16
  This guide will walk through setting up your first workflow in a NestJS app. Along the way, you'll learn more about the concepts that are fundamental to using the Workflow SDK in your own projects.
13
17
 
14
18
  <Callout>
@@ -10,6 +10,10 @@ related:
10
10
  - /docs/deploying/world/vercel-world
11
11
  ---
12
12
 
13
+ <CopyPrompt
14
+ text="In this Next.js app, run `npm i workflow`. Wrap `next.config.ts` with `withWorkflow` from `workflow/next`. If the app has `proxy.ts` or middleware, exclude `.well-known/workflow/` from its matcher. Add `workflows/user-signup.ts` exporting `handleUserSignup(email)` with `&quot;use workflow&quot;`, `sleep` from `workflow`, and `&quot;use step&quot;` helper functions that create a user, send a welcome email, and send an onboarding email. Add `app/api/signup/route.ts` with a POST handler that reads `{ email }`, calls `start(handleUserSignup, [email])` from `workflow/api`, and returns JSON. Run `npm run dev`, trigger `curl -X POST --json '{&quot;email&quot;:&quot;hello@example.com&quot;}' http://localhost:3000/api/signup`, and inspect with `npx workflow web` or `npx workflow inspect runs`."
15
+ />
16
+
13
17
  <Steps>
14
18
 
15
19
  <Step>
@@ -9,6 +9,10 @@ related:
9
9
  - /docs/foundations/workflows-and-steps
10
10
  ---
11
11
 
12
+ <CopyPrompt
13
+ text="In this Nitro app, run `npm i workflow`. In `nitro.config.ts`, use `defineConfig` from `nitro`, set `serverDir: &quot;./server&quot;`, and add `modules: [&quot;workflow/nitro&quot;]`. Add the TypeScript plugin `{ &quot;name&quot;: &quot;workflow&quot; }` to `tsconfig.json` if TypeScript is used. Create `workflows/user-signup.ts` with `handleUserSignup(email)`, `&quot;use workflow&quot;`, `sleep` from `workflow`, and `&quot;use step&quot;` helpers. Add `server/api/signup.post.ts` using `defineEventHandler` from `nitro/h3` and `start` from `workflow/api` to start the workflow from `{ email }`. Run `npm run dev`, call `curl -X POST --json '{&quot;email&quot;:&quot;hello@example.com&quot;}' http://localhost:3000/api/signup`, then inspect with `npx workflow web` or `npx workflow inspect runs`."
14
+ />
15
+
12
16
  <Steps>
13
17
 
14
18
  <Step>
@@ -9,6 +9,10 @@ related:
9
9
  - /docs/foundations/workflows-and-steps
10
10
  ---
11
11
 
12
+ <CopyPrompt
13
+ text="In this Nuxt app, run `npm i workflow`. In `nuxt.config.ts`, add `modules: [&quot;workflow/nuxt&quot;]` and keep `compatibilityDate: &quot;latest&quot;`. Create `workflows/user-signup.ts` exporting `handleUserSignup(email)` with `&quot;use workflow&quot;`, `sleep` from `workflow`, and `&quot;use step&quot;` helpers that create a user and send emails. Add `server/api/signup.post.ts` using `defineEventHandler` from `h3` or `nitro/h3` and `start` from `workflow/api` to read `{ email }` and start the workflow. Run `npm run dev`, call `curl -X POST --json '{&quot;email&quot;:&quot;hello@example.com&quot;}' http://localhost:3000/api/signup`, and inspect with `npx workflow web` or `npx workflow inspect runs`."
14
+ />
15
+
12
16
  <Steps>
13
17
 
14
18
  <Step>
@@ -10,6 +10,10 @@ related:
10
10
  - /docs/foundations/workflows-and-steps
11
11
  ---
12
12
 
13
+ <CopyPrompt
14
+ text="In this Python project, run `pip install vercel`. Add `vercel.json` with `experimentalServices.ai_content_workflow.type = &quot;worker&quot;`, `entrypoint = &quot;app/workflows/ai_content_workflow.py&quot;`, and `topics = [&quot;__wkf_*&quot;]`. Create `app/workflow.py` with `from vercel import workflow` and `wf = workflow.Workflows()`. Create `app/workflows/ai_content_workflow.py` importing `wf`, define `@wf.workflow async def ai_content_workflow(*, topic: str)`, and call step functions such as `generate_draft` and `summarize_draft`. Mark step functions with `@wf.step`, use `await workflow.sleep(&quot;7 days&quot;)` for durable delays where needed, and use a `workflow.BaseHook` Pydantic model plus `.wait(token=...)` and `.resume(token)` for external approval events. Verify the worker entrypoint and route names match the project."
15
+ />
16
+
13
17
  <Callout type="warn">
14
18
  The Python SDK is currently in **beta**. APIs and behavior may change. For the latest documentation and updates, see the [official Vercel Workflow Python documentation](https://vercel.com/docs/workflow/python?language=py).
15
19
  </Callout>
@@ -9,6 +9,10 @@ related:
9
9
  - /docs/foundations/workflows-and-steps
10
10
  ---
11
11
 
12
+ <CopyPrompt
13
+ text="In this SvelteKit app, run `npm i workflow`. In `vite.config.ts`, import `workflowPlugin` from `workflow/sveltekit` and add it to `plugins` with `sveltekit()`. Add the TypeScript plugin `{ &quot;name&quot;: &quot;workflow&quot; }` to `tsconfig.json` if TypeScript is used. Create `workflows/user-signup.ts` exporting `handleUserSignup(email)` with `&quot;use workflow&quot;`, `sleep` from `workflow`, and `&quot;use step&quot;` helpers that create a user and send emails. Add `src/routes/api/signup/+server.ts` with a POST `RequestHandler` that reads `{ email }`, calls `start(handleUserSignup, [email])` from `workflow/api`, and returns `json({ message: &quot;User signup workflow started&quot; })`. Run `npm run dev`, call `curl -X POST --json '{&quot;email&quot;:&quot;hello@example.com&quot;}' http://localhost:5173/api/signup`, then inspect with `npx workflow web` or `npx workflow inspect runs`."
14
+ />
15
+
12
16
  <Steps>
13
17
 
14
18
  <Step>
@@ -9,6 +9,10 @@ related:
9
9
  - /docs/foundations/workflows-and-steps
10
10
  ---
11
11
 
12
+ <CopyPrompt
13
+ text="In this TanStack Start app, run `npm i workflow`. In `vite.config.ts`, import `workflow` from `workflow/vite` and add `workflow()` first in the existing `plugins` array before `tanstackStart()`, `nitro()`, or other plugins. Add `{ &quot;name&quot;: &quot;workflow&quot; }` to `compilerOptions.plugins` in `tsconfig.json` if TypeScript is used. Create `src/workflows/user-signup.ts` with `handleUserSignup(email)`, `&quot;use workflow&quot;`, `sleep`, and `&quot;use step&quot;` helpers. Add `src/routes/api/signup.ts` using `createFileRoute(&quot;/api/signup&quot;)`, a POST server handler, `start` from `workflow/api`, and `json` from `@tanstack/react-start`. Run `npm run dev`, call `curl -X POST --json '{&quot;email&quot;:&quot;hello@example.com&quot;}' http://localhost:3000/api/signup`, and inspect with `npx workflow web` or `npx workflow inspect runs`."
14
+ />
15
+
12
16
  This guide will walk through setting up your first workflow in a TanStack Start app. Along the way, you'll learn more about the concepts that are fundamental to using the Workflow SDK in your own projects.
13
17
 
14
18
  ---
@@ -9,6 +9,10 @@ related:
9
9
  - /docs/foundations/workflows-and-steps
10
10
  ---
11
11
 
12
+ <CopyPrompt
13
+ text="In this Vite app, run `npm i workflow nitro`. In `vite.config.ts`, import `nitro` from `nitro/vite`, `workflow` from `workflow/vite`, and configure `plugins: [nitro(), workflow()]` plus `nitro: { serverDir: &quot;./&quot; }`. Add the TypeScript plugin `{ &quot;name&quot;: &quot;workflow&quot; }` to `tsconfig.json` if TypeScript is used. Create `workflows/user-signup.ts` with `&quot;use workflow&quot;`, `sleep`, and `&quot;use step&quot;` helpers. Add `api/signup.post.ts` using `defineEventHandler` from `nitro/h3` and `start` from `workflow/api`. Run `npm run dev`, call `curl -X POST --json '{&quot;email&quot;:&quot;hello@example.com&quot;}' http://localhost:3000/api/signup`, and inspect with `npx workflow web` or `npx workflow inspect runs`."
14
+ />
15
+
12
16
  This guide will walk through setting up your first workflow in a Vite app. Along the way, you'll learn more about the concepts that are fundamental to using the Workflow SDK in your own projects.
13
17
 
14
18
  ---
@@ -405,12 +405,17 @@ Two queue topics are created per deployment:
405
405
  | `step.func` | `__wkf_step_*` | Step execution (long-running, `maxDuration: max`) |
406
406
  | `flow.func` | `__wkf_workflow_*` | Workflow orchestration (`maxDuration: 60`) |
407
407
 
408
- If you're building a framework integration that targets Vercel, you should write these triggers into the `.vc-config.json` for each generated function. The `STEP_QUEUE_TRIGGER` and `WORKFLOW_QUEUE_TRIGGER` constants are exported from `@workflow/builders` for this purpose:
408
+ If you're building a framework integration that targets Vercel, you should write these triggers into the `.vc-config.json` for each generated function. Use `getWorkflowQueueTrigger()` for flow functions so `WORKFLOW_SEQUENTIAL_REPLAYS=1` is reflected in the generated trigger configuration (it also accepts a `namespace` option, matching `createWorkflowQueueTrigger`); `STEP_QUEUE_TRIGGER` is exported for step functions:
409
409
 
410
410
  ```typescript
411
- import { STEP_QUEUE_TRIGGER, WORKFLOW_QUEUE_TRIGGER } from "@workflow/builders";
411
+ import { getWorkflowQueueTrigger, STEP_QUEUE_TRIGGER } from "@workflow/builders";
412
+
413
+ const flowTriggers = [getWorkflowQueueTrigger()];
414
+ const stepTriggers = [STEP_QUEUE_TRIGGER];
412
415
  ```
413
416
 
417
+ If your integration constructs the flow trigger object itself instead of calling `getWorkflowQueueTrigger()`, it must add `maxConcurrency: 1` to that trigger when sequential replays are enabled at build time (`WORKFLOW_SEQUENTIAL_REPLAYS=1`, or `WORKFLOW_SAFE_MODE=1` when the specific variable is unset — the exported `isSequentialReplaysEnabled()` helper implements this check). The runtime half of the feature (per-run queue topics) activates from the environment variable alone — without the trigger half, those per-run topics are not serialized and the setting only adds queue-topic cardinality.
418
+
414
419
 
415
420
  ### Custom implementations
416
421
 
@@ -14,6 +14,10 @@ related:
14
14
  - /docs/deploying/world/vercel-world
15
15
  ---
16
16
 
17
+ <CopyPrompt
18
+ text="Replace this AWS Step Functions state machine with Workflow SDK. Run `npm i workflow`. Translate the ASL state machine into an exported async TypeScript function with `&quot;use workflow&quot;`. Move each Lambda/Task side effect into a helper function with `&quot;use step&quot;`. Replace Wait states with `sleep()` from `workflow`. Replace callback tokens with `defineHook()` plus `resumeHook()` from `workflow/api`, or `createWebhook()` for HTTP callbacks. Replace Parallel and Map states with `Promise.all`, bounded batching, or child workflow runs started with `start()`. Replace Retry/Catch rules with normal errors, `RetryableError`, and `FatalError`. Add an API route or server function that starts the workflow with `start(workflowFn, args)` and verify it against the previous state machine behavior."
19
+ />
20
+
17
21
  Move an AWS Step Functions state machine to the Workflow SDK by replacing JSON state definitions with TypeScript functions. This guide shows the direct mapping between ASL states and Workflow SDK primitives.
18
22
 
19
23
  <Callout type="info">
@@ -14,6 +14,10 @@ related:
14
14
  - /docs/deploying/world/vercel-world
15
15
  ---
16
16
 
17
+ <CopyPrompt
18
+ text="Migrate this Inngest code to Workflow SDK. Run `npm i workflow`. Replace each `inngest.createFunction` handler with an exported async function that contains `&quot;use workflow&quot;`. Replace each `step.run()` callback with a helper function that contains `&quot;use step&quot;`. Replace `step.sleep()` with `sleep()` from `workflow`. Replace event waits with `defineHook()` or `createWebhook()` from `workflow`, and resume them from server routes with `resumeHook()` or webhook requests. Start runs from API routes or server code with `start(workflowFn, args)` from `workflow/api`, and use `getRun(runId)` where callers need status, streams, cancellation, or results. Preserve retries by throwing normal errors for retryable failures and `FatalError` for permanent failures. Add a local test or route call that starts the migrated workflow and verifies the same observable behavior as the Inngest function."
19
+ />
20
+
17
21
  <Callout type="info">
18
22
  Install the Workflow SDK migration skill:
19
23
 
@@ -14,6 +14,10 @@ related:
14
14
  - /docs/deploying/world/vercel-world
15
15
  ---
16
16
 
17
+ <CopyPrompt
18
+ text="Refactor this Temporal TypeScript code to Workflow SDK. Run `npm i workflow`. Replace Temporal workflow functions with exported async functions containing `&quot;use workflow&quot;`. Replace Activities with helper functions containing `&quot;use step&quot;`. Replace timers with `sleep()` from `workflow`. Replace Signals with `defineHook()` plus `resumeHook()` from `workflow/api`, or `createWebhook()` when an HTTP callback URL is needed. Replace Child Workflows with `start(childWorkflow, args)` and coordinate completion with hooks or `getRun()`. Remove Temporal workers; instead expose API routes or server functions that call `start()` and `getRun()` from `workflow/api`. Preserve idempotency and retries with normal errors, `RetryableError`, and `FatalError`, then add a verification path for each migrated workflow."
19
+ />
20
+
17
21
  <Callout type="info">
18
22
  Install the Workflow SDK migration skill:
19
23
 
@@ -14,6 +14,10 @@ related:
14
14
  - /docs/deploying/world/vercel-world
15
15
  ---
16
16
 
17
+ <CopyPrompt
18
+ text="Convert this trigger.dev setup to Workflow SDK. Run `npm i workflow`. Replace each `task()` or job entrypoint with an exported async workflow function containing `&quot;use workflow&quot;`. Replace trigger.dev task bodies that do side effects with `&quot;use step&quot;` helper functions. Replace waits/delays with `sleep()` from `workflow`. Replace external triggers or resume points with `defineHook()`/`resumeHook()` or `createWebhook()`. Replace run creation/status calls with `start()` and `getRun()` from `workflow/api`. Replace metadata/progress streaming with `getWritable()` from `workflow` when the UI needs live updates. Preserve retry semantics with normal errors, `RetryableError`, and `FatalError`, then add a local verification that starts the migrated workflow and checks the expected result or stream."
19
+ />
20
+
17
21
  <Callout type="info">
18
22
  Install the Workflow SDK migration skill:
19
23
 
@@ -11,8 +11,7 @@ related:
11
11
  - /docs/api-reference/workflow-errors/workflow-world-error
12
12
  ---
13
13
 
14
- [`setAttributes`](/docs/api-reference/workflow/set-attributes) attaches plaintext string metadata to the current workflow run. These attributes are displayed in observability CLI/UI.
15
- In the future, you'll be able to search and filter runs by attributes.
14
+ [`setAttributes`](/docs/api-reference/workflow/set-attributes) attaches plaintext string metadata to the current workflow run. These attributes are displayed in observability CLI/UI, and can be used to search and filter runs through the [Analytics API](/docs/api-reference/workflow-runtime/world/analytics).
16
15
 
17
16
  You can also seed any attributes directly when starting a run:
18
17
 
@@ -72,10 +71,33 @@ Expanding an `attr_set` event — in the run sidebar or the Events tab — shows
72
71
 
73
72
  ![Expanded attr_set events showing changes and the writer](/screenshots/attributes/run-details-attr-set-events.png)
74
73
 
74
+ ## Searching and filtering by attributes
75
+
76
+ The [Analytics API](/docs/api-reference/workflow-runtime/world/analytics) can discover which attribute keys exist and filter run listings by them. The `analytics` namespace is optional on `World` — feature-detect it before use; it is absent on local, Postgres, and other custom Worlds:
77
+
78
+ ```typescript lineNumbers
79
+ import { getWorld } from "workflow/runtime";
80
+
81
+ const world = await getWorld();
82
+ if (!world.analytics) {
83
+ throw new Error("This World does not support analytics queries"); // [!code highlight]
84
+ }
85
+
86
+ // Which attribute keys exist, and on how many runs?
87
+ const keys = await world.analytics.attributes.list();
88
+
89
+ // List runs whose latest attributes match every pair
90
+ const stuck = await world.analytics.runs.list({
91
+ attributes: { phase: "received" }, // [!code highlight]
92
+ });
93
+ ```
94
+
95
+ Matching is latest-write-wins: once the run above writes `phase: "complete"`, it stops matching `phase: "received"`.
96
+
75
97
  ## Behavior
76
98
 
77
99
  - Attributes require a World implementing spec version 4 or later.
78
100
  - Writes from workflow and step bodies append native `attr_set` events and immediately materialize `run.attributes`.
79
101
  - Storage errors surface rather than being silently ignored: transient errors on workflow-body writes are retried, and a write the World rejects as invalid (for example, exceeding the per-run attribute cap across multiple calls) fails the run with the validation error.
80
102
  - Step-body storage errors throw from `setAttributes` like any other step-side network write. Catch the error inside the step if the attribute is best-effort.
81
- - Reading and querying attributes is not available yet. A query API is planned.
103
+ - Reading and querying: each run's current attributes are returned on the run objects from the [Storage](/docs/api-reference/workflow-runtime/world/storage) and [Analytics](/docs/api-reference/workflow-runtime/world/analytics) APIs, and the Analytics API supports discovering attribute keys and filtering run listings by key=value pairs (see [Searching and filtering by attributes](#searching-and-filtering-by-attributes)). On Worlds without the optional `analytics` namespace, attributes are readable on run objects but not searchable.
@@ -42,6 +42,7 @@ No workflow-specific configuration is required. As soon as a tracer provider and
42
42
  | `step.execute <name>` | internal (inline) / consumer + root (queue-delivered) | a step function executes |
43
43
  | `http <method>` | client | the SDK calls the workflow backend (event reads/writes) |
44
44
  | `workflow.stream.write` | client | a stream chunk (or the stream close) is flushed to the backend |
45
+ | `workflow.stream.flush` | client | a buffered batch of stream writes settles; back-dated to the batch's first `write()`, so its duration is the app-perceived batch latency (buffer dwell + RPC) |
45
46
  | `workflow.stream.read.connect` | client | a live stream read opens; the span covers dispatch → response headers (network connect) |
46
47
  | `workflow.stream.read` | client | a live stream read receives its first chunk; the span's duration is the end-to-end time-to-first-chunk (see `workflow.stream.read.ttfc_ms`) |
47
48
 
@@ -59,8 +60,9 @@ Stream spans are emitted by the SDK's world backend on the client that writes or
59
60
  | `workflow.trace.propagated` | Whether the invocation received trace context from the queue message. |
60
61
  | `workflow.queue.overhead_ms` | Time between the message being enqueued and the handler starting — queue dwell plus any cold start. |
61
62
  | `workflow.stream.name` | The stream name, on stream write/read spans. |
62
- | `workflow.stream.operation` | The stream operation: `write`, `write_multi`, `close`, or `read`. |
63
+ | `workflow.stream.operation` | The stream operation: `write`, `write_multi`, `close`, `read`, or `flush`. |
63
64
  | `workflow.stream.write.chunk_rtt` | Time between emissions of a chunk to the wire, and receiving the `ack` message for that chunk. |
65
+ | `workflow.stream.flush.buffer_dwell_ms` | On `workflow.stream.flush`: time the batch's first chunk waited in the client-side write buffer (flush timer, run-ready barrier) before the request was dispatched. `workflow.stream.flush.chunks` / `.bytes` carry the batch shape. |
64
66
  | `workflow.stream.read.ttfc_ms` | Time between opening a read connection and observing and receiving the first chunk back. |
65
67
 
66
68
  ## Trace shape: one trace per invocation
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "workflow",
3
- "version": "5.0.0-beta.31",
3
+ "version": "5.0.0-beta.33",
4
4
  "description": "Workflow SDK - Build durable, resilient, and observable workflows",
5
5
  "main": "dist/typescript-plugin.cjs",
6
6
  "type": "module",
@@ -57,22 +57,23 @@
57
57
  },
58
58
  "dependencies": {
59
59
  "ms": "2.1.3",
60
- "@workflow/astro": "5.0.0-beta.31",
61
- "@workflow/cli": "5.0.0-beta.31",
62
- "@workflow/core": "5.0.0-beta.31",
60
+ "@workflow/astro": "5.0.0-beta.33",
61
+ "@workflow/cli": "5.0.0-beta.33",
62
+ "@workflow/core": "5.0.0-beta.33",
63
63
  "@workflow/errors": "5.0.0-beta.10",
64
64
  "@workflow/typescript-plugin": "5.0.0-beta.5",
65
65
  "@workflow/utils": "5.0.0-beta.6",
66
- "@workflow/next": "5.0.0-beta.31",
67
- "@workflow/nest": "5.0.0-beta.31",
68
- "@workflow/nitro": "5.0.0-beta.31",
69
- "@workflow/nuxt": "5.0.0-beta.31",
70
- "@workflow/sveltekit": "5.0.0-beta.31",
71
- "@workflow/rollup": "5.0.0-beta.31"
66
+ "@workflow/next": "5.0.0-beta.33",
67
+ "@workflow/nest": "5.0.0-beta.33",
68
+ "@workflow/nitro": "5.0.0-beta.33",
69
+ "@workflow/nuxt": "5.0.0-beta.33",
70
+ "@workflow/sveltekit": "5.0.0-beta.33",
71
+ "@workflow/rollup": "5.0.0-beta.33"
72
72
  },
73
73
  "devDependencies": {
74
74
  "@types/ms": "2.1.0",
75
75
  "@types/node": "22.19.0",
76
+ "typescript": "^6.0.3",
76
77
  "@workflow/tsconfig": "5.0.0-beta.0"
77
78
  },
78
79
  "peerDependencies": {