> 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<String> response = Unirest.get("https://api.smallest.ai/atoms/v1/events?callId=CALL-1758124225863-80752e")
  .header("Authorization", "Bearer <token>")
  .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 <token>");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["Authorization": "Bearer <token>"]

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()
```