Overview
After you purchase a phone number and configure your SIP trunking provider, you must create an inbound trunk and dispatch rule to accept incoming calls. The inbound trunk allows you to limit incoming calls to those coming from your SIP trunking provider.
You can also configure additional properties for all incoming calls that match the trunk including SIP headers, participant metadata and attributes, and session properties. For a full list of available parameters, see CreateSIPInboundTrunk.
If you're using LiveKit Phone Numbers, you do not need to create an inbound trunk.
Trunks are long-lived configuration objects that LiveKit caches and reuses. Create an inbound trunk once and reuse it for every call, typically one trunk per phone number. Creating a new trunk for each call bypasses this caching and can degrade reliability at scale. To place each caller in a separate room, use a dispatch rule.
LiveKit supports username and password authentication for inbound trunks, but your SIP trunking provider must also support it. Support varies by provider. For example, Twilio Elastic SIP Trunking doesn’t support it, though you can use username and password authentication with TwiML. Check with your provider to confirm.
To learn more about LiveKit SIP, see SIP overview. To learn more about SIP API endpoints and types, see SIP API.
Restricting calls to a region
When you configure your SIP trunking provider for inbound calls, you need to specify the LiveKit SIP endpoint to use. By default, this is a global endpoint and incoming calls are routed to the region closest to the call's origination point — typically the region where your telephony provider initiated the call. You can limit calls to a specific region using region pinning.
Inbound trunk example
The following examples create an inbound trunk that accepts calls made to the number +1-510-555-0100. This phone number is the number purchased from your SIP trunking provider.
Create a file named
inbound-trunk.jsonwith the following content:{"trunk": {"name": "My trunk","numbers": ["+15105550100"]}}ImportantIf you're using Telnyx, the leading
+in the phone number assumes theDestination Number Formatis set to+E.164for your number.Create the inbound trunk using
lk:lk sip inbound create inbound-trunk.json
import { LiveKitAPI } from 'livekit-server-sdk';const api = new LiveKitAPI();// An array of one or more provider phone numbers associated with the trunk.const numbers = ['+15105550100'];const name = 'My trunk';const trunk = await api.sip.createSipInboundTrunk(name, numbers);console.log(trunk);
import asynciofrom livekit import apiasync def main():lkapi = api.LiveKitAPI()trunk = api.SIPInboundTrunkInfo(name = "My trunk",numbers = ["+15105550100"],)request = api.CreateSIPInboundTrunkRequest(trunk = trunk)trunk = await lkapi.sip.create_sip_inbound_trunk(request)print(trunk)await lkapi.aclose()asyncio.run(main())
require 'livekit'name = "My trunk"numbers = ["+15105550100"]lkapi = LiveKit::LiveKitAPI.newtrunk = lkapi.sip.create_sip_inbound_trunk(name,numbers)puts trunk
package mainimport ("context""fmt"lksdk "github.com/livekit/server-sdk-go/v2""github.com/livekit/protocol/livekit")func main() {trunkName := "My inbound trunk"numbers := []string{"+15105550100"}trunkInfo := &livekit.SIPInboundTrunkInfo{Name: trunkName,Numbers: numbers,}// Create a requestrequest := &livekit.CreateSIPInboundTrunkRequest{Trunk: trunkInfo,}api, err := lksdk.NewLiveKitAPI()if err != nil {fmt.Println(err)return}// Create trunktrunk, err := api.SIP().CreateSIPInboundTrunk(context.Background(), request)if err != nil {fmt.Println(err)} else {fmt.Println(trunk)}}
import io.livekit.server.LiveKitAPIval api = LiveKitAPI.createClient()val response = api.sip.createSipInboundTrunk(name = "My inbound trunk",numbers = listOf("+15105550100"),).execute()if (!response.isSuccessful) {println(response.errorBody())} else {val trunk = response.body()if (trunk != null) {println("Created inbound trunk: ${trunk.sipTrunkId}")}}
use livekit_api::services::LiveKitApi;#[tokio::main]async fn main() {let api = LiveKitApi::new("https://my-project.livekit.cloud").unwrap();let trunk = api.sip().create_sip_inbound_trunk("My trunk".to_string(),vec!["+15105550100".to_string()],Default::default(),).await.unwrap();println!("Created inbound trunk: {:?}", trunk);}
Sign in to the LiveKit Cloud dashboard .
Select Telephony → SIP trunks .
Select Create new trunk.
Select the JSON editor tab.
NoteYou can also use the Trunk details tab to create a basic trunk. However, the JSON editor allows you to configure all available parameters. For example, the
krispEnabledparameter is only available in the JSON editor.Select Inbound for Trunk direction.
Copy and paste the following text into the editor:
{"name": "My trunk","numbers": ["+15105550100"],"krispEnabled": true}Select Create.
Accepting calls to any phone number
You can configure an inbound trunk to accept incoming calls to any phone number by setting the numbers parameter to an empty array. This is useful if you want to use the same inbound trunk for incoming calls to all your phone numbers.
When you use an empty numbers parameter, you must set either a username and password for authentication or the allowed_addresses parameter. See CreateSIPInboundTrunk for parameter details.
The allowed_addresses field must be enabled for your project before you can use it. Contact LiveKit support to request access.
Accepting calls from specific phone numbers
You can configure an inbound trunk to accept phone calls only from specific numbers. The following example configuration accepts inbound calls to the number +1-510-555-0100 from caller numbers +1-310-555-1100 and +1-714-555-0100.
Remember to replace the numbers in the example with actual phone numbers when creating your trunks.
You can also filter allowed caller numbers with a Dispatch Rule.
Create a file named
inbound-trunk.jsonwith the following content:{"trunk": {"name": "My trunk","numbers": ["+15105550100"],"allowedNumbers": ["+13105550100","+17145550100"]}}ImportantIf you're using Telnyx, the leading
+in the phone number assumes theDestination Number Formatis set to+E.164for your number.Create the inbound trunk using
lk:lk sip inbound create inbound-trunk.json
For an executable example, replace the trunk in the Inbound trunk example to include the following trunkOptions:
// Trunk optionsconst trunkOptions = {allowedNumbers: ['+13105550100', '+17145550100'],};const trunk = await api.sip.createSipInboundTrunk(name, numbers, trunkOptions);
For an executable example, replace the trunk in the Inbound trunk example with the following:
trunk = api.SIPInboundTrunkInfo(name = "My trunk",numbers = ["+15105550100"],allowed_numbers = ["+13105550100", "+17145550100"])
For an executable example, replace trunk in the Inbound trunk example with the following:
trunk = lkapi.sip.create_sip_inbound_trunk(name,numbers,allowed_numbers: ["+13105550100", "+17145550100"])
For an executable example, replace trunkInfo in the Inbound trunk example with the following:
allowedNumbers := []string{"+13105550100", "+17145550100"}trunkInfo := &livekit.SIPInboundTrunkInfo{Name: trunkName,Numbers: numbers,AllowedNumbers: allowedNumbers,}
import io.livekit.server.LiveKitAPIimport io.livekit.server.CreateSipInboundTrunkOptionsval api = LiveKitAPI.createClient()val response = api.sip.createSipInboundTrunk(name = "My inbound trunk",numbers = listOf("+15105550100"),options = CreateSipInboundTrunkOptions(allowedNumbers = listOf("+13105550100", "+17145550100"))).execute()if (!response.isSuccessful) {println(response.errorBody())} else {val trunk = response.body()if (trunk != null) {println("Created inbound trunk: ${trunk.sipTrunkId}")}}
For an executable example, replace the options in the Inbound trunk example with the following:
let options = CreateSIPInboundTrunkOptions {allowed_numbers: Some(vec!["+13105550100".to_string(),"+17145550100".to_string(),]),..Default::default()};
Sign in to the LiveKit Cloud dashboard .
Select Telephony → SIP trunks .
Select Create new trunk.
Select the JSON editor tab.
NoteThe
krispEnabledandallowedNumbersparameters are only available in the JSON editor tab.Select Inbound for Trunk direction.
Copy and paste the following text into the editor:
{"name": "My trunk","numbers": ["+15105550100"],"krispEnabled": true,"allowedNumbers": ["+13105550100","+17145550100"]}Select Create.
List inbound trunks
Use the ListSIPInboundTrunk API to list all inbound trunks and trunk parameters.
lk sip inbound list
import { LiveKitAPI } from 'livekit-server-sdk';const api = new LiveKitAPI();const trunks = await api.sip.listSipInboundTrunk();console.log(trunks);
import asynciofrom livekit import apiasync def main():lkapi = api.LiveKitAPI()trunks = await lkapi.sip.list_sip_inbound_trunk(api.ListSIPInboundTrunkRequest())print(f"{trunks}")await lkapi.aclose()asyncio.run(main())
require 'livekit'lkapi = LiveKit::LiveKitAPI.newtrunks = lkapi.sip.list_sip_inbound_trunk()puts trunks
package mainimport ("context""fmt"lksdk "github.com/livekit/server-sdk-go/v2""github.com/livekit/protocol/livekit")func main() {api, err := lksdk.NewLiveKitAPI()if err != nil {fmt.Println(err)return}// List inbound trunkstrunks, err := api.SIP().ListSIPInboundTrunk(context.Background(), &livekit.ListSIPInboundTrunkRequest{})if err != nil {fmt.Println(err)} else {fmt.Println(trunks)}}
import io.livekit.server.LiveKitAPIval api = LiveKitAPI.createClient()val response = api.sip.listSipInboundTrunk().execute()if (!response.isSuccessful) {println(response.errorBody())} else {val trunks = response.body()if (trunks != null) {println("Inbound trunks: ${trunks}")}}
use livekit_api::services::sip::ListSIPInboundTrunkFilter;use livekit_api::services::LiveKitApi;#[tokio::main]async fn main() {let api = LiveKitApi::new("https://my-project.livekit.cloud").unwrap();let trunks = api.sip().list_sip_inbound_trunk(ListSIPInboundTrunkFilter::All).await.unwrap();println!("Inbound trunks: {:?}", trunks);}
- Sign in to the LiveKit Cloud dashboard .
- Select Telephony → SIP trunks .
- The Inbound section lists all inbound trunks.
Update inbound trunk
Use the UpdateSIPInboundTrunk API to update specific fields of an inbound trunk or replace an inbound trunk with a new one.
Update specific fields of an inbound trunk
The UpdateSIPInboundTrunkFields API allows you to update specific fields of an inbound trunk without affecting other fields.
Create a file named
inbound-trunk.jsonwith the following content:{"name": "My trunk","numbers": ["+15105550100"]}ImportantIf you're using Telnyx, the leading
+in the phone number assumes theDestination Number Formatis set to+E.164for your number.
Update the inbound trunk using lk:
lk sip inbound update --id <trunk-id> inbound-trunk.json
import { ListUpdate } from '@livekit/protocol';import { LiveKitAPI } from 'livekit-server-sdk';const api = new LiveKitAPI();async function main() {const updatedTrunkFields = {numbers: new ListUpdate({ set: ['+15105550100'] }), // Replace existing listallowedNumbers: new ListUpdate({ add: ['+14155550100'] }), // Add to existing listname: 'My updated trunk',};const trunk = await api.sip.updateSipInboundTrunkFields('<inbound-trunk-id>', updatedTrunkFields);console.log('updated trunk ', trunk);}await main();
import asynciofrom livekit import apifrom livekit.protocol.models import ListUpdateasync def main():livekit_api = api.LiveKitAPI()# To update specific trunk fields, use the update_inbound_trunk_fields method.trunk = await livekit_api.sip.update_inbound_trunk_fields(trunk_id = "<sip-trunk-id>",numbers = ListUpdate(add=['+15105550100']), # Add to existing listallowed_numbers = ["+13105550100", "+17145550100"], # Replace existing listname = "My updated trunk",)print(f"Successfully updated trunk {trunk}")await livekit_api.aclose()asyncio.run(main())
require 'livekit'lkapi = LiveKit::LiveKitAPI.newupdate = LiveKit::Proto::SIPInboundTrunkUpdate.new(name: "My updated trunk",numbers: LiveKit::Proto::ListUpdate.new(set: ["+15105550100"]), # Replace existing listallowed_numbers: LiveKit::Proto::ListUpdate.new(add: ["+14155550100"]) # Add to existing list)trunk = lkapi.sip.update_sip_inbound_trunk_fields("<sip-trunk-id>", update)puts trunk
package mainimport ("context""fmt"lksdk "github.com/livekit/server-sdk-go/v2""github.com/livekit/protocol/livekit")func main() {trunkName := "My updated inbound trunk"numbers := &livekit.ListUpdate{Set: []string{"+16265550100"}} // Replace existing listallowedNumbers := &livekit.ListUpdate{Add: []string{"+13105550100", "+17145550100"}} // Add to existing listtrunkId := "<sip-trunk-id>"trunkInfo := &livekit.SIPInboundTrunkUpdate{Name: &trunkName,Numbers: numbers,AllowedNumbers: allowedNumbers,}// Create a requestrequest := &livekit.UpdateSIPInboundTrunkRequest{SipTrunkId: trunkId,Action: &livekit.UpdateSIPInboundTrunkRequest_Update{Update: trunkInfo,},}api, err := lksdk.NewLiveKitAPI()if err != nil {fmt.Println(err)return}// Update trunktrunk, err := api.SIP().UpdateSIPInboundTrunk(context.Background(), request)if err != nil {fmt.Println(err)} else {fmt.Println(trunk)}}
import io.livekit.server.LiveKitAPIimport io.livekit.server.UpdateSipInboundTrunkOptionsval api = LiveKitAPI.createClient()val response = api.sip.updateSipInboundTrunk(sipTrunkId = trunkId,options = UpdateSipInboundTrunkOptions(name = "My updated trunk",numbers = listOf("+15105550123"))).execute()if (!response.isSuccessful) {println(response.errorBody())} else {val trunk = response.body()if (trunk != null) {println("Updated inbound trunk: ${trunk}")}}
use livekit_api::services::LiveKitApi;use livekit_protocol as proto;#[tokio::main]async fn main() {let api = LiveKitApi::new("https://my-project.livekit.cloud").unwrap();let update = proto::SipInboundTrunkUpdate {name: Some("My updated inbound trunk".to_owned()),numbers: Some(proto::ListUpdate { set: vec!["+16265550100".to_owned()], ..Default::default() }),allowed_numbers: Some(proto::ListUpdate {add: vec!["+13105550100".to_owned(), "+17145550100".to_owned()],..Default::default()}),..Default::default()};let trunk = api.sip().update_sip_inbound_trunk("<sip-trunk-id>".to_owned(), update).await.unwrap();println!("Updated inbound trunk: {:?}", trunk);}
Update and replace functions are the same in the LiveKit Cloud dashboard. For an example, see the replace inbound trunk section.
Replace inbound trunk
The UpdateSIPInboundTrunk API allows you to replace an existing inbound trunk with a new one using the same trunk ID.
The CLI doesn't support replacing inbound trunks.
import { LiveKitAPI, SIPInboundTrunkInfo } from 'livekit-server-sdk';const api = new LiveKitAPI();async function main() {// Replace an inbound trunk entirely.const trunk = new SIPInboundTrunkInfo({name: 'My replaced trunk',numbers: ['+17025550100'],metadata: 'Replaced metadata',allowedAddresses: ['192.168.254.10'],allowedNumbers: ['+14155550100', '+17145550100'],});const updatedTrunk = await api.sip.updateSipInboundTrunk(trunkId, trunk);console.log('replaced trunk ', updatedTrunk);}await main();
To replace an existing trunk, edit the previous example by adding the import line,trunk and calling the update_inbound_trunk function:
async def main():livekit_api = api.LiveKitAPI()trunk = api.SIPInboundTrunkInfo(numbers = ['+15105550100'],allowed_numbers = ["+13105550100", "+17145550100"],name = "My replaced inbound trunk",)# This takes positional parameterstrunk = await livekit_api.sip.update_inbound_trunk("<sip-trunk-id>", trunk)
require 'livekit'lkapi = LiveKit::LiveKitAPI.new# Replace an inbound trunk entirely.trunk = LiveKit::Proto::SIPInboundTrunkInfo.new(name: "My replaced inbound trunk",numbers: ["+15105550100"],allowed_numbers: ["+13105550100", "+17145550100"],)updated_trunk = lkapi.sip.update_sip_inbound_trunk("<sip-trunk-id>", trunk)puts updated_trunk
To replace the trunk, update the previous example with the following trunkInfo and request objects:
// To replace the trunk, use the SIPInboundTrunkInfo object.trunkInfo := &livekit.SIPInboundTrunkInfo{Numbers: numbers,AllowedNumbers: allowedNumbers,Name: trunkName,}// Create a request.request := &livekit.UpdateSIPInboundTrunkRequest{SipTrunkId: trunkId,// To replace the trunk, use the Replace action instead of Update.Action: &livekit.UpdateSIPInboundTrunkRequest_Replace{Replace: trunkInfo,},}
import io.livekit.server.LiveKitAPIimport livekit.LivekitSipval api = LiveKitAPI.createClient()// Replace an inbound trunk entirely.val trunk = LivekitSip.SIPInboundTrunkInfo.newBuilder().setName("My replaced inbound trunk").addAllNumbers(listOf("+15105550100")).addAllAllowedNumbers(listOf("+13105550100", "+17145550100")).build()val response = api.sip.updateSipInboundTrunk(trunkId, trunk).execute()if (!response.isSuccessful) {println(response.errorBody())} else {val updatedTrunk = response.body()if (updatedTrunk != null) {println("Replaced inbound trunk: ${updatedTrunk}")}}
use livekit_api::services::LiveKitApi;use livekit_protocol as proto;#[tokio::main]async fn main() {let api = LiveKitApi::new("https://my-project.livekit.cloud").unwrap();// Replace an inbound trunk entirely.let trunk = proto::SipInboundTrunkInfo {name: "My replaced inbound trunk".to_owned(),numbers: vec!["+15105550100".to_owned()],allowed_numbers: vec!["+13105550100".to_owned(), "+17145550100".to_owned()],..Default::default()};let trunk = api.sip().update_sip_inbound_trunk_replace("<sip-trunk-id>".to_owned(), trunk).await.unwrap();println!("Replaced inbound trunk: {:?}", trunk);}
Sign in to the Telephony → SIP trunks page.
Navigate to the Inbound section.
Find the inbound trunk you want to replace → select the more (⋮) menu → select Configure trunk.
Copy and paste the following text into the editor:
{"name": "My replaced trunk","numbers": ["+17025550100"],"metadata": "Replaced metadata","allowedAddresses": ["192.168.254.10"],"allowedNumbers": ["+14155550100","+17145550100"]}Select Update.