🎯

cloudflare-workflows

🎯Skill

from itechmeat/llm-code

VibeIndex|
What it does

Orchestrates multi-step, durable cloud workflows with state persistence, retries, scheduling, and external event handling using Cloudflare Workers.

πŸ“¦

Part of

itechmeat/llm-code(31 items)

cloudflare-workflows

Installation

npxRun with npx
npx wrangler deploy
πŸ“– Extracted from docs: itechmeat/llm-code
3Installs
-
AddedFeb 4, 2026

Skill Details

SKILL.md

"Cloudflare Workflows durable execution playbook: multi-step orchestration, state persistence, retries, sleep/scheduling, waitForEvent, external events, bindings, lifecycle management, limits, pricing. Keywords: Cloudflare Workflows, durable execution, WorkflowEntrypoint, step.do, step.sleep, waitForEvent, sendEvent, retries, NonRetryableError, Workflow binding."

Overview

# 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

```typescript

// 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 {

async run(event: WorkflowEvent, 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 {

const instance = await env.MY_WORKFLOW.create({

params: { userId: "123", action: "activate" },

});

return Response.json({ instanceId: instance.id });

},

};

```

wrangler.jsonc

```jsonc

{

"name": "my-workflow-worker",

"main": "src/index.ts",

"workflows": [

{

"name": "my-workflow",

"binding": "MY_WORKFLOW",

"class_name": "MyWorkflow"

}

]

}

```

Deploy

```bash

npx wrangler deploy

```

---

Core Concepts

WorkflowEntrypoint

```typescript

export class MyWorkflow extends WorkflowEntrypoint {

async run(event: WorkflowEvent, step: WorkflowStep) {

// Workflow logic with steps

return optionalResult;

}

}

```

WorkflowEvent

```typescript

interface WorkflowEvent {

payload: Readonly; // 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

```typescript

interface WorkflowStep {

do(name: string, callback: () => Promise): Promise;

do(name: string, config: StepConfig, callback: () => Promise): Promise;

sleep(name: string, duration: Duration): Promise;

sleepUntil(name: string, timestamp: Date | number): Promise;

waitForEvent(name: string, options: WaitOptions): Promise;

}

```

See [api.md](references/api.md) for full type definitions.

---

Steps

Basic Step

```typescript

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

```typescript

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

```typescript

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

```typescript

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

```typescript

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

```typescript

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:

```typescript

try {

const event = await step.waitForEvent("optional event", { type: "update", timeout: "1 hour" });

} catch (e) {

// Continue without event

}

```

Send Event from Worker

```typescript

export default {

async fetch(request: Request, env: Env): Promise {

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](references/events.md) for REST API.

---

Error Handling

NonRetryableError

```typescript

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

```typescript

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

```typescript

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

```typescript

const instances = await env.MY_WORKFLOW.createBatch([{ params: { userId: "1" } }, { params: { userId: "2" } }, { params: { userId: "3" } }]);

// Up to 100 instances per batch

```

Get Instance

```typescript

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

```

Instance Methods

```typescript

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

```typescript

// βœ… 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

```bash

# List instances

wrangler workflows instances list my-workflow

# Describe instance

wrangler workflows instances describe my-workflow --id

# Terminate instance

wrangler workflows instances terminate my-workflow --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

```jsonc

{

"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](references/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](references/api.md) β€” Full API reference
  • [events.md](references/events.md) β€” Event handling and REST API
  • [patterns.md](references/patterns.md) β€” Saga, approval, reminders
  • [pricing.md](references/pricing.md) β€” Billing details

Cross-References (Skills)

  • cloudflare-workers β€” Worker development
  • cloudflare-queues β€” Message queue integration
  • cloudflare-r2 β€” Large state storage
  • cloudflare-kv β€” Key-value references