ThunderPhone 2.0 is live.Self-serve, from 2¢/min.Read the announcement

Using the dashboard

Transfer calls

Save the numbers an agent may transfer to, then route callers with a cold transfer, or stay on the line and introduce them with a warm transfer.

An agent transfers calls only to its transfer numbers: a list of names and phone numbers you save on the agent. The prompt refers to each number by name and says when to transfer and whether the handoff should be cold or warm. The agent sees only the names, never the numbers. An agent with no transfer numbers can't transfer calls.

ModeWhat happensUse it when
ColdThe caller is sent directly to the destination. The agent does not introduce the caller or confirm that anyone will answer.Fast routing matters more than an introduction, or the call's number can't place outbound calls.
WarmAs soon as someone answers, the caller is connected, the agent introduces the caller on the line, and drops. You can instead have the agent ask first or whisper.The destination should hear who is calling and why, or the caller should return to the agent when nobody answers.

If the agent omits the mode, the transfer is cold.

Configure transfers

In the dashboard:

  1. Open Voice Agents, select the agent, and open Config.
  2. Under Transfer numbers, add each destination with a name and a phone number, such as Billing and +1 415 555 2671. An agent can have up to 50 transfer numbers.
  3. In the prompt, refer to each destination by its name and say when to use it and whether to use cold or warm mode, for example "transfer billing questions to Billing". You do not create a custom tool.
  4. For warm transfers, choose what happens Before connecting and, optionally, what the Briefing should say. See Before connecting.
  5. If needed, set Advanced → Ring duration from 5 to 120 seconds.
  6. Select Deploy.

With the API, PATCH /v1/agents/{agent_id} to stage transfer_destinations, the prompt, and the optional warm_transfer_style, warm_transfer_briefing and ring_duration_seconds, then POST /v1/agents/{agent_id}/deploy:

{
  "transfer_destinations": [
    {"name": "Billing", "number": "+14155552671"},
    {"name": "Front desk", "number": "+19375551234"}
  ],
  "warm_transfer_style": "introduce",
  "warm_transfer_briefing": "The caller's name and what they need."
}

A write replaces the whole list, and [] removes every transfer number. See the Agents API for the field rules and validation errors.

Names and numbers

  • Name: 1 to 60 characters, unique on the agent ignoring case. This is the word the prompt uses.
  • Number: a full phone number. Numbers without a country code are read as US numbers, and every number is stored in E.164 format. Extensions and the fictional range 555-0100 through 555-0199 are refused.
  • Variable number: instead of a phone number, one call variable on its own, such as {{rep_phone}}. It is filled from the call's variables when the call starts. If the variable is missing or isn't a valid number on that call, a transfer to that name is refused.
  • SIP address: a sip: or sips: address instead of a phone number, such as a contact center queue. See Transfer to a SIP address.
  • Return: no number at all. The agent hands the call back to the contact center it came from. See Return the call to your contact center.

Transfer to a SIP address

A transfer number can be a SIP address, such as sip:support@pbx.example.com, so the agent can hand callers back to your contact center or PBX. The transfer is a cold transfer: ThunderPhone sends a SIP REFER with that address, and your system takes the call from there.

A SIP address works only when the call came from your own SIP system:

  • calls to a SIP address (per-call URI or SIP endpoint), and
  • calls on a number you connected through a manual SIP connection (your own SIP trunk).

On any other call, including calls on ThunderPhone numbers and on numbers connected through Twilio or Telnyx, a transfer to a SIP address is refused: the agent is told it can't transfer there and keeps talking to the caller. A warm transfer to a SIP address runs as a cold transfer.

The address must be written out in full; it can't be a {{variable}}. It is at most 255 characters, needs a host, and can't contain spaces, quotes, commas or angle brackets. Parameters such as ;transport=tls are fine. The one header the address itself can carry is ?User-to-User=, for platforms that read user-to-user information from the transfer address: sip:queue@sbc.example.com?User-to-User=00a1b2;encoding=hex.

SIP headers on the transfer

Any transfer number can also list headers to send with the REFER of a cold transfer, for example the caller's account ID for your contact center to look up:

{
  "name": "Support queue",
  "number": "sip:support@pbx.example.com",
  "headers": {
    "X-Account-Id": "{{account_id}}",
    "User-to-User": "{{uui}}"
  }
}
  • Up to 10 headers. Names start with X- or are User-to-User. X-TP- names are reserved for ThunderPhone.
  • Values are at most 256 bytes and can use call variables, filled when the call starts. A missing variable is left empty, line breaks are removed, and a header whose filled-in value is over 256 bytes is not sent.
  • Headers go only on a cold transfer's REFER, and only reach your system when the carrier passes them on. They are not sent on a warm transfer's new call or when ThunderPhone has to connect a refused transfer itself.

Headers inside the transfer address

Some contact centers read context only from the transfer address (the Refer-To of the REFER), not from separate headers. Twilio, Genesys Cloud and Talkdesk work this way, and most of them expect User-to-User in hex. Set two options on the SIP address entry:

{
  "name": "Escalation",
  "number": "sip:escalation@talkdesk.example.com",
  "headers": {
    "User-to-User": "autopilot_result={{handoff_summary}}",
    "X-Call-ID": "{{handoff_id}}"
  },
  "headers_in_uri": true,
  "uui_encoding": "hex"
}
  • headers_in_uri: true sends the headers inside the address instead of on the REFER: sip:escalation@talkdesk.example.com?User-to-User=…&X-Call-ID=…. Values are escaped for a SIP address, so ; becomes %3B and a space %20.
  • The whole address is kept to 254 characters, inside Twilio's limit of 256 with the angle brackets. User-to-User goes first, then the other headers in alphabetical order. A long User-to-User is cut to fit rather than left out, so put the part your platform needs most at its start. Another header that doesn't fit beside it is left out whole, and the transfer still goes ahead.
  • uui_encoding: "hex" sends User-to-User as hex with ;encoding=hex. Write the value as plain text: it is encoded at the transfer, after variables and placeholders are filled. Text that would make the encoded value longer than 256 bytes is cut first. It also works on a return.
  • headers_in_uri can't be used with a ?User-to-User= written into the address itself; move that value into headers instead.

Add SIP addresses and headers with the Agents API (transfer_destinations), or in an inline call config.

Return the call to your contact center

When your contact center sent the call, the agent can hand it back instead of transferring it: it says goodbye, ThunderPhone hangs up, and the SIP BYE it sends carries routing data your platform reads to decide where the caller goes next. Add a transfer number with "action": "return" and no number:

{
  "name": "Contact center",
  "action": "return",
  "headers": {
    "X-RouteType": "queue",
    "X-RouteValue": "{{queue_id}}",
    "User-to-User": "{{uui}}"
  }
}
  • The prompt names it like any other transfer number, for example "when the caller asks for a person, transfer to Contact center".
  • The BYE can carry X-RouteType, X-RouteValue, X-RouteReason, X-CustomMetaData (the four Five9 reads) and User-to-User, each optional. Values follow the rules for SIP headers on the transfer: at most 256 bytes, call variables allowed, line breaks removed. The BYE also carries X-TP-Handoff-Id (see Pass context to your contact center). A return without headers otherwise just hangs up, and your platform handles the call as after any hangup.
  • It works on the same calls as a transfer to a SIP address: calls to a SIP address and calls on a number on a manual SIP connection. Other calls don't offer it to the agent, and Browser Talk and simulations refuse it. A warm transfer to it runs as a plain return.
  • Nothing is dialed and no REFER is sent. The call ends with the end reason returned_to_platform.
  • The headers ride on your SIP trunk's settings. SIP addresses and manual SIP connections set up before this option existed need a one-time update from ThunderPhone; ask support before you rely on the headers.

Add a return with the Agents API (transfer_destinations), or in an inline call config. The dashboard keeps one you added, and its headers, when you edit the other transfer numbers.

Pass context to your contact center

On a call that can reach your contact center (a transfer to a SIP address, or a return), the agent can say what the caller needs, so the person or flow that takes the call doesn't start from scratch. Use three placeholders in that entry's headers:

PlaceholderWhat it holds
{{handoff_summary}}What the caller needs and what the agent already did, written by the agent at the transfer. At most 300 characters.
{{handoff_intent}}A short label the agent picks, like billing_dispute. At most 60 characters.
{{handoff_id}}An id for this transfer, for the lookup below.
{
  "name": "Support queue",
  "number": "sip:support@pbx.example.com",
  "headers": {
    "X-Summary": "{{handoff_summary}}",
    "X-Intent": "{{handoff_intent|general}}",
    "X-Handoff-Id": "{{handoff_id}}"
  }
}
  • They are filled at the moment of the transfer, not when the call starts. A value the agent left out is empty, or the |default you wrote.
  • Line breaks are removed, and a header whose filled-in value is over 256 bytes is cut to 256 bytes. The transfer goes ahead either way.
  • A return always sends the id as X-TP-Handoff-Id on the BYE, whatever its headers say.
  • On a phone number entry, or on a call that can't reach your contact center, they are ordinary call variables: empty unless the call has variables of those names. Other calls never see the extra fields, so their agents behave as before.

To get the full context, call GET /v1/calls/handoff with your organization's API key and either the id or your platform's own id for the call (external_call_id, from the call-id header it sent with the call). It returns the call's id and status, the summary and intent, the SIP headers and variables the call started with, and once the call has ended, its summary and extracted data.

Which numbers a call can reach

What a transfer can reach depends on which line carries it. A transfer carried on ThunderPhone's line can reach US numbers only, excluding premium-rate and other special-service numbers. A transfer carried by your own carrier can reach any number you saved.

TransferCarried by
Cold transfer on an inbound callThe carrier the call arrived on: ThunderPhone's line for a ThunderPhone number, your carrier for a number on your own carrier.
Cold transfer on an outbound callThe line the call was placed on.
Warm transfer (the new leg to the target)The line the new leg is dialed on.

A number on your own carrier places outbound calls and warm legs only through its connection's own outbound trunk: manual SIP connections with an outbound trunk, Twilio connections with outbound calling set up, and Telnyx connections. Without that trunk the number can't dial out at all, so it supports cold transfer only. Those calls never fall back to ThunderPhone's line.

If your organization has no numbers on its own carrier, saving a number that ThunderPhone's line can't reach fails with an error. Once you connect your own carrier, those numbers save and are marked as working only on transfers your carrier carries (platform_ok: false in the API). One agent can answer on both kinds of number, so the check runs on each call.

Before connecting

Each agent chooses what happens when the person at the destination answers a warm transfer. The setting applies to every warm transfer the agent makes.

SettingAPI valueWhat happensCaller's wait
Introduce on the line (default)introduceThe caller is connected as soon as the destination answers and hears its greeting. When the person who answered has finished, the agent introduces the caller in one or two sentences that both of them hear, then drops. Nobody is asked anything.Ringing only.
Ask firstask_firstThe agent privately briefs the person who answers and asks whether they can take the call. They can ask questions, accept, or decline. If they accept, the caller is connected, the agent introduces the caller on the line, and drops.Ringing plus the private conversation, often 15 seconds or more.
WhisperwhisperThe agent privately tells the person who answers who is calling and why, in one short sentence. Nothing is asked. Then the caller is connected and the agent drops without saying anything on the line.Ringing plus one private sentence.

Use Introduce on the line unless the person answering needs to be able to turn calls away or should hear about the caller before the caller is on the line. Ask first lets them decline, but keeps the caller on hold longer, and voicemail, silence or a decline all send the caller back to the agent. Whisper gives them the context privately without asking; voicemail sends the caller back to the agent.

The briefing

The Briefing setting shapes what the agent tells the person who answers about the caller. With Introduce on the line, that is the introduction both of them hear once they are connected. By default it is very short: who is calling and why, such as "Hi, I have Alex on the line about an order. Alex, go ahead." With Ask first, it is the one private sentence before the caller is connected, ending with whether they can take the call; the introduction after they accept follows the same idea. With Whisper, it is the one private sentence before the caller is connected, such as "I have Alex calling about a late order, putting them through now." It is a statement, not a question.

To change what it says, write a Briefing instruction on the agent (warm_transfer_briefing, up to 300 characters), for example "The caller's name, their account number, and what they need." The agent follows it when it writes each introduction or briefing. Keep it to the few facts that matter: an introduction or Whisper sentence over about 40 words, or an Ask first briefing over about 25 words, is replaced by a short standard line. Leave it blank for the default.

What happens during a warm transfer

With Introduce on the line (the default):

  1. The agent tells the caller it is starting the transfer. The call recording stops, and ThunderPhone starts calling the destination from the number handling the call right away, while the agent is still speaking.
  2. While the destination rings, the caller waits briefly in silence. There is no hold music in this style.
  3. As soon as the destination answers, the caller is already on the line, so the caller hears the greeting from its first word.
  4. When the person who answered has finished and is waiting for a reply, the agent introduces the caller in one or two sentences that both of them hear, such as "Hi, I have Alex on the line about an order. Alex, go ahead." The agent judges this from what they said, not from a pause: a recording notice or "please hold" is not the end of their turn, "How can I help you?" is. If they say nothing, the agent starts after about 3 seconds. If they start talking over the introduction, the agent stops, lets them finish and says it again. After about 15 seconds, for example a long phone menu, the agent introduces the caller anyway. A missing or overly long introduction is replaced by a short standard line, spoken in the call's language.
  5. The agent drops automatically; nothing more is said.

Whoever answers is connected. If it is a voicemail greeting, the agent says nothing into it and drops, so the caller can leave a message. If the destination does not answer, is busy, the call cannot be placed, or the person who answered hangs up before the introduction ends, ThunderPhone ends that call and the caller returns to the agent. The agent receives the outcome and continues the original conversation. It can take a message, suggest another route, or make a cold transfer if the caller still wants one.

With Ask first

  1. The agent tells the caller it is starting the transfer, then the caller hears hold music while the destination rings.
  2. When the destination answers, the agent lets the person who answers finish their greeting first, judged the same way as above. If the agent's consent announcement is on, the person who answers hears it first. Then the agent speaks its briefing and asks whether they can take the call. The caller, on hold, hears none of it.
  3. The person can ask questions, which the agent answers from the conversation so far. If they accept, the caller is connected, the agent introduces the caller in one or two sentences that both of them hear, and drops.
  4. If they decline, don't reply, or the call reaches voicemail, ThunderPhone ends that call and takes the caller off hold. The agent gets the outcome, including any message the person asked it to pass on, and continues with the caller.

In this style the transfer does not stop the call recording. If the agent records calls, the private conversation with the person who answers and the connected call are both recorded. If call recording is off for the agent, neither is.

With Whisper

  1. The agent tells the caller it is starting the transfer, then the caller hears hold music while the destination rings.
  2. When the destination answers, the agent waits for the person who answers to finish their greeting. If the agent's consent announcement is on, the person who answers hears it first. Then the agent tells them, in one short sentence, who is calling and why. It does not ask whether they can take the call. The caller, on hold, hears none of it.
  3. The caller is connected and the agent drops. Nothing is said on the line.
  4. If the call reaches voicemail, the agent hangs up on it and takes the caller off hold. If the destination does not answer, is busy, or the call cannot be placed, the caller also returns to the agent. The agent gets the outcome and continues with the caller.

Recording works as it does for Ask first: if the agent records calls, the private sentence and the connected call are both recorded.

Prompt the agent to transfer well

Put the routing rules in the agent prompt and name each destination exactly as it appears under Transfer numbers. Do not write phone numbers in the prompt; the agent transfers by name. Specify the transfer mode for each route and give the agent a fallback for failed warm transfers.

Transfer policy
 
Confirm the caller's name and reason for calling before any transfer.
 
| Caller needs | Destination | Transfer mode |
| --- | --- | --- |
| A new purchase or plan change | Sales | warm |
| Help with an existing order | Order support | warm |
| The main office | Front desk | cold |
 
For a warm transfer:
- Keep the introduction to one or two short sentences: who is calling and
  why.
- Do not plan to speak after the introduction because you will be dropped.
- If the target does not answer or is busy, tell the caller what happened,
  then offer to take a message or try another route. Do not cold-transfer
  unless the caller asks.

The agent supplies these fields when it calls the built-in action:

FieldWhen usedPurpose
destinationAlwaysThe name of one of the agent's transfer numbers.
modeOptionalcold or warm; defaults to cold.
screen_messageRequired for warmWith Introduce on the line, the introduction the caller and the person who answers both hear once connected. With Ask first, the private briefing the person who answers hears before deciding. With Whisper, the private sentence the person who answers hears before the caller is connected. The caller does not hear either private line.
introductionOptional for warmWith Ask first, the introduction both hear once the person accepts. With Introduce on the line, used only when screen_message is empty. Not offered with Whisper, since nothing is said on the line.

You do not need to define or attach a custom tool named transfer_call. The action's description follows the agent's Before connecting and Briefing settings, so the prompt does not need to repeat them.

When a transfer is refused

ThunderPhone checks every transfer before placing it. It refuses a transfer when the name isn't one of the agent's transfer numbers, when a variable number has no valid value on this call, when a transfer carried on ThunderPhone's line would reach a number outside the US or a premium-rate number, or when the destination is a SIP address or a return and the call didn't come from your own SIP system (see Transfer to a SIP address).

A refused transfer does not end the call. The agent does not announce the transfer, receives the reason (including the list of valid names when the name didn't match), and keeps talking to the caller. Tell it in the prompt what to do instead, such as taking a message. The refusal appears as a tool error in the call's history.

Whisper transfers

“Whisper transfer” is a common industry name for a warm transfer where the destination hears private context before the caller is connected, without being asked whether to take the call. That is the Whisper setting under Before connecting.

This is different from POST /v1/calls/{id}/whisper. That endpoint lets a staff member or owner send private text guidance to the AI agent during a live call. It does not call or brief a transfer destination.

Requirements, limits, and testing

QuestionAnswer
Which destinations can a transfer reach?On a ThunderPhone-provisioned number, only US numbers are supported, toll-free included; Canada, other +1 countries, and premium-rate numbers are not. ThunderPhone refuses any other destination before dialing: the caller stays on the line and the agent gets the reason and keeps talking (see When a transfer is refused). To transfer to other countries, connect your own carrier and take the call on a number imported through it. The transfer then goes out through your carrier, so the carrier must accept call transfers and allow calls to that country. On a Twilio SIP trunk, turn on Call Transfers and Enable call transfers to the PSTN.
Which numbers support warm transfer?A number that can place outbound calls: a verified number imported through a VoIP connection with outbound calling, or a ThunderPhone-provisioned number once your organization's outbound calling request is approved. Other ThunderPhone numbers support cold transfer only. If warm mode is unavailable, the action falls back to cold transfer. Connect a VoIP provider.
Which call types support it?Cold transfer is available on ordinary phone calls. Browser Talk, widgets, simulations, and test calls cannot test a warm handoff. Saved-agent Realtime omits transfer_call; inline Realtime with call_events can request cold transfer.
What about calls without a saved agent?An outbound call or call.incoming response with an inline config, and an inline Realtime session, accept the same transfer_destinations list. With a list, the call transfers only to those names. Without one, the agent keeps a transfer action that takes a phone number, and calls on ThunderPhone numbers can still reach only US numbers.
How do I test it?Check routing language in Browser Talk or a simulation: you see which name the agent picks, or the refusal. Both always refuse a transfer to a SIP address or a return, since they don't come from your SIP system; that refusal doesn't mean the entry is misconfigured. Then test the complete handoff with a real phone call to the agent's number. Any ThunderPhone or imported number works for a cold transfer; a warm transfer needs a ThunderPhone number whose outbound calling request is approved, or an imported number with outbound calling.
How long does the destination ring?ring_duration_seconds accepts 5–120 seconds. null uses 45 seconds for warm transfer and the carrier's default for cold transfer. Ordinary outbound calls default to 60 seconds and never ring for less than 14 seconds. The setting does not change inbound ringing.
What if my carrier refuses a cold transfer?Normally your carrier hands the call over to the destination. Some carrier connections refuse transfers, for example a Twilio trunk without call transfer (SIP REFER) enabled. When the call's number can place outbound calls, ThunderPhone then connects the call itself: it dials the destination, the caller hears ringing until it answers, and the agent leaves without an introduction. Recording stops at the transfer. If nobody answers, or the number cannot place outbound calls, the call ends. A refused transfer to a SIP address always ends the call. Your organization's admins get an email the first time a connection refuses a transfer. When you connect a Twilio account in the dashboard, Allow call transfers (on by default) turns transfers on for its SIP trunk. For a Twilio connection you already have, use Turn on call transfers on the VoIP connections page. Other carriers have the setting in their own portal.
How long can the human bridge last?Up to 4 hours after the agent connects the two people. With Introduce on the line the bridge is not recorded: the recording stops when the transfer starts. With Ask first and Whisper it is recorded when the agent records calls.
What happens on failure?In a warm transfer, if the target does not answer, is busy, or the outbound leg fails, the caller returns to the agent with the result. The prompt should define the next step. A cold transfer has already ended the agent's part of the call, so a failed cold transfer ends the call.
What does it cost?There is no transfer-tool fee. Normal call charges include the warm-transfer flow until the agent leaves. After the AI leaves a warm transfer, ThunderPhone keeps the two people connected and bills that time at a per-minute bridge rate; see Pricing. A cold transfer stops metering at the handoff. When ThunderPhone connects a refused cold transfer itself, the connected time is billed at the same bridge rate. On imported VoIP numbers, your carrier can continue billing the transfer leg while the two people remain connected.

Webhooks and end reasons

The completion payload records the destination in transfer_number and uses one of these end reasons:

end_reasonMeaning
ai_transferThe agent initiated a cold transfer.
ai_warm_transferThe destination answered a warm transfer, the parties were connected, and the agent dropped.
returned_to_platformThe agent returned the call to your contact center. transfer_number is not set.

An unanswered, busy, or failed warm attempt is not a completed transfer. The caller returns to the agent, so the eventual end reason reflects what ends the original call.

For a successful warm transfer, the completion event and the AI call's end reason are recorded when the AI leaves, right after the parties are connected. With Introduce on the line, the recording covers the call up to the transfer and ends there (recording_stopped_at); the conversation between the two people is neither recorded nor transcribed. With Ask first and Whisper, the recording (when the agent records calls) continues until the connected call ends.

Legacy completion webhook

A legacy single-URL webhook can delay a cold transfer for up to five minutes by returning {"transfer_ready": false} and later updating the call's transfer-ready state. Webhook endpoint deliveries and warm transfers do not use this coordination.

See Completion webhooks for the complete payload and legacy behavior.

Troubleshooting

The call drops when the agent transfers

Check the destination's country. Calls on ThunderPhone numbers support transfers to US numbers only. The carrier can reject a transfer to another country, which disconnects the caller, and the call still ends with ai_transfer. To reach other countries, connect your own carrier and route the call through a number imported from it. The carrier must accept call transfers and allow calls to that country. On a Twilio SIP trunk, turn on Call Transfers and Enable call transfers to the PSTN.

The agent makes a cold transfer instead of a warm one

Check that the call is using a number that can place outbound calls: a verified, imported VoIP number with working outbound routing, or a ThunderPhone number with approved outbound calling. Warm mode is removed from the tool options for other numbers and for non-phone sessions. Also make the mode explicit in the prompt's routing table.

The target never answers

Confirm that your VoIP trunk can place outbound calls to the destination and that its authentication and routing rules are current. Check the number saved under Transfer numbers. Increase ring_duration_seconds if 45 seconds is too short for a warm transfer.

The target reaches voicemail

With Introduce on the line, the caller is connected to whatever answers, including a voicemail greeting. The agent recognizes the greeting, says nothing into the mailbox and drops, and the caller can leave a message. With Ask first and Whisper, the agent hangs up on the voicemail and the caller returns to the agent. If a destination often goes to voicemail, give the agent another route for it in the prompt.

A cold transfer waits before connecting

If you use the legacy single-URL webhook, check whether it returned {"transfer_ready": false}. The caller hears hold music while ThunderPhone waits. Update the call's transfer-ready state when the destination is prepared.

The agent says it can't transfer

Check that the agent has transfer numbers and that the prompt uses the same names. An agent with no transfer numbers has no transfer action at all. If the transfer rides ThunderPhone's line (see Which numbers a call can reach), the destination must be a US number that is not premium-rate. For a variable number, check that the call's variables include a valid phone number for it.

Deploy fails with a transfer number error

Every deploy re-checks the saved transfer numbers. A non-US number saved while your organization had a number on its own carrier fails the check once that organization no longer has one. Remove the number, or connect your own carrier again.

Staff whispering does not start a transfer

Live-call whispering only coaches the AI. Add transfer instructions to the agent prompt and let it call transfer_call. See Monitor live calls.