<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>SIP Gateway on Ulai Docs</title><link>https://docs.ulai.co.in/docs/sip/</link><description>Recent content in SIP Gateway on Ulai Docs</description><generator>Hugo</generator><language>en</language><atom:link href="https://docs.ulai.co.in/docs/sip/index.xml" rel="self" type="application/rss+xml"/><item><title>Overview</title><link>https://docs.ulai.co.in/docs/sip/overview/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://docs.ulai.co.in/docs/sip/overview/</guid><description>&lt;h2 id="what-it-is"&gt;What it is&lt;a class="td-heading-self-link" href="#what-it-is" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;The Ulai SIP Gateway is a single Go binary (&lt;code&gt;sip-sfu-gateway&lt;/code&gt;) that joins two&#10;worlds:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;SIP/RTP&lt;/strong&gt; — a carrier trunk, speaking SIP over UDP, TCP or TLS, carrying&#10;G.711 audio over RTP or SRTP.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Ulai SFU rooms&lt;/strong&gt; — WebRTC sessions on the Ulai control plane, where browsers&#10;and AI agents meet.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;A phone call that reaches the gateway becomes an ordinary participant in a&#10;room. Everything the gateway does is in service of that one sentence: decide&#10;whether the call is allowed, get a room for it, answer it, and move audio in&#10;both directions until somebody hangs up.&lt;/p&gt;</description></item><item><title>Configuration</title><link>https://docs.ulai.co.in/docs/sip/configuration/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://docs.ulai.co.in/docs/sip/configuration/</guid><description>&lt;p&gt;Configuration is read from the environment &lt;strong&gt;once&lt;/strong&gt;, at startup, into a single&#10;validated struct. Everything downstream reads that struct, never the&#10;environment — which is also why a test can construct one directly.&lt;/p&gt;&#10;&lt;p&gt;Validation reports &lt;em&gt;every&lt;/em&gt; problem at once, so a misconfigured deployment does&#10;not have to be fixed one restart at a time.&lt;/p&gt;&#10;&lt;h2 id="required"&gt;Required&lt;a class="td-heading-self-link" href="#required" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Variable&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Description&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;ULAI_CONTROL_PLANE_URL&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Base URL of the Ulai control plane, e.g. &lt;code&gt;https://stgcp.ulai.co.in&lt;/code&gt;. Rooms are created, joined and terminated here.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;SIP_ROUTING_REDIS_URL&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;The routing store every call is resolved against. &lt;code&gt;REDIS_URL&lt;/code&gt; is accepted as a fallback.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;SIP_PUBLIC_IP&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;The address advertised in SDP and &lt;code&gt;Contact&lt;/code&gt;. Must be routable by the carrier. &lt;code&gt;0.0.0.0&lt;/code&gt; is rejected — it is not a listen address, it is what the carrier is told to stream media to.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h2 id="sip-signalling"&gt;SIP signalling&lt;a class="td-heading-self-link" href="#sip-signalling" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Variable&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Default&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Description&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;SIP_LISTEN_ADDR&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;0.0.0.0:5060&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;What sipgo binds to. &lt;code&gt;host:port&lt;/code&gt;; the host may be &lt;code&gt;0.0.0.0&lt;/code&gt;.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;SIP_TRANSPORT&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;udp&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;udp&lt;/code&gt;, &lt;code&gt;tcp&lt;/code&gt; or &lt;code&gt;tls&lt;/code&gt;.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;SIP_TLS_CERT_PATH&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;—&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Required when &lt;code&gt;SIP_TRANSPORT=tls&lt;/code&gt;.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;SIP_TLS_KEY_PATH&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;—&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Required when &lt;code&gt;SIP_TRANSPORT=tls&lt;/code&gt;.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;SIP_SESSION_HEADER&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;X-Session-Id&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;The INVITE header that may point a call at an existing room. Must not be blank.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h2 id="rtp-media"&gt;RTP media&lt;a class="td-heading-self-link" href="#rtp-media" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Variable&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Default&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Description&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;SIP_RTP_HOST&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;0.0.0.0&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;What RTP sockets &lt;strong&gt;bind&lt;/strong&gt; to, as distinct from &lt;code&gt;SIP_PUBLIC_IP&lt;/code&gt;, which is what is &lt;strong&gt;advertised&lt;/strong&gt;. On most cloud VMs the public IP is attached at a NAT or load-balancer layer and is not on any interface inside the VM, so binding to it fails with &lt;em&gt;cannot assign requested address&lt;/em&gt;. The default is right nearly everywhere.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;SIP_RTP_PORT_LOW&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;10000&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Bottom of the RTP allocation range.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;SIP_RTP_PORT_HIGH&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;10500&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Top of the range. Must be above the low end, and both within 1–65535. Even ports only, by RTCP convention.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;SIP_RTP_TIMEOUT_SECONDS&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;30&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;End a call whose inbound audio has stopped for this long, as if the far end had hung up — the backstop for a &lt;code&gt;BYE&lt;/code&gt; that never arrives. Paused while the call is on hold. &lt;code&gt;0&lt;/code&gt; disables it.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h2 id="http-api"&gt;HTTP API&lt;a class="td-heading-self-link" href="#http-api" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Variable&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Default&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Description&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;SIP_HTTP_PORT&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;8082&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Port for &lt;code&gt;/health&lt;/code&gt;, &lt;code&gt;/calls&lt;/code&gt; and &lt;code&gt;/sip/originate&lt;/code&gt;. Chosen clear of the worker (8080) and the Tata gateway (8081).&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h2 id="rooms"&gt;Rooms&lt;a class="td-heading-self-link" href="#rooms" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Variable&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Default&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Description&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;ULAI_PROJECT_ID&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;—&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Default project for &lt;code&gt;POST /sip/originate&lt;/code&gt; when the body omits &lt;code&gt;project_id&lt;/code&gt;. The single-tenant deployment&amp;rsquo;s answer to sending it every time.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;ULAI_MAX_PARTICIPANTS&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;8&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Size of a room the gateway creates. Overridden per request by &lt;code&gt;max_participants&lt;/code&gt;. Must be positive.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;SIP_TERMINATE_SESSION_ON_HANGUP&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;End the whole session — every participant, every transport — when the SIP leg drops, rather than merely leaving the room. Set &lt;code&gt;false&lt;/code&gt; only when a room reached via &lt;code&gt;X-Session-Id&lt;/code&gt; or &lt;code&gt;session_id&lt;/code&gt; is genuinely shared. A room the gateway created is always terminated regardless.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h2 id="ice"&gt;ICE&lt;a class="td-heading-self-link" href="#ice" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;The gateway is STUN-only by default (&lt;code&gt;stun:stun.l.google.com:19302&lt;/code&gt;), which is&#10;fine on a public host and will fail behind symmetric NAT.&lt;/p&gt;</description></item><item><title>HTTP API</title><link>https://docs.ulai.co.in/docs/sip/http-api/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://docs.ulai.co.in/docs/sip/http-api/</guid><description>&lt;p&gt;Three endpoints, served on &lt;code&gt;SIP_HTTP_PORT&lt;/code&gt; (default &lt;code&gt;8082&lt;/code&gt;). Only&#10;&lt;code&gt;/sip/originate&lt;/code&gt; is authenticated.&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Method&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Path&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Auth&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;/health&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;none&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;/calls&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;none&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;/sip/originate&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;Authorization: Bearer &amp;lt;ulai api key&amp;gt;&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;div class="alert alert-warning" role="alert"&gt;&lt;div class="h4 alert-heading" role="heading"&gt;These endpoints are not public&lt;/div&gt;&#10;&lt;p&gt;&lt;code&gt;/health&lt;/code&gt; and &lt;code&gt;/calls&lt;/code&gt; are unauthenticated, and &lt;code&gt;/calls&lt;/code&gt; lists live phone&#10;numbers. Keep the HTTP port on a private network or behind a firewall; only&#10;&lt;code&gt;/sip/originate&lt;/code&gt; checks a credential.&lt;/p&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="get-health"&gt;GET /health&lt;a class="td-heading-self-link" href="#get-health" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -s http://148.113.58.51:8082/health&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ok&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;service&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;sip-sfu-gateway&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Always &lt;code&gt;200&lt;/code&gt; while the process is serving. It says nothing about the SIP leg or&#10;the routing store — see &lt;a href="https://docs.ulai.co.in/docs/sip/operations/observability/"&gt;observability&lt;/a&gt; for&#10;a check that covers both.&lt;/p&gt;</description></item></channel></rss>