steep-balloon-41261
03/23/2026, 6:23 AMtall-airline-16756
03/23/2026, 6:24 AMtall-airline-16756
03/23/2026, 6:24 AMsession.say() / TTS) is generated and appears in transcripts, the SIP bridge subscribes to the agent track and callStatus is "active", but the callee hears silence until they speak first.
Short answer
• Self-hosted LiveKit commonly needs explicit early-media / preconnect handling and proper NAT traversal (STUN/TURN) so outbound agent-first audio is actually delivered to the PSTN/SIP endpoint. Cloud handles many of those optimizations for you; self-hosted does not. There are three practical areas to check and steps to try right away.
What to check and try (priority order) 1) Ensure the call really has an established RTP path before sending audio
• Even if sip.callStatus == "active", the remote SIP endpoint may not have an established bidirectional RTP/early-media path until it sends media. Confirm ICE/DTLS/RTP negotiation completed for the SIP participant and the SIP bridge shows RTP packets flowing to the trunk.
• Action: enable verbose logs on your livekit-sip service and the LiveKit server and watch ICE connection states / RTP packet counters during the outbound flow. Look for RTP being sent from LiveKit to the SIP trunk when the agent speaks.
2) Enable/force early media / preconnect buffering
• Self-hosted can miss early-media; enable buffering or preconnect so audio is ready and sent immediately:
◦ If you use the agent session options, enable preconnect buffering (example maps to agent SDK RoomOptions audio preconnect).
◦ When creating the SIP participant, set play_dialtone=True (this often forces early media path establishment).
• Action: try creating the SIP participant with play_dialtone=True and enable any is_preconnect_buffer_enabled / preconnect options in your agent session so the TTS audio is buffered and the bridge has media to forward immediately.
3) Wait for explicit subscription/track confirmation before speaking
• Confirm the SIP bridge has subscribed to the agent audio publication and wait for that subscription event before calling `session.say()`:
◦ Wait for the publication subscribed event or otherwise wait for the SIP bridge subscription promise/future to resolve.
• Action: add logic to wait on the track publication subscribed event (or an equivalent SDK subscription confirmation) before starting TTS playback.
Additional environment/config items to verify
• STUN/TURN: Make sure your STUN/TURN are configured and reachable from both LiveKit and the SIP bridge so ICE can establish a routable path to the trunk if NAT is involved.
• SIP trunk early-media settings: Some PSTN/SIP trunks need early-media explicitly allowed or have call flow settings that block early RTP until the callee answers or sends media. Check your Twilio SIP trunk settings for early media / WebRTC-to-SIP behavior.
• Opus/DTX/VAD: If the SIP bridge or trunk treats silence as no-media, disable aggressive DTX/VAD to avoid the trunk dropping incoming RTP pre-voice.
• Timing: Try a short non-speech buffer (brief tone or ~200ms silence) before the agent speech as a pragmatic workaround that can nudge the path open.
Why this happens (concise)
• LiveKit Cloud pre-warms/handles early media and ICE behavior for you. Self-hosted deployments require correct early-media handling, ICE candidate flow, and trunk settings; without these, outbound audio from the agent can be generated locally and published, but the SIP/telephony leg won’t accept/route it until the remote side establishes or signals media presence first.
If you want, I can help you debug this step-by-step
• I can show the exact places to enable/collect logs from livekit-sip and LiveKit and which log lines/metrics to share.
• I can provide a small code snippet to wait for publication subscribed and a snippet to create the SIP participant with play_dialtone=True.
• If you prefer, paste the relevant livekit-sip and SIP trunk configuration snippets (redact secrets) and a short excerpt of the SIP/LiveKit logs around the call start and the agent-first TTS time, and I’ll point to the exact indicators to look for.
If you'd like the small code snippets now, tell me whether you want Python (agent SDK) or another SDK and I’ll give the exact wait-for-subscription and create-sip-participant examples.
Sources: SIP Lifecycle Management Agent | LiveKit Documentation | examples/voice_agents | livekit/livekit_composite | DeepWiki, with interactive elementstall-airline-16756
03/23/2026, 6:24 AM