Claude Code: Agent Handoffs and Collaboration

· 5 min read

Overview

Handoffs are the mechanism that turns a collection of independent agents into a coordinated team. When one agent transfers work to another — passing along context, intermediate results, and instructions — the system can handle tasks that no single agent could manage alone. A research agent gathers data, then hands off to an analysis agent that produces insights, which in turn hands off to a report agent that formats the final output.

The Claude Agent SDK supports handoffs as a first-class concept. You define handoff targets and conditions directly in agent configuration, and the SDK manages the context transfer when a handoff occurs. This is more structured than ad-hoc approaches where you manually concatenate outputs and feed them to the next agent — the SDK preserves conversation context and provides clear transition points.

Getting handoffs right is critical because they are the most common failure point in multi-agent systems. A handoff that drops important context, routes to the wrong agent, or creates an infinite loop between agents can render an otherwise well-designed system unusable.

When to use it

Use explicit handoffs when:

Avoid handoffs when:

Getting started

Define handoffs in agent configuration

The simplest handoff pattern uses the SDK's built-in handoff configuration.

import { Agent } from "@anthropic-ai/agent-sdk";

const billingAgent = new Agent({
  name: "billing-specialist",
  model: "claude-sonnet-4-20250514",
  instructions: `You handle billing inquiries: invoices, payment methods,
    subscription changes, and refund requests. You have access to the
    billing system and can make changes to customer accounts.

    If the customer has a technical issue unrelated to billing, hand off
    to the technical support agent.`,
  tools: [invoiceTool, paymentTool, subscriptionTool],
  handoffs: [
    {
      agent: techSupportAgent,
      condition: "Customer has a technical issue, not a billing question",
    },
    {
      agent: escalationAgent,
      condition: "Customer is requesting manager review or is dissatisfied with resolution",
    },
  ],
});

const techSupportAgent = new Agent({
  name: "tech-support",
  model: "claude-sonnet-4-20250514",
  instructions: `You handle technical issues: bugs, configuration problems,
    integration questions, and feature usage guidance. You can access
    system logs and documentation.

    If the customer has a billing question, hand off to the billing specialist.`,
  tools: [logSearchTool, docSearchTool, systemStatusTool],
  handoffs: [
    {
      agent: billingAgent,
      condition: "Customer has a billing or payment question",
    },
    {
      agent: escalationAgent,
      condition: "Issue requires engineering team involvement",
    },
  ],
});

Build a triage router with handoffs

A common pattern is a lightweight triage agent that classifies requests and routes them immediately.

const triageAgent = new Agent({
  name: "triage-router",
  model: "claude-sonnet-4-20250514",
  instructions: `You are the first point of contact. Your only job is to
    understand the customer's request and route them to the right specialist.

    Routing rules:
    1. Billing, invoices, payments, subscriptions -> billing-specialist
    2. Bugs, errors, technical configuration -> tech-support
    3. Product questions, feature requests, how-to -> product-expert
    4. Account deletion, data export, compliance -> account-manager

    Ask ONE clarifying question if the category is ambiguous.
    Never attempt to solve the problem yourself.`,
  handoffs: [
    { agent: billingAgent, condition: "Billing or payment related inquiry" },
    { agent: techSupportAgent, condition: "Technical issue or bug report" },
    { agent: productExpert, condition: "Product question or feature request" },
    { agent: accountManager, condition: "Account management or compliance" },
  ],
});

Implement context-preserving handoffs

For complex workflows, you may need to explicitly structure what context transfers between agents.

interface HandoffContext {
  originalQuery: string;
  previousAgentName: string;
  previousAgentFindings: string;
  handoffReason: string;
  metadata: Record<string, unknown>;
}

function buildHandoffMessage(context: HandoffContext): string {
  return [
    `## Handoff from ${context.previousAgentName}`,
    `**Original request:** ${context.originalQuery}`,
    `**Reason for handoff:** ${context.handoffReason}`,
    `**Previous findings:**`,
    context.previousAgentFindings,
    `**Additional context:** ${JSON.stringify(context.metadata)}`,
  ].join("\n\n");
}

async function sequentialHandoff(query: string) {
  // Stage 1: Research
  const researchResult = await researchAgent.run(query, { maxTurns: 10 });

  // Stage 2: Analysis with structured handoff context
  const analysisInput = buildHandoffMessage({
    originalQuery: query,
    previousAgentName: "research-agent",
    previousAgentFindings: researchResult.output,
    handoffReason: "Research complete, ready for analysis",
    metadata: { sourcesFound: 5, confidenceLevel: "high" },
  });

  const analysisResult = await analysisAgent.run(analysisInput, { maxTurns: 10 });

  // Stage 3: Report with full chain context
  const reportInput = buildHandoffMessage({
    originalQuery: query,
    previousAgentName: "analysis-agent",
    previousAgentFindings: analysisResult.output,
    handoffReason: "Analysis complete, ready for report generation",
    metadata: { findingsCount: 8, recommendationsCount: 3 },
  });

  return reportAgent.run(reportInput, { maxTurns: 8 });
}

Prevent infinite handoff loops

Guard against agents handing back and forth indefinitely by tracking handoff depth.

async function runWithHandoffLimit(
  agent: Agent,
  input: string,
  maxHandoffs: number = 5,
  currentDepth: number = 0
): Promise<{ output: string; handoffCount: number }> {
  if (currentDepth >= maxHandoffs) {
    const fallbackAgent = new Agent({
      name: "fallback",
      model: "claude-sonnet-4-20250514",
      instructions: `The conversation has been routed ${maxHandoffs} times
        without resolution. Provide the best possible answer based on the
        conversation history, and note any limitations.`,
    });
    const result = await fallbackAgent.run(input, { maxTurns: 5 });
    return { output: result.output, handoffCount: currentDepth };
  }

  const result = await agent.run(input, { maxTurns: 10 });

  if (result.handoff) {
    return runWithHandoffLimit(
      result.handoff.targetAgent,
      result.handoff.message,
      maxHandoffs,
      currentDepth + 1
    );
  }

  return { output: result.output, handoffCount: currentDepth };
}

Integration with agent teams

Handoffs are the connective tissue in every multi-agent coordination pattern:

In Sequential Pipelines, handoffs transfer work forward through stages. Each handoff should include a structured summary of the previous stage's output, not the raw conversation history. This prevents context windows from filling up in later stages.

In Router patterns, the triage agent's handoff decision is the most critical single decision in the system. A misroute sends the customer to an agent that cannot help, creating frustration and wasting time. Invest heavily in triage prompt engineering and test with edge cases.

In Hierarchical patterns, a manager agent delegates to specialists and receives their results. The handoff in both directions matters — the delegation message must be specific enough for the specialist, and the result message must be structured enough for the manager to synthesize.

Best practices and common pitfalls

  1. Summarize, do not forward raw history. When handing off between agents, send a structured summary of findings and context rather than the entire conversation transcript. This keeps context windows manageable and prevents the receiving agent from being confused by irrelevant turns.

  2. Make handoff conditions mutually exclusive. If two handoff conditions can match the same input, the agent will pick one unpredictably. Define clear boundaries and handle ambiguous cases with a specific rule (e.g., "when in doubt, route to the general support agent").

  3. Track handoff chains for debugging. Log every handoff with the source agent, target agent, reason, and transferred context. When a user reports a bad outcome, the handoff chain is usually the first thing you need to examine.

  4. Test the full handoff cycle. Do not just test individual agents in isolation. Test scenarios that require multiple handoffs end-to-end, including cases where the first agent misclassifies and the second agent must re-route.

  5. Set a hard cap on handoff depth. Without a limit, two agents with overlapping responsibilities can bounce requests between each other indefinitely. A maximum of 3-5 handoffs covers legitimate use cases; anything beyond that indicates a routing problem.

Skip the setup — generate agent teams instantly →