transfer hands the caller to a human being on a phone number. It is the
escape hatch every voice agent needs: the question the agent cannot answer, the
caller who has asked three times for a person, the account that has to be opened
by someone with a login.
The configuration
The destination is fixed at publish
destination is a literal number, frozen into the published tool version. It is
never templated and never chosen by the model. {{args.number}} is refused at
save, and so is a number the caller reads out.
4155550101
is refused with the number we think you meant, rather than silently normalised
against some default country:
What the caller experiences
How the person is reached is the platform’s business. We dial them into the call and step out; the caller does not hear a click, a menu, or anything to indicate which mechanism ran.The person answering sees the workspace’s own number, not the caller’s. The
leg to them is dialled from your workspace’s own carrier trunk, and a trunk can
only present a number it owns. Caller ID for a transfer is trunk-level on every
carrier and cannot be set per transfer.Plan for it: if the person answering needs to know who is calling, use
warm,
or put the caller’s number in the CRM record the same tool writes.mode: cold or warm
This is the only choice you get about how a transfer runs, and it is about the
caller’s experience rather than about plumbing.
cold is the right default. The agent finishes its line, the caller hears
hold music for as long as the other phone takes to ring, and a person answers.
There is no introduction and no “let me hand you over to Sarah”.
warm puts the caller on hold while the agent rings the person, tells them
who is calling and what they want, answers whatever they ask, and only then puts
the two together. The caller hears none of it. It suits escalation to a named
colleague; it does not suit a busy queue, where the briefing is a minute of hold
music per call for no benefit.
On warm, the person can decline
They can also turn out to be a voicemail greeting rather than a person. In both
cases the transfer does not happen: the caller comes off hold, the agent is told
in general terms why, and the conversation carries on exactly where it left off.
The agent is never told — and therefore can never repeat — the words that person
used. Their own explanation is kept on the call’s timeline, not in the caller’s
ear.
While the caller is on hold
Their microphone and speaker are both switched off and hold music plays. That is deliberate: nobody is listening during a ring, so an open microphone would only bill speech-to-text for a caller saying “hello?” into it. Hold time sits inside the call’s duration, its recording and its bill, and the call’s timeline marks where it started and ended.ringing_timeout
ringing_timeout (5–120 seconds, default 30) bounds the ringing, and
nothing after it. Below five seconds no phone has finished its first ring; past
120 a caller has had time to hang up twice.
on_failure
Busy line, nobody home, a declined warm transfer — on_failure decides what
happens next.
continue(default) — the agent is told plainly why the transfer did not happen and keeps talking to the caller. Almost always what you want: the caller is still on the line and the agent can offer something else.end_call— the call is ended, exactly as anend_calloperation would end it.
That last line deliberately also covers failures on our own side — a trunk that
is not authorised, a carrier having a bad day. The caller is never told the
destination refused them when the fault was not theirs. The carrier’s own
verdict is kept on the call record for whoever debugs the trunk.
The agent is also instructed to report the failure and carry on helping, rather
than restarting the conversation — which is what a model that has just watched a
tool fail mid-turn otherwise does.
The line before the transfer
Put asay immediately before the transfer if the caller should hear
something. There is no message field on the operation: a line before a
transfer is an ordinary say, so it templates, it shows as its own node in the
editor’s flowchart, and it reads the same way as a goodbye before an end_call.
You do not need wait_for_playback on it. The transfer already waits for
outstanding speech to finish before it dials.
Unlike an end_call, “let me transfer you” is a promise the platform keeps,
so say it here. Word it for the mode:
Before a
cold transfer, “let me speak to them first” is simply a lie: nobody
is briefed and the caller lands on a stranger.
Tick disable_interruptions
Set the tool-level disable_interruptions flag on any transfer tool.
The dial runs for up to ringing_timeout while the caller hears nothing but
ringback, which is exactly long enough that saying “hello?” into the gap is the
normal thing for a caller to do. An interrupted line is dead five seconds later,
and that line is the only channel left to tell the caller what happened. With
the flag on, the caller cannot talk over “connecting you now” and turn a waited
line into an interrupted one.
Two shapes a transfer tool cannot have
Both are refused when you publish. It cannot belong_running_task. A background tool is detached from the
turn that called it, so the handover would happen outside the turn that asked
for it — mid-sentence, or not at all.
One tool per destination
If the caller could go to billing, sales or support, build three tools —transfer_to_billing, transfer_to_sales, transfer_to_support — each taking
no arguments, each one say and one transfer. Do not build one
transfer_to_department that branches on an argument.
Three reasons, in order of how much they matter:
- The model picks a tool more reliably than it picks an enum value. Choosing
between three named functions is the thing tool-calling models are best at.
Filling in
department: "billing"correctly is a strictly harder problem you have created for no reason. - Every destination gets its own description, which is where the routing policy lives (below). One tool means one description covering three different escalation rules.
- The tool list reads like the org chart. Anyone opening the agent can see
where a call can go. Five nested
ifs inside one tool hide it.
The description is the escalation policy
For every other kind of tool a weak description costs you a mis-timed API call. Here it decides whether a frustrated caller reaches a person, and how soon. Write the policy instead:Use only after you have tried to answer the question yourself and the caller has explicitly asked for a person, or is clearly distressed. Do not use it for questions about order status, delivery dates or returns — you can answer those.Decide when escalation should happen before you write it, and put the answer in the description. That is the entire routing logic of the tool.
A complete transfer tool
transfer_to_billing: argument-free, one line, one transfer, interruptions off.
After the transfer
Once the transfer connects, this call is over as far as the platform is concerned. The agent says nothing more, and the call’s duration and recording cover only the part the agent was on.
The
transfer.briefing entry is the only record of what your AI said about a
customer to a third party, so it is kept as a compliance record rather than as
telemetry. It is also where a declined transfer’s real reason lives.
A transferred call stays on your carrier’s clock until one of the two people
hangs up. Two humans who both walk away from the phone will run to the
three-hour call cap. See transfers for the operator’s
view of a transferred call.
Next
Transfers in operation
Reading a transferred call, and what it costs.
Handoff operation
Moving the call to another agent instead of a person.
Schema and description
Writing a description the model can act on.
Patterns
The escalation recipe, and five more complete tools.