INTEGRITY Documentation

Using Agents with Workflows

What are Workflows?

Cloudflare Workflows provide durable, multi-step execution for tasks that need to survive failures, retry automatically, and wait for external events. When integrated with Agents, Workflows handle long-running background processing while Agents manage real-time communication.

Agents vs. Workflows

Agents and Workflows have complementary strengths:

Capability Agents Workflows
Execution model Long-lived identity that wakes on events Run to completion
Real-time communication WebSockets, HTTP streaming Not supported
State persistence Built-in SQL database Step-level persistence
Failure handling Application-defined Automatic retries and recovery
External events Direct handling Pause and wait for events
User interaction Direct (chat, UI) Through Agent callbacks

Agents can loop, branch, and interact directly with users. Workflows execute steps sequentially with guaranteed delivery and can pause for days waiting for approvals or external data.

When to use each

Use Agents alone for:

Use Agents with Workflows for:

Use Workflows alone for:

How Agents and Workflows communicate

The AgentWorkflow class (imported from agents/workflows) provides bidirectional communication between Workflows and their originating Agent.

Workflow to Agent

Workflows can communicate with Agents through several mechanisms:

// Inside a workflow's run() method
await this.agent.updateTaskStatus(taskId, "processing"); // RPC call
await this.reportProgress({ step: "process", percent: 0.5 }); // Progress (non-durable)
this.broadcastToClients({ type: "update", taskId }); // Broadcast (non-durable)
await step.mergeAgentState({ taskProgress: 0.5 }); // State update (durable)
// Inside a workflow's run() method
await this.agent.updateTaskStatus(taskId, "processing"); // RPC call
await this.reportProgress({ step: "process", percent: 0.5 }); // Progress (non-durable)
this.broadcastToClients({ type: "update", taskId }); // Broadcast (non-durable)
await step.mergeAgentState({ taskProgress: 0.5 }); // State update (durable)

Agent to Workflow

Agents can interact with running Workflows by:

Durable vs. non-durable operations

Understanding durability is key to using workflows effectively:

Non-durable (may repeat on retry)

These operations are lightweight and suitable for frequent updates, but may execute multiple times if the workflow retries:

Durable (idempotent, won't repeat)

These operations use the step parameter and are guaranteed to execute exactly once:

Durability guarantees

Workflows provide durability through step-based execution:

  1. Step completion is permanent — Once a step completes, it will not re-execute even if the workflow restarts
  2. Automatic retries — Failed steps retry with configurable backoff
  3. Event persistence — Workflows can wait for events for up to one year
  4. State recovery — Workflow state survives infrastructure failures

This durability model means workflows are well-suited for tasks where partial completion must be preserved, such as multi-stage data processing or transactions spanning multiple systems.

Workflow tracking

When an Agent starts a workflow using runWorkflow(), the workflow is automatically tracked in the Agent's internal database. This enables:

Common patterns

Background processing with progress

An Agent receives a request, starts a Workflow for heavy processing, and broadcasts progress updates to connected clients as the Workflow executes each step.

// Workflow reports progress after each item
for (let i = 0; i < items.length; i++) {
	await step.do(`process-${i}`, async () => processItem(items[i]));
	await this.reportProgress({
		step: `process-${i}`,
		percent: (i + 1) / items.length,
		message: `Processed ${i + 1}/${items.length}`,
	});
}
// Workflow reports progress after each item
for (let i = 0; i < items.length; i++) {
	await step.do(`process-${i}`, async () => processItem(items[i]));
	await this.reportProgress({
		step: `process-${i}`,
		percent: (i + 1) / items.length,
		message: `Processed ${i + 1}/${items.length}`,
	});
}

Human-in-the-loop approval

A Workflow prepares a request, pauses to wait for approval using waitForApproval(), and the Agent provides UI for users to approve or reject via approveWorkflow() / rejectWorkflow(). The Workflow resumes or throws WorkflowRejectedError based on the decision.

Resilient external API calls

A Workflow wraps external API calls in durable steps with retry logic. If the API fails or the workflow restarts, completed calls are not repeated and failed calls retry automatically.

const result = await step.do(
	"call-api",
	{
		retries: { limit: 5, delay: "10 seconds", backoff: "exponential" },
		timeout: "5 minutes",
	},
	async () => {
		const response = await fetch("https://api.example.com/process");
		if (!response.ok) throw new Error(`API error: ${response.status}`);
		return response.json();
	},
);
const result = await step.do(
	"call-api",
	{
		retries: { limit: 5, delay: "10 seconds", backoff: "exponential" },
		timeout: "5 minutes",
	},
	async () => {
		const response = await fetch("https://api.example.com/process");
		if (!response.ok) throw new Error(`API error: ${response.status}`);
		return response.json();
	},
);

State synchronization

A Workflow updates Agent state at key milestones using step.updateAgentState() or step.mergeAgentState(). These state changes broadcast to all connected clients, keeping UIs synchronized without polling.

Run Workflows API

Implementation details for agent workflows.

Cloudflare Workflows

Workflow fundamentals and documentation.

Human-in-the-loop

Approval flows and manual intervention.