Buy a number & configure inbound trunk
Buy a number and configure inbound trunk
You understand how phone calls flow through SIP into LiveKit rooms. Now it is time to get a real phone number and wire it up. By the end of this chapter, you will have a phone number that routes incoming calls to LiveKit's SIP bridge -- the first half of the connection between the phone network and your dental receptionist agent.
What you'll learn
- How to get a phone number through LiveKit Cloud, Twilio, or Telnyx
- What a SIP trunk is and why you need one
- How to create an inbound SIP trunk via the CLI
- How to create an inbound SIP trunk programmatically in Python and TypeScript
Getting a phone number
You need a phone number that the public can dial. There are three paths, and all lead to the same result: a number whose calls arrive at LiveKit's SIP bridge.
Option 1: LiveKit Cloud phone numbers (simplest)
LiveKit Cloud can provision phone numbers directly. This is the fastest path because the number is automatically configured to route to LiveKit's SIP bridge -- no third-party account needed.
# List available numbers in a region
lk sip phone list-available --area-code 415
# Purchase a number
lk sip phone purchase --number "+14155551234"Best for getting started
LiveKit Cloud phone numbers require zero SIP configuration. The number is automatically linked to your project's SIP infrastructure. If you are following along for the first time, this is the recommended path.
Option 2: Twilio
If you already have a Twilio account or need features like international numbers in many countries, you can use Twilio as your trunk provider.
Buy a number in the Twilio Console
Go to Phone Numbers in the Twilio Console and buy a number with voice capability. Note the number in E.164 format (e.g., +15551234567).
Create an Elastic SIP Trunk
In the Twilio Console, go to Elastic SIP Trunking and create a new trunk. Under Origination, add a SIP URI pointing to LiveKit's SIP bridge. The URI will be provided when you create the inbound trunk in LiveKit (next section).
Associate the number with the trunk
Under the trunk's Phone Numbers tab, associate your purchased number with this SIP trunk. Incoming calls to that number will now be forwarded via SIP.
Option 3: Telnyx
Telnyx is another popular SIP trunk provider with competitive per-minute pricing and good global coverage.
Buy a number in the Telnyx Mission Control Portal
Purchase a number under the Numbers section. Note the number in E.164 format.
Create a SIP Connection
Under Voice, create a new SIP Connection. Configure the outbound settings to point to LiveKit's SIP bridge URI.
Assign the number to the connection
Associate your purchased number with the SIP connection so incoming calls are routed through it.
All providers work the same way
Regardless of which provider you choose, the end result is identical: when someone dials your number, the provider sends a SIP INVITE to LiveKit. The rest of this chapter and course works the same with any provider.
What is an inbound SIP trunk?
A SIP trunk is the configuration object in LiveKit that says: "I expect incoming SIP calls on these phone numbers. Accept them." It is the handshake between your trunk provider and LiveKit's SIP bridge.
An inbound trunk stores:
- Name -- a human-readable label (e.g., "Dental Office Inbound")
- Numbers -- the phone numbers associated with this trunk (E.164 format)
- Authentication -- optional credentials or IP allowlists to restrict who can send SIP traffic
- Trunk ID -- an auto-generated identifier (e.g.,
ST_xxxxxxxxxxxx) used to reference the trunk in dispatch rules
Think of the inbound trunk as a labeled front door. The label (numbers) tells the SIP bridge which door to open when a call arrives for a particular number. Without a trunk, incoming SIP traffic is rejected -- LiveKit does not know it should accept those calls.
Creating an inbound trunk via CLI
The fastest way to create an inbound trunk is with the LiveKit CLI:
lk sip trunk create --name "Dental Office" --numbers "+15551234567"This returns a trunk object with an ID like ST_xxxxxxxxxxxx. Save this ID -- you will need it in the next chapter when configuring dispatch rules.
You can verify the trunk was created:
lk sip trunk listCreating an inbound trunk via the API
For production systems, you will typically create trunks programmatically as part of your provisioning workflow. Here is how to do it in both Python and TypeScript.
Python
import asyncio
from livekit.api import LiveKitAPI, CreateSIPInboundTrunkRequest, SIPInboundTrunkInfo
async def main():
api = LiveKitAPI()
trunk = await api.sip.create_sip_inbound_trunk(
CreateSIPInboundTrunkRequest(
trunk=SIPInboundTrunkInfo(
name="Dental Office Inbound",
numbers=["+15551234567"],
)
)
)
print(f"Created trunk: {trunk.sip_trunk_id}")
print(f"Name: {trunk.name}")
print(f"Numbers: {trunk.numbers}")
await api.aclose()
asyncio.run(main())TypeScript
import { SipClient } from "livekit-server-sdk";
const sipClient = new SipClient(
process.env.LIVEKIT_URL!,
process.env.LIVEKIT_API_KEY!,
process.env.LIVEKIT_API_SECRET!
);
const trunk = await sipClient.createSipInboundTrunk({
name: "Dental Office Inbound",
numbers: ["+15551234567"],
});
console.log(`Created trunk: ${trunk.sipTrunkId}`);
console.log(`Name: ${trunk.name}`);
console.log(`Numbers: ${trunk.numbers}`);Protect your API credentials
The LIVEKIT_API_KEY and LIVEKIT_API_SECRET environment variables grant full administrative access to your LiveKit project. Never commit them to source control. Use environment variables or a secrets manager.
Listing and managing trunks
Once created, you can list all trunks in your project:
import asyncio
from livekit.api import LiveKitAPI, ListSIPInboundTrunkRequest
async def main():
api = LiveKitAPI()
response = await api.sip.list_sip_inbound_trunk(
ListSIPInboundTrunkRequest()
)
for trunk in response.items:
print(f"{trunk.sip_trunk_id}: {trunk.name} ({trunk.numbers})")
await api.aclose()
asyncio.run(main())import { SipClient } from "livekit-server-sdk";
const sipClient = new SipClient(
process.env.LIVEKIT_URL!,
process.env.LIVEKIT_API_KEY!,
process.env.LIVEKIT_API_SECRET!
);
const trunks = await sipClient.listSipInboundTrunk();
for (const trunk of trunks) {
console.log(`${trunk.sipTrunkId}: ${trunk.name} (${trunk.numbers})`);
}Test your knowledge
Question 1 of 2
What is the role of an inbound SIP trunk in LiveKit's architecture?
What you learned
- You can get a phone number through LiveKit Cloud (simplest), Twilio, or Telnyx
- An inbound SIP trunk tells LiveKit to accept incoming calls on specific phone numbers
- Trunks are created with the
lk sip trunk createCLI command or theCreateSIPInboundTrunkRequestAPI - Each trunk gets a unique ID (
ST_xxxxxxxxxxxx) used to link it to dispatch rules - Trunks can be managed programmatically for production provisioning workflows
Next up
You have a phone number and an inbound trunk, but LiveKit does not yet know what to do when a call arrives. In the next chapter, you will configure dispatch rules that route incoming calls to your dental receptionist agent.