make all prints a key when the stack comes up.
This is not the same as Use a custom STT endpoint, which keeps Vexa’s
real-time pipeline and swaps the engine behind it. Here there is no engine: nothing is transcribed
during the call, and the audio file is the deliverable.
Two flags, not one
transcribe_enabled: false on its own leaves you nothing to retrieve. It turns transcription
off. It does not turn recording on — recording is gated separately by recording_enabled, and a bot
that ran with both off captured a meeting that now exists nowhere.
Set both:
POST /bots
TRANSCRIBE_ENABLED / RECORDING_ENABLED env, else true. A non-boolean is refused with 422
rather than coerced.
Because transcribe_enabled is false, the spawn does not require an STT backend, so the 503
that normally refuses a bot on a deployment with no speech-to-text configured cannot fire. A
capture-only bot joins a stack that has no ASR at all.
The meeting’s transcription_provider is frozen to "none" at spawn — the durable record that this
meeting was never transcribed by anyone. It surfaces in the meeting.completed webhook’s
service_provenance block.
Confirm nothing was transcribed
GET /transcripts/{platform}/{native_meeting_id}
Response — 200
Find the recording
GET /recordings
Response — 200
status: "completed". While the bot is still in the call the recording exists with
status: "in_progress" and grows chunk by chunk.
/master hands you JSON, not audio
The master route returns metadata. It finalizes the recording and tells you where the bytes
are; it does not serve them.
GET /recordings/{recording_id}/master
Response — 200
media_file_id is not the recording id. Both are independent random 12-digit numbers and
neither can be derived from the other. Repeating the recording id in the byte path —
/recordings/481159273044/media/481159273044/raw — answers 404 No such media file. Use the
raw_url the response hands you, or read media_files[].id off the recording. Never construct it.
Download the audio
GET — the raw_url from above
200 with Content-Type: audio/webm (or audio/wav where the deployment records WAV). The body is
a real WebM container: its first four bytes are the EBML magic 1a 45 df a3.
The route honours HTTP Range, so you can pull the file in parts, resume an interrupted transfer,
or stream it straight into a transcoder. See
Retrieve a meeting recording.
Transcribe it on your own terms
From here it is an ordinary audio file. Nothing about the next step is Vexa’s: feed it to a local Whisper, an OpenAI-compatible endpoint, a batch service, or leave it in cold storage until somebody asks. Until you fetch it, the recording sits in the bucket configured byMINIO_*
(Configuration) — on a self-host, your own object storage, which
the audio never leaves.
What this path covers
What has been verified. The sequence on this page — capture-only spawn, empty transcript,
finalized recording,
/master, byte download — was run end-to-end against the hosted API on
2026-08-21: a bot joined a live Google Meet with transcribe_enabled: false and
recording_enabled: true, both participants spoke, GET /transcripts/… returned zero segments, and
the downloaded body was a WebM whose length matched the recording’s file_size_bytes exactly.Not verified by that run: video recording (there is no video path), WAV-format deployments, and the
real-time customer-backend route above.Related
- Retrieve a meeting recording — listing,
Rangeplayback, where the bytes live. - Use a custom STT endpoint — keep transcription live, change the engine.
- Meetings API — the full
POST /botsparameter set.