Call Transfer Tool
Configure blind call transfers to phone numbers or SIP endpoints, allowing your voice agent to hand off calls to human agents or other destinations.
What you'll learn
- How blind (unattended) call transfers work
- Which telephony providers support transfers
- How to configure the transfer destination, pre-transfer message, and timeout
- What happens during and after the transfer
The Call Transfer tool performs a blind transfer during a live call. When the LLM decides a transfer is needed (based on the tool's description and conversation context), the platform initiates the transfer to the configured destination and disconnects the AI from the call.
How It Works
The caller says something that matches the tool's description (e.g., "Can I speak to a manager?").
The LLM triggers the transfer tool.
If a pre-transfer message is configured, it is spoken or played first.
The platform initiates a blind transfer to the destination number or SIP endpoint.
The AI disconnects from the call. The caller is now connected directly to the destination.
This is a blind transfer (also called an unattended transfer). The call is forwarded without first confirming the destination is available. If the destination does not answer within the timeout period, the call may go to voicemail or disconnect depending on the telephony provider's behavior.
Supported Telephony Providers
Call transfer support depends on the telephony provider handling the call:
| Provider | Phone Number Transfer | SIP Transfer | Destination Format | Bridging Mechanism |
|---|---|---|---|---|
| Twilio | Yes | No | E.164 (e.g., +14155551234) | Conference: destination dials in, then the A-leg's TwiML is updated to join the same conference. |
| Vobiz | Yes | No | E.164 (e.g., +919876543210) | A-leg redirect: the active call leg is repointed to a new <Dial> XML answer URL, no conference needed. |
| Plivo | Yes | No | E.164 | A-leg redirect (legs=aleg): the live call is repointed to a new <Dial> XML answer URL. |
| Telnyx | Yes | No | E.164 | Linked dial: the destination is dialed as a new leg sharing the call session (link_to) and auto-bridged to the caller at answer (bridge_on_answer). |
| Vonage | Yes | No | E.164 | Named conversation: the destination is dialed into a conversation "room", and the caller's leg is moved into the same conversation when they answer. |
| Cloudonix | Yes | No | E.164 | Application switch (cold transfer): the live session's voice application is replaced with a <Dial> to the destination. |
| Tata Smartflo | Yes (blind) | No | E.164 | Platform-side blind transfer (/v1/call/options type 4) — the Tata platform owns the bridge. |
| Asterisk ARI | Yes (via SIP) | Yes | PJSIP/SIP endpoints (e.g., PJSIP/1234) | Native ARI bridge swap. |
WebRTC-only calls (browser calls) do not support call transfer because there is no telephony leg to transfer. Call transfer requires an active telephony connection — all eight telephony providers support it.
Caller-ID on the transfer leg
The destination always sees the agent's phone number on caller ID, not the original caller's number. The platform picks this up automatically from the call's direction:
- Outbound calls → the number the agent dialed from is used as the CID.
- Inbound calls → the DID the user dialed into is used as the CID.
This matches every business-friendly use case: a caller who reaches your support line shouldn't have their own number forwarded to whoever you transfer them to.
Configuration Reference
Common Fields
| Field | Type | Constraints | Description |
|---|---|---|---|
name | string | Max 255 characters | Descriptive name for the tool (e.g., "Transfer to Support"). The LLM sees this name. |
description | string | -- | Tells the LLM when to transfer the call. This is the primary signal for tool selection. |
Example description:
Use this tool when the caller asks to speak with a human agent, requests to be transferred, or when you cannot resolve their issue and they need human assistance.
Transfer Destination
| Field | Type | Format | Description |
|---|---|---|---|
destination | string | E.164 or SIP | The phone number or SIP endpoint to transfer to. |
The destination supports two formats:
| Format | Pattern | Example | Used With |
|---|---|---|---|
| Phone number | E.164 international | +14155551234 | All telephony providers |
| SIP endpoint | Asterisk-style SIP URI | PJSIP/1234 or SIP/[email protected] | Asterisk ARI |
You can toggle between phone number and SIP mode in the dashboard using the link below the destination input.
Phone numbers must be in E.164 format (starts with +, followed by country code and number, no spaces or dashes). Example: +14155551234 for a US number, +442071234567 for a UK number.
Pre-Transfer Message
Controls what happens before the transfer is initiated.
| Field | Type | Valid Values | Default | Description |
|---|---|---|---|---|
messageType | string | "none", "custom", "audio" | "none" | Whether to play a message before transferring. |
customMessage | string or null | -- | null | Text to speak before transfer (when messageType is "custom"). |
audioRecordingId | string or null | -- | null | Recording ID of a pre-recorded audio file (when messageType is "audio"). |
Message Options
| Option | Behavior |
|---|---|
No Message ("none") | Transfer immediately without any announcement. |
Custom Message ("custom") | Speak a text message before transferring. The text is synthesized by the agent's TTS provider and spoken as-is. Example: "Please hold while I transfer your call." |
Pre-recorded Audio ("audio") | Play a pre-recorded audio file before transferring. Use this for polished hold messages or multilingual announcements. |
When using a custom text message, the text is synthesized by whatever TTS provider is configured on the agent. Phrase it carefully for multilingual use cases -- the TTS engine will read it exactly as written.
Transfer Timeout
| Field | Type | Range | Default | Description |
|---|---|---|---|---|
timeout | integer | 5 -- 120 seconds | 30 | Maximum time to wait for the transfer destination to answer. |
If the destination does not answer within the timeout, the behavior depends on the telephony provider:
| Provider | Timeout Behavior |
|---|---|
| Twilio | Call may go to voicemail or disconnect, depending on Twilio's configuration. |
| Asterisk ARI | Call is typically disconnected. Behavior depends on your Asterisk dialplan. |
Tool Definition Schema
The full JSON schema for a Call Transfer tool definition:
{
"schema_version": 1,
"type": "transferCall",
"config": {
"destination": "+14155551234",
"messageType": "custom",
"customMessage": "Please hold while I transfer your call to our support team.",
"audioRecordingId": null,
"timeout": 30
}
}Example: Transfer to Support Team
Configuration:
- Name:
Transfer to Support - Description:
Use this tool when the caller asks to speak with a human agent, requests to be transferred to support, or when you cannot resolve their issue. - Destination:
+18005551234 - Message Type: Custom
- Custom Message:
I'm transferring you to our support team now. Please hold for a moment. - Timeout:
30seconds
What happens during a call:
- Caller: "I'd like to speak with someone about my billing issue."
- LLM recognizes the transfer intent and triggers the tool.
- Agent says: "I'm transferring you to our support team now. Please hold for a moment."
- The platform initiates a blind transfer to
+18005551234. - The AI disconnects. The caller hears ringing and is connected to the support team.
What Happens After the Transfer
The platform waits for the destination to actually answer before letting go of the call:
- The AI agent stays on the line while the destination's phone is ringing. The pipeline does not end until the destination picks up — so a no-answer or busy doesn't kill the original call mid-ring.
- The caller hears ringing as the transfer destination's phone rings — the platform's hold music where the caller stays on our stream (Twilio, Telnyx, Vonage, ARI), or carrier-delivered hold/ringback for redirect-style providers (Vobiz, Plivo, Cloudonix, Smartflo).
- If the destination answers, the bridge is set up provider-side (conference for Twilio, linked dial for Telnyx, conversation for Vonage, A-leg redirect for Vobiz/Plivo), the agent's pipeline closes cleanly, and the caller is now connected directly. This is a normal phone call between the caller and the destination.
- If the destination does not answer (busy, no-answer, failed, timed out), the agent does not drop the call. Instead the LLM is told the transfer failed and can verbalize the outcome — "Sorry, no one picked up. Is there anything else I can help with?" — so the conversation continues.
- The call record is updated with the transfer event, including the destination number, timestamp, and the result.
`end_call` is gated during an in-flight transfer
If the LLM tries to call end_call in the same response as transfer_call, the platform refuses the end_call until the transfer reaches a terminal state. This prevents the pipeline from closing prematurely between "transfer initiated" and "destination answered".
Because this is a blind transfer, the AI doesn't supervise the new call once the destination answers and the bridge takes over. If you need guaranteed human availability, consider validating agent schedules before offering transfer as an option (via your tool description).
Recording the Transfer
Two flags decide what gets captured on the carrier's side. Both default to a sensible value, so you don't have to set them unless you want to override. The transfer tool's Record Transferred Call toggle in the dashboard maps to recordTransfer.
| Flag | Where | Default | What it controls |
|---|---|---|---|
recordInProviderConsole | Call config (agent / snapshot) | false (off — Zoxa already records the call itself) | When true, the carrier records the entire call (pre + post transfer) into one file in their console. |
recordTransfer | transferCall tool config | true (on) | When true and recordInProviderConsole is false, the carrier records just the transfer leg (from destination answer to hangup). |
With both defaults untouched, a transfer is tail-recorded out of the box: recordInProviderConsole defaults off and recordTransfer defaults on, so an API caller who never mentions either flag gets the transferred conversation recorded and attached to the call automatically.
Automatic download to the call record
When a transfer-leg recording is captured, the platform fetches it from the carrier after the transferred conversation ends and stores it with the call:
- At transfer time the call row records the transfer correlation (
transfer_id, provider, destination, B-leg ID). - When the carrier signals the recording is ready (Twilio's conference
recordingStatusCallback, Vobiz/Plivo<Record>completion callbacks, Telnyx'scall.recording.savedevent, Vonage'srecordevent, Cloudonix's Dial recording callback, ARI teardown, Smartflo CDR), a background job downloads the media with the call's own provider credentials. - The file is re-hosted in platform storage and appended to the call's recordings as a
kind: "transfer"entry. - The call detail page then shows a second player — Transferred Call Recording — below the main one, with its own playback-speed control and download. The
GET /calls/{callId}response includes the entry (as a signed URL) only when the recording exists. - If the call has a webhook configured, a
call.transfer_recording.completedevent fires with the recording link and the samecallIdas the earlier lifecycle events — so your receiver can attach the late artifact to the right call.
Because the transferred conversation can continue long after the agent leaves the call, the transfer recording typically appears in call history minutes after the main recording. If the carrier never produces a file (for example the transfer was too short) or the file can't be downloaded, the entry never appears — and a configured webhook receives call.transfer_recording.failed with the reason instead.
Cascade
recordInProviderConsole | recordTransfer | What lands in the carrier console |
|---|---|---|
true | (any) | One full-call recording. Captures pre-transfer (agent ↔ user) and post-transfer (user ↔ destination) in a single file. The per-transfer flag is ignored to avoid producing a second, redundant recording of the transfer leg. |
false | true | One transfer-leg recording. Captures the user ↔ destination conversation from the moment the destination answers. |
false | false | No carrier-side recording. (zoxa's own S3 recording is independent of these flags.) |
What the caller hears while the destination rings
After the optional pre-transfer message, the caller hears the platform's hold music until the destination answers, rejects, or the transfer times out — at which point the music stops immediately (they're either bridged to the human, or the agent comes back to explain the failure). The agent does not hear the caller during this window (user input is muted for the duration of the transfer attempt).
How the hold music is delivered depends on the provider:
| Provider | Hold music mechanism |
|---|---|
| Twilio | Played by the agent pipeline (the caller stays on our media stream until the destination answers) |
| Telnyx | Played by the agent pipeline (the caller stays on our stream until Telnyx bridges the legs at answer) |
| Vonage | Played by the agent pipeline (the caller stays on our websocket leg until moved into the transfer conversation) |
| ARI (Asterisk) | Played by the agent pipeline (same — the bridge swap happens on answer) |
| Vobiz | Played by the carrier via the Dial dialMusic XML attribute, fetched from the platform |
| Plivo | Played by the carrier via the Dial dialMusic XML attribute, fetched from the platform |
| Cloudonix | Not controllable — the application switch detaches the caller immediately; they hear standard carrier ringback |
| Smartflo | Not controllable — the blind transfer hands the bridge to the Tata platform, which plays its own ringback |
Pre-answer ring audio
No provider's transfer recording captures the pre-answer ring audio on the transfer leg — recording starts the instant the destination picks up. The "from ringing" you might see in other platforms typically refers to Twilio's machine-detection feature, not to ring-back capture.
Provider support matrix
The two flags above only do something when the underlying telephony provider supports the relevant primitive. Use this table to set realistic expectations per provider.
| Provider | Call transfers | recordInProviderConsole (full call) | recordTransfer (transfer leg) | Auto-attached to call record |
|---|---|---|---|---|
| Twilio | ✓ | ✓ via POST /Calls/{Sid}/Recordings.json | ✓ via <Conference record="record-from-start"> + recording status callback | ✓ (MP3, downloaded when Twilio reports the file ready) |
| Vobiz | ✓ | ✓ via POST /v1/Account/{id}/Call/{uuid}/Record/ | ✓ via <Record startOnDialAnswer="true"> before the <Dial> (recording starts at answer, so ring/hold audio is excluded) | ✓ (MP3, downloaded on the RecordStop callback) |
| Plivo | ✓ | ✓ via POST /v1/Account/{id}/Call/{uuid}/Record/ | ✓ via <Record startOnDialAnswer="true"> before the <Dial> (recording starts at answer, so ring/hold audio is excluded) | ✓ (MP3, downloaded on the Record completion callback) |
| Telnyx | ✓ | ✓ via POST /v2/calls/{id}/actions/record_start | ✓ via record: record-from-answer on the transfer dial (scoped to the destination leg) | ✓ (MP3, re-resolved through the Recordings API — Telnyx webhook URLs expire in 10 minutes) |
| Vonage | ✓ | Not available (Vonage mid-call NCCO update would drop the active connect) | ✓ via a record action on the destination's NCCO (spans the whole transferred conversation) | ✓ (MP3, JWT-authenticated download; Vonage retains recordings 30 days) |
| Cloudonix | ✓ (cold) | Not available (live-call recording endpoint not documented) | ✓ via <Dial record="record-from-answer"> on the transfer CXML | ✓ (downloaded from the Dial recording callback URL) |
| ARI (Asterisk) | ✓ | Partial — channel recording via POST /ari/channels/{id}/record (captures one side only per the Asterisk docs note about channel vs bridge recording) | ✓ via bridge recording (POST /ari/bridges/{id}/record, mixed two-party audio) | ✓ (WAV, pulled from Asterisk's stored recordings at teardown) |
| Smartflo | ✓ (blind) | n/a (recording is a platform-side account setting; no per-call API) | Partial — no per-leg control; when the toggle is on, the platform CDR recording is fetched after the call. Per Tata's docs it covers the initial leg and the first transfer in one file. | ✓ (MP3, when the account has recording enabled) |
Where full-call carrier recording is marked Not available (Vonage, Cloudonix), setting recordInProviderConsole=true is logged as "skipped" by the runner and the call proceeds normally — the flag is harmless, just inactive. zoxa's own S3 recording is independent of all of this and continues to work on every provider.
What gets captured per cascade row (provider-aware)
Combining the cascade with the provider matrix, the practical outcomes per provider:
- Twilio, Vobiz, Plivo, Telnyx: full cascade behavior — both flags do exactly what they describe, and the transfer-leg file is auto-attached to the call record.
- Vonage, Cloudonix:
recordTransferworks and the file is auto-attached;recordInProviderConsole(full-call carrier recording) is not available, so zoxa's S3 recording is the only full-call copy. - ARI:
recordInProviderConsole=truerecords the user's channel only (Asterisk's note about channel vs bridge); the transfer leg, however, is recorded properly as mixed bridge audio whenrecordTransferis on. - Smartflo: recording is controlled on the Tata side (account/route settings), not per call. With
recordTransferon, the platform fetches the CDR recording after the call — note it spans the whole call (initial leg + first transfer), since that is how Tata records.
Where to find the recording
The transferred-call recording appears on the call detail page as a second player under the main recording, once the background fetch completes. The carrier-side IDs remain visible in the tool-call Outcome block on the Transcript tab:
Transfer SID→ the transfer leg's carrier ID (Twilio B-leg CallSid / Vobiz BLegCallUUID), useful for cross-referencing in the carrier console.Original SID→ the original caller's leg, useful whenrecordInProviderConsoleistrue(the full-call recording is indexed by this).
Transfer Outcome Reasons
Every transfer ends with one of the following reason values inside the transferCall tool result. The same value is emitted on the call.completed webhook's tool entry. Two categories — carrier-reported outcomes and tool-side errors — with different semantics.
Carrier-reported outcomes
These mean the transfer attempt ran end-to-end and the telephony provider reported a definitive result. The tool itself succeeded; the destination just didn't get bridged. Use these to drive your post-transfer business logic (retry, queue, voicemail fallback, etc.).
reason | Twilio CallStatus source | Plivo/Vobiz DialStatus source | Meaning |
|---|---|---|---|
no-answer | no-answer | no-answer, timeout | The destination's phone rang past the configured timeout without being picked up. |
busy | busy | busy | The destination's line was busy. |
failed | failed | failed | The carrier couldn't route the call — typically an invalid number or unreachable destination. |
canceled | canceled | cancel | The transfer leg was cancelled before the destination answered (e.g., the caller hung up the original leg during ring). |
All four use Twilio's canonical spelling (no-answer with hyphen, canceled with one L) so a single parser handles both providers. The accompanying transfer_call_sid lets you cross-reference the carrier's console.
Tool-side errors
These indicate the transfer attempt never completed. Either the platform refused to dial, the provider rejected our request, or the carrier never reported back. Treat them as bugs to triage, not customer-facing outcomes.
reason | Source | Meaning |
|---|---|---|
execution_error | Tool handler caught an unexpected exception | Investigate logs by call_id. |
provider_not_supported | The telephony provider's supports_transfers() returned False, or its credentials failed validate_config() | Misconfigured provider or missing capability. |
web_call_not_supported | A transferCall tool was invoked on a web (browser) call | Don't expose the tool on web calls. |
no_destination | The tool was registered without a destination field | Fix the agent config. |
transfer_in_progress | A concurrent transfer was already running for this call | Should be rare — the LLM emitted two transfer_call requests in parallel. |
timeout | The wait for the carrier's status callback timed out (default 30s) | The carrier never reported a definitive outcome. Check that your provider can reach our webhook URL and that Redis pub/sub is healthy. |
Status interpretation
result.status === "success"plusaction: "destination_answered"— the destination picked up. The agent's pipeline is closing; the caller is now in the human conversation.result.status === "transfer_failed"plus any of the carrier-reported reasons above — the tool succeeded but the bridge didn't form. The agent continues; the LLM verbalizes the outcome to the caller.result.status === "transfer_failed"plus a tool-side error reason — the tool itself failed. The agent continues; treat as an internal alert.
Best Practices
- Write clear trigger descriptions -- the LLM decides when to transfer based on the description. Be explicit about what phrases or scenarios should trigger a transfer.
- Always use a pre-transfer message -- even a simple "Please hold while I transfer your call" prevents the caller from experiencing unexpected silence.
- Set appropriate timeouts -- 30 seconds is usually sufficient. Increase for call centers with longer queue times.
- Use E.164 format -- always include the country code with a
+prefix. Never use local number formats. - Test the destination number -- verify the number is correct and reachable before deploying the tool to production.
HTTP API Tool
Configure tools that make HTTP requests to external APIs during voice calls, allowing your agent to fetch data, submit forms, and trigger actions in real time.
End Call Tool
Configure the End Call tool to let your voice agent programmatically hang up with an optional goodbye message and reason tracking.