Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Cloudflare platform skill covering Workers, D1, R2, KV, AI, Durable Objects, and security.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/workflows/api.md
1# Workflow APIs23## Step APIs45```typescript6// step.do()7const result = await step.do('step name', async () => { /* logic */ });8const result = await step.do('step name', { retries, timeout }, async () => {});910// step.sleep()11await step.sleep('description', '1 hour');12await step.sleep('description', 5000); // ms1314// step.sleepUntil()15await step.sleepUntil('description', Date.parse('2024-12-31'));1617// step.waitForEvent()18const data = await step.waitForEvent<PayloadType>('wait', {type: 'webhook-type', timeout: '24h'});19try { const event = await step.waitForEvent('wait', { type: 'approval', timeout: '1h' }); } catch (e) { /* Timeout */ }20```2122## WorkflowStepContext2324The `WorkflowStepContext` is passed as the first argument to the `step.do()` callback. It provides runtime information about the current step execution.2526```typescript27type WorkflowStepContext = {28step: {29name: string; // Step name as passed to step.do()30count: number; // How many times step.do() called with this name in current run (1-indexed)31};32attempt: number; // Current attempt number (1-indexed): 1 = first try, 2 = first retry, etc.33config: WorkflowStepConfig; // Resolved config for this step, including runtime defaults34};35```3637**Use cases:**38```typescript39// Adjust behavior based on retry attempt40await step.do('call api', { retries: { limit: 3, delay: '5 seconds', backoff: 'exponential' } }, async (ctx) => {41if (ctx.attempt > 1) console.log(`Retry attempt ${ctx.attempt} for step "${ctx.step.name}"`);42const res = await fetch('https://api.example.com/data');43if (!res.ok) throw new Error(`API failed (attempt ${ctx.attempt})`);44return res.json();45});4647```4849## Instance Management5051```typescript52// Create single53const instance = await env.MY_WORKFLOW.create({id: crypto.randomUUID(), params: { userId: 'user123' }}); // id optional, auto-generated if omitted; throws if ID already exists within retention period5455// Create with custom retention (check docs for default per plan)56const instance = await env.MY_WORKFLOW.create({57id: crypto.randomUUID(),58params: { userId: 'user123' },59retention: '30 days' // Override default retention period60});6162// Batch (max 100, idempotent: skips existing IDs)63const instances = await env.MY_WORKFLOW.createBatch([{id: 'user1', params: {name: 'John'}}, {id: 'user2', params: {name: 'Jane'}}]);6465// Get & Status66const instance = await env.MY_WORKFLOW.get('instance-id');67const status = await instance.status(); // {status: 'queued' | 'running' | 'paused' | 'errored' | 'terminated' | 'complete' | 'waiting' | 'waitingForPause' | 'unknown', error?, output?}6869// Control70await instance.pause(); await instance.resume(); await instance.terminate(); await instance.restart();7172// Send Events73await instance.sendEvent({type: 'approval', payload: { approved: true }}); // Must match waitForEvent type74```7576## Triggering Workflows7778```typescript79// From Worker80export default { async fetch(req, env) { const instance = await env.MY_WORKFLOW.create({id: crypto.randomUUID(), params: { userId: 'user123' }}); return Response.json({ id: instance.id }); }};8182// From Queue83export default { async queue(batch, env) { for (const msg of batch.messages) { await env.MY_WORKFLOW.create({id: `job-${msg.id}`, params: msg.body}); } }};8485// From Cron86export default { async scheduled(event, env) { await env.CLEANUP_WORKFLOW.create({id: `cleanup-${Date.now()}`, params: { timestamp: event.scheduledTime }}); }};8788// From Another Workflow (non-blocking)89export class ParentWorkflow extends WorkflowEntrypoint<Env, Params> {90async run(event, step) {91const child = await step.do('start child', async () => await this.env.CHILD_WORKFLOW.create({id: `child-${event.instanceId}`, params: {}}));92}93}94```9596## Error Handling9798```typescript99import { NonRetryableError } from 'cloudflare:workflows';100101// NonRetryableError102await step.do('validate', async () => {103if (!event.payload.paymentMethod) throw new NonRetryableError('Payment method required');104const res = await fetch('https://api.example.com/charge', { method: 'POST' });105if (res.status === 401) throw new NonRetryableError('Invalid credentials'); // Don't retry106if (!res.ok) throw new Error('Retryable failure'); // Will retry107return res.json();108});109110// Catching Errors111try { await step.do('risky op', async () => { throw new NonRetryableError('Failed'); }); } catch (e) { await step.do('cleanup', async () => {}); }112113// Idempotency114await step.do('charge', async () => {115const sub = await fetch(`https://api/subscriptions/${id}`).then(r => r.json());116if (sub.charged) return sub; // Already done117return await fetch(`https://api/subscriptions/${id}`, {method: 'POST', body: JSON.stringify({ amount: 10.0 })}).then(r => r.json());118});119```120121## Type Constraints122123Params and step returns must be `Rpc.Serializable<T>`:124125```typescript126// ✅ Valid types127type ValidParams = {128userId: string;129count: number;130tags: string[];131metadata: Record<string, unknown>;132};133134// ❌ Invalid types135type InvalidParams = {136callback: () => void; // Functions not serializable137symbol: symbol; // Symbols not serializable138circular: any; // Circular references not allowed139};140141// Step returns follow same rules142const result = await step.do('fetch', async () => {143return { userId: '123', data: [1, 2, 3] }; // ✅ Plain object144});145146// ✅ ReadableStream<Uint8Array> for large binary output (bypasses non-stream step result size limit)147const stream = await step.do('read from R2', async () => {148const obj = await this.env.BUCKET.get('large-file.csv');149return obj.body; // Return the ReadableStream directly150});151```152153## Sleep & Scheduling154155```typescript156// Relative157await step.sleep('wait 1 hour', '1 hour');158await step.sleep('wait 30 days', '30 days');159await step.sleep('wait 5s', 5000); // ms160161// Absolute162await step.sleepUntil('launch date', Date.parse('24 Oct 2024 13:00:00 UTC'));163await step.sleepUntil('deadline', new Date('2024-12-31T23:59:59Z'));164```165166Units: second, minute, hour, day, week, month, year.167Sleeping instances don't count toward concurrency.168169## Parameters170171**Pass from Worker:**172```typescript173const instance = await env.MY_WORKFLOW.create({174id: crypto.randomUUID(),175params: { userId: 'user123', email: '[email protected]' }176});177```178179**Access in Workflow:**180```typescript181async run(event: WorkflowEvent<Params>, step: WorkflowStep) {182const userId = event.payload.userId;183const instanceId = event.instanceId;184const createdAt = event.timestamp;185}186```187188**CLI Trigger:**189```bash190npx wrangler workflows trigger my-workflow '{"userId":"user123"}'191```192193## Wrangler CLI194195```bash196npm create cloudflare@latest my-workflow -- --template "cloudflare/workflows-starter"197npx wrangler deploy198npx wrangler workflows list199npx wrangler workflows trigger my-workflow '{"userId":"user123"}'200npx wrangler workflows instances list my-workflow201npx wrangler workflows instances describe my-workflow instance-id202npx wrangler workflows instances pause/resume/terminate my-workflow instance-id203```204205## REST API206207```bash208# Create209curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/workflows/{workflow_name}/instances" -H "Authorization: Bearer {token}" -d '{"id":"custom-id","params":{"userId":"user123"}}'210211# Status212curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/workflows/{workflow_name}/instances/{instance_id}/status" -H "Authorization: Bearer {token}"213214# Send Event215curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/workflows/{workflow_name}/instances/{instance_id}/events" -H "Authorization: Bearer {token}" -d '{"type":"approval","payload":{"approved":true}}'216```217218See: [configuration.md](./configuration.md), [patterns.md](./patterns.md)219