twiML dial sip is not able to connect to livekit s...
# ask-ai
r
twiML dial sip is not able to connect to livekit sip trunk
t
Thanks for reaching out, Saurabh. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I’m sorry you’re hitting this SIP trunk connection issue. I can help troubleshoot, but I need a few details to narrow down the cause. Please share the following information (redact any sensitive credentials): 1. Environment & basic setup ◦ Are you using Twilio (TwiML) to Dial SIP into a LiveKit SIP trunk? ◦ LiveKit deployment: cloud/livekit hosted or self-hosted (on-prem / cloud region)? ◦ Are you using TLS (sips) or plain UDP/TCP (sip) for the SIP transport? 1. SIP addressing & authentication ◦ The exact SIP URI you’re dialing (example:
<sip:username@sip.exampl>e.com
) — redact passwords. ◦ Does your LiveKit SIP trunk require authentication (username/password or IP ACL)? If yes, which method? ◦ If using credentials, confirm Twilio is configured to send them (TwiML Dial attributes or Twilio SIP domain settings). 1. TwiML (the exact snippet) ◦ Paste the TwiML Dial/SIP element you’re using (or the relevant portion of your app that generates it). For example:
Copy code
<Dial>
       <Sip><sip:user@sip.exampl>e.com</Sip>
     </Dial>
• Any custom SIP headers you set? 1. Errors & logs ◦ The exact error message you see in Twilio (call logs, call attempt status) or in your LiveKit logs. ◦ If available, SIP traces (INVITE/401/407/486/etc.) from Twilio or LiveKit (redact sensitive fields). The INVITE and final response are especially helpful. 1. Network / firewall details ◦ Are there firewalls or NAT between Twilio and LiveKit? Have you opened SIP ports (UDP/TCP 5060 and/or TLS 5061) and RTP ports required by LiveKit? ◦ If self-hosted, does your LiveKit server have a public IP or reachable hostname and valid certificate for TLS? 1. Reproduction & timing ◦ Is the failure for all calls or intermittent? When did it start? ◦ Can you reproduce consistently? If yes, note a recent call SID/timestamp so I can examine patterns you paste here. What I’ll check next once you share the details: • Verify TwiML syntax and credentials format. • Identify SIP transport or authentication mismatches (e.g., Twilio sending INVITE without auth while LiveKit expects it). • Look for network/firewall or TLS certificate issues. • Suggest exact TwiML changes, LiveKit SIP trunk config adjustments, and test steps (including SIP trace extraction). If you paste the TwiML and any INVITE/response lines you have, I can point to the likely failure point and give step-by-step fixes.
r
what is username ?
t
Thanks — good question. I’m responding to your ask: “what is username?” in the context of a TwiML <Dial><Sip> connecting to a LiveKit SIP trunk. In short: • The “username” in a SIP URI like
sip:username@domain
is the user portion of the SIP address — the identifier the SIP provider expects (for example a trunk account name or endpoint ID). • When SIP uses username/password (SIP digest) authentication, the “username” is the authentication account name provided by the SIP provider (often a trunk credential or account SID), and it’s paired with a password. • LiveKit itself does not require a per-user SIP digest credential for trunking. LiveKit SIP integrations commonly use IP allowlisting or token/JWT-based methods rather than exposing a traditional SIP username/password for each endpoint. What this means for a TwiML Dial to LiveKit: • If Twilio (TwiML) is dialing a third-party SIP provider, put the SIP provider’s expected username in the SIP URI or in Twilio’s trunk credential set (e.g.,
<sip:username@sip.provid>er.com
or Twilio trunk credentials). • If you’re dialing LiveKit directly, LiveKit typically expects connectivity via the trunk configuration you set up (IP allowlist, TLS with valid certs, or token-based headers) instead of a simple SIP digest username/password. If you want, tell me: • The exact SIP URI or TwiML snippet you’re using (you can redact passwords), and • Whether your LiveKit trunk is configured for IP allowlisting or for credential-based auth. With that I can point out exactly where the “username” should go and recommend the right TwiML or LiveKit trunk configuration. Sources: How Vicidial Handles Bridging, Transfers, and SIP Calls | sip | livekit/livekit_composite | DeepWiki | How VICIdial handles bridging, transfers, and SIP calls | LiveKit
r
am I passing username incorrectly
Copy code
return fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Dial>
    <Sip username="%s" password="%s">
      sip:%s@%s%s
    </Sip>
  </Dial>
</Response>`, username, password, toNumber, sipHost, queryString)
}
t
Thanks — you’re asking whether you’re passing the
username
incorrectly in that TwiML snippet. I’ll address that and explain what to do next. Short answer • If LiveKit is the endpoint you’re dialing directly, the
username
and
password
attributes on TwiML’s
<Sip>
element will not authenticate against LiveKit in the way a traditional SIP digest account would. LiveKit SIP trunking typically relies on IP allowlisting or token/JWT-based mechanisms rather than per-call SIP digest credentials. • If you are actually dialing a third‑party SIP provider (e.g., Twilio trunk → another SIP provider) that expects SIP digest auth, then embedding credentials in the SIP URI or configuring Twilio trunk credential sets is the correct approach — not necessarily the TwiML element attributes as you have them. What’s happening with your snippet • Your code builds a TwiML
<Sip username="..." password="...">sip:toNumber@sipHost{query}</Sip>
. Twilio does support `username`/`password` attributes on
<Sip>
for trunk credential use, but whether those credentials will be used to authenticate depends on the SIP endpoint’s expectations. • LiveKit does not use per-call SIP digest username/passwords for trunking. If LiveKit expects IP allowlisting or token headers, those
<Sip>
attributes will be ignored by LiveKit and the call will fail to authenticate. How to fix or verify depending on your setup 1. If LiveKit is the SIP endpoint: ◦ Confirm how your LiveKit trunk is configured: IP allowlist, TLS with certs, or header/token-based authentication. ◦ Use IP allowlisting (whitelist Twilio IPs) or configure the token/header-based method in the LiveKit trunk configuration — do not rely on `username`/`password` attributes. ◦ Ensure SIP transport (TLS vs plain SIP) and ports (5060/5061) and certificates are correct and reachable. 1. If the SIP endpoint expects SIP digest auth (a third‑party SIP provider): ◦ You can provide credentials in TwiML attributes, but often the recommended pattern is to use Twilio Trunk credential lists or to embed credentials in the SIP URI: ▪︎ Example SIP URI with credentials (redact in logs):
sip:username:password@sip.example.com
◦ Alternatively, set the Credential List in the Twilio SIP Domain settings and reference it, so Twilio will supply correct auth on outbound INVITE. Concrete next steps I can help with • Tell me which endpoint you are dialing: ◦ A LiveKit trunk (hosted at a LiveKit domain / your LiveKit server), or ◦ A third‑party SIP provider/trunk (e.g., another SIP service)? • Share the LiveKit trunk auth method (IP allowlist, token/JWT, or other). If you don’t know, I can list how to check. • If you are dialing a non‑LiveKit SIP provider, paste the TwiML you’re generating (redact passwords) and the Twilio call log errors or SIP responses (INVITE/401/407/503). I’ll point out exactly where the username should be placed. If you confirm which of the two cases above applies, I’ll give the exact TwiML or LiveKit configuration changes to try.
r
A LiveKit trunk
t
Thanks — that helps. You said you’re dialing a LiveKit trunk. I’ll explain what’s happening with your TwiML and what to do next. Quick answer • LiveKit trunks do not use per-call SIP digest username/password the way a traditional SIP endpoint does. Putting `username`/`password` attributes on TwiML’s
<Sip>
will not authenticate to LiveKit. • To connect Twilio → LiveKit you should use whatever auth method your LiveKit trunk is configured for (typically IP allowlisting or token/header-based auth and correct TLS/cert setup), and ensure Twilio can reach the LiveKit host and ports. Why your current TwiML is likely failing • Your snippet:
Copy code
<Sip username="%s" password="%s">
    sip:%s@%s%s
  </Sip>
sets SIP digest credentials for Twilio to use. LiveKit does not expect SIP digest credentials for its trunks, so those attributes will be ignored by LiveKit and authentication will fail. • If LiveKit expects IP-based trust or special headers/JWTs, the correct solution is to configure the LiveKit trunk and Twilio network settings accordingly — not to pass a username/password in the TwiML. What to do next — checklist and concrete steps 1. Confirm LiveKit trunk auth method ◦ Tell me whether your LiveKit trunk is set up with IP allowlisting, token/JWT or header-based auth, or something else. 1. If LiveKit uses IP allowlisting (common): ◦ Whitelist Twilio’s outbound SIP IPs on your LiveKit side (or allow Twilio’s SIP traffic). ◦ Make sure LiveKit’s SIP port (UDP/TCP 5060 or TLS 5061) and RTP ports are reachable from Twilio. ◦ Use TwiML without username/password, e.g.:
Copy code
<Response>
       <Dial>
         <Sip><sip:destination@your-livekit-host.exampl>e.com</Sip>
       </Dial>
     </Response>
• Ensure TLS is used if LiveKit requires it (use
sips:
and port 5061). 1. If LiveKit uses token/header-based auth: ◦ Configure Twilio to send the required headers or tokens (Twilio supports custom SIP headers in
<Sip>
). ◦ Example (how header might look — adapt to your required header name/value):
Copy code
<Sip>
       <sip:destination@your-livekit-host.exampl>e.com
       <Header name="X-Auth-Token">your-token-value</Header>
     </Sip>
• Or configure a SIP proxy/relay that injects tokens before reaching LiveKit. 1. Verify TLS & DNS ◦ If you use
sips:
, ensure your LiveKit host has a valid TLS certificate for the hostname Twilio is connecting to. ◦ Make sure the SIP host in the URI matches the cert CN/SAN. 1. Collect logs if it still fails ◦ Twilio call logs and SIP traces (INVITE and final response) and LiveKit logs (INVITE arrival and reject reason) will show the rejection cause. Paste those (redact secrets) and I’ll point to the exact failure. If you tell me: • Which auth method your LiveKit trunk is configured for (IP allowlist or token/header), and • The TwiML you’re currently sending (redact passwords) and any Twilio or LiveKit error messages or SIP responses you see, I’ll give exact TwiML examples and configuration steps tailored to that method.
r
how to setup token/JWT or header-based auth ?
t
Thanks — I’ll walk you through how to secure a TwiML → LiveKit SIP trunk using token/JWT and header-based approaches, and which method to use for each purpose. You said you’re dialing a LiveKit trunk, so this will focus on how LiveKit expects authentication and how to configure Twilio/TwiML accordingly. Key point up front • LiveKit uses JWTs for API/server authentication (managing trunks, dispatch rules, creating SIP participants). LiveKit does NOT rely on traditional per-call SIP digest username/password for trunk authentication. For SIP signaling from providers like Twilio, the recommended pattern is IP allowlisting (trust Twilio IPs) plus optional validation using custom SIP headers that LiveKit can map into participant attributes. 1) JWT (what it’s for) • Purpose: JWTs authenticate your server when calling LiveKit APIs or SDKs to create/manage trunks, dispatch rules, or to create SIP participants programmatically. • Where to use: Generate server-side and use when calling LiveKit REST/SDK endpoints (not normally embedded as SIP digest credentials). • What a token contains: SIP-related grants such as
SIPAdmin
and
SIPCall
which permit trunk management and programmatic call actions. • Result: Use the JWT to create and configure your LiveKit SIP trunk and routing, not as the primary SIP transport credential Twilio will use on the wire. 2) Primary SIP transport authentication — IP allowlisting (recommended) • For Twilio → LiveKit trunks, the usual secure option is to whitelist Twilio’s outbound SIP IP addresses on your LiveKit server/firewall. • Steps: a. Collect Twilio’s outbound SIP IP addresses (Twilio publishes these). b. Add those IP addresses to your LiveKit instance / firewall allowlist so INVITEs from Twilio are accepted. c. Ensure the SIP transport and ports are reachable (UDP/TCP 5060 or TLS 5061) and that TLS certs match the SIP host if using
sips:
. • Why: This treats Twilio as a trusted, authenticated source without requiring SIP digest credentials per call. 3) Optional: Header-based authentication for additional validation • Purpose: Add another layer so LiveKit can extract and validate credentials carried as SIP headers (for example an API key or token injected by Twilio). • How it works: ◦ Configure Twilio to add custom SIP headers in the outbound INVITE from your TwiML or Twilio trunk settings. ◦ Configure the LiveKit trunk to map specific SIP headers into participant attributes using
headers_to_attributes
in the trunk config. ◦ Your backend or LiveKit routing logic validates those attributes and decides whether to accept the call. • Example TwiML (conceptual):
Copy code
<Response>
    <Dial>
      <Sip>
        <sip:destination@your-livekit-host.exampl>e.com
        <Header name="X-Auth-Token">your-token-value</Header>
      </Sip>
    </Dial>
  </Response>
• Note: exact header names and values depend on how your LiveKit trunk is configured to read them. • LiveKit side mapping (conceptual JSON): { "trunk": { "headers_to_attributes": { "X-API-Key": "auth.api_key", "X-Auth-Token": "auth.jwt" } } } • Then validate these attributes in your application (or via LiveKit hooks) to permit the incoming call. 4) Practical recommended flow 1. Use JWT server-side to create/configure the LiveKit inbound trunk and any dispatch rules. 2. Whitelist Twilio IPs in your LiveKit firewall/trunk settings. 3. If you need extra verification, have Twilio add a custom header with a token; configure LiveKit to map that header to an attribute and validate it. 4. Use simple TwiML without SIP digest username/password when calling LiveKit:
Copy code
<Response>
     <Dial>
       <Sip><sip:destination@your-livekit-host.exampl>e.com</Sip>
     </Dial>
   </Response>
or include a header if you are using header-based auth (see example above). 5) TLS, DNS, and connectivity checks • If you use
sips:
ensure the certificate on your LiveKit host covers the hostname Twilio connects to. • Validate that Twilio can resolve your SIP host and reach the required ports. • If calls still fail, collect Twilio SIP INVITE and LiveKit logs to see whether the INVITE reaches LiveKit and how LiveKit rejects/accepts it. What I can do next • If you want, paste the LiveKit trunk auth method (IP allowlist or header mapping) and your current TwiML (redact secrets). I’ll give an exact TwiML snippet and the LiveKit trunk config you should use. • If calls are failing now, share the Twilio SIP INVITE and the LiveKit log response (redact secrets) and I’ll point to the precise mismatch. Would you like to: • Walk through generating the JWT you need to create the trunk, or • See an exact TwiML example with a header that matches a sample LiveKit
headers_to_attributes
mapping? Sources: server-sdk-go | livekit/livekit_composite | DeepWiki | sip | livekit/livekit_composite | DeepWiki | How VICIdial handles bridging, transfers, and SIP calls | LiveKit | How to do call transfers with Five9 | LiveKit | server-sdk-ruby | livekit/livekit_composite | DeepWiki
r
what is destination in SIP uri ?
t
Good question — I’ll explain what “destination” means in a SIP URI and how to pick the right value when dialing into a LiveKit trunk. Short answer • The destination is the user portion of the SIP URI (the part before the
@
) that identifies the target endpoint you want to reach. It can represent a phone number, an extension, a LiveKit room prefix, or a participant identity depending on how your trunk/dispatch rules are configured. Examples and how they map to LiveKit • sip:+15551234567@your-livekit-host.example.com ◦ Destination:
+15551234567
— typically a PSTN phone number. LiveKit can map incoming numbers to trunk numbers or dispatch rules. • sip:alice@your-livekit-host.example.com ◦ Destination:
alice
— could map to a participant identity, an extension, or a routing key your dispatch rules recognize. • sip:room-123@your-livekit-host.example.com ◦ Destination:
room-123
— often used when you want the call routed into a specific LiveKit room named
room-123
. • sip:1001@your-livekit-host.example.com ◦ Destination:
1001
— an internal extension that your dispatch rule can interpret and route to a specific room or participant. How LiveKit typically uses it • Dispatch rules: LiveKit dispatch rules inspect the SIP URI (destination) and decide which room or participant to route the call to. For example, you might configure a rule that maps
sip:+1...
to an inbound trunk, or routes
sip:room-*
into rooms. • Headers/attributes: If you use header-based auth, you can also pass routing info or tokens in headers — but the basic routing key is usually the destination in the SIP URI. • JWT/API setup: JWTs are used for API actions (creating trunks, dispatch rules, participants) rather than per-call SIP digest auth. Use those API actions to define how destination strings should be interpreted. What to choose for your setup • If you want each incoming call to land in a specific LiveKit room, use the room name as the destination (e.g.,
sip:myroom@host
). • If you need simple phone-number routing, use the E.164 number (
+1555...
) as the destination and map that in the trunk. • If you have an IVR or PBX-like setup, use extensions (1000, 2000, etc.) and configure dispatch rules to route those extensions. If you share: • One sample SIP URI you plan to use (redact secrets), or • How you want calls routed (to a specific room, to phone numbers, or to participant identities), I’ll give the exact SIP URI format and the LiveKit dispatch rule mapping you should use.
r
my dispatch rule is direct
t
Thanks — you said your dispatch rule is set to "direct." To make sure your TwiML -> LiveKit SIP call routes correctly, I just need one quick confirmation: Which identifier do you want to use as the SIP URI destination (the user portion before the @)? Choose one of the following or describe your own: • A LiveKit room name (example:
room-123
) • A phone number in E.164 format (example:
+15551234567
) • An internal extension/endpoint (example:
1001
) • A participant identity (example:
alice
) Once you confirm which of those you intend as the destination, I’ll give the exact TwiML SIP URI to use and verify the dispatch rule behavior for a direct routing setup. If you also want to include header-based token validation, say so and I’ll include the header example and the matching LiveKit mapping.
r
Copy code
<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n
<Response>\n  
	<Dial>\n    
		<Sip>\n      <sip:call-CA1b43268a5dda5c6fde49e2a4ff681011@persistence-stage-3bg9ne5m.sip>.livekit.cloud?X-Call-Sid=CA1b43268a5dda5c6fde49e2a4ff681011&X-Direction=inbound&X-From=%2B918005994757&X-Room-Name=call-CA1b43268a5dda5c6fde49e2a4ff681011&X-To=%2B12762219741\n    </Sip>\n  
	</Dial>\n
</Response>
t
Thanks — I reviewed your TwiML. The SIP URI you’re sending looks correctly formed for a direct dispatch to a LiveKit room, and your query params (X-Call-Sid, X-Room-Name, etc.) are a good way to pass call context. What I can confirm from this TwiML: • Destination (user part) is: call-CA1b43268a5dda5c6fde49e2a4ff681011 — that matches a room-prefixed routing style (room name = call-...). • Host is: persistence-stage-3bg9ne5m.sip.livekit.cloud — that looks like a LiveKit SIP domain. • You’re passing context as query parameters which LiveKit can map to attributes or read for routing. Next checks to make (most common reasons this still fails) 1. IP allowlist / network reachability ◦ Ensure Twilio’s outbound SIP IPs are whitelisted on your LiveKit trunk/firewall. LiveKit expects IP-based trust for Twilio invocations. ◦ Verify Twilio can resolve the SIP host and reach the SIP port you expect (UDP/TCP 5060 or TLS 5061). If using
sips:
require a valid cert for that hostname. 1. Dispatch rule alignment ◦ Confirm your LiveKit dispatch rule in “direct” mode is configured to map the incoming destination to the room name exactly (roomPrefix = "call-" or direct mapping to
call-CA1b43268...
). ◦ If dispatch expects a prefix but the full room name is used, ensure it still matches. 1. How LiveKit reads your query params/headers ◦ Query string params in the SIP URI are pass-through; if you need LiveKit to convert them into participant attributes, confirm your trunk’s
headers_to_attributes
or similar mapping is set up to capture the keys you send (
X-Call-Sid
,
X-Room-Name
, etc). ◦ Alternatively you can send them as SIP headers instead of query params if your mapping expects headers. 1. TLS / certificate ◦ If Twilio is dialing via TLS (
sips:
) make sure the certificate presented by
persistence-stage-3bg9ne5m.sip.livekit.cloud
covers that hostname and is trusted by Twilio. 1. Logs to inspect ◦ Twilio SIP trace for the call (INVITE and final response) — this will show whether the INVITE reached LiveKit and, if rejected, why (401/407/403/503). ◦ LiveKit logs showing the inbound INVITE and any rejection reason. If you want, I can give the exact TwiML variations for: • Sending those values as SIP headers (example) instead of query params, or • Using
sips:
vs
sip:
depending on your TLS requirement. Next step from you • Share which of the checks above you’ve already verified, and paste (redact secrets) either: ◦ The Twilio SIP trace (INVITE + response), or ◦ The LiveKit inbound log for that call. I’ll read the trace/log and point to the exact failure and the exact change to make. Sources: How VICIdial handles bridging, transfers, and SIP calls | LiveKit | Accepting calls overview | LiveKit Documentation | Agents telephony integration | LiveKit Documentation | How to do call transfers with Five9 | LiveKit | Understanding TLS in SIP | LiveKit
r
dispatch rule
Copy code
{
  "sipDispatchRuleId": "SDR_3vv5hVkSrjBp",
  "rule": {
    "dispatchRuleDirect": {
      "roomName": "$header.X-Room-Name"
    }
  },
  "trunkIds": [
    "ST_wehZTvDLYbiz"
  ],
  "name": "Twilio Inbound - URI Parameters",
  "metadata": "{\"call_sid\": \"$header.X-Call-Sid\", \"from\": \"$header.X-From\", \"to\": \"$header.X-To\", \"direction\": \"$header.X-Direction\", \"call_status\": \"$header.X-Call-Status\", \"from_city\": \"$header.X-From-City\", \"from_state\": \"$header.X-From-State\", \"from_country\": \"$header.X-From-Country\", \"to_city\": \"$header.X-To-City\", \"to_state\": \"$header.X-To-State\", \"to_country\": \"$header.X-To-Country\", \"source\": \"twilio_inbound\"}"
}
t
Thanks — this is very helpful. Your dispatch rule is configured to route directly to the room name taken from the SIP header `X-Room-Name`: "roomName": "$header.X-Room-Name" That means LiveKit is looking for
X-Room-Name
as a SIP header, not as a URI query parameter. Your TwiML currently puts those values in the SIP URI query string (e.g., ?X-Room-Name=...), which will not populate
$header.X-Room-Name
for the dispatch rule. What to do • Send the routing/auth fields as SIP headers from Twilio, not only as query params. • Keep IP allowlisting/TLS checks in place (Twilio IPs must be allowed and your cert must match if you use
sips:
). Corrected TwiML example (adds headers)
Copy code
<Response>
  <Dial>
    <Sip>
      <sip:call-CA1b43268a5dda5c6fde49e2a4ff681011@persistence-stage-3bg9ne5m.sip>.livekit.cloud
      <Header name="X-Call-Sid">CA1b43268a5dda5c6fde49e2a4ff681011</Header>
      <Header name="X-Direction">inbound</Header>
      <Header name="X-From">+918005994757</Header>
      <Header name="X-Room-Name">call-CA1b43268a5dda5c6fde49e2a4ff681011</Header>
      <Header name="X-To">+12762219741</Header>
    </Sip>
  </Dial>
</Response>
Notes and checks • The key header your dispatch rule needs is
X-Room-Name
. Once Twilio includes it as a SIP header, LiveKit’s
"$header.X-Room-Name"
expression will resolve and the call will be routed to that room. • If you prefer query parameters for other reasons, you must change the dispatch rule to read from URI params instead of headers. But header-based mapping is cleaner and supported by your current dispatch rule. • Confirm Twilio can reach
persistence-stage-3bg9ne5m.sip.livekit.cloud
and that Twilio’s IPs are whitelisted on your LiveKit trunk. If you use TLS (
sips:
), ensure the certificate matches the hostname. If you’d like, I can: • Provide the exact Go code snippet you should generate (matching your earlier formatter), or • Show a TwiML variation using
sips:
if you need TLS, or • Review a Twilio SIP trace / LiveKit log if the call still fails after switching to headers. Which would you like next?
r
how to change the dispatch rule to read from URI params instead of headers
t
I don't have the answer you're looking for. You could also try asking your question: • in one of the other Slack channels or • to https://deepwiki.com/livekit/livekit_composite which is trained on all LiveKit source code If you find the answer, please post it here to help others!