cloudflare-workflows

Workflows provide durable multi-step execution for Workers. Steps persist state, survive restarts, support retries, and can sleep for days.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "cloudflare-workflows" with this command: npx skills add itechmeat/llm-code/itechmeat-llm-code-cloudflare-workflows

Cloudflare Workflows

Workflows provide durable multi-step execution for Workers. Steps persist state, survive restarts, support retries, and can sleep for days.

Quick Start

Create Workflow

// src/index.ts import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from "cloudflare:workers";

interface Env { MY_WORKFLOW: Workflow; }

interface Params { userId: string; action: string; }

export class MyWorkflow extends WorkflowEntrypoint<Env, Params> { async run(event: WorkflowEvent<Params>, step: WorkflowStep) { const user = await step.do("fetch user", async () => { const resp = await fetch(https://api.example.com/users/${event.payload.userId}); return resp.json(); });

await step.sleep("wait before processing", "1 hour");

const result = await step.do("process action", async () => {
  return { processed: true, user: user.id };
});

return result; // Available in instance.status().output

} }

export default { async fetch(request: Request, env: Env): Promise<Response> { const instance = await env.MY_WORKFLOW.create({ params: { userId: "123", action: "activate" }, }); return Response.json({ instanceId: instance.id }); }, };

wrangler.jsonc

{ "name": "my-workflow-worker", "main": "src/index.ts", "workflows": [ { "name": "my-workflow", "binding": "MY_WORKFLOW", "class_name": "MyWorkflow" } ] }

Deploy

npx wrangler deploy

Core Concepts

WorkflowEntrypoint

export class MyWorkflow extends WorkflowEntrypoint<Env, Params> { async run(event: WorkflowEvent<Params>, step: WorkflowStep) { // Workflow logic with steps return optionalResult; } }

WorkflowEvent

interface WorkflowEvent<T> { payload: Readonly<T>; // Immutable input params timestamp: Date; // Creation time instanceId: string; // Unique instance ID }

Warning: Event payload is immutable. Changes are NOT persisted across steps. Return state from steps instead.

WorkflowStep

interface WorkflowStep { do<T>(name: string, callback: () => Promise<T>): Promise<T>; do<T>(name: string, config: StepConfig, callback: () => Promise<T>): Promise<T>; sleep(name: string, duration: Duration): Promise<void>; sleepUntil(name: string, timestamp: Date | number): Promise<void>; waitForEvent<T>(name: string, options: WaitOptions): Promise<T>; }

See api.md for full type definitions.

Steps

Basic Step

const result = await step.do("step name", async () => { const response = await fetch("https://api.example.com/data"); return response.json(); // State persisted });

Step with Config

const data = await step.do( "call external API", { retries: { limit: 10, delay: "30 seconds", backoff: "exponential", }, timeout: "5 minutes", }, async () => { return await externalApiCall(); } );

Default Step Config

const defaultConfig = { retries: { limit: 5, delay: 10000, // 10 seconds backoff: "exponential", }, timeout: "10 minutes", };

Option Type Default Description

retries.limit

number 5 Max attempts (use Infinity for unlimited)

retries.delay

string/number 10000 Delay between retries

retries.backoff

string exponential constant , linear , exponential

timeout

string/number 10 min Per-attempt timeout

Sleep & Scheduling

Relative Sleep

await step.sleep("wait before retry", "1 hour"); await step.sleep("short pause", 5000); // 5 seconds (ms)

Duration units: second , minute , hour , day , week , month , year .

Sleep Until Date

const targetDate = new Date("2024-12-31T00:00:00Z"); await step.sleepUntil("wait until new year", targetDate);

// Or with timestamp await step.sleepUntil("wait until launch", Date.parse("24 Oct 2024 13:00:00 UTC"));

Maximum sleep: 365 days.

Note: step.sleep and step.sleepUntil do NOT count towards the 1024 steps limit.

Wait for Events

Wait in Workflow

const approval = await step.waitForEvent<{ approved: boolean }>("wait for approval", { type: "user_approval", timeout: "7 days", });

if (approval.approved) { await step.do("proceed", async () => { /* ... */ }); }

Default timeout: 24 hours.

Timeout behavior: Throws error and fails instance. Use try-catch to continue:

try { const event = await step.waitForEvent("optional event", { type: "update", timeout: "1 hour" }); } catch (e) { // Continue without event }

Send Event from Worker

export default { async fetch(request: Request, env: Env): Promise<Response> { const { instanceId, approved } = await request.json(); const instance = await env.MY_WORKFLOW.get(instanceId);

await instance.sendEvent({
  type: "user_approval", // Must match waitForEvent type
  payload: { approved },
});

return new Response("Event sent");

}, };

Event buffering: Events can be sent before Workflow reaches waitForEvent . They are buffered and delivered when the matching step executes.

See events.md for REST API.

Error Handling

NonRetryableError

import { NonRetryableError } from "cloudflare:workflows";

await step.do("validate input", async () => { if (!event.payload.data) { throw new NonRetryableError("Missing required data"); } return event.payload.data; });

Catch and Continue

try { await step.do("risky operation", async () => { await riskyApiCall(); }); } catch (error) { await step.do("handle failure", async () => { await sendAlertEmail(error.message); }); }

Workflow States

Status Description

queued

Waiting to start

running

Actively executing

paused

Manually paused

waiting

Sleeping or waiting for event

waitingForPause

Pause requested, waiting to take effect

complete

Successfully finished

errored

Failed (uncaught exception or retry limit)

terminated

Manually terminated

Workflow Bindings

Create Instance

const instance = await env.MY_WORKFLOW.create({ id: "order-12345", // Optional custom ID (max 100 chars) params: { orderId: 12345 }, }); console.log(instance.id);

Create Batch

const instances = await env.MY_WORKFLOW.createBatch([{ params: { userId: "1" } }, { params: { userId: "2" } }, { params: { userId: "3" } }]); // Up to 100 instances per batch

Get Instance

const instance = await env.MY_WORKFLOW.get("order-12345");

Instance Methods

await instance.pause(); // Pause execution await instance.resume(); // Resume paused await instance.terminate(); // Stop permanently await instance.restart(); // Restart from beginning

const status = await instance.status(); console.log(status.status); // "running", "complete", etc. console.log(status.output); // Return value from run() console.log(status.error); // { name, message } if errored

await instance.sendEvent({ type: "approval", payload: { approved: true }, });

Rules of Workflows

✅ DO

  • Make steps granular and self-contained

  • Return state from steps (only way to persist)

  • Name steps deterministically

  • await all step calls

  • Keep step return values under 1 MiB

  • Use idempotent operations in steps

  • Base conditions on event.payload or step returns

❌ DON'T

  • Store state outside steps (lost on restart)

  • Mutate event.payload (changes not persisted)

  • Use non-deterministic step names (Date.now() , Math.random() )

  • Skip await on step calls

  • Put entire logic in one step

  • Call multiple unrelated services in one step

  • Do heavy CPU work in single step

Step State Persistence

// ✅ Good: Return state from step const userData = await step.do("fetch user", async () => { return await fetchUser(userId); });

// ❌ Bad: State stored outside step (lost on restart) let userData; await step.do("fetch user", async () => { userData = await fetchUser(userId); // Will be lost! });

Wrangler Commands

List instances

wrangler workflows instances list my-workflow

Describe instance

wrangler workflows instances describe my-workflow --id <instance-id>

Terminate instance

wrangler workflows instances terminate my-workflow --id <instance-id>

Trigger new instance

wrangler workflows trigger my-workflow --params '{"key": "value"}'

Delete Workflow

wrangler workflows delete my-workflow

Limits

Feature Free Paid

CPU time per step 10 ms 30 sec (max 5 min)

Wall clock per step Unlimited Unlimited

State per step 1 MiB 1 MiB

Event payload 1 MiB 1 MiB

Total state per instance 100 MB 1 GB

Max sleep duration 365 days 365 days

Max steps per Workflow 1024 1024

Concurrent instances 25 10,000

Instance creation rate 100/sec 100/sec

Queued instances 100,000 1,000,000

Subrequests per instance 50/req 1000/req

Instance ID length 100 chars 100 chars

Retention (completed) 3 days 30 days

Note: Instances in waiting state (sleeping, waiting for event) do NOT count against concurrency limits.

Increase CPU Limit

{ "limits": { "cpu_ms": 300000 // 5 minutes } }

Pricing

Based on Workers Standard pricing:

Metric Free Paid

Requests 100K/day (shared) 10M/mo included, +$0.30/M

CPU time 10 ms/invocation 30M ms/mo included, +$0.02/M ms

Storage 1 GB 1 GB included, +$0.20/GB-mo

Storage notes:

  • Calculated across all instances (running, sleeping, completed)

  • Deleting instances frees storage (updates within minutes)

  • Free plan: instance errors if storage limit reached

See pricing.md for details.

Prohibitions

  • ❌ Do not mutate event.payload (changes not persisted)

  • ❌ Do not store state outside steps

  • ❌ Do not use non-deterministic step names

  • ❌ Do not exceed 1 MiB per step return

  • ❌ Do not skip await on step calls

  • ❌ Do not rely on in-memory state between steps

References

  • api.md — Full API reference

  • events.md — Event handling and REST API

  • patterns.md — Saga, approval, reminders

  • pricing.md — Billing details

Links

  • Documentation

  • Changelog

Cross-References (Skills)

  • cloudflare-workers — Worker development

  • cloudflare-queues — Message queue integration

  • cloudflare-r2 — Large state storage

  • cloudflare-kv — Key-value references

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

react-testing-library

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

social-writer

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

commits

No summary provided by upstream source.

Repository SourceNeeds Review