plain-summer-46051
07/27/2025, 11:39 AMtall-airline-16756
07/27/2025, 11:39 AMtall-airline-16756
07/27/2025, 11:40 AMapi_key
, api_secret
, ws_url
, and Redis connection details.
• Expose a public IP address for remote SIP peers to connect to.
2. Create an Inbound SIP Trunk:
• Use the CreateSIPTrunk
API or LiveKit CLI to create an inbound trunk. This trunk limits incoming calls to those from your SIP trunking provider.
• Example CLI command: Sources: src/setup-livekit.ts | Accepting inbound calls | LiveKit Docs | README.md | SIP inbound trunk | LiveKit Docsplain-summer-46051
07/27/2025, 11:43 AMtall-airline-16756
07/27/2025, 11:44 AM{
"rule": {
"dispatchRuleIndividual": {
"roomPrefix": "call-"
}
},
"roomConfig": {
"agents": [
{
"agentName": "my-telephony-agent"
}
]
}
}
• No worries – you can create this rule super easily with the LiveKit CLI:
lk sip dispatch create dispatch-rule.json
2. Agent Code for Answering Calls:
• When the call comes in, make sure your agent gives a warm welcome! Here's a quick example in Python:
await session.generate_reply(
instructions="Greet the user and offer your assistance."
)
3. TypeScript Setup Code:
• If you're more of a TypeScript fan, I've got you covered with a complete setup example:
import {SipClient} from 'livekit-server-sdk';
const sipClient = new SipClient(
LIVEKIT_URL,
LIVEKIT_API_KEY,
LIVEKIT_API_SECRET,
);
console.log('Setting up LiveKit SIP inbound trunk and dispatch rule');
// Create inbound trunk
const trunkName = 'inbound-trunk';
const trunks = await sipClient.listSipInboundTrunk();
let trunk = trunks.find(t => t.name === trunkName);
if (!trunk) {
console.log('Creating LiveKit SIP inbound trunk');
trunk = await sipClient.createSipInboundTrunk(
trunkName,
[TWILIO_PHONE_NUMBER],
{
auth_username: TWILIO_SIP_USERNAME,
auth_password: TWILIO_SIP_PASSWORD,
},
);
}
// Create dispatch rule
const dispatchRuleName = 'inbound-dispatch-rule';
const dispatchRules = await sipClient.listSipDispatchRule();
let dispatchRule = dispatchRules.find(r => r.name === dispatchRuleName);
if (!dispatchRule) {
console.log('Creating LiveKit SIP dispatch rule');
dispatchRule = await sipClient.createSipDispatchRule(
{
type: 'individual',
roomPrefix: 'call',
},
{
name: dispatchRuleName,
trunkIds: [trunk.sipTrunkId],
},
);
}
4. Running Your Agent:
• Ready to get things rolling? Just start your agent with:
python agent.py dev
One last pro tip: Make sure your SIP trunking provider is all set to send traffic to your LiveKit SIP instance, and those dispatch rules are ready to route calls to the right LiveKit rooms. You've got this!
Sources: Agents telephony integration | LiveKit Docs | README.md | Accepting inbound calls | LiveKit Docsplain-summer-46051
07/27/2025, 11:52 AMtall-airline-16756
07/27/2025, 11:53 AMlk app create --template voice-pipeline-agent-python
2. Complete Python Agent Code:
import asyncio
import logging
import os
from pathlib import Path
from dotenv import load_dotenv
from livekit.agents import JobContext, WorkerOptions, cli
from livekit import rtc
from livekit.agents.voice import Agent, AgentSession
from livekit.plugins import deepgram, openai, silero, elevenlabs
load_dotenv(dotenv_path=Path(__file__).parent / '.env')
logger = logging.getLogger("inbound-agent")
logger.setLevel(<http://logging.INFO|logging.INFO>)
class InboundAgent(Agent):
def __init__(self, job_context=None) -> None:
self.job_context = job_context
super().__init__(
instructions="""
You are a helpful AI assistant answering incoming phone calls.
Greet callers warmly and offer your assistance.
""",
stt=deepgram.STT(),
llm=openai.LLM(model="gpt-4o"),
tts=elevenlabs.TTS(model="eleven_multilingual_v2"),
vad=silero.VAD.load()
)
async def entrypoint(ctx: JobContext):
session = AgentSession()
agent = InboundAgent(job_context=ctx)
await session.start(
agent=agent,
room=ctx.room
)
# Greet the caller after picking up
await session.generate_reply(
instructions="Greet the user and offer your assistance."
)
def on_participant_connected_handler(participant: rtc.RemoteParticipant):
asyncio.create_task(async_on_participant_connected(participant))
async def async_on_participant_connected(participant: rtc.RemoteParticipant):
<http://logger.info|logger.info>(f"New participant connected: {participant.identity}")
# Check if this is a SIP participant
if participant.kind == rtc.ParticipantKind.PARTICIPANT_KIND_SIP:
<http://logger.info|logger.info>(f"SIP participant connected: {participant.identity}")
# Log SIP call information
if participant.attributes:
call_id = participant.attributes.get('sip.callID', 'Unknown')
call_status = participant.attributes.get('sip.callStatus', 'Unknown')
phone_number = participant.attributes.get('sip.phoneNumber', 'Unknown')
<http://logger.info|logger.info>(f"SIP Call ID: {call_id}")
<http://logger.info|logger.info>(f"SIP Call Status: {call_status}")
<http://logger.info|logger.info>(f"SIP Phone Number: {phone_number}")
await agent.session.say(f"Welcome, {participant.name or participant.identity}!")
ctx.room.on("participant_connected", on_participant_connected_handler)
if __name__ == "__main__":
cli.run_app(
WorkerOptions(
entrypoint_fnc=entrypoint,
# Give this agent a name for dispatch
agent_name="inbound-agent",
),
)
3. Environment Setup:
• Let's get your environment variables sorted – it's super quick!
export LIVEKIT_URL=<your livekit server url>
export LIVEKIT_API_KEY=<your api key>
export LIVEKIT_API_SECRET=<your api secret>
4. SIP Trunk Configuration:
• Create a friendly inbound trunk configuration file `inbound-trunk.json`:
{
"trunk": {
"name": "My inbound trunk",
"numbers": [
"+15105550100"
]
}
}
• And create that trunk with just one command:
lk sip inbound create inbound-trunk.json
5. Dispatch Rule Setup:
• Whip up a dispatch rule file `dispatch-rule.json`:
{
"dispatch_rule": {
"rule": {
"dispatchRuleIndividual": {
"roomPrefix": "call-"
}
},
"roomConfig": {
"agents": [
{
"agentName": "inbound-agent"
}
]
}
}
}
• Create the dispatch rule – you're almost there!
lk sip dispatch create dispatch-rule.json
6. Running the Agent:
• Time to bring your agent to life:
python agent.py dev
And just like that, you're all set! Once your agent is up and running, you can dial the configured phone number and start chatting with your awesome AI-powered assistant. How cool is that? 😊
Sources: Integrating with Telephony | LiveKit Docs | Accepting incoming calls | LiveKit Docs | README.md | Agents telephony integration | LiveKit Docsplain-summer-46051
07/27/2025, 12:04 PMtall-airline-16756
07/27/2025, 12:05 PMtwilio api trunking v1 trunks create \
--friendly-name "My test trunk" \
--domain-name "<http://my-test-trunk.pstn.twilio.com|my-test-trunk.pstn.twilio.com>"
Step 2: Configure Origination URL
• Now, we'll point your trunk to your LiveKit SIP host:
twilio api trunking v1 trunks origination-urls create \
--trunk-sid <twilio_trunk_sid> \
--friendly-name "LiveKit SIP URI" \
--sip-url "sip:<your_sip_host>" \
--weight 1 --priority 1 --enabled
Step 3: Associate Phone Number with Trunk
• Time to link your Twilio phone number to the SIP trunk:
twilio api trunking v1 trunks phone-numbers create \
--trunk-sid <twilio_trunk_sid> \
--phone-number-sid <twilio_phone_number_sid>
Approach 2: Twilio Programmable Voice
Step 1: Set up a TwiML Bin
• We'll create a quick TwiML Bin with this handy snippet:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial>
<Sip username="<sip_trunk_username>" password="<sip_trunk_password>">
sip:<your_phone_number>@<your_sip_host>
</Sip>
</Dial>
</Response>
Step 2: Configure Phone Number
• Head over to your Twilio Manage Numbers page, find your phone number, and set the "A call comes in" field to use the TwiML Bin we just created.
Required Environment Variables
• Don't forget to set these environment variables - they're super important:
LIVEKIT_SIP_URI=<your_livekit_sip_uri>
TWILIO_ACCOUNT_SID=<your_twilio_account_sid>
TWILIO_AUTH_TOKEN=<your_twilio_auth_token>
TWILIO_PHONE_NUMBER=<your_twilio_phone_number>
TWILIO_SIP_USERNAME=<your_twilio_sip_username>
TWILIO_SIP_PASSWORD=<your_twilio_sip_password>
Quick pro tip: The Elastic SIP Trunking approach is our recommended route for full SIP functionality. The Programmable Voice method is simpler but has a few more limitations. Got questions? I'm right here to help you out!
Sources: Create and configure a Twilio SIP trunk | LiveKit Docs | .env.example | Inbound calls with Twilio Voice | LiveKit Docsplain-summer-46051
07/28/2025, 4:52 PMtall-airline-16756
07/28/2025, 4:53 PMauth_username
and auth_password
are matching up perfectly between LiveKit and your SIP provider. It's like making sure your keys fit the right lock!
• Take a quick peek at your allowed_addresses
to make sure all your SIP provider's IP addresses are included.
• Watch out for flood protection – if too many calls are coming from the same spot, it might trigger a block.
2. Missing Dispatch Rule:
• Make sure you've got a dispatch rule set up for incoming calls.
• Confirm that the rule is linked to the right trunk_ids
.
• Do a quick check that the rule covers all the phone numbers you're expecting to receive calls on.
3. SIP Provider Setup:
• Verify that your SIP trunk credentials are matching up on both sides – LiveKit and your provider.
• Ensure your LiveKit SIP service IP is on your provider's approved list.
• Double-check that calls are actually being routed to your LiveKit SIP instance.
I know this might seem a bit technical, but we'll get through it together! If you're stuck on any of these steps or need a bit more guidance, just give me a shout. We've got this! 💪
Sources: Accepting inbound calls | LiveKit Docs | test/lktest-sip-outbound/README.md | livekit/livekit_sip.pb.goplain-summer-46051
07/28/2025, 4:54 PMtall-airline-16756
07/28/2025, 4:56 PMauth_username
and auth_password
fields in the trunk configuration. Here's a quick example to help you out:
{
"trunk": {
"name": "My inbound trunk",
"auth_username": "<sip_trunk_username>",
"auth_password": "<sip_trunk_password>"
}
}
• No worries if that looks complicated - you can also use the LiveKit CLI, which is super straightforward:
lk sip inbound create --auth-user "username" --auth-pass "password"
• Or if you're more of a code person, here's how you can do it programmatically with the SDK (Node.js example):
await sipClient.createSipInboundTrunk(name, numbers, {
authUsername: "username",
authPassword: "password"
});
For Outbound SIP Trunks:
• Same deal here - just set those auth_username
and auth_password
fields. Here's a handy CLI command:
lk sip outbound create --auth-user "username" --auth-pass "password"
The key is making sure your credentials match exactly what your SIP provider is expecting. Don't worry if something doesn't look right - I'm here to help! Feel free to reach out if you need any extra guidance or run into any hiccups.
Sources: SIP APIs | LiveKit Docs | Inbound calls with Twilio Voice | LiveKit Docs | cmd/lk/sip.go | packages/livekit-server-sdk/src/SipClient.ts | protobufs/livekit_sip.proto