---
title: "Agent Handoffs: Seamlessly Transferring Conversations Between Specialized Agents"
description: "Learn how to implement clean agent handoffs using the OpenAI Agents SDK, including handoff triggers, context transfer, conversation continuity, and patterns for preserving user experience across agent boundaries."
canonical: https://callsphere.ai/blog/agent-handoffs-seamlessly-transferring-conversations-between-agents
category: "Learn Agentic AI"
tags: ["Agent Handoffs", "Multi-Agent Systems", "OpenAI Agents SDK", "Conversation Design", "Context Transfer"]
author: "CallSphere Team"
published: 2026-03-17T00:00:00.000Z
updated: 2026-05-08T10:00:57.684Z
---

# Agent Handoffs: Seamlessly Transferring Conversations Between Specialized Agents

> Learn how to implement clean agent handoffs using the OpenAI Agents SDK, including handoff triggers, context transfer, conversation continuity, and patterns for preserving user experience across agent boundaries.

## What Is an Agent Handoff?

An agent handoff is the moment when one agent transfers control of a conversation to another agent. The first agent stops processing, the second agent takes over, and the user ideally notices nothing — the conversation continues naturally.

Handoffs are the fundamental building block of multi-agent systems. Without them, you either have a single monolithic agent or completely disconnected agents that cannot collaborate. Handoffs enable specialization while maintaining conversation continuity.

## Handoffs in the OpenAI Agents SDK

The SDK provides the `handoff()` function to declare that one agent can transfer to another. When the model decides a handoff is appropriate, it calls a special tool that the SDK intercepts to perform the transfer:

```mermaid
flowchart TD
    INPUT(["Task input"])
    SUPER["Supervisor agent
plans plus monitors"]
    W1["Worker 1
research"]
    W2["Worker 2
code"]
    W3["Worker 3
writing"]
    CRITIC{"Output meets
rubric?"}
    REWORK["Rework or
retry path"]
    SHARED[("Shared scratchpad
and memory")]
    OUT(["Final result"])
    INPUT --> SUPER
    SUPER --> W1 --> CRITIC
    SUPER --> W2 --> CRITIC
    SUPER --> W3 --> CRITIC
    W1 --> SHARED
    W2 --> SHARED
    W3 --> SHARED
    SHARED --> SUPER
    CRITIC -->|Pass| OUT
    CRITIC -->|Fail| REWORK --> SUPER
    style SUPER fill:#4f46e5,stroke:#4338ca,color:#fff
    style CRITIC fill:#f59e0b,stroke:#d97706,color:#1f2937
    style OUT fill:#059669,stroke:#047857,color:#fff
    style SHARED fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
```

```python
from agents import Agent, Runner, handoff

billing_agent = Agent(
    name="Billing Agent",
    instructions="""You handle billing questions: invoices, payments,
    refunds, and subscription changes. If the user asks about technical
    issues, hand off to the Technical Agent.""",
    handoffs=[],  # Will be set after technical_agent is defined
)

technical_agent = Agent(
    name="Technical Agent",
    instructions="""You handle technical support: bugs, configuration,
    API issues, and troubleshooting. If the user asks about billing,
    hand off to the Billing Agent.""",
    handoffs=[handoff(billing_agent)],
)

# Now set billing agent's handoffs
billing_agent.handoffs = [handoff(technical_agent)]

triage_agent = Agent(
    name="Triage Agent",
    instructions="""You are the first point of contact. Determine
    whether the user needs billing help or technical help, and hand off
    to the appropriate specialist immediately.""",
    handoffs=[handoff(billing_agent), handoff(technical_agent)],
)

result = Runner.run_sync(triage_agent, "I was charged twice for my subscription")
print(result.final_output)
```

The triage agent reads the message, recognizes it as a billing issue, and hands off to the billing agent. The billing agent receives the full conversation history and responds directly.

## How the SDK Manages Handoffs Internally

When you declare `handoffs=[handoff(billing_agent)]`, the SDK registers a special tool for each handoff target. The tool name follows the pattern `transfer_to_`. When the model calls this tool, the SDK does not execute a normal function — instead, it swaps the active agent to the target, passes the accumulated conversation history forward, and continues the agent loop with the new agent.

This means the target agent sees the entire conversation that preceded the handoff. It knows what the user said, what the previous agent said, and why the handoff was triggered — all from the conversation history.

## Customizing Handoff Behavior

You can provide additional context during a handoff by passing a description or an `on_handoff` callback:

```python
from agents import Agent, handoff

def prepare_billing_context(ctx):
    """Called when handing off to billing. Can enrich context."""
    # You could fetch the customer's billing record here
    # and inject it into the conversation
    pass

triage_agent = Agent(
    name="Triage Agent",
    instructions="Route users to the right specialist.",
    handoffs=[
        handoff(
            billing_agent,
            tool_description_override="Transfer to billing for payment, invoice, or refund questions",
            on_handoff=prepare_billing_context,
        ),
        handoff(
            technical_agent,
            tool_description_override="Transfer to tech support for bugs, errors, or configuration help",
        ),
    ],
)
```

The `tool_description_override` helps the model make better routing decisions by providing explicit criteria for when to trigger each handoff. The `on_handoff` callback lets you run setup logic — like fetching user-specific data — before the target agent starts processing.

## Bidirectional Handoffs

Agents can hand off back and forth. A billing agent might discover that a payment failure is actually caused by a technical issue and hand off to the technical agent. The technical agent might find that the fix requires a subscription change and hand back to billing.

The SDK supports this naturally, but you must be careful about infinite loops. If both agents keep handing off to each other, the conversation will ping-pong indefinitely. Prevent this with clear instructions:

```python
billing_agent = Agent(
    name="Billing Agent",
    instructions="""Handle billing questions. If you discover the issue
    is technical (API errors, integration bugs), hand off to Technical.

    IMPORTANT: If Technical already handed this conversation to you,
    do NOT hand it back. Instead, acknowledge the complexity and ask
    the user if they'd like to escalate to a human.""",
    handoffs=[handoff(technical_agent)],
)
```

You can also set `max_turns` on the Runner to enforce a hard ceiling on the total number of agent turns:

```python
result = Runner.run_sync(triage_agent, user_message, max_turns=10)
```

## Maintaining Conversation Continuity

The biggest risk with handoffs is a jarring user experience. The user is talking to one agent, and suddenly the tone, vocabulary, or knowledge level shifts. Three practices help maintain continuity:

**1. Consistent persona framing.** Give all agents the same base personality traits. If your brand voice is friendly and concise, every agent should reflect that.

**2. Invisible handoffs.** Do not announce "I am now transferring you to the billing department." Instead, the new agent should pick up naturally: "I can see you were charged twice on March 12. Let me look into that refund."

**3. Context summaries.** For long conversations where full history transfer is impractical, use a summarization step during handoff to compress the relevant context.

## Handoff Triggers

There are several patterns for when to trigger a handoff:

- **Intent-based**: The user's intent does not match the current agent's specialty
- **Capability-based**: The current agent lacks a tool needed for the request
- **Confidence-based**: The current agent is uncertain about its response
- **Escalation-based**: The conversation has exceeded a complexity threshold

The model handles intent-based and capability-based triggers naturally through the handoff tool descriptions. For confidence-based triggers, you can instruct the agent explicitly: "If you are less than 80% confident in your answer, hand off to a senior specialist."

## FAQ

### What happens to the conversation history during a handoff?

The full conversation history is passed to the target agent. The target agent sees all user messages, all previous agent messages, and all tool call results from before the handoff. This means the target agent has complete context without the user repeating themselves.

### Can I hand off to an agent running on a different model?

Yes. Each agent in the OpenAI Agents SDK can specify its own `model` parameter. The triage agent could run on GPT-4o-mini for fast routing, while the specialist agents run on GPT-4o for deeper reasoning. Handoffs work seamlessly across models.

### How do I test handoff behavior?

Write test cases where the input clearly belongs to a specific specialist and verify the final output comes from the correct agent. The SDK's tracing system shows which agents were active during a run, making it easy to assert that the expected handoff path was followed.

---

#AgentHandoffs #MultiAgentSystems #OpenAIAgentsSDK #ConversationDesign #ContextTransfer #AgenticAI #LearnAI #AIEngineering

---

Source: https://callsphere.ai/blog/agent-handoffs-seamlessly-transferring-conversations-between-agents
