Module · Voice
Transfer Call
Lesson 14 of 21 · 4 min
An AI agent handling a call will eventually meet something it cannot do. A customer who is angry, a question about an invoice it has no access to, or someone who simply says “put me through to a human”. What happens next is the difference between a voice agent people tolerate and one they resent.
Transfer Call is what happens next. And it is the first node in this course that does not sit on the workflow's path at all: you attach it to a Voice Agent as a tool, and the agent invokes it when the conversation calls for it. Nothing you draw decides when a transfer happens. The agent does.
No workflow on this page. Transfers need a telephony provider the tutorial organisation does not have — and this node would not appear in the flow in any case, because it hangs off a Voice Agent rather than sitting between two steps.
The fields
| Field | What it holds |
|---|---|
destinations | List of transfer destinations |
transfer_message | Message to speak before transferring Defaults to Please hold while I transfer your call.. |
fallback_message | Message if transfer fails Defaults to I'm sorry, I couldn't complete the transfer. Let me help you directly.. |
operation | Exotel only. 'transfer_call' transfers the live call to a destination; 'check_agent_availability' returns the agents currently available to take a call (name, number, free/busy) without transferring. One of: transfer_call · check_agent_availability Defaults to transfer_call. |
active_only | For check_agent_availability: only return agents who are verified, online and free (not busy). Defaults to true. |
destinations
- What it holds
- List of transfer destinations
transfer_message
- What it holds
- Message to speak before transferring Defaults to
Please hold while I transfer your call..
fallback_message
- What it holds
- Message if transfer fails Defaults to
I'm sorry, I couldn't complete the transfer. Let me help you directly..
operation
- What it holds
- Exotel only. 'transfer_call' transfers the live call to a destination; 'check_agent_availability' returns the agents currently available to take a call (name, number, free/busy) without transferring. One of:
transfer_call · check_agent_availabilityDefaults totransfer_call.
active_only
- What it holds
- For check_agent_availability: only return agents who are verified, online and free (not busy). Defaults to
true.
Everything hinges on destinations. Each entry has a name, a number, and — the field that actually does the work — a description.
The description is the routing logic
The agent does not see your canvas. What it sees is a list of destination names and their descriptions, assembled from this node's configuration and handed to it as part of the tool. That text is the entire basis on which it decides where to send someone.
- A name alone gives the agent almost nothing. “Support” and “Success” are indistinguishable to a model that has only the words.
- A description tells it when. “Billing questions, refunds and invoice queries” versus “technical problems with the product after purchase” is a routing table written in English, and it works.
- Write them as conditions, not as job titles. What the caller wants, not what the team is called.
This is also the fix for most bad transfers. If calls are landing in the wrong queue, the destination descriptions are wrong or missing — not the agent.
What the agent says, and when
transfer_message is not played by the phone system. It is an instruction to the agent to say that exact sentence before invoking the tool. So the caller hears it in the agent's own voice, mid-conversation, and then the transfer begins.
One consequence worth planning for: the message is spoken first, so it is spoken even when the transfer subsequently fails. Somebody hears “please hold while I transfer your call” and then stays exactly where they were. That is what fallback_message is for, and it should read like a recovery rather than an apology — say what you will do instead.
Matching is exact, and the failure is graceful
When the agent asks for a destination, the node resolves it in two steps and neither of them is clever:
| Step | What counts as a match |
|---|---|
| An exact match, ignoring case. “sales” finds “Sales”. “sales team” does not find “Sales”. | |
| Anything that leaves ten or more digits once spaces, dashes and a leading plus are removed. This is how an agent transfers to a number a caller read out. | |
| No transfer. The node tells the agent which destinations exist and the call continues — the agent can ask the caller again. |
- What counts as a match
- An exact match, ignoring case. “sales” finds “Sales”. “sales team” does not find “Sales”.
- What counts as a match
- Anything that leaves ten or more digits once spaces, dashes and a leading plus are removed. This is how an agent transfers to a number a caller read out.
- What counts as a match
- No transfer. The node tells the agent which destinations exist and the call continues — the agent can ask the caller again.
That last row is better behaviour than it sounds. A failed transfer does not drop the call or end the run; it hands the agent a list and lets it recover in conversation. But it does mean a mismatch is invisible in your metrics unless you look for it — the call carries on and nothing is marked failed.
Checking who is free first
Setting operation to check_agent_availability turns this into a different, read-only tool: it reports which agents are available rather than transferring anyone. With active_only on — the default — that means verified, online and not currently on a call.
That operation is Exotel only. On any other provider it does not work, and the node checks. The pattern it enables — the agent asks who is free, then transfers to a named person rather than a queue — is therefore not portable. Build the plain transfer first and add availability checking only if you are on Exotel and staying there.
What breaks
Destinations mean different things on different providers. The number field is a phone number on Exotel and Twilio and an intercom extension on Tata Tele, and a destination can be typed as an agent instead, which uses a provider-side agent id. A set of destinations that works on one provider is not a set that works on another — this is configuration to re-check after any provider change, not to copy across.
Nothing stops the agent transferring badly. There is no confirmation step and no rule you can write here that says “only after two failed attempts”. If you need that, it belongs in the agent's own instructions, not in this node. This node is capability; the agent's prompt is policy.
A transfer ends the AI's part of the call, and its transcript with it. Whatever you wanted to record about the conversation — the reason, the summary, what the customer asked for — read it from this node's output at the moment of transfer. The optional reason the agent supplies is the cheapest way to find out later why calls are being escalated, and it costs nothing to ask for.
Try it
- Add a Transfer Call node and attach it to a Voice Agent as a tool. Configure one destination with your own number and a description like “anything the agent cannot answer”.
- Call the agent and ask for a human. Listen for the transfer message in the agent's voice before the line moves.
- Now add a second destination and give both real descriptions. Call again and describe a billing problem without naming a team — the routing should happen on the description alone.
- Ask to be transferred to a department that does not exist. The call should continue and the agent should tell you the real options. That recovery is the behaviour to confirm before you go live.
- Delete one destination's description and try the same call again. The drop in routing quality is the argument for writing them properly.
Next: Google Calendar — the first of seven nodes that reach outside Expedify, starting with the one that books the meeting an agent just agreed to.

