GET /v1/speech/sessions.
Find a conversation
turns and median_reply_ms are both derived from the transcript, so with no
transcript there is nothing to derive: zero turns and a null median describe a
conversation that happened and was not kept, not one that went badly.
source is whatever the client passed on the query string when it opened the
socket — live, duet, reader, ios, api. It is an optional parameter
with no allowed-values list, so a client that omits it stores null, which is
what a third-party client gets unless it opts in. paired_session_id is the
other half of a duet, so both sides can be replayed together.
median_reply_ms is a float. It is the median of the per-turn ttfb_ms
values, so an even number of replies puts it on a half millisecond — compare it
numerically rather than matching an integer.
The list takes
limit (1 to 200, default 50) and offset, and nothing else.
There is no server-side filter by model, source or date: page through and filter
in your own code, using total to know how far the list goes. The list omits
transcripts on purpose — fifty conversations of text to render five columns that
show none of it.Read one
GET /v1/speech/sessions/{session_id} returns the same object with transcript
included.
{ "role", "text", "at" } — at
being seconds from the start of the session, so a transcript lines up with the
recording without a second clock:
ttfb_ms on an assistant turn is end-of-user-speech to first audio out — the
number the person on the other end actually felt. median_reply_ms on the
summary is the median of those. Watch the tail rather than the mean: slow turns
are what make people talk over the model. See Latency.
An interrupted turn was cut short by barge-in, and its text is trimmed to what
the listener actually heard rather than what the model had generated. Without
that trim a transcript claims things were said that nobody heard.
Download the audio
Audio is stored per direction, not mixed. The realtime protocol carries the two sides separately, so keeping them separate means you know who said what with no diarization step. The tracks areuser and assistant.
Each download mints a fresh presigned URL rather than proxying bytes through the
API, which is why the storage bucket needs no anonymous access at all:
404 lists the ones it does.
What is kept, and what is not
Three fields tell you what survived a session that was recorded:
Transcript capture is bounded so one runaway session cannot grow without limit:
6,000 entries, 2 MiB of text, and 64 KiB per entry. A normal two-hour
conversation fits inside all three. Past a limit, capture stops explicitly and
transcript_retention.truncated is true with a reason — retained text is
never sliced mid-word to make room.
Audio can also be missing from a recorded session for a reason that is not your
doing: if the recorder cannot reserve capacity or storage is unavailable, the
transcript is still kept and the client was told at session start that recording
was unavailable. See
the recording disclosure.
Erase one
deleted_tracks is the number of storage keys the erasure covered, not the
number of audio files that existed. The set unions whatever the row recorded
with the two deterministic keys, user.wav and assistant.wav, so a session
that was never recorded also deletes 2 and a row carrying historical keys can
report more. Read it as “this many objects were accounted for”, not as a count
of what was found.
If a track fails to erase you get a 502 and the row is left in place, so
the delete can be retried rather than half-succeeding in silence. Retry it; a
second pass finds whatever is left.
The same erasure path runs from the organization-wide sweep and from the
retention schedule, so there is one definition of what “erased” covers. See
Compliance for the schedule and what a data subject
request maps to.
Retention
A session is kept until your organization has a retention period configured, and that period is not self-serve today — ask support to set it. Once set, one period governs realtime sessions, saved transcription runs and synthesis history alike, and a daily sweep erases anything older through exactly the path above. An organization that has never configured a period has no retention limit on recorded data. If your own privacy policy states one, that is the gap to close. Compliance has the full schedule, including the parts that are not configurable.Access
These are organization-scoped reads: only the organization that owns a session can see it, whether you authenticate with an API key or a signed-in console session. A session id appears in storage keys, so possession of an id must not on its own grant access — every call above requires credentials, and a session belonging to another organization answers404, identical to one that never
existed.
Going further
Realtime interaction
The sessions these rows record.
Turn-taking
Diagnosing a bad conversation from its own record.
Compliance
Consent, disclosure and the retention schedule.
Errors
Classifying a failure in one lookup.