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:
| Method | Path | Purpose |
|---|---|---|
| GET | /sessions | List recorded sessions, newest first. |
| GET | /sessions/capabilities | Which realtime model providers this workspace may select. |
| POST | /sessions/import | Seed a completed call from an external recording bundle. |
| GET | /sessions/{session_id} | One session record. |
| GET | /sessions/{session_id}/transcript | The stored transcript turns. |
| GET | /sessions/{session_id}/usage | The 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
}
]| Field | Type | Description |
|---|---|---|
id | UUID | The session id — the same one ready carried during the live run. |
started_at / ended_at | float / float | null | Epoch seconds; ended_at is null while the session is live. |
status | "active" | "completed" | "error" | Terminal state of the run. |
provider | string | null | The model provider that served the session (for example, gemini, openai). |
has_resumption_handle | bool | Whether 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 field | Type | Description |
|---|---|---|
started_at | float | Call start, epoch seconds. Required. |
ended_at | float | null | Call end, epoch seconds. |
title | string | null | Display title. |
provider | string | null | Provider name to record. |
transcript | turn[] | Ordered speech turns, { ts, role: "user" | "assistant", text }. Max 100,000 turns. |
audio_wav_base64 | string | null | Optional 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
}
}| Field | Type | Description |
|---|---|---|
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_seconds | float | null | Wall-clock span of the session; null while the session is live. |
turn_count | int | null | Model-response turns. |
user_speaking_seconds / agent_speaking_seconds | float | null | Talk time per side. |
provider / model | string | null | The model provider and model that served the session. |
tokens | object | null | Token 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.
| Status | Cause |
|---|---|
| 401 / 403 | Credential 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. |
| 404 | Unknown session_id in the credential's project. |
| 400 | Import: transcript over 100,000 turns, or audio_wav_base64 not valid base64. |
| 413 | Import: 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_..."