Claude API Multi-Agent Workflow Design Patterns
What "multi-agent workflow design" actually means
A multi-agent workflow is a system where several independent Claude calls — each with its own prompt, role, and sometimes its own tools — cooperate to produce a result that one single call can't reliably produce. Instead of asking one model to "research, write, and fact-check this report" in one shot, you split the job into a researcher agent, a writer agent, and a reviewer agent, and you design the handoffs between them explicitly.
If you're searching for how to do this with the Claude API, you're usually solving one of three problems: a single prompt is doing too many unrelated things and quality suffers, you need parallel work (multiple sub-tasks that don't depend on each other), or you need a loop where one agent checks and corrects another's output. This article covers the orchestration patterns that actually work in production, how to structure the calls, and where a single well-tuned agent is simply the better choice.
When you don't need multiple agents
Before building an orchestration layer, rule out the cheaper option: a longer, better-structured single prompt with clear sections and explicit output format. Multi-agent systems add latency (each hop is a full round trip), cost (you pay for every agent's context), and failure surface (every handoff can lose information). If your task is "summarize this doc and extract three action items," one call with a structured system prompt beats three agents every time.
Multi-agent design earns its complexity when:
- Sub-tasks require genuinely different context or tools (a coding agent vs. a research agent vs. a planning agent)
- You need isolation — one agent's reasoning shouldn't leak into another's context window
- You want a reviewer/critic loop that catches errors before output reaches the user
- Work can run in parallel and you want wall-clock speed, not just logical separation
Core orchestration patterns
1. Sequential pipeline
Each agent's output becomes the next agent's input. This is the simplest and most debuggable pattern.
Researcher → Drafter → Editor → Final output
Implementation detail that matters: pass structured intermediate output, not free text. Ask each agent to return JSON with a fixed schema (e.g., { "findings": [...], "sources": [...] }), and validate it before forwarding. This prevents the classic failure mode where agent 2 misinterprets agent 1's prose.
const research = await callAgent("researcher", topic);
const draft = await callAgent("writer", research);
const final = await callAgent("editor", draft);
2. Orchestrator-worker (fan-out/fan-in)
One "orchestrator" agent breaks a task into sub-tasks and dispatches them to worker agents, often in parallel, then merges results. Good for tasks like "analyze these 5 codebases" or "research these 4 competitors."
const subtasks = await planTasks(mainTask); // orchestrator call
const results = await Promise.all(
subtasks.map(t => callAgent("worker", t))
);
const merged = await callAgent("merger", results);
This is the pattern where tool use matters most — each worker often needs its own tool set (web search for a research worker, a code execution tool for an analysis worker). See /docs/tools for how tool definitions and tool_use blocks work with the Claude API.
3. Critic/reviewer loop
A generator agent produces output, a critic agent checks it against criteria, and the generator revises until the critic approves or a max-iteration limit is hit. This pattern catches factual errors, tone mismatches, and format violations that a single pass misses.
let draft = await callAgent("generator", task);
for (let i = 0; i < 3; i++) {
const review = await callAgent("critic", draft);
if (review.approved) break;
draft = await callAgent("generator", { task, draft, feedback: review.feedback });
}
Always cap iterations. Unbounded critic loops are a common source of runaway API costs.
4. Router
A lightweight classification agent decides which specialized agent should handle a request. Useful for support systems or multi-domain assistants where routing to the wrong agent wastes a full generation cycle.
const route = await callAgent("router", userMessage); // returns a category
const handler = agents[route.category];
const response = await handler(userMessage);
Keep the router agent cheap — a fast model with a short, constrained prompt is usually enough; you don't need your most capable model just to classify intent.
Practical design rules
- Give every agent a narrow, explicit role. "You are the fact-checker. You only verify claims against the provided sources and return pass/fail per claim" produces far more reliable behavior than a generic "help with this" prompt reused across agents.
- Use structured output everywhere a handoff happens. JSON schemas between agents are the single biggest reliability improvement you can make.
- Keep context windows separate. Don't dump the entire conversation history into every agent — each one should only see what it needs. This also keeps token costs down.
- Log every hop. When something goes wrong in a 4-agent pipeline, you need to know which agent produced the bad output, not just that the final result was wrong.
- Set hard limits on loops and fan-out width. A critic loop with no cap or an orchestrator that spawns an unbounded number of workers is how costs spiral.
Where API infrastructure matters
Multi-agent systems multiply the number of API calls you make, which multiplies the operational surface: more keys to manage if you split by team or environment, more usage to track per agent type, and more streaming connections if agents report progress to a UI.
This is the part where tooling like SubToAPI helps rather than the orchestration logic itself. SubToAPI turns your existing Claude access into a standard HTTPS API with application-level keys (sub_live_...), so you can issue a separate key per agent role or per environment, see usage broken down per key in one dashboard, and stream responses for agents that need to report progress incrementally — see /docs/streaming. For teams building multi-agent products, seat-based plans (Team at €19/seat, Scale at €49/seat) mean the people building and debugging each agent get their own access without sharing credentials. Start with the /docs/quickstart guide and the /docs/messages reference for request/response shapes, or check /pricing for plan details. A free trial is available at /signup.
A minimal working example
async function callAgent(role, input) {
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-5",
max_tokens: 1024,
system: AGENT_PROMPTS[role],
messages: [{ role: "user", content: JSON.stringify(input) }]
})
});
const data = await res.json();
return JSON.parse(data.content[0].text);
}
Each role maps to a distinct system prompt in AGENT_PROMPTS, and every agent returns JSON that the next step can parse without ambiguity.
FAQ
Do I need a separate API key per agent? Not strictly, but it makes debugging and cost attribution far easier. If you're tracking which agent is driving usage or errors, separate keys per role or environment save significant investigation time later.
How many agents is too many? There's no fixed number, but each added agent increases latency and failure points. Start with the fewest agents that solve the problem — usually 2 to 4 — and only add more if you can point to a specific quality or speed problem the current setup doesn't solve.
Should agents share conversation history? Generally no. Pass only the structured data each agent needs for its specific task. Sharing full history bloats context, increases cost, and often confuses the model about which role it's playing.