VOICE_SERVER_BASE_URL, what breaks when it is wrong, and how to proxy it correctly.
Why a public URL is required
Two separate connections come inbound from your provider: Neither is outbound, so NAT traversal and firewall punching do not help. The provider must resolve and reach your hostname.Setting it
Both read the same value, so they cannot disagree.
The one-way door
build_answer_urls() composes:
create_application(agent_id, answer_url) sends it to your provider when the agent is created.
VoicEra currently uses the same URL for answer and hangup.
What breaks, and how it looks
Reverse proxy
The runtime serves HTTP and WebSocket on the same port, so one server block covers both — but the upgrade headers are mandatory:proxy_http_version 1.1 and the Upgrade/Connection headers the WebSocket never establishes, and nginx’s 60-second read timeout kills any call longer than a minute.
Tunnels for local testing
- cloudflared
- ngrok
VOICE_SERVER_BASE_URL, restart the API and runtime, then create your telephony agents.
Free tunnels get a new hostname every restart. Since the URL is baked in at agent-create time, recreate or
PATCH your agents whenever the tunnel address changes.Verifying
Check the runtime is reachable from outside:localhost, no real call will ever connect.
Finally, test the WebSocket upgrade itself:
101 Switching Protocols means the proxy is configured correctly. 200 or 400 means it is not upgrading.