transfer(...), how the sid
survives, and how a human — on a browser softphone or a real deskphone — gets
rung and takes over the media. For the product-level model and the
bidirectional (bot ⇄ human) framing, start at
Handoffs; this page is the mechanics
underneath it.
A handoff can be driven two ways over the same gateway. You (your app, or
an operator dashboard) call transfer(...) on the SDK. Or the AI agent
itself decides mid-conversation and calls a built-in telephony tool —
transfer_call, route_to_skill, warm_transfer / complete_transfer, or
consult / consult_transfer / consult_cancel — with no operator config
required. Both land on the same B2BUA and preserve the same sid; the
tool path just lets the model pick
the moment and the style. These tools are the same operations exposed to ISV
apps as CSTA verbs — see CTI call control.
The one call keeps its sid
A call is a SIP dialog with two legs — caller and agent — held together by a back-to-back user agent (B2BUA) in the SIP gateway. Thesid names that
dialog and is the universal key: the same value addresses the CDR, the
recording, the analytics segments, and the media tracks. A handoff re-points
the agent leg only; the caller leg never moves, so the caller hears
uninterrupted audio and the sid is unchanged before, during, and after.
sid (not an END), and downstream folds the
call into multiple segments under one sid, split by whether an AI or a human
handled each stretch — the ”12s with the bot, 4m18s with a human” breakdown.
See Call lifecycle for
the full event sequence and
Sessions, calls, tracks & streams
for the object model.
When is a sid created vs preserved? A
sid is minted once, when the call
first exists — at the inbound INVITE, or when you call calls.originate()
outbound. Every handoff after that preserves it. You never create a sid
for a transfer; you pass the one you already hold.Two mechanisms, picked by target
The SDK exposes one method —transfer(...) — and the gateway chooses the
mechanism from which field you set:
| You pass | Mechanism | Media stays on our plane? |
|---|---|---|
agent: "<id>" | In-place re-attach of the agent leg (skill-queue router or a named human/AI agent) | Yes |
to: "<E.164>" | SIP REFER — ask the far end to re-target its leg to a phone number or PBX | No (once accepted, off-net) |
agent / to per transfer. A bare string is shorthand for
{ to }.
Escalate to a human (re-attach)
Route the caller into the skill-queue router (ACD), which rings an available human and bridges them in on accept — or transfer to one named person. The caller leg holds; the agent leg re-points to the human once they answer.- TypeScript
- Python
INVITE to the registered contact, or out through a trunk for a
PSTN callback), waits for the 200 OK, and bridges the leg into the same
B2BUA. How agents register, get rung, and claim a call — including ring
strategy and glare handling — is in
PBX & ACD.
Transfer off-net via SIP REFER
When the target is a phone number (a PSTN destination or another PBX), the gateway performs a SIPREFER (RFC 3515) — the standard request that asks the
far end to re-target its leg. You don’t build the REFER yourself; passing
to is enough.
- TypeScript
- Python
Handoff tools the AI agent calls itself
Everything above is the operator-driven view — your code holds thesid
and calls transfer(...). But most escalations should be the agent’s own
decision, made in the flow of the conversation. Every AI agent gets a
built-in telephony toolset with no operator config: the runtime advertises
these tools to the model automatically, and when the model calls one, the
gateway runs the exact same B2BUA operation on the live sid. (These are
first-class runtime tools, not user-declared functions — see
Tool calling for how the toolset is
advertised and how an operator tool of the same name overrides a built-in.)
There are three transfer styles, and the right one is a caller-experience
choice — does the caller hear the handoff, and does the AI stay on the line?
| Tool | What the caller hears | AI stays or leaves | When to use |
|---|---|---|---|
transfer_call | Brief pause, then the destination — no handoff spoken | Leaves immediately | Fastest path. You know exactly where the caller should go (a queue URI, a PSTN number, another PBX) and no context needs to pass by voice. |
route_to_skill | Queue treatment (ringback / hold audio) until a human answers | Leaves (steps out to the ACD) | “Get me a human” with no specific number — hand to a human skill/queue and let the ACD pick the agent. |
warm_transfer → complete_transfer | Everything — the AI and the human brief each other in a shared conference, caller included | Stays through the brief, then leaves on complete_transfer | A smooth, human-feeling handoff where the caller hears “I’ve got Priya here, she can take it from…”. Context passes out loud. |
consult → consult_transfer / consult_cancel | Nothing — caller is held (hold audio) while the AI checks privately | Stays; can return to the caller with consult_cancel | Verify a human is available / get an answer before committing the caller. If it works out, consult_transfer; if not, consult_cancel and keep talking to the caller. |
destination for transfer_call, warm_transfer, and consult accepts a
skill name, an extension, a SIP URI (sip:queue@pbx.example.com),
or a PSTN number in E.164 (+15551234567); transfer_call also accepts a
tel: URI.
Blind — transfer_call
The fastest exit. The AI hands the caller away and drops off the call in one
step (a SIP REFER under the hood, same as an operator transfer({ to })).
Nothing is spoken to the receiving party first.
Where to send the caller — a skill name, extension, SIP URI,
tel: URI, or
E.164 number.Optional
Referred-By identity to present on the REFER. Defaults to the
trunk identity.Queue to a human skill — route_to_skill
Send the caller to a human skill/queue (the ACD) without naming a person or
number. The AI steps out; the skill-queue
router rings an available human
and bridges them onto the same sid.
The target skill/queue. Omit to use the agent’s default escalation skill.
Warm (attended) — warm_transfer then complete_transfer
The AI dials the destination and joins a conference so it can speak the
handoff. The caller hears everything and the AI stays live — this is the
transfer that feels human.
AI calls warm_transfer { destination }
The gateway dials
destination and joins it into a conference with the
caller. The caller hears the ring, then all three parties are connected —
caller, human, and AI.The AI briefs the human, out loud, in the conference
“This is Sam calling about order 4471, they need a refund exception.” The
caller hears the whole brief, so nothing feels like it happened behind their
back. The human can ask the AI (or the caller) questions before accepting.
Private consult — consult then consult_transfer / consult_cancel
An attended, one-to-one check with a third party that the caller does
not hear. The caller is effectively on hold while the AI confirms
availability or gets an answer, then commits or backs out.
AI calls consult { destination }
The caller is placed on hold (hold audio) and the AI dials
destination
on a private leg. The caller hears none of the consult conversation.The AI speaks privately with the consulted party
“I have a caller who needs a refund exception on order 4471 — can you take
it?” Only the AI and the consulted party are on this leg.
Commit — consult_transfer { }
Connects the caller to the consulted party and drops the AI out. The caller
comes off hold already bridged to the human.
Transfer-rule policy still applies. Whichever tool the model calls, the
transfer passes the realm’s transfer-rule policy. A denied cross-realm transfer
returns a structured error the model can react to (apologize, try
route_to_skill, offer a callback) rather than failing silently.Supporting in-call tools
The same built-in toolset gives the AI the rest of the mid-call controls, so it can shape the caller experience around a handoff:| Tool | Params | What it does |
|---|---|---|
hold_call / unhold_call | — | Park the caller on hold audio and bring them back. |
disconnect_call | reason? | End the call; reason is written to the CDR. |
send_dtmf | digit (0-9*#A-D), duration_ms? (default 100), mode (rfc2833|inband) | Play DTMF into the call — e.g. to navigate a downstream IVR before or after a transfer. |
request_supervisor | reason, urgency (low|medium|high) | Ask a supervisor to monitor / whisper / barge in without moving the caller leg. |
What happens on the wire
You call transfer(sid, …)
The control-plane API forwards the request to the SIP gateway over its
correlated RPC channel. The gateway looks up the live dialog by
sid.The agent leg is re-pointed, REFERred, or conferenced
For
agent: / route_to_skill the gateway re-attaches the agent leg in the
B2BUA (dial the human endpoint or hand to the skill-queue router). For to:
/ transfer_call it issues a SIP REFER toward the leg you’re forwarding.
For warm_transfer it joins the destination into a conference with the
caller; for consult it holds the caller and opens a private leg to the
consult target.A transfer lifecycle event fires on the same sid
The gateway emits a
transferred transition (and, when it is the receiver
of a peer REFER, auto-acknowledges with 202 Accepted). No END is
emitted — the sid lives on. Recording continues; analytics opens a new
segment tagged with the new handler (AI vs human).Peer-initiated REFER (inbound)
The reverse also happens: a remote party sends us aREFER (a carrier or
upstream PBX asking us to forward a call). The gateway auto-answers it with
202 Accepted and records a transferred lifecycle event on the sid, so the
transfer shows up correctly in traces and CDRs.
Honest status — executing the outbound leg from a peer
REFER. We
acknowledge an incoming REFER today, but automatically placing the new
outbound leg it asks for (following the referred-to target end to end) is
partially in progress. If your topology depends on upstream-initiated blind or
attended transfer driving a fresh outbound call, validate it against your
carrier in staging before you rely on it. Operator-initiated transfer (you
calling transfer(...)) and off-net REFER from us are the fully supported
paths.Bot ⇄ human re-attach on the media plane
For an on-plane handoff (agent:), the media stays on our transport the whole
time, which is what makes bot → human → bot bouncing possible on one sid:
- The caller’s audio is carried as a caller-audio track keyed on the
sid; the human agent takes over by picking up that track and injecting their mic into the caller’s egress. Because subscription is bysid(not a fixed shard), the human can live on a different core or host and still hear the caller with no central funnel. - Handing back to an AI agent is the same
transfer({ agent })primitive with an AI agent id — the bridge re-points in place, thesidis unchanged, and the call remains one continuous record.
Human-agent return audio (browser softphone). The human agent’s mic
uplink into the caller is live. The downlink — the human hearing the
caller over the media-over-QUIC track in the browser softphone — is still being
hardened: the caller-audio origin publish needs to be bridged into the relay
fan-out. If you’re standing up browser softphone agents today, validate the
return-audio path in staging. Real SIP deskphone/softphone agents are not
affected — their media rides the normal RTP bridge.
Related
Handoffs (concept)
The product model: AI ⇄ human, warm handoff, sid preservation.
Tool calling
How the built-in telephony toolset is advertised to the AI agent.
CTI call control
The same transfer / consult operations as CSTA verbs for ISV apps.
PBX & ACD
How the skill-queue router rings and claims human agents.
Call lifecycle
Every event a call emits, including transfer transitions.
SIP trunking
The trunk edge REFER rides out over for off-net transfers.
Support agent with handoff
A full inbound bot that escalates to a human, end to end.
Transfer to a human
Copy-paste transfer snippet.