Connect LiveKit Cloud to an Indian phone number
Step by step. Create the LiveKit outbound trunk, inbound trunk and dispatch rule for a RingTrunk trunk, point RingTrunk at your LiveKit Cloud SIP endpoint, place a call from an agent and receive calls on your Indian number.
Updated 6 September 2026
In short: A RingTrunk trunk becomes one LiveKit outbound trunk, one inbound trunk and a dispatch rule; after that your LiveKit Cloud agent places and receives calls on an Indian number.
A RingTrunk trunk is one object that carries both directions. LiveKit models the same thing as two objects: an outbound trunk, where your agent places calls and authenticates to us, and an inbound trunk plus a dispatch rule, where we deliver calls into a room. That asymmetry is the one thing people get wrong, so it comes first.
You need a RingTrunk trunk with a number attached, its trunk id and SIP password, and the lk CLI. If you have ever pointed LiveKit at Twilio Elastic SIP Trunking, the objects are the same; only the address and the numbers change.
1. Point lk at your project
lk cloud authYour SIP endpoint is <project-id>.sip.livekit.cloud, where the project id is the p_… value on Settings → Project without its prefix.
2. Create the outbound trunk (your agent calls out)
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.jsonnumbers is the caller ID this trunk may present. It must be a number attached to your RingTrunk trunk, in plain 10-digit form. Pin the transport to TCP: a trunk left on automatic may ignore the SRV port and try 5060, which is our carrier side and will not answer you.
3. Create the inbound trunk (we deliver calls to you)
Our INVITE carries no SIP credentials, exactly like Twilio Elastic SIP Trunking. Do not set auth_username or auth_password on the inbound trunk. If you do, every inbound call fails with 401. Trust us by IP instead, and list the number in every form the To header may take.
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.jsonThe source IP is shown on your trunk's Origination tab in the portal.
4. Create the dispatch rule
cat > dispatch-rule.json <<'EOF'
{
"name": "RingTrunk inbound",
"rule": { "dispatchRuleIndividual": { "roomPrefix": "call-" } }
}
EOF
lk sip dispatch create dispatch-rule.jsonEach caller lands in their own room. Add room_config.agents if you dispatch LiveKit Agents. A webhook-driven worker, such as a Pipecat pipeline, needs nothing here.
5. Tell RingTrunk where to deliver calls
In the RingTrunk portal, open the trunk's Origination tab and set the origination URI to your project's SIP endpoint:
<project-id>.sip.livekit.cloud:5060;transport=tcpThere is nothing to open on your side. LiveKit Cloud accepts our INVITE because of allowed_addresses. To keep every room in Mumbai regardless of where your agent runs, use <project-id>.india.sip.livekit.cloud instead.
6. Place a call
lk sip participant create \
--trunk <YOUR_OUTBOUND_TRUNK_ID> \
--call +91XXXXXXXXXX \
--number 9XXXXXXXXX \
--room my-roomfrom livekit import api
lkapi = api.LiveKitAPI()
await lkapi.sip.create_sip_participant(
api.CreateSIPParticipantRequest(
sip_trunk_id="<YOUR_OUTBOUND_TRUNK_ID>",
sip_call_to="+91XXXXXXXXXX",
# Must be one of the numbers attached to this trunk,
# or the call is rejected with SIP 403 cli_not_allowed.
sip_number="9XXXXXXXXX",
room_name="my-room",
)
)Flags verified against lk v2.12: --call is the destination and --number is the caller ID. Destinations are Indian mobile numbers only, 10 digits starting 6, 7, 8 or 9.
7. Receive a call
Dial your number from any phone. The INVITE arrives at your LiveKit project, the dispatch rule creates a room named call-…, and the caller joins it as a SIP participant. Your agent joins the same room and the conversation starts.
If it does not work
- Inbound never arrives. The inbound trunk has credentials set (remove them), or the number is not listed in all four forms.
- Outbound returns 403. Read the reason.
cli_not_allowedmeans the caller ID is not attached to the trunk.destination_not_supportedmeans the number is not a 10-digit Indian mobile. - 401 on REGISTER. Wrong password, or you rotated it within the last minute.
The full list is in Troubleshooting.
Working example
The three JSON files above, a script that places a call, and a Pipecat bot that answers are in the open-source example repository: github.com/Zingaro-Ai/ringtrunk-livekit-example (MIT).