API Reference
Calls

Live Server-Sent Events stream of a call's lifecycle

GET
/v1/calls/{id}/stream

Opens a text/event-stream (SSE) connection that pushes call lifecycle transitions (ringing, answered, completed, failed, …) as they happen. Intended for the in-app dialer's live call view.

Past lifecycle events are also embedded inline on the Call resource (Call.events) — this endpoint is only for the live tail; there is no separate read endpoint for historical events.

For a call handled by an AI agent the same stream also carries the live transcript, as two additional event kinds:

  • transcript.turn — a complete, flushed turn ({kind, call_id, seq, role, text, final, occurred_at}). The durable copy of the same turns is on GET /v1/calls/{id}?transcript=1.
  • transcript.delta — an incremental chunk of the turn being spoken right now (delta instead of text). Stream-only: partials are never persisted.

The stream opens with a : ready SSE comment the moment it attaches — useful as a connection check, and ignorable by any conforming SSE client.

A subscriber that joins mid-call is replayed the last 20 transcript events so it does not sit silent until the next turn.

Neither transcript kind is available as a workspace webhook — they are per-call and far too chatty for HTTP fan-out. Read them here, with an API key (calls.read) like any other call read. Note that a browser EventSource cannot send an Authorization header, so an API-key subscriber must be server-side (curl, fetch with a reader, or any SSE client that sets headers).

The connection stays open until the call reaches a terminal state or the client disconnects.

The two id forms behave slightly differently by design. A session id (HD_…) attaches immediately — the browser dialer knows it before the call row exists, and subscribing early is the point, so no existence check is made; the channel is always scoped to the calling workspace, so an unknown session id simply yields a stream that never emits. A call UUID is looked up, and 404s when this workspace owns no such call. A call whose PBX session has not landed yet holds the request open for a few seconds rather than failing, since origination returns the UUID before the box calls back.

Authorization

bearerAuth
AuthorizationBearer <token>

Long-lived ES256 JWT minted from the dashboard (https://app.sautikit.com/developers/api-keys). Signed by the platform keyring. Carries workspace_id and scopes claims; revoked via the platform deny-list.

In: header

Path Parameters

id*string

The call's Sautikit UUID, or the PBX session id the same call carries on the wire (HD_…, returned as session_id on POST /v1/calls and on every webhook). Either form addresses the same call.

Response Body

text/event-stream

application/json

application/json

curl -X GET "https://example.com/v1/calls/string/stream"
"string"
{  "error": {    "code": "validation.bad_request",    "message": "string",    "request_id": "string",    "details": [      "string"    ],    "resolution": "string",    "reason": "invalid_characters",    "suggested_e164": "+254727524723"  }}
{  "error": {    "code": "validation.bad_request",    "message": "string",    "request_id": "string",    "details": [      "string"    ],    "resolution": "string",    "reason": "invalid_characters",    "suggested_e164": "+254727524723"  }}