Cosmo Realtime SDK
ReferenceREST API

Session history

The REST endpoints for recorded sessions — list, read, transcript, delete, provider capabilities, and import.

Read back and manage the sessions your project has run, over plain REST — separate from any live session. The SDKs wrap one route — session.usage() reads GET /{session_id}/usage; call the rest directly with your HTTP client and credential.

All routes live under /api/v1/external/sessions. The workspace and project are implied by the credential and never echoed back.

Schema and try-it: these endpoints in the API explorer.


Authentication

Bearer credential carrying realtime:read for the read routes (list, get, transcript, recordings, capabilities), realtime:logs for the timeline, and realtime:delete for deletion. The usage route alone takes either realtime:start or realtime:read — the session object's own surface rides the scope that started the run, so session.usage() works with a default minted token. Minted end-user tokens carry realtime:read or realtime:delete only when minted with them (token scopes); they can never pull the timeline. The deprecated realtime:use umbrella implies all three — see Scopes.

Every route is project-scoped, whichever credential you hold: an API key operates on the developer's own project, and each minted end-user token operates on the project auto-provisioned for its external_user_id. In practice: listing with your API key won't show sessions your end users ran under minted tokens — read those with the end user's token — and a session_id from another project returns 404.

The one exception is POST /import, which requires a user access token (acts-as-user credential) carrying the recordings:write capability and a recordings-admin role instead of an API key.


Endpoints

The routes in the family, and what each returns:

MethodPathPurpose
GET/sessionsList recorded sessions, newest first.
GET/sessions/capabilitiesWhich realtime model providers this workspace may select.
POST/sessions/importSeed a completed call from an external recording bundle.
GET/sessions/{session_id}One session record.
GET/sessions/{session_id}/transcriptThe stored transcript turns.
GET/sessions/{session_id}/usageThe session's usage summary.
DELETE/sessions/{session_id}Remove the stored session.

GET /sessions

Query parameters: limit (1–100, default 20) and before_started_at (epoch seconds — pass the oldest started_at you have to page backwards). Returns an array of session records:

[
  {
    "id": "8f7e6d5c-4b3a-2190-aaaa-bbbbccccdddd",
    "started_at": 1754400000.0,
    "ended_at": 1754400180.5,
    "status": "completed",
    "provider": "gemini",
    "has_resumption_handle": false
  }
]
FieldTypeDescription
idUUIDThe session id — the same one ready carried during the live run.
started_at / ended_atfloat / float | nullEpoch seconds; ended_at is null while the session is live.
status"active" | "completed" | "error"Terminal state of the run.
providerstring | nullThe model provider that served the session (for example, gemini, openai).
has_resumption_handleboolWhether a native resumption handle is still stored — a hint that resume_session_id would resume natively rather than by transcript seeding.

GET /sessions/capabilities

No parameters. Returns { "openai_provider_available": bool } — whether the server is configured for the OpenAI realtime providers. Hide a provider picker the server would override. This is how you discover provider availability at runtime; there is no external /models endpoint.

POST /sessions/import

Seed a completed call — transcript plus optional audio — so it reads back through the same list/get/transcript endpoints and the recordings dashboard. Returns 201 with the created session record.

Body fieldTypeDescription
started_atfloatCall start, epoch seconds. Required.
ended_atfloat | nullCall end, epoch seconds.
titlestring | nullDisplay title.
providerstring | nullProvider name to record.
transcriptturn[]Ordered speech turns, { ts, role: "user" | "assistant", text }. Max 100,000 turns.
audio_wav_base64string | nullOptional mono WAV recording, base64-encoded. Decoded audio caps at 200 MB; the whole request body at 256 MB (413 beyond either).

GET /sessions/{session_id}/usage

The session's usage summary — duration, talk time, and token counts in provider-reported units. Satisfied by realtime:start as well as realtime:read, so every credential that can start a session can read its usage back. Every field but status, usage_status, duration_seconds, and provider arrives with the summary, so they are populated only once usage_status is recorded. Each SDK also wraps this route as session.usage().

{
  "status": "completed",
  "usage_status": "recorded",
  "duration_seconds": 121.0,
  "turn_count": 7,
  "user_speaking_seconds": 41.5,
  "agent_speaking_seconds": 63.25,
  "provider": "gemini",
  "model": "gemini-3.1-flash-live-preview",
  "tokens": {
    "input_tokens": 900,
    "output_tokens": 400,
    "total_tokens": 1300,
    "input_audio_tokens": 700,
    "input_text_tokens": 150,
    "input_image_tokens": 20,
    "input_cached_tokens": 30,
    "output_audio_tokens": 350,
    "output_text_tokens": 50
  }
}
FieldTypeDescription
status"active" | "completed" | "error"Lifecycle state of the session.
usage_status"pending" | "recorded" | "unavailable"Whether the detailed summary is available. pending while the session runs and for a short window after it ends; recorded once the numbers are final; unavailable when none was written and none will be — a session with no turn or speech activity records none, and neither does one torn down abnormally. Poll while pending; stop on either terminal value.
duration_secondsfloat | nullWall-clock span of the session; null while the session is live.
turn_countint | nullModel-response turns.
user_speaking_seconds / agent_speaking_secondsfloat | nullTalk time per side.
provider / modelstring | nullThe model provider and model that served the session.
tokensobject | nullToken counts by direction and modality — the live cosmo.usage event's counters plus the input and output totals, with the same cumulative semantics. null when the provider does not report token usage (for example, speech-time-billed providers).

GET /sessions/{session_id} · transcript · DELETE

GET /{session_id} returns one session record (shape above). GET /{session_id}/transcript returns the stored turns as [{ ts, role, text }]. DELETE /{session_id} returns 204 and removes the stored session. All three return 404 when the id doesn't exist in the credential's project.


Errors

Error bodies use the standard envelope — see POST /session/start.

StatusCause
401 / 403Credential absent, invalid, or lacking the route's scope — realtime:read (usage also accepts realtime:start), realtime:logs (timeline), or realtime:delete (delete); import: lacking the recordings capability or role.
404Unknown session_id in the credential's project.
400Import: transcript over 100,000 turns, or audio_wav_base64 not valid base64.
413Import: body over 256 MB or decoded audio over 200 MB.

Example — curl

curl "https://platform.askcosmo.ai/api/v1/external/sessions?limit=5" \
  -H "Authorization: Bearer cosmo_..."

On this page