Realtime Agent WebSocket: per-direction audio format contract
Realtime Agent WebSocket: per-direction audio format contract
The realtime voice-agent WebSocket API now has a documented contract for the audio you send and the audio the agent sends back. Previously the only knob was sample_rate, and it silently set only the output; input audio was read at a hardcoded rate regardless of what the client asked for. That mismatch was invisible: the call worked, the transcription was wrong.
Two new fields on POST /conversation/register-call and on the WebSocket connect URL:
input_audio_format— what you send.pcm_{8000,16000,22050,24000,44100,48000},mulaw_8000,alaw_8000, oropus_{8000,16000,24000,48000}.output_audio_format— what the agent sends back.pcm_{8000,16000,24000}on every voice, pluspcm_44100onlightning-v3.1andlightning-v3.1-pro.
One <encoding>_<rate> token per direction. sample_rate is deprecated in favor of output_audio_format and stays accepted forever; existing integrations do not need to change. Sending both is fine when they agree; a disagreement is refused with HTTP 400 rather than one silently winning.
An unsupported input token is refused at register-call with the list of accepted values in the error. An output_audio_format the agent’s voice cannot render is refused there too, before any session exists, rather than failing mid-call.
The accepted tokens and error bodies are on Register Call and Agent WebSocket. Opus framing, the voice-dependent output rate, and migrating off sample_rate are on the new Audio Formats page.
Save-agent API: timezone object shape, expanded language enum, native-types callout
Save-agent API: timezone object shape, expanded language enum, native-types callout
Fixed two drifts on PUT /agent/{id}/branches/{branchId}/draft that were sending real integrations into hours of 400s.
timezone is an object, not a string. The UpdateBranchDraftRequest.timezone field was documented as a bare string; the backend expects {"label": "(GMT+5:30) Asia/Kolkata", "offset": 330}. A brand-new customer sending "Asia/Kolkata" was refused with Invalid config: timezone: Expected object, received string for 48h before we caught it. Corrected + example added.
language.default and language.supported now list all 18 supported codes. Docs previously listed 9 (en, hi, mr, gu, ta, es, north_indic, bn, or); the platform accepts 18 (adding te, kn, ml, fr, de, it, nl, pt, ru). Verified against packages/atoms-types/src/common/enums.ts:15-35 at atoms-platform@main.
Native JSON types callout on the endpoint. The single most common integration bug on this endpoint is sending JSON-stringified values instead of native types (e.g. "language.supported": "en" instead of ["en"], or "isEnabled": "false" instead of false). The endpoint description now says so up front, with the exact Zod error string the API returns.
Every claim in this update was live-tested with scripts/spec-live-tests/save_agent_shapes_live_test.py. 15/15 checks pass.
Cross-product follow-up: language enum sync automation between Waves and Atoms is queued so this class of drift is caught in CI going forward.
SIP Trunking: transport contract, E.164 formatting, and a real troubleshooting section
SIP Trunking: transport contract, E.164 formatting, and a real troubleshooting section
SIP Trunking is rewritten to document what actually works and to remove guidance that was silently wrong.
The transport contract. Our SIP ingress accepts TCP (port 5060) and TLS (port 5061). UDP INVITEs are dropped with no response, no rejection, no ICMP. The origination URL your dashboard shows is bare (sip:YOUR_SIP_INGRESS.sip.smallest.ai); customers need to append ;transport=tls (preferred) or ;transport=tcp when they paste it into their provider or SBC. That single missing parameter is what has been costing hours on new integrations. The page now leads with the transport table, the two working URL shapes, and a sipsak OPTIONS ping you can run to confirm reachability.
E.164 with the leading +. The request URI must carry the country code with a +. sip:919240923803@... reaches the ingress but does not match a mapped number, so calls ring nowhere. Documented explicitly, with an FAQ entry so it is discoverable from outside the SIP flow.
Troubleshooting is now real. New sections on the silence-means-UDP case, the missing-+ case, one-way audio (RTP-in-SDP check), duplicate-INVITE-means-UDP-fallback case, plus Kamailio-specific listen= and TLS module configuration.
Provider guides corrected. Twilio, Telnyx, and Vonage origination examples all say sip:...;transport=tls now. The Vonage page previously said “Transport: UDP or TCP (UDP recommended)”; corrected to “TLS (preferred) or TCP. Do not use UDP.”
Two FAQ entries on the top-level FAQ page for the two most common symptoms (silence with no SIP response, INVITE reaches you but agent never picks up), each linking back to the full section on the SIP Trunking page.

