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) and LIVEKIT_SIP_NUMBER (caller ID) before starting your agent.
  • Place outbound calls: Use JobContext.add_sip_participant() from livekit/agents/job.py to dial PSTN numbers programmatically.
  • Handle complex workflows: Implement WarmTransferTask for 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 RoomOptions with BVCTelephony() noise cancellation and appropriate participant_kinds for 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →