Moving a LiveKit trunk from Twilio to RingTrunk
A migration guide for developers who already run LiveKit with Twilio Elastic SIP Trunking and want an Indian phone number. Which LiveKit objects stay the same, which two fields change, and which Indian rules apply that Twilio never asked you about.
Updated 6 September 2026
In short: Moving a LiveKit trunk from Twilio to RingTrunk changes two fields on the outbound trunk and the allowed address on the inbound trunk; the agent code stays the same.
If your LiveKit project already places and receives calls through Twilio Elastic SIP Trunking, you already know how RingTrunk works. Both are SIP trunks with the same shape: an address to authenticate to for outbound calls, a source to trust for inbound calls, numbers attached to the trunk, and credentials. This page lists exactly what changes and what does not.
What stays the same
- The LiveKit objects. One outbound trunk, one inbound trunk, one dispatch rule, created with
lkor the LiveKit API. - Your agent code. Rooms, participants,
create_sip_participant, webhooks: nothing changes. - The inbound model. The provider's INVITE carries no credentials and is trusted by source IP, on Twilio and on RingTrunk alike.
- The response conventions. Rejections come back as SIP responses with a reason you can read.
What changes
| Field | Twilio | RingTrunk |
|---|---|---|
Outbound trunk address | <your-domain>.pstn.twilio.com | pstn.ringtrunk.com |
Outbound trunk transport | as configured | SIP_TRANSPORT_TCP, pinned |
Outbound trunk numbers | your Twilio numbers | your Indian number, 10 digits, for example 9XXXXXXXXX |
Outbound trunk auth_username | your credential list username | your trunk id, TRxxxxxxxxxxxxxxxx |
Inbound trunk allowed_addresses | Twilio's IP ranges | RingTrunk's source IP, shown on your trunk's Origination tab |
Inbound trunk numbers | E.164 | four forms: 9XXXXXXXXX, +919XXXXXXXXX, 09XXXXXXXXX, 919XXXXXXXXX |
| Where inbound calls are routed | Twilio console origination URI | RingTrunk portal, the trunk's Origination tab |
Two lines in the outbound trunk, the address and the numbers, are the whole migration for outbound. For inbound, swap the allowed address and list the number in the four forms.
1. Edit the outbound trunk
cat > outbound-trunk.json <<'EOF'
{
"trunk": {
"name": "RingTrunk outbound",
"address": "pstn.ringtrunk.com",
"transport": "SIP_TRANSPORT_TCP",
"numbers": ["9XXXXXXXXX"],
"auth_username": "TRxxxxxxxxxxxxxxxx",
"auth_password": "YOUR_SIP_PASSWORD"
}
}
EOF
lk sip outbound create outbound-trunk.jsonPin the transport to TCP. Left on automatic, a trunk may ignore the SRV port and try 5060, which will not answer you.
2. Edit the inbound trunk
cat > inbound-trunk.json <<'EOF'
{
"trunk": {
"name": "RingTrunk inbound",
"numbers": ["9XXXXXXXXX", "+919XXXXXXXXX", "09XXXXXXXXX", "919XXXXXXXXX"],
"allowed_addresses": ["<RINGTRUNK_SOURCE_IP>"]
}
}
EOF
lk sip inbound create inbound-trunk.jsonKeep auth_username and auth_password off this object, exactly as with Twilio. Credentials on an inbound trunk make every call fail with 401.
3. Keep the dispatch rule
Your existing dispatch rule works unchanged. If you dispatch LiveKit Agents through room_config.agents, keep it; a webhook-driven worker needs nothing.
4. Point RingTrunk at LiveKit
In the RingTrunk portal, set the trunk's origination URI to your project's SIP endpoint, <project-id>.sip.livekit.cloud:5060;transport=tcp, or <project-id>.india.sip.livekit.cloud to keep rooms in Mumbai.
Rules that are different in India
Three things Twilio never asked you about apply on every call:
- Caller ID must be one of your own attached numbers. Any other From number is rejected with
403 cli_not_allowed. This is an Indian regulatory requirement, enforced on every call, so a "spoof the customer's number" pattern will not work. - Destinations are Indian mobile numbers only. 10 digits starting 6, 7, 8 or 9. Landlines, short codes and international destinations are rejected with
403 destination_not_supported. - The codec is G.711 A-law (PCMA), 20 ms. Offering only G.729 or Opus fails with
488 Not Acceptable Here.
Placing a call after the switch
lk sip participant create \
--trunk <YOUR_RINGTRUNK_OUTBOUND_TRUNK_ID> \
--call +91XXXXXXXXXX \
--number 9XXXXXXXXX \
--room my-room--number is now your Indian number. Everything else in the command is the same as before.
Running both at once
Nothing stops a LiveKit project from holding a Twilio trunk for one set of numbers and a RingTrunk trunk for Indian numbers. Trunks are selected per call by sip_trunk_id, so an agent can dial an Indian customer on RingTrunk and a US customer on Twilio from the same code.
See Connect LiveKit Cloud for the full setup from scratch and Troubleshooting for every SIP response.