The Call Transfer function (type: "transfer_call") moves the caller to another phone number or SIP URI. Tried supports three transfer types: Cold, Warm and Agentic warm. Transfers run on LiveKit SIP and need a real phone call. Needs keys Transfers need LiveKit Cloud SIP and an outbound trunk (Telnyx or Twilio).
When to use which type#
| Type | Caller experience | Use it when |
|---|---|---|
| Cold | Hears ringback, then the destination | The destination can take any call without a briefing |
| Warm | Hears hold music while the agent reaches a person and briefs them | The destination needs context, or you must wait through a hold queue or phone menu |
| Agentic warm | Hears hold music while a separate transfer agent talks to the destination and decides | The destination may decline, and you want a full conversation with them before connecting |
Configure in the dashboard#
- Open the agent, expand Functions, click Add and choose Call Transfer.
- Set Name and Description (when to transfer). The default description is "Transfer the call to a human agent."
- Under Transfer to, pick Static number or Dynamic (prompt).
- Optional: set Extension (optional).
- Pick a Transfer type and fill in its options.
- Optional: turn on Show transferee as caller and add Custom SIP headers.
- Click Save.
Destination#
transfer_destination is one of two shapes.
| UI | Shape | Behaviour |
|---|---|---|
| Static number | { "type": "predefined", "number": "+61290000000" } |
Always dials this number. {{variables}} are rendered, so {{transfer_number}} works |
| Dynamic (prompt) | { "type": "inferred", "prompt": "..." } |
The LLM picks the number. Your prompt becomes the description of a number argument on the tool |
The destination can be an E.164 number or a SIP URI (sip: or sips:). If the LLM calls a dynamic transfer without a number, the worker makes one extra LLM call to infer it from your prompt. If no destination results, the transfer fails with "Transfer failed: no destination."
Example dynamic prompt: "Transfer to the billing team (+61 2 9000 0001) if the caller asks about invoices, otherwise to reception (+61 2 9000 0000)."
Extension#
transfer_destination.extension holds DTMF digits pressed after the destination answers. Allowed characters are 0-9, *, #, w and , (1 to 32 characters). w is a half-second pause, so ww204 waits one second then dials 204.
- With SIP INVITE (and warm or agentic warm) transfers, LiveKit SIP sends the digits as DTMF after answer.
- With SIP REFER, the extension is appended to the REFER target:
;postd=ontel:URIs,;x-extension=on SIP URIs. Whether it is dialled depends on the carrier.
Cold transfer#
transfer_option: { "type": "cold_transfer", "cold_transfer_mode": "sip_invite", "show_transferee_as_caller": false } is the default.
| UI label | Field | Type | Default | Description |
|---|---|---|---|---|
| Mode | cold_transfer_mode |
"sip_invite" or "sip_refer" |
"sip_invite" |
See below |
| Ring duration | transfer_ring_duration_ms |
integer, 5,000 to 300,000 | 30,000 at runtime | How long the destination may ring before the transfer counts as failed |
| Show transferee as caller | show_transferee_as_caller |
boolean | false |
Present the caller's number to the destination |
Modes:
- SIP INVITE (platform bridges the call): the worker dials the destination into the call's media room while the caller hears a ringtone. When the destination answers, the room bridges the two and the agent leaves. The caller and destination stay connected through the room.
- SIP REFER (carrier redirects the call): the worker sends a SIP REFER for the caller's leg and leaves after one second. The carrier connects the caller to the destination, so success depends on the carrier supporting REFER. Ring duration and custom headers are passed on the request.
Warm transfer#
{
"type": "warm_transfer",
"public_handoff_option": { "type": "prompt", "prompt": "Introduce the caller and summarise their issue." },
"private_handoff_option": { "type": "prompt", "prompt": "Tell the agent the caller wants a refund for order {{order_id}}." },
"agent_detection_timeout_ms": 45000,
"on_hold_music": "relaxing"
}What happens:
Hold#
The caller hears hold music on a track only they can hear.
Dial#
The worker dials the destination into the room with the caller muted from it. Extension and phone-menu digits are sent after answer.
Wait for a person#
Unless Skip human detection is on, the worker listens to the destination's audio for a human voice, through hold queues, for up to Human detection timeout. If no person is detected, the transfer fails with reason human_not_detected.
Whisper#
The Private whisper is spoken to the destination only. The caller still hears hold music.
Bridge#
Hold music stops, the caller and destination hear each other, an optional two-tone cue plays, and the Public handoff is spoken to both. The agent leaves.
| UI label | Field | Type | Description |
|---|---|---|---|
| Public handoff | public_handoff_option |
{type: "prompt" or "static_message", prompt?, message?} |
Said to both parties after bridging |
| Private whisper | private_handoff_option |
same | Said to the destination only, before bridging |
| Ring duration | transfer_ring_duration_ms |
integer, 5,000 to 300,000 | Default 30 s at runtime |
| Human detection timeout | agent_detection_timeout_ms |
integer, 1,000 to 600,000 | Default 30 s at runtime |
| Hold music | on_hold_music |
see below | Played to the caller while connecting |
| Navigate the destination's phone menu | ivr_option |
{digits?, prompt?, timeout_ms?} |
Digits to press after the destination answers |
| Skip human detection | opt_out_human_detection |
boolean | Bridge as soon as the destination answers, even if it is a machine |
| Bridge audio cue | enable_bridge_audio_cue |
boolean | Two short tones when the caller joins. On at runtime unless set to false; the dashboard shows the toggle off while the field is unset |
| Show transferee as caller | show_transferee_as_caller |
boolean | See caller ID |
Handoff text with type: "prompt" is written by the LLM from your prompt and the call transcript (one or two short sentences). With type: "static_message", message is spoken as written. Both render {{variables}}. The dashboard saves handoffs as prompts.
Phone menu at the destination#
Turn on Navigate the destination's phone menu and fill in Digits to press (for example ww2w1, where w is a half-second pause). The digits are sent as DTMF after the destination answers. If an extension is also set, the extension goes first, then a pause, then these digits. Digits to press accepts 0-9, *, #, w and , (up to 64 characters).
The worker dials ivr_option.digits. The Or describe the route and Give up after fields (ivr_option.prompt, ivr_option.timeout_ms) are saved with the tool, but the current worker only sends the digits.
Agentic warm transfer#
An agentic warm transfer dials the destination and hands that leg to a second agent, the transfer agent. The caller hears hold music. The transfer agent talks to the destination, then calls Bridge Transfer to connect the caller or Cancel Transfer to give up. See Transfer agent tools.
{
"type": "agentic_warm_transfer",
"transfer_agent": { "agent_id": "agent_7c1e...", "agent_version": 2 },
"transfer_timeout_ms": 90000,
"action_on_timeout": "cancel_transfer",
"on_hold_music": "uplifting"
}| UI label | Field | Type | Description |
|---|---|---|---|
| Transfer agent | transfer_agent.agent_id, transfer_agent.agent_version |
string, integer | Only agents with Transfer agent turned on are listed |
| Timeout | transfer_timeout_ms |
integer, 10,000 to 600,000 | How long the transfer agent may take. Default 120 s at runtime |
| On timeout | action_on_timeout |
"bridge_transfer" or "cancel_transfer" |
Bridge the call or Cancel the transfer when the timeout passes. Unset counts as cancel at runtime; the dashboard sets Bridge the call when you pick this type |
| Ring duration | transfer_ring_duration_ms |
integer | As above |
| Hold music | on_hold_music |
see below | |
| Show transferee as caller | show_transferee_as_caller |
boolean |
The transfer agent runs as its own call record (an outbound leg in the same room) with these dynamic variables: original_transcript, original_call_id, transfer_destination and caller_number, plus the original call's request and collected variables. A transfer agent without its own prompt uses a built-in prompt that briefs the destination and asks whether they can take the call.
If the transfer is cancelled, the destination leg is removed and the original agent gets "Transfer failed (cancel_transfer)." so it can tell the caller.
Hold music#
on_hold_music is one of none, relaxing, uplifting, ringtone, or an uploaded asset id asset_<24 hex>. In the dashboard, Hold music lists None, Relaxing, Uplifting, Ringtone and your uploads; Upload adds an audio file (10 MB maximum). Uploaded files are stored with POST /create-asset (kind hold_music) and looped during the transfer. If an asset cannot be loaded, the worker falls back to the relaxing track.
When on_hold_music is not set, a warm transfer plays the ringtone track and an agentic warm transfer plays the relaxing track. The dashboard shows None while the field is unset, so pick None again (or set "none" through the API) to save it explicitly for no hold audio.
Caller ID#
Show transferee as caller (show_transferee_as_caller) controls the number the destination sees:
- Off: the agent's own number on this call.
- On: the caller's number.
Showing the caller's number on an outbound leg can be rejected by carriers that enforce caller ID ownership.
Custom SIP headers#
Custom SIP headers (custom_sip_headers on the tool) are added to the transfer INVITE or REFER. Values can use {{variables}}, for example X-Account-Id: {{account_id}}. Transfer nodes in flows do not have this field.
Result, events and webhooks#
| Outcome | What the LLM gets | Call |
|---|---|---|
| Success | Nothing more: the agent leaves | Ends for the agent with disconnection_reason: "call_transfer" |
| Failure | "Transfer failed (reason)." and a chance to reply | Continues with the agent |
Failure reasons from SIP: busy, no_answer, invalid_destination, declined, failed. Warm and agentic transfers can also fail with human_not_detected or cancel_transfer. A caller that is not a SIP participant (a web call) fails with caller_not_sip.
The worker emits transfer_started (with transfer_type), then transfer_bridged or transfer_cancelled (with reason). These are also webhook events: they are delivered to the agent's webhook URL (or the workspace URL) only when the event name is in the agent's webhook_events. The payload is { event, call, transfer: { destination, transfer_type?, reason?, timestamp } }.
Gotchas#
- Say something before transferring. The agent stops talking once the transfer starts; put "Tell the caller you are transferring them, then call transfer_to_billing" in the prompt, or use a call transfer node with an instruction.
- Web calls cannot transfer. Test transfers on a real phone number.
- REFER hands the call to the carrier: Tried cannot report whether the destination answered.
- The dashboard shows the ring and timeout fields empty with a placeholder. Empty means the runtime default applies.