MCP: prompt-cache hit rate and per-turn LLM timings in debug_call, new get_latency_summary tool

debug_call now returns the LLM-side numbers for a call alongside the caller-perceived latency it already reported: usage (prompt, completion and cached tokens, LLM call count, and prompt-cache hit percentage), turns (per-turn LLM time to first token, generation time and total turn time) and toolCalls (each tool’s execution time and context tokens). Before this, those numbers were only recoverable by parsing the raw event timeline.

Call actions endpoints removed from API reference

POST /call-actions, GET /call-actions, GET /call-actions/{id}, PUT /call-actions/{id}, and DELETE /call-actions/{id} are no longer part of the customer API surface and have been removed from the reference.

The routes remain in place for the dashboard, which uses cookie-session auth, but they were never reachable with a bearer API key. Any client that tried to call them with an API token received a 401. The API-ref section previously implied otherwise; that was incorrect.

Calls per second (CPS) — control how fast outbound calls start

You can now control how fast the platform starts your outbound calls, separately from how many run at once. Instead of sending requests slowly to protect your phone carrier, send everything in one go and tell us the rate your carrier accepts. Calls wait in the queue and start at exactly that rate. Nothing is dropped.

For numbers you import over SIP, org admins set the rate per SIP trunk on the Concurrency page. Every number on a trunk shares one rate, because that is where carriers enforce the limit. New trunks start at the safe default of 1 call/sec, and a change takes effect within about a second, including for calls already queued. Rented Smallest numbers are paced per provider by your plan.

PCA: aggregate limits on disposition metrics: combined prompt length and enum choices

The Write to draft endpoint now enforces two aggregate limits when the payload includes postCallAnalyticsConfig.dispositionMetrics:

  • Combined dispositionMetricPrompt length. The sum across all metrics must be ≤ 50,000 characters. Exceeding it returns 400 with The combined disposition metric prompts can be at most 50000 characters.
  • ENUM choices per metric. Each dispositionMetricType: ENUM metric can have at most 20 choices. Exceeding it returns 400 with Each enum metric can have at most 20 choices. Ones already over the limit can stay as they are, but can't grow any further. Remove the extra choices to save.

PCA: document the three no-transcript fallback shapes

Three call outcomes skip post-call analytics and return a fixed fallback in postCallAnalytics. Documented on the Post call metrics prose page and on the postCallAnalytics object description across the three call-response schemas.

Response is still 200; every dispositionMetrics[].value is null with confidence: 0 and a reasoning string. Exact strings:

Inbound SIP trunks: connection details for every trunk

Every inbound SIP trunk row on the SIP Trunks dashboard page now has a Connection details dialog. It shows the SIP origination host to point your carrier at, your numbers as entered, and a ready-to-paste example SIP URI with the transport suffix (;transport=tls preferred, ;transport=tcp fallback).

The same value is exposed in the API: GET /sip-trunk/inbound and POST /sip-trunk/inbound responses now include sipHost on every trunk. The host is derived at read time and is identical across your trunks.

Recordings: authenticated presigned-URL flow

New endpoint documented: GET /atoms/v1/recordings/{callId}?channel=mono|dual returns a short-lived presigned S3 URL for the call’s audio (atoms-platform#3625, Phase 1 of PRO-2775). Presigned URLs expire in 15 minutes. Response envelope is { status: true, data: { url: "..." } }.

Both channels are supported: channel=mono (default) for the composite track, channel=dual for stereo when the call was captured with per-side audio.

Telephony rework: SIP trunks as resources, agents answer, calls choose their caller ID

The telephony model is reorganized around one idea: who answers a number is durable configuration, what a call dials from is a property of the call. Full details and migration steps: Telephony API migration guide.

New

  • SIP trunks are first-class resources, one per direction: POST /sip-trunk/inbound and POST /sip-trunk/outbound, with a dedicated SIP Trunks dashboard page.
  • An agent answers on a number or trunk via POST /agent/{agentId}/answers; a source is answered by exactly one agent, and conflicts identify the agent holding it.
  • Every call names its caller ID: fromNumber on outbound calls, fromNumbers (rotating, frozen at creation, retry-stable) on campaigns, and a per-agent transfer caller ID via PUT /agent/{agentId}/transfer-source.
  • A temporary caller-ID bridge on the agent (POST /agent/{agentId}/caller-ids): shared attachments that outbound calls without a fromNumber dial from, first attached wins. Deprecated at introduction (responses carry Deprecation: true) and removed with the migration window; pass fromNumber per call instead.
  • Guarded deletions everywhere: deleting a trunk still in use returns 409 naming what uses it, and releasing a number an agent answers on or dials from is refused.

New page: SIP Wire Reference

New deep-dive page for SIP trunk integrators: SIP Wire Reference, linked from the SIP Trunking page.

It documents the exact wire contract, sourced from production packet captures: inbound and outbound call-flow ladders (including the digest-auth challenge on outbound), a real sanitised INVITE with its SDP, how each of your final responses is handled (what gets retried, what gets CANCELled), transfer semantics (a second outbound INVITE carrying X-Caller-ID, never SIP REFER, two channels per transferred call), the timer table (RTP keepalive, ringing, max duration), and how to debug with support using the SCL_ call ID that rides on the From tag of every INVITE we send.

SIP Trunking: UDP is accepted; the real hazard is fragmentation

Correction to the 2026-08-28 transport contract. The previous version stated our SIP ingress rejects UDP signalling outright. That was wrong: the ingress accepts UDP, TCP, and TLS, verified with a live OPTIONS probe over UDP answered with 200 OK.

What is actually true, and why the old advice still mostly worked. Full-size INVITEs (SDP body, several codecs, custom headers) can exceed the size a UDP datagram survives on the path. Fragments are silently discarded by many networks and firewalls, so the INVITE never arrives: no reply, no ICMP, retransmissions time out. A small OPTIONS ping over the same UDP path succeeds, which is what makes the failure look mysterious. Switching signalling to TCP or TLS fixes it, which is why the earlier “use TCP/TLS” recommendation resolved real integrations even though the stated reason was wrong.

Voice Agents: Tools registry and Secrets vault on the API reference

The reusable Tools library and Secrets vault now render on the API reference (and generate SDK methods).

Tools

  • GET /tool, POST /tool, PATCH /tool/{toolId}, DELETE /tool/{toolId}, POST /tool/{toolId}/duplicate: create org-level tools once and reference them from any agent. toolId format tool_<24 lowercase alphanumerics>. Editing a tool propagates to every referencing agent; deleting one is blocked while agents still reference it.

API reference: schema and enum corrections against backend Zod validators

Atoms and Waves API reference updated to match what the backend actually enforces. Every change is Zod-derived and, where possible, live-verified against production.

Atoms

  • DELETE /agent/{id}/archive. on query flipped from optional boolean (default true) to required string enum "true" | "false". This matches the Zod validator; passing a JSON boolean was already rejected, the previous docs were misleading. See the endpoint description for the exact accepted values.
  • GET /conversation/{callId}/recording/download-url. Added the callId regex pattern ^CALL-\d{13}-[a-fA-F0-9]{6}$ so the SDK types it correctly and clients see it in the API-ref.
  • POST /conversation/{callId}/cancel. Added minLength: 1 on the path parameter (matches Zod).
  • POST /campaign. name gains maxLength: 40.
  • POST /knowledgebase. description gains maxLength: 150.
  • POST /audience. name gains maxLength: 80.
  • PUT /account/update-org-name. name minLength grew from 1 to 2, gains maxLength: 50.
  • GET /product/get-available-numbers and POST /product/rent-number. provider enum extended to [twilio, plivo, custom, whatsapp] (was [plivo, twilio]) on both query and response.
  • POST /product/import-phone-number. Adds cpsLimit (number, minimum: 1, default: 1, capped server-side by CUSTOM_NUMBER_CPS_CEILING). Every imported number is always paced.
  • slmModel and the three CreateAgentRequest LLM enums. Extended to include gpt-5.2-azure alongside gpt-5.2 for Azure-hosted access.
  • WorkflowType enum. Adds multi_agents alongside the existing workflow_graph and single_prompt. multi_agents is restricted; the server requires slmModel to be one of [electron, gpt-5.2-azure].

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.

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.

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.

Voice Agents API reference: Agents-first ordering, widget endpoints deprecated

The API reference sidebar now opens with Agents at the top, followed by Calls, Campaigns, Phone Numbers, and Knowledge Base. User, Account, and Billing sit at the bottom. Matches the ordering pattern used by every peer voice-AI reference (Vapi, Retell, Bland, Deepgram Voice Agent, ElevenLabs Conversational AI). No URL slugs changed; only the sidebar ordering.

Voice Agents: Account, Web Call, and Campaign export endpoints on the API reference

Six previously undocumented Voice Agents endpoints now render on the API reference:

Account

  • GET /account/get-account-details: user profile plus the orgs the user belongs to
  • PUT /account/update-org-name: rename the org scoped by the API key (owner role)

Web Call

Voice Agents: Subscription and plan-limits endpoint on the API reference

One previously undocumented Voice Agents endpoint now renders on the API reference:

User

  • GET /user/subscription: the org’s plan id, credit balance, per-plan limits (agents, campaigns, numbers, daily and concurrent calls, knowledge-base sizes), and the features flags that gate capabilities

SDK 5.5.0: typed errors, waves helpers, CLI expansion, crew custom-LLM gotcha, warm/cold transfer clarified

Docs updates for the smallestai 5.5.0 release, plus the sibling platform fix for crew transfer audio.

Typed errors with actionable hints. Plan or entitlement-gated requests (HTTP 400 with “…upgrade to a higher plan…”) now raise PlanNotEntitledError (subclass of BadRequestError), importable from smallestai or the new smallestai.errors module. Error messages carry an actionable hint: 401 points at SMALLEST_API_KEY, plan-gated 400 points at upgrading, org-gated 403 points at the account team.

New docs: smallestai agents CLI, expanded SDK overview, quickstart env + layout FAQs

Three updates under Developer → Get Started.

Agents CLI reference. New page walking every command under smallestai agents (create, list, get, dashboard, phone-status, call) with argument and option tables, an end-to-end example, and the SMALLEST_API_KEY / SMALLEST_BASE_URL env-var contract.

SDK overview expanded. What is Atoms Agent Crews SDK? now covers the full picture in one page. The crew, node, and crew-code term model. The two independent versioning axes (config vs builds) with a summary table. An eight-step lifecycle from create to live call. A versioning API sample. A working end-to-end sample (server.py, assistant.py, requirements.txt). A six-item FAQ that names the common gotcha: agent-crew links to an existing agent, it doesn’t create one.