Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 53 additions & 1 deletion examples/warm-transfer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ The `WarmTransferTask` from `livekit.agents.beta.workflows` simplifies the warm

```python
result = await WarmTransferTask(
target_phone_number=SUPERVISOR_PHONE_NUMBER,
sip_call_to=SUPERVISOR_PHONE_NUMBER,
sip_trunk_id=SIP_TRUNK_ID,
chat_ctx=self.chat_ctx, # Provides conversation history to the supervisor
)
Expand All @@ -47,3 +47,55 @@ result = await WarmTransferTask(
```python
python warm_transfer.py dev
```

## Preserve the inbound caller's number with Telnyx

A warm transfer creates a new outbound call to the supervisor. To let the
supervisor identify the customer, an application can preserve the customer's
caller ID instead of presenting its business number. The existing `sip_number`
and `sip_headers` options support this with a Telnyx SIP trunk.

For an inbound call from customer A to your Telnyx number B, followed by a
transfer to supervisor C, [Telnyx requires](https://support.telnyx.com/en/articles/13117410-how-external-call-transfers-work):

- The original A-to-B call must still be active when dialing C.
- The outbound call must present A as the caller and include a SIP `Diversion`
header identifying B.

In `SIPSupportAgent._start_transfer`, use the following options when creating
the task. `inbound_customer_number` and `inbound_telnyx_number` are application
variables: retrieve the original caller and called number from trusted,
server-side context for this specific inbound call. Normalize both to E.164
before constructing the header. `SIP_TRUNK_ID` must select your configured
Telnyx outbound trunk.

```python
return await WarmTransferTask(
sip_call_to=SUPERVISOR_PHONE_NUMBER,
sip_trunk_id=SIP_TRUNK_ID,
sip_number=inbound_customer_number,
sip_headers={
"Diversion": f"<sip:{inbound_telnyx_number}@sip.telnyx.com>;reason=unconditional",
},
chat_ctx=self.chat_ctx,
extra_instructions=SUMMARY_INSTRUCTIONS,
)
```

This uses the existing SIP warm-transfer flow; it does not require a CallToken
or a Telnyx-specific SDK flag. Keep the customer's original call connected
during consultation. Applications that want their business caller ID can keep
the existing example configuration.

### Verify and troubleshoot

Call B from phone A, request a transfer to phone C, then check the number shown
on C and complete the consultation and transfer. Caller-ID presentation also
depends on the receiving network. This snippet illustrates configuration; it
does not by itself verify carrier acceptance.

For `403 Unverified origination number D51`, check that the A-to-B call is still
active, the outbound caller number matches A, and `Diversion` identifies the
actual Telnyx number B that received the call. Inspect the outbound SIP INVITE
to confirm that the header reaches Telnyx. An arbitrary customer number or a
header copied from a different call does not establish a valid transfer.