By Sagar Shankaran, Founder of CallSphere
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.
Key takeaways
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.
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:
flowchart TD
INPUT(["Task input"])
SUPER["Supervisor agent<br/>plans plus monitors"]
W1["Worker 1<br/>research"]
W2["Worker 2<br/>code"]
W3["Worker 3<br/>writing"]
CRITIC{"Output meets<br/>rubric?"}
REWORK["Rework or<br/>retry path"]
SHARED[("Shared scratchpad<br/>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
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.
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_<agent_name>. 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.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
You can provide additional context during a handoff by passing a description or an on_handoff callback:
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.
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:
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:
result = Runner.run_sync(triage_agent, user_message, max_turns=10)
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.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
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.
There are several patterns for when to trigger a handoff:
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."
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.
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.
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

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
How we built a fault-tolerant HVAC emergency triage and tech-dispatch platform on Kubernetes — three-tier CQRS, 11 micro-agents on the OpenAI Agents SDK + LangGraph, NATS JetStream, DTMF/SMS/WebSocket acceptance, circuit breakers, and an evaluation pipeline that catches regressions before they wake a tech at 3 AM.
Five proven multi-agent architecture patterns built on A2A — orchestrator, peer mesh, hub-and-spoke, marketplace, and tiered specialist.
96% of well-designed agents close calls politely; the rest leave callers with the robotic-hangup feeling that undermines the whole flow. We map endCallPhrase tuning, silence-timeout policies, and CallSphere's vertical farewell library.
How to build a safety eval pipeline that runs known jailbreak corpora, prompt-injection attacks, and tool-misuse scenarios on every release — and gates merges on it.
OpenAI's parallel function calling can cut latency in half — or burn money on dependent calls. The architecture, code, and an eval that proves the win.
Honest principal-engineer comparison of the OpenAI Agents SDK and the legacy Assistants API, with a migration checklist and eval-parity strategy so you don't ship regressions.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco
Watch how CallSphere handles real customer calls, schedules appointments, and processes payments — live.
Try Live DemoBook a DemoCalculate Your ROI