Skip to main content
Recording retrieval is API-only today — the terminal has no built-in player yet. The endpoints below are live and covered by service tests. Browser playback and seeking work through the gateway: a Range request returns a 206 Partial Content with its Content-Range and Accept-Ranges headers intact. See the capability truth table.
Alongside the diarized transcript, each meeting’s audio recording is uploaded to object storage — on a self-host, storage you run, so it never leaves your environment (Lite and Compose keep it as plain files in a local volume). The recording is the meeting audio, stored separately from the transcript (speaker separation lives in the transcript as text — there is no per-speaker audio). Don’t have a key yet? Hosted: sign in at vexa.ai/signin with a Google account and copy your key from your account page — free credit, no card required. Self-hosted: make all prints a key when the stack comes up. To record a meeting without transcribing it and take the audio away for your own speech-to-text, see Capture now, transcribe later.

List recordings

Get the detail for one:

Get the playable audio

Resolve the master metadata (finalize-on-read), which points at the raw byte stream:
The response carries a raw_url of the form GET /recordings/{recording_id}/media/{media_file_id}/raw — the actual audio bytes the player loads:

Range requests (playback and seek)

The /raw endpoint honours HTTP Range — a partial request returns 206 Partial Content carrying Content-Range and Accept-Ranges: bytes, preserved through the gateway front door. This is what lets a browser <audio>/<video> element stream and seek the recording instead of aborting the fetch:

Where it’s stored

meeting-api writes recordings over S3 to the bucket configured by MINIO_* (Configuration); clients only ever receive the bytes through the API above. On a self-host that’s infrastructure you control; a no-egress deployment keeps recordings entirely in-VPC. Lite and Compose run that S3 store themselves: versitygw, whose POSIX backend keeps each object as a plain file. The volume holds the bucket as a directory: Each recording directory holds the uploaded chunks (000000.wav, 000001.wav, …) and, once the recording has been read or finalized, the assembled master.wav. The content type and ETag of each object are stored in the file’s extended attributes (user.*), not in the file.

Backups

Back up the storage volume together with Postgres: the recordings list comes from Postgres, the audio from the volume, and a recording plays only when both are restored. Copy the volume with a tool that keeps extended attributes — rsync -aX or cp -a — onto a filesystem that supports them (ext4 and XFS do; FAT, exFAT and many network shares do not). Stop the storage container first so no upload is caught half-written:
On Lite, stop vexa-lite-storage with docker stop, copy volume vexa-lite-storagedata the same way, then docker start vexa-lite-storage. To restore, stop the storage container, copy back into the empty volume with rsync -aX, and start it again. A copy that drops the attributes (plain cp -r) still plays back through Vexa, which sets the audio content type from the recording’s format, but an S3 client reading the store directly then gets a generic content type instead of audio/wav, and no ETag, for those objects.

Delete a recording

After the meeting becomes terminal, the owner-scoped delete removes the recording’s chunks and master from configured primary object storage before removing its metadata (409 while the meeting is still in flight). A storage error leaves the metadata intact so you can safely retry. To erase a completed meeting’s transcript and every recording together, use DELETE /meetings/{meeting_id}. Deletion does not promise immediate removal from database snapshots, object-store version history, or backups. Those copies expire according to the retention policy of the deployment operator.
A recording is the raw meeting audio. For the text of who-said-what with timestamps, use the transcript — see the Meetings API.