Claude API Multi-Agent Workflow Example
What a Claude multi-agent workflow actually is
A multi-agent workflow is two or more Claude calls that each play a distinct role — researcher, writer, critic, router — and pass structured output between them instead of one giant prompt trying to do everything. The pattern matters because single-prompt Claude calls degrade in quality as task complexity grows: a model asked to research, draft, and fact-check in one pass tends to skip steps. Splitting the work into separate agents, each with a narrower system prompt and its own context, produces more reliable results and is easier to debug.
Below is a concrete, working example: an orchestrator agent that breaks down a task, a researcher agent that gathers facts using a tool call, a writer agent that drafts content, and a reviewer agent that checks the draft against the research before returning a final answer. This is the pattern most "agentic" apps built on Claude actually use in production — not a single omniscient agent, but a small pipeline of focused calls.
The core architecture
Most Claude API multi-agent workflows share four parts:
- Orchestrator — decides which sub-agents to call and in what order
- Worker agents — each with a tight system prompt and a single job
- Shared state — a JSON object passed between calls (not full conversation history)
- Termination logic — a check that decides when the loop is done
You don't need a framework to build this. Plain function calls with Claude's Messages API and tool use are enough for most real workflows.
Step 1: the orchestrator decides the plan
async function orchestrate(task) {
const res = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"content-type": "application/json"
},
body: JSON.stringify({
model: "claude-sonnet-4",
max_tokens: 500,
system: "You are a task planner. Break the task into: research, draft, review. Return JSON only.",
messages: [{ role: "user", content: task }]
})
});
const data = await res.json();
return JSON.parse(data.content[0].text);
}
The orchestrator's only job is to produce a plan — it never does the actual research or writing. Keeping its system prompt narrow keeps its output predictable, which matters because every downstream agent depends on parsing it correctly.
Step 2: the researcher agent uses a tool
The research agent is the one place where tool use earns its keep — it can call a search function, a database lookup, or an internal API, and return structured facts rather than prose.
async function research(topic) {
const res = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"content-type": "application/json"
},
body: JSON.stringify({
model: "claude-sonnet-4",
max_tokens: 800,
system: "You are a research agent. Use the lookup tool to gather facts. Return a bullet list of findings with sources.",
tools: [{
name: "lookup",
description: "Fetch facts about a topic from an internal knowledge base",
input_schema: {
type: "object",
properties: { query: { type: "string" } },
required: ["query"]
}
}],
messages: [{ role: "user", content: `Research: ${topic}` }]
})
});
return res.json();
}
When Claude returns a tool_use block, your code executes the actual lookup, then sends the result back in a follow-up message with role: "tool_result". See /docs/tools for the exact request shape — the pattern is the same regardless of which agent is calling it.
Step 3: the writer and reviewer agents
The writer takes the research output as context and produces a draft. The reviewer then checks the draft against the same research — this is the step that catches hallucinated claims, because the reviewer has the facts, not just the draft.
async function review(draft, facts) {
const res = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"content-type": "application/json"
},
body: JSON.stringify({
model: "claude-sonnet-4",
max_tokens: 600,
system: "Compare the draft to the facts. List any unsupported claims. If none, reply APPROVED.",
messages: [{
role: "user",
content: `Facts:\n${facts}\n\nDraft:\n${draft}`
}]
})
});
return res.json();
}
If the reviewer flags unsupported claims, the orchestrator loops back to the writer with the feedback appended to its context. That loop, capped at two or three iterations, is usually enough to get a clean output without burning excessive tokens.
Passing state between agents without blowing up context
The biggest practical mistake in multi-agent setups is passing the full conversation history between every agent. Each agent only needs the output it depends on, not the full trace. Keep a shared state object:
{
"task": "write a comparison of two databases",
"facts": "...",
"draft": "...",
"review": "APPROVED"
}
Each agent reads only the keys it needs and writes back its own key. This keeps token usage predictable and makes the pipeline easy to log and replay when something goes wrong.
Streaming and observability across agents
For longer pipelines, stream the writer agent's output to the UI so users see progress while the reviewer step runs in the background — see /docs/streaming for the event format. If you're running this across a team, it also helps to see per-agent token and cost breakdown rather than one aggregated number, since the researcher and reviewer calls are usually much cheaper than the writer call. SubToAPI's dashboard reports usage per API key, so if you give each agent role its own sub_live_ key you get that breakdown for free without building custom logging. Check /pricing if you're scoping a team rollout with multiple seats.
Error handling in a multi-agent chain
A failure in any single agent call shouldn't crash the whole pipeline. Wrap each step so a failed research call falls back to a cached result or a "no data found" marker the writer can work around, rather than letting an exception propagate. Set reasonable max_tokens per agent — a stuck or overly long researcher response is a common cause of slow pipelines. Start with /docs/quickstart if you're setting up API access for the first time, and /docs/messages for the full request and response schema each agent call uses.
questions
Do I need a framework like LangChain to build a Claude multi-agent workflow? No. The example above uses plain fetch calls. Frameworks add convenience for complex graphs, but a basic orchestrator-worker pipeline is simpler and easier to debug without one.
How many agents is too many? There's no fixed limit, but each added agent adds latency and failure points. Most production workflows stay under five roles — orchestrator, researcher, writer, reviewer, and sometimes a router.
Should each agent use the same Claude model? Not necessarily. Lighter tasks like routing or classification can use a smaller, faster model, while drafting and review can use a stronger one. Separating API keys per role also makes it easier to track cost and usage by agent.