We're trying to show the original customer's phone...
# ask-ai
m
We're trying to show the original customer's phone number (caller ID) to the supervisor during a warm transfer. The flow is: 1. Customer calls in → LiveKit agent answers (via Twilio Elastic SIP Trunk) 2. Agent triggers warm transfer → CreateSIPParticipantRequest dials supervisor 3. Supervisor currently sees our Twilio trunk number, not the customer's number We've tried: - sip_number with wildcard trunk → Twilio returns 403 (unverified caller ID) - sip_number on Telnyx trunk → Telnyx returns 403 (unverified origination number D51) - display_name → mobile phones ignore CNAM Twilio has a CallToken mechanism (docs) that allows forwarding the original caller's number on an outbound call by passing a token from the inbound webhook. However, this requires: 1. Receiving the inbound call via Twilio Programmable Voice (HTTP webhook) to get the CallToken 2. Creating the outbound call via Twilio's REST API with the CallToken parameter Both are incompatible with LiveKit's SIP-based architecture — inbound calls arrive via SIP INVITE (no webhook, no CallToken), and outbound calls are created via CreateSIPParticipantRequest (SIP INVITE, not Twilio REST API). Questions: 1. Is there a way to pass a Twilio CallToken (or equivalent) through CreateSIPParticipantRequest so Twilio accepts the original caller's number as the outbound caller ID? 2. Is there a planned feature to support caller ID forwarding for warm transfer scenarios? 3. Is there any other approach we're missing to present the original inbound caller's number on an outbound SIP call? We're using LiveKit Cloud with the Python Agents SDK and Twilio Elastic SIP Trunking.
t
Thanks for reaching out, arijit. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — that’s a clear description of the problem and what you’ve tried. I’ll respond to each of your three questions and then suggest concrete next steps you can try. Acknowledging your setup: you’re on LiveKit Cloud using the Python Agents SDK, inbound calls arrive via SIP INVITE on Twilio Elastic SIP Trunking, and outbound supervisor calls are created with CreateSIPParticipantRequest (SIP INVITE). 1) Can you pass a Twilio CallToken (or equivalent) in CreateSIPParticipantRequest so Twilio accepts the original caller’s number? • No. CreateSIPParticipantRequest does not provide a field for Twilio’s CallToken or an equivalent REST-only token. The request supports fields such as
sip_number
,
sip_trunk_id
,
sip_call_to
, etc., but there is no mechanism to inject Twilio’s programmable-voice CallToken into the outbound call from LiveKit. 2) Is there a planned LiveKit feature to support caller-ID forwarding via CallToken-like mechanism? • There is no indication that such a feature (passing a Twilio CallToken through CreateSIPParticipantRequest) is currently available or scheduled. 3) Any other approaches to present the original inbound caller’s number on the outbound SIP call? • Use CreateSIPParticipantRequest’s
sip_number
to request the original caller number as the outbound caller ID. This is the LiveKit-side way to present a specific caller number on the outbound SIP INVITE. • Whether that caller ID is actually accepted and shown depends entirely on the SIP trunk provider’s policies. Twilio and Telnyx both return 403 when an unverified caller ID is used, which matches your tests. • For Twilio specifically: Twilio supports preserving/passing the transferee’s caller ID at the trunk/configuration level (not via the CallToken flow). You can configure the SIP trunk to pass the original caller ID through transfers (for example, using the trunk transfer-caller-id setting such as
from-transferee
). That means instead of a CallToken-based REST call, you must ensure your Twilio trunk is configured to allow caller ID passthrough for transferred calls and that the number is permitted by Twilio’s verification/policy for that account/jurisdiction. • In short: LiveKit can request the original number via
sip_number
, but acceptance requires Twilio (or whichever SIP provider) to allow that number on the trunk. The CallToken REST flow is incompatible with a pure SIP INVITE path and can’t be injected by CreateSIPParticipantRequest. Recommended next steps 1. Re-check Twilio trunk settings ◦ Confirm the trunk is configured to allow caller ID passthrough on transfer (e.g.,
transfer-caller-id from-transferee
or equivalent in Twilio console / SIP trunk settings). ◦ Verify whether Twilio requires caller numbers to be validated for your account or region; if so, follow Twilio’s verification process for the original caller numbers you need to present (some jurisdictions or accounts enforce verification). 1. Continue using
sip_number
in CreateSIPParticipantRequest ◦ Keep setting
sip_number
to the original caller’s number when creating the supervisor participant. LiveKit will put that number in the outbound SIP INVITE; acceptance depends on the trunk. 1. If Twilio’s trunk policy disallows the passthrough for your use-case ◦ Consider whether your architecture can route inbound calls through Twilio Programmable Voice (HTTP webhook) instead of SIP INVITE so you can use the CallToken/REST mechanism — I realize that’s incompatible with your current SIP-based inbound flow, but it is how Twilio’s CallToken flow works. ◦ Alternatively, check with Twilio whether they can enable per-account transfer caller ID passthrough or provide a recommended pattern for warm transfers when the incoming call arrives by SIP INVITE (some customers use trunk-level transfer settings or specific call transfer flows Twilio supports). If you’d like, I can help with any of the following: • Review the exact CreateSIPParticipantRequest payload you’re sending (mask sensitive data) and confirm you’re populating
sip_number
correctly. • Walk through Twilio trunk settings to find the specific
transfer-caller-id
or equivalent option and the steps to enable it. • Outline an alternate architecture that uses Programmable Voice for inbound calls (so the CallToken approach is possible), if you want to evaluate that tradeoff. Which would you like to do next? Sources: Preserving Caller ID During SIP Transfers in LiveKit Agents | Preserve caller ID during SIP transfers with Twilio | LiveKit
m
how can I use twilio Programmable Voice with livekit
t
Thanks — I’ll walk you through how you can use Twilio Programmable Voice with LiveKit, what that enables, and practical options to address your caller ID use case. Quick acknowledgement: you asked how to use Twilio Programmable Voice with LiveKit. Below are the main integration options, concrete steps, and recommendations for preserving the original caller’s number during warm transfers. How Twilio Programmable Voice can work with LiveKit • LiveKit connects to PSTN providers via SIP. You can bridge Twilio calls into LiveKit rooms by using Twilio Programmable Voice to hand off the call to LiveKit over SIP (the Twilio <sip> verb or a configured trunk), or by routing calls through a Twilio Programmable Voice webhook/REST flow and then connecting to LiveKit. • Two common patterns: ◦ SIP Trunking path: Incoming call arrives at Twilio, Twilio sends a SIP INVITE to your LiveKit SIP endpoint (LiveKit accepts the call and joins the caller into a room). ◦ Programmable Voice REST/Twiml path: Incoming call is handled by Twilio’s webhook/Programmable Voice; TwiML or Twilio REST can then dial a SIP endpoint pointed at LiveKit (via <Dial><Sip> or a REST-initiated call). Step-by-step options to connect Twilio Programmable Voice to LiveKit 1) Using Twilio SIP Trunk -> LiveKit (SIP INVITE) 1. Provision a Twilio number and configure a SIP Trunk that targets your LiveKit SIP domain. 2. In Twilio, set the trunk’s Termination/Origination and SIP URI to route calls to LiveKit. 3. In LiveKit, create and configure a SIP trunk pointing to Twilio so LiveKit can place outbound calls and accept inbound SIP INVITEs. 4. On inbound SIP INVITEs, LiveKit will create a SIP participant and place that caller into the target LiveKit room (you can use dispatch rules or Agents to attach logic). • Use when you want a pure SIP-based flow with minimal webhook logic in Twilio. 2) Using Twilio Programmable Voice (webhook / REST) -> TwiML -> SIP to LiveKit 1. Provision a Twilio phone number and configure the number’s voice webhook to point to your server. 2. In your webhook handler, respond with TwiML that dials a SIP URI pointing to LiveKit: ◦ Example: <Dial><Sip>sip:participant@your-livekit-sip-domain.com</Sip></Dial> 1. Twilio will place an outbound SIP call to LiveKit; LiveKit will join the inbound caller into a room. • Use when you want to leverage Twilio programmatic features (CallTokens, REST APIs, richer webhooks) before handing off to LiveKit. Preserving/passing original caller ID during warm transfer (your main concern) • LiveKit can set the requested outbound caller ID via the
sip_number
field when creating a SIP participant (CreateSIPParticipantRequest). That places the desired number into the outgoing SIP INVITE. • Whether Twilio will accept and present that caller ID depends entirely on Twilio’s trunk/account configuration and number verification policies. • Important distinction for Twilio: ◦ Twilio’s CallToken approach (used with Programmable Voice REST API) lets you preserve/present the original caller number when Twilio itself is creating the outbound call via its REST/Twilio webhook flow. ◦ When using pure SIP INVITE (SIP trunking), the CallToken flow is not available. Instead you must configure the Twilio trunk to allow caller ID passthrough on transfers (e.g., use the trunk setting to set caller ID from the transferee:
from-transferee
/ “transfer caller ID from transferee”). • Practical outcomes: ◦ If you keep your SIP-trunk-only flow (LiveKit issuing SIP INVITEs to Twilio), continue using
sip_number
. Then make sure your Twilio trunk is configured to allow that number to be presented (and verify numbers if Twilio requires). ◦ If Twilio refuses unverified caller IDs on your trunk (403), you have two options: ▪︎ Configure trunk-level caller ID passthrough or verify numbers in Twilio per their policy. ▪︎ Change the flow so Twilio creates the outbound call (Programmable Voice REST/webhook + CallToken) before connecting to LiveKit. That lets you use Twilio’s CallToken to present original caller ID, but requires inbound calls to be handled by Twilio’s webhook/REST rather than arriving directly to LiveKit via SIP INVITE. Recommended next steps I can help with • If you want to keep SIP trunking (current flow): ◦ I can review the CreateSIPParticipantRequest payload you’re using (mask sensitive data) and confirm
sip_number
usage. ◦ Check Twilio trunk settings: I can walk you through the specific Twilio trunk option to enable transfer caller-ID passthrough (
from-transferee
) and what verification steps Twilio may require. • If you’re open to switching to a Programmable Voice webhook flow: ◦ I can outline the webhook + TwiML approach that returns a TwiML <Dial><Sip> to LiveKit, and explain how to use CallToken when Twilio makes the outbound call so the original caller number is preserved. • If you want a direct checklist right now: a. Try setting
sip_number
on CreateSIPParticipantRequest to the original caller’s number. b. In Twilio Console, enable caller ID passthrough/transfer settings on the trunk (look for “transfer caller ID” /
from-transferee
). c. If Twilio returns 403, verify whether Twilio requires caller ID registration or whether you must use the CallToken REST flow instead. d. If CallToken is required and you cannot change Twilio trunk settings, route inbound calls through Twilio Programmable Voice (webhook) so Twilio can create the outbound call with CallToken. Which path would you like to try first? • Keep SIP Trunk path and I’ll help confirm LiveKit payload + Twilio trunk settings, or • Move to Programmable Voice webhook/CallToken flow and I’ll give the exact webhook/TwiML and flow changes needed. Tell me which option you prefer and, if you want the SIP-trunk path, please share (masked) the CreateSIPParticipantRequest payload you’re sending and the exact Twilio 403 error message — I’ll help diagnose next. Sources: OpenAI and LiveKit partner to turn Advanced Voice into an API | Outbound Calling Script | LiveKit Documentation | Accepting calls overview | LiveKit Documentation | Preserve caller ID during SIP transfers with Twilio | LiveKit | Agents (Node.js) | livekit/livekit_composite | DeepWiki