How to Configure Telephony Integration with SIP in LiveKit Agents
To configure telephony integration with SIP in LiveKit Agents, provision a LiveKit SIP trunk, export the LIVEKIT_SIP_OUTBOUND_TRUNK and LIVEKIT_SIP_NUMBER environment variables, and use JobContext.add_sip_participant() to place outbound calls or handle inbound calls via dispatch rules.
LiveKit Agents enables AI voice agents to place outbound calls and receive inbound calls through the PSTN using SIP (Session Initiation Protocol) integration. This guide covers the complete setup process using the livekit/agents repository, including trunk configuration, API implementation, and advanced warm-transfer workflows.
Prerequisites and Environment Configuration
Before implementing SIP functionality, you must provision a SIP trunk on your LiveKit server and configure the required environment variables.
First, create an outbound SIP trunk through the LiveKit console or API, noting the trunk ID (formatted as ST_xxxxxx). Then export the following variables in your agent's runtime environment:
export LIVEKIT_URL=wss://your.livekit.server
export LIVEKIT_API_KEY=your_api_key
export LIVEKIT_API_SECRET=your_api_secret
# Outbound trunk ID from your LiveKit SIP configuration
export LIVEKIT_SIP_OUTBOUND_TRUNK=ST_abcxyz
# Caller ID that appears on the recipient's phone (E.164 format)
export LIVEKIT_SIP_NUMBER=+15005006000
Placing Outbound SIP Calls
Once configured, agents can initiate PSTN calls programmatically. The system treats SIP participants as standard remote participants within LiveKit rooms, allowing bidirectional audio streaming and standard agent API interactions.
Direct SIP Dialing with add_sip_participant
Use the add_sip_participant method on the JobContext object to dial a phone number and add the resulting SIP participant to the current room. This method is implemented in livekit-agents/livekit/agents/job.py#L528-L570.
import os
from livekit.agents import JobContext
async def dial_outbound(ctx: JobContext):
# Initiate the call to a PSTN number
sip_future = ctx.add_sip_participant(
call_to="+15551234567", # Destination phone number (E.164)
trunk_id=os.getenv("LIVEKIT_SIP_OUTBOUND_TRUNK"),
participant_identity="customer-call",
)
# Wait for the call to be answered (optional, based on wait_until_answered parameter)
sip_info = await sip_future # Returns api.SIPParticipantInfo
By default, add_sip_participant returns immediately. Pass wait_until_answered=True to block until the callee answers.
Implementing Warm Transfers
For call-center scenarios requiring "hold-and-transfer" functionality, use the WarmTransferTask class from the beta workflows module. This high-level helper automates creating a supervisor room, dialing via SIP, playing hold music, and merging calls upon connection.
The implementation resides in [livekit-agents/livekit/agents/beta/workflows/warm_transfer.py](https://github.com/livekit/agents/blob/main/livekit-agents/livekit/agents/beta/workflows/warm_transfer.py).
from livekit.agents.beta.workflows import WarmTransferTask
async def transfer_to_supervisor(ctx: JobContext):
result = await WarmTransferTask(
target_phone_number="+15559876543", # Supervisor's phone number
sip_trunk_id=os.getenv("LIVEKIT_SIP_OUTBOUND_TRUNK"),
sip_number=os.getenv("LIVEKIT_SIP_NUMBER"),
hold_audio=AudioConfig.BuiltinAudioClip.HOLD_MUSIC,
)
# Access the supervisor's participant identity after connection
supervisor_id = result.human_agent_identity
Handling Inbound SIP Calls
To receive incoming PSTN calls, define a dispatch rule that routes SIP traffic to a specific agent entrypoint. When a call arrives, LiveKit creates a room and invokes your registered handler with the SIP participant already connected.
from livekit.agents import AgentServer, AgentSession, JobContext
server = AgentServer()
@server.rtc_session(agent_name="sip-inbound")
async def inbound_entrypoint(ctx: JobContext):
# SIP caller is available in ctx.room.remote_participants
session = AgentSession(
vad=silero.VAD.load(),
llm="openai/gpt-4.5-mini",
stt="deepgram/nova-3:en",
tts="cartesia/sonic-3:default",
)
await session.start(agent=my_agent, room=ctx.room)
See the complete inbound handling implementation in [examples/warm-transfer/warm_transfer.py](https://github.com/livekit/agents/blob/main/examples/warm-transfer/warm_transfer.py).
Room Configuration for SIP Participants
By default, RoomOptions accepts both standard and SIP participants. You can explicitly control participant acceptance using the participant_kinds parameter defined in livekit-agents/livekit/agents/voice/room_io/types.py#L18-L22.
from livekit import rtc
from livekit.agents.voice.room_io import RoomOptions
# Restrict room to SIP participants only
room_opts = RoomOptions(
participant_kinds=[rtc.ParticipantKind.PARTICIPANT_KIND_SIP]
)
For telephony-specific audio processing, enable noise cancellation optimized for PSTN audio:
from livekit.plugins import noise_cancellation
from livekit.agents.voice.room_io import AudioInputOptions
room_opts = RoomOptions(
audio_input=AudioInputOptions(
noise_cancellation=noise_cancellation.BVCTelephony()
)
)
Summary
- Configure environment variables: Export
LIVEKIT_SIP_OUTBOUND_TRUNK(trunk ID) andLIVEKIT_SIP_NUMBER(caller ID) before starting your agent. - Place outbound calls: Use
JobContext.add_sip_participant()fromlivekit/agents/job.pyto dial PSTN numbers programmatically. - Handle complex workflows: Implement
WarmTransferTaskfor hold-and-transfer scenarios without manual room management. - Receive inbound calls: Register dispatch rules for SIP traffic; participants appear as standard remote participants in
ctx.room. - Optimize configuration: Set
RoomOptionswithBVCTelephony()noise cancellation and appropriateparticipant_kindsfor telephony-specific behavior.
Frequently Asked Questions
What environment variables are required for SIP integration?
You must export LIVEKIT_SIP_OUTBOUND_TRUNK containing your outbound trunk ID (e.g., ST_abcxyz) and LIVEKIT_SIP_NUMBER containing the E.164 caller ID (e.g., +15005006000). These variables are read by the agent process to authenticate and route calls through your LiveKit SIP infrastructure.
How do I handle inbound versus outbound SIP calls differently?
For outbound calls, use ctx.add_sip_participant() within an active agent session to dial a number and add the resulting participant to the current room. For inbound calls, create a dispatch rule that routes SIP traffic to a specific agent entrypoint; the SIP participant will already be present in ctx.room.remote_participants when your handler executes.
What is the difference between add_sip_participant and WarmTransferTask?
add_sip_participant is a low-level API that dials a single phone number and adds the participant to your room. WarmTransferTask is a high-level workflow that manages complex call-center scenarios: it creates a separate supervisor room, plays hold music to the original caller, dials the supervisor via SIP, and automatically merges the calls when the supervisor answers.
How do I configure caller ID for outbound SIP calls?
Set the LIVEKIT_SIP_NUMBER environment variable to the E.164 phone number you want displayed on the recipient's caller ID. This number must be associated with your LiveKit SIP trunk configuration. When using add_sip_participant or WarmTransferTask, the system uses this variable as the default caller ID for all outbound PSTN calls.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →