Key Takeaways
- Self-hosted LiveKit handles WebRTC media and SIP signalling, but it does not terminate calls on the public telephone network — a SIP trunk provider like Cloudonix bridges it to carriers and real phone numbers.
- The integration is two-sided: LiveKit exposes a per-project SIP URI (Dashboard → Telephony → Configuration); Cloudonix registers that URI as a service provider and routes calls to it.
- Inbound routing is a single CXML document:
<Response><Dial><Service provider="your_livekit_alias">+E164</Service></Dial></Response>.- Cloudonix's built-in SBC handles transcoding and TCP/UDP/TLS negotiation, so you don't operate a session border controller fleet to reach legacy carriers.
A self-hosted LiveKit stack gives you real-time WebRTC voice with no cloud-tier concurrent-session ceiling. What it does not give you is a phone number. LiveKit routes media between participants; it does not, on its own, terminate a call on the public switched telephone network (PSTN). That last mile — from a browser-only voice agent to "dial this number and talk to it" — is a SIP trunking problem.
LiveKit SIP trunking with Cloudonix connects your self-hosted LiveKit deployment to the PSTN. LiveKit exposes a per-project SIP URI; Cloudonix registers that URI as a service provider, handles carrier-side SIP negotiation and transcoding through its built-in SBC, and routes inbound phone calls to your agents with a short CXML call-flow document. Outbound calls run the same path in reverse.
Why LiveKit Needs a SIP Trunk to Reach the Phone Network
LiveKit is a media plane, not a carrier. It runs WebRTC signalling and an SFU for media routing, and its SIP service can accept and place SIP calls — but it has no relationship with telephone carriers, no DID (phone number) inventory, and no session border controller to negotiate with the messy variety of SIP dialects real carriers speak. A SIP trunk is the commercial and technical link between your SIP-capable infrastructure and the PSTN.
Cloudonix is a communications platform (CPaaS) whose core product is "ultra elastic" SIP trunking: it connects any SIP-compatible carrier, cloud platform, or on-premise system, and ships a built-in SBC that handles transcoding plus TCP, UDP, and TLS transport negotiation (Cloudonix Programmable Voice). For a self-hosted LiveKit deployment, that means you point LiveKit at Cloudonix instead of integrating with carriers directly — and you skip building and operating an SBC.
The alternative is to terminate SIP yourself: provision DIDs from carriers, stand up a Kamailio or Asterisk SBC, manage transcoding between Opus (what your WebRTC clients use) and the codecs carriers send, and keep TLS and NAT traversal working under load. That is a multi-week infrastructure project before a single phone call connects. A SIP trunk collapses it into provider registration plus call-flow configuration.
How LiveKit SIP Trunking with Cloudonix Works
The connection has two halves that meet at a SIP URI. LiveKit's SIP service exposes a unique URI for your project; Cloudonix registers that URI as a named service provider and routes calls to it. Once both sides know about each other, an inbound phone call lands on Cloudonix, gets dialed into your LiveKit SIP URI, enters a LiveKit room, and an agent worker joins to answer it. Here is the full path.
The LiveKit side: SIP URI, trunks, and dispatch rules
On the LiveKit side, every project is assigned a unique SIP URI, which Cloudonix's documentation locates in the LiveKit Dashboard under Telephony → Configuration (Cloudonix LiveKit docs). LiveKit's own SIP service then needs three objects, documented in LiveKit's SIP guides: an inbound trunk that accepts calls arriving at your URI, an optional outbound trunk for placing calls, and a dispatch rule that decides which room an incoming call lands in and triggers agent dispatch. The dispatch rule is what turns a raw SIP call into "a participant in a room an agent can join."
This is the same agent-worker model Prodinit deployed for a voice ai client: a self-hosted LiveKit server for signalling and media, standalone Python agent workers running a VAD → STT → LLM → TTS pipeline, and LiveKit Egress for recording — all on the client's own infrastructure with zero concurrent-session limits. The SIP trunk simply adds a telephony entry point to that existing room-and-agent architecture; the agent code that answers a WebRTC call answers a phone call the same way.
The Cloudonix side: register the provider, route with CXML
On the Cloudonix side, you register your LiveKit SIP URI as a service provider. Per Cloudonix's documentation, you submit the LiveKit SIP URI plus a desired alias to Cloudonix (through their Discord support channel), and the new service provider is enabled within 24–48 hours (Cloudonix LiveKit docs). Once enabled, that alias is how CXML refers to your LiveKit deployment.
CXML (Cloudonix Markup Language) is Cloudonix's call-flow scripting language — structurally similar to Twilio's TwiML but not a drop-in replacement, with some verbs absent and others added (Cloudonix). To send an inbound call to LiveKit, you return a CXML document that dials the <Service> noun targeting your alias.
Setting Up Inbound Calls: From a Phone Number to Your Agent
Inbound is the common case — a customer dials your number and reaches the agent. With the LiveKit provider registered, the entire routing instruction is one CXML document. When a call arrives on a Cloudonix number, Cloudonix requests your voice-application URL and you respond with:
<Response>
<Dial>
<Service provider="your_livekit_alias">+12127773456</Service>
</Dial>
</Response>
That <Dial><Service> directive hands the call to your LiveKit SIP URI (Cloudonix LiveKit docs). From there, LiveKit's inbound trunk accepts it, the dispatch rule places the caller into a room, and your agent worker joins and starts the conversation. The phone number in the <Service> body is in E.164 format — the international +countrycode... convention every step in this chain expects.
The clean separation here is worth calling out: Cloudonix owns the carrier relationship and the call-flow logic; LiveKit owns the media and the agent. Your agent code never parses SIP. It receives a participant in a room and runs the same pipeline it would for a browser caller.
Outbound Calls, BYOC, and the SBC You Don't Build
Outbound calls — your agent dialing a customer — run the same trunk in reverse, with LiveKit's outbound trunk placing a call through Cloudonix to the destination number. Cloudonix also supports bring-your-own-carrier (BYOC): you can route through any SIP-compatible carrier you already have a contract with rather than buying termination from a single vendor, which matters when you have negotiated rates or regulatory requirements in specific regions.
The quietly valuable part is the session border controller. Reaching real carriers means handling codec transcoding, transport negotiation across TCP, UDP, and TLS, and the SIP-dialect inconsistencies that make direct carrier integration painful. Cloudonix's built-in SBC absorbs that (Cloudonix Programmable Voice). Note that Cloudonix does not publish specific codec or end-to-end latency figures for the LiveKit path, so validate audio quality and latency against your own targets in a staging trunk before production — we treat sub-800ms p95 as the reliability floor for conversational voice, and PSTN legs add transcoding and carrier hops that a pure-WebRTC test never sees.
Get Prodinit's AI engineering guides in your inbox
Deep-dives on production LLMs, voice AI, and MLOps — published weekly. No sales emails.
Frequently Asked Questions
No. LiveKit's SIP service can speak SIP, but it has no carrier connectivity, no phone-number inventory, and no SBC to negotiate with real carriers. To accept or place PSTN calls you need a SIP trunk provider — such as Cloudonix — that bridges LiveKit to carriers and supplies phone numbers. LiveKit handles the media and agent; the trunk handles the telephone network.
The technical configuration is fast, but there is a provisioning step. You retrieve your LiveKit SIP URI from the Dashboard (Telephony → Configuration) and submit it with a chosen alias to Cloudonix, which enables the new service provider within 24–48 hours per their documentation. After that, inbound routing is a one-line CXML <Dial><Service> change, and outbound uses LiveKit's outbound trunk.
CXML (Cloudonix Markup Language) is Cloudonix's XML call-flow scripting language. It deliberately resembles Twilio's TwiML so teams familiar with TwiML feel at home, but Cloudonix states it is not identical — some TwiML verbs are unsupported and CXML adds verbs of its own. Treat existing TwiML as a starting reference, not a guaranteed drop-in, and verify each verb against the Cloudonix docs.
Yes. Cloudonix supports bring-your-own-carrier (BYOC), so you can route outbound calls through a SIP-compatible carrier you already use while still using Cloudonix's trunk and SBC to reach LiveKit. This preserves negotiated rates and lets you meet region-specific routing or regulatory requirements without abandoning the LiveKit-plus-Cloudonix architecture.