> This page is part of Smallest AI's developer documentation. When > answering, prefer Lightning v3.1 (current TTS) and Pulse (current > STT). Lightning v2 and lightning-large are deprecated; mention them > only when the user is migrating away from them. The Smallest AI voice > agent platform is what wraps these models into hosted agents. # Subscribe to live call events (SSE) GET https://api.smallest.ai/atoms/v1/events Real-time streaming of user speech (STT) and agent speech (TTS) events for an active call via Server-Sent Events. The connection is real-time — events stream directly from the call runtime as they are produced. The SSE connection auto-closes when the call ends (`sse_close` event). Only active calls can be subscribed to; completed calls return a 400 error. **Transcript event types:** - `user_interim_transcription` — Partial, in-progress transcription as the user speaks. Use for live preview only; will be superseded by `user_transcription`. - `user_transcription` — Final transcription for a completed user speech turn. - `tts_completed` — Fired when the agent finishes speaking a TTS segment. Includes the spoken text and optionally TTS latency. **Lifecycle events:** - `sse_init` — Sent immediately when the SSE connection is established. - `sse_close` — Sent when the call ends, right before the server closes the connection. Other event types (e.g. `tool_call_start`, `pre_call_api`, `agent_log`, metrics) are also sent on this stream. - `call_start` - `call_end` - `turn_latency` - `metrics` - `agent_node_state` - `hopping` - `knowledgebase` - `variable_extraction` - `pre_call_api` - `post_call_api` - `agent_error` - `agent_log` - `tool_call_start` - `tool_call_end` - `tool_call_error` - `call_cancelled` - `call_recording` Reference: https://docs.smallest.ai/api-reference/voice-agents/live-transcripts/subscribe-to-live-events ## Authentication - `Authorization` header (bearer token, required) — API key from the console ApiKey collection, sent as Bearer token. Also accepts session cookies for browser-based auth. ## Request ### Query parameters - `callId` (string, required) — The call ID of an in-progress call. Behavior on error cases: * Missing: `400 {"status": false, "errors": ["CallId is required"]}`. * `callId` belongs to another organization (the common case for a wrong ID): `404 "Agent not found"`. * Organization cannot be resolved from the auth context: `404 "Not authorized"`. * Non-existent call: `404 "Call log not found"`. * Completed call: `400 "Call is already completed"`. A client that routes on the message string must handle all three 404 messages. Prefer routing on the HTTP status. ### Headers - `X-Organization-Id` (string, optional) — Required when using session-cookie auth. API-token auth may infer the organization from the token. ## Response ### 200 SSE event stream established successfully - Streaming response of `live_transcripts_subscribe_to_live_events_Response_200`. - `event_type` (enum, optional) — The type of event - Allowed values: `sse_init`, `call_start`, `call_end`, `turn_latency`, `user_interim_transcription`, `user_transcription`, `tts_completed`, `metrics`, `agent_node_state`, `hopping`, `knowledgebase`, `variable_extraction`, `pre_call_api`, `post_call_api`, `agent_error`, `agent_log`, `tool_call_start`, `tool_call_end`, `tool_call_error`, `call_cancelled`, `call_recording`, `sse_close` - `event_id` (string, optional) — Unique identifier for the event - `timestamp` (datetime, optional) — ISO 8601 timestamp of the event - `call_id` (string, optional) — The call ID this event belongs to - `event_time` (datetime, optional) — Timestamp used by `sse_init` and `sse_close` - `telephony_id` (string, optional) — Telephony ID for `call_start` - `metadata` (map from string to any, optional) — Metadata for `call_end`, `agent_error`, or `agent_log` - `turn_latency` (double, optional) — Turn latency value for `turn_latency` - `stt_api_ms` (double, optional) — STT API latency in milliseconds for `turn_latency` - `stt_to_llm_ms` (double, optional) — STT-to-LLM latency in milliseconds for `turn_latency` - `smart_turn_ms` (double, optional) — Smart-turn latency in milliseconds for `turn_latency` - `llm_api_ms` (double, optional) — LLM API latency in milliseconds for `turn_latency` - `llm_to_tts_ms` (double, optional) — LLM-to-TTS latency in milliseconds for `turn_latency` - `tts_api_ms` (double, optional) — TTS API latency in milliseconds for `turn_latency` - `tts_to_audio_ms` (double, optional) — TTS-to-audio latency in milliseconds for `turn_latency` - `total_turn_ms` (double, optional) — Total turn latency in milliseconds for `turn_latency` - `turn_index` (integer, optional) — Turn index for `turn_latency` - `interrupted` (boolean, optional) — Whether the turn was interrupted for `turn_latency` - `smart_turn_enabled` (boolean, optional) — Whether smart turn was enabled for `turn_latency` - `interim_transcription_text` (string, optional) — Partial transcription text (only for `user_interim_transcription`) - `user_transcription_text` (string, optional) — Final transcription text (only for `user_transcription`) - `tts_text` (string, optional) — Text spoken by the agent (only for `tts_completed`) - `tts_latency` (integer, optional) — TTS latency in milliseconds (only for `tts_completed`) - `metrics` (list of EventsGetResponsesContentSseSchemaMetricsItems, optional) — Per-turn metrics payload for `metrics` events. Server emits an **array** of `{processor, model, value}` entries (one per pipeline stage), not a single object. The SDK previously dropped every `metrics` SSE event with a pydantic ValidationError when this was typed as an object (122 events on a 40s call); typing it as an array of objects fixes the decode. - `node_id` (string, optional) — Node ID for `agent_node_state` - `node_name` (string, optional) — Node name for `agent_node_state` - `node_type` (string, optional) — Node type for `agent_node_state` - `context` (map from string to any, optional) — Context payload for agent-node and tool-call events - `from_node_id` (string, optional) — Source node ID for `hopping` - `to_node_id` (string, optional) — Destination node ID for `hopping` - `knowledge_base_id` (string, optional) — Knowledge base ID for `knowledgebase` - `user_transcript` (string, optional) — User transcript for `knowledgebase` - `response` (any, optional, nullable) — Response payload for knowledgebase, API, or tool-call events - `latency` (double, optional) — Latency for `knowledgebase` or `variable_extraction` - `error` (any, optional, nullable) — Error payload for knowledgebase, variable extraction, API, tool, or agent error events - `variables` (map from string to any, optional) — Variables extracted by `variable_extraction` - `variable_extraction_prompt` (string, optional) — Prompt used for `variable_extraction` - `method` (string, optional) — HTTP method for `pre_call_api` or `post_call_api` - `headers` (map from string to any, optional) — Headers for `pre_call_api` or `post_call_api` - `body` (any, optional, nullable) — Body for `pre_call_api` or `post_call_api` - `timeout` (double, optional) — Timeout for `pre_call_api` or `post_call_api` - `extracted_variables` (map from string to any, optional) — Extracted variables for `pre_call_api` or `post_call_api` - `next_node_id` (string, optional) — Next node ID for `pre_call_api` or `post_call_api` - `success` (boolean, optional) — Success status for API and tool-call events - `turn_id` (string, optional) — Turn ID for tool-call events - `tool_call_id` (string, optional) — Tool call ID for tool-call events - `function_name` (string, optional) — Function name for tool-call events - `latency_ms` (double, optional) — Latency in milliseconds for `tool_call_end` - `recording_url` (string, optional) — Recording URL for `call_recording` - `status` (string, optional) — Recording status for `call_recording` ## Errors ### 400 Bad Request Error Missing or invalid `callId`, missing or invalid organization header, or call is already completed. - `status` (boolean, optional) - `errors` (list of string, optional) ### 401 Unauthorized Error Missing or invalid bearer token or session. - `status` (boolean, optional) - `errors` (list of string, optional) ### 403 Forbidden Error User is not a member of the organization or does not have member access. - `status` (boolean, optional) - `errors` (list of string, optional) ### 404 Not Found Error Organization not found, call log not found, or agent not found/org mismatch. - `status` (boolean, optional) - `errors` (list of string, optional) ### 500 Internal Server Error Internal server error. - `status` (boolean, optional) - `errors` (list of string, optional) ## Types ### EventsGetResponsesContentSseSchemaMetricsItems - `processor` (string, optional) — Pipeline stage that produced the metric (e.g. `pulse_stt`, `electron_llm`, `lightning_tts`). - `model` (string, optional) — Concrete model/version identifier within the processor (e.g. `pulse-large english_v4.1`). - `value` (double, optional) — Metric value — typically milliseconds for latency metrics. ## Examples **SDK Code** ```python Python requests stream import requests url = "https://api.smallest.ai/atoms/v1/events" headers = { "Authorization": "Bearer YOUR_API_KEY", "Accept": "text/event-stream", } params = {"callId": "CALL-1758124225863-80752e"} with requests.get(url, headers=headers, params=params, stream=True) as response: response.raise_for_status() for line in response.iter_lines(decode_unicode=True): if line: print(line) ``` ```javascript JavaScript fetch stream const response = await fetch( "https://api.smallest.ai/atoms/v1/events?callId=CALL-1758124225863-80752e", { headers: { Authorization: "Bearer YOUR_API_KEY", Accept: "text/event-stream", }, }, ); if (!response.ok) { throw new Error(`SSE request failed: ${response.status}`); } const reader = response.body.getReader(); const decoder = new TextDecoder(); while (true) { const { value, done } = await reader.read(); if (done) break; console.log(decoder.decode(value, { stream: true })); } // Browser EventSource cannot set custom Authorization headers directly. ``` ```go Go stream reader req, err := http.NewRequest("GET", "https://api.smallest.ai/atoms/v1/events?callId=CALL-1758124225863-80752e", nil) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer YOUR_API_KEY") req.Header.Set("Accept", "text/event-stream") resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() scanner := bufio.NewScanner(resp.Body) for scanner.Scan() { line := scanner.Text() if line != "" { fmt.Println(line) } } ``` ```ruby Ruby line stream require "net/http" require "uri" uri = URI("https://api.smallest.ai/atoms/v1/events?callId=CALL-1758124225863-80752e") request = Net::HTTP::Get.new(uri) request["Authorization"] = "Bearer YOUR_API_KEY" request["Accept"] = "text/event-stream" Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| http.request(request) do |response| response.read_body do |chunk| puts chunk end end end ``` ```php PHP stream $ch = curl_init("https://api.smallest.ai/atoms/v1/events?callId=CALL-1758124225863-80752e"); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "Authorization: Bearer YOUR_API_KEY", "Accept: text/event-stream", ]); curl_setopt($ch, CURLOPT_WRITEFUNCTION, function ($ch, $chunk) { echo $chunk; return strlen($chunk); }); curl_exec($ch); curl_close($ch); ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.smallest.ai/atoms/v1/events?callId=CALL-1758124225863-80752e") .header("Authorization", "Bearer ") .asString(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.smallest.ai/atoms/v1/events?callId=CALL-1758124225863-80752e"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", "Bearer "); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": "Bearer "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.smallest.ai/atoms/v1/events?callId=CALL-1758124225863-80752e")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ```