config.handoffs lists the agents this agent may hand the conversation to. Each entry becomes one tool the model can call, named handoff_to_<name>, and the model chooses between them from each entry’s description. Handing the caller to a person is a transfer, not a handoff.
handoffs works on every channel: voice, video and text.
Fields
message, description and summary_prompt take {{userdata.…}}, {{system_vars.…}} and {{vars.…}}, resolved when the agent is built. Any other token root is a save error.
A handoff gives the conversation away for good. To borrow the floor for a sub-job and get a typed result back, use an agent task instead.
Writing description
The entry’s description becomes the tool description and is the only thing the model routes on. Write what belongs at that desk, not an instruction:
Keep descriptions on one agent non-overlapping, and have the routing agent’s prompt say what it does not answer itself — otherwise it answers the billing question badly instead of routing it.
The generated tool takes no arguments, except under
context: "summary", where it takes one: the summary.
Tool names
The tool name ishandoff_to_ plus name lowercased, with each run of non-alphanumeric characters replaced by _: Tier 2 support → handoff_to_tier_2_support. It is derived from the entry’s name, not the target agent’s, so renaming the target never changes it.
Refused at save or publish:
- Two entries on one agent with the same
name. - A
namewith no letters or digits. - A generated name that collides with an attached tool, a hook tool or a provider tool on the same agent.
Targets
Withagent_id, each handoff enters the target at whatever version is published at that moment. Unlike config.tools, the target is not pinned at the source’s publish: republish the target and the next handoff lands on the new version. Publishing refuses an edge whose target has no published version:
agent_id, name resolves against the team defined on the call. Publishing warns rather than refuses, because the team is unknown until the call starts; a call whose team does not define the name is refused at creation.
message
message is queued before the target is built, so the caller hears it while the target loads, and it finishes before the target’s on_enter hook runs.
Omit it for a silent handover, which is fine when the target has a greeting. A delivery tag written by hand into
message is not validated; see expressive delivery.
What the target does on arrival
- Its own
on_enterhook runs to completion. Its latency is silence before the target’s first word. - It speaks its own
greeting, if it has one, with{{userdata.…}}resolved against the live session. - With no greeting, it generates an opening line, guided only by the edge’s
instructionsplus its own prompt and inherited context. Ifon_enteralready ran agenerate_reply, that was the opening.
instructions (“Don’t introduce yourself; answer the caller’s last question.”) and always-true arrival behaviour in the target’s prompt. Publishing warns when the target has a greeting, because instructions is then never used.
The target’s own conversation.context is not consulted; what it starts from is set by the edge’s context.
On text, a handoff lasts for the rest of the chat: the agent last handed the conversation answers every later message until the chat ends.
Routing from a tool: the handoff operation
When the decision follows from data rather than from what the caller said — an account tier from a CRM lookup, business hours — put a handoff operation in a tool’s operation tree, after the lookup and inside an if. It takes target_agent_id or agent_name, plus the same context, recent_turns, message and instructions. Under context: "summary" it requires a summary template (for example {{args.reason}} or {{tooldata.brief}}), because the tree runs after the model’s call and no summary is being written.
A handoff operation is also allowed in an on_enter hook, which routes the caller before the answering agent speaks. An agent can carry both handoffs entries and tools with handoff operations. A task cannot attach a tool that contains one.
Rules across a team
Every edge is checked at publish and again at runtime before the switch. Errors:
A warning, not an error: a target with recording on behind a source with recording off. Recording cannot start part-way through a call, so nothing is recorded if the source is the agent that answered. See recording.
POST /v1/agents/{agent_id}/validate reports all of these without publishing. See validation.
Handing back and the per-call cap
A target hands back with its ownhandoffs entry pointing at the agent it came from. For a return edge, use context: "summary" with recent_turns: 1 so the front desk does not carry the specialist’s detour on every turn.
Nothing prevents a routing cycle. A call allows 25 handoffs, counting both handoffs edges and handoff operations; the next attempt fails and the model is given “I’m sorry, I can’t pass you to another assistant again on this call.” If you get near the cap, two agents’ descriptions overlap.
Example
Set a front desk’s handoffs, validate and publish.handoffs in a PATCH replaces the whole list; the rest of the config is kept.
Next
Context policies
transcript, summary and none: what the target starts from.The handoff operation
Every field on the operation, for routing decided by a tool.
Patterns
Front desk to specialists, handing back, routing on a lookup.