Skip to main content
Every meeting that reaches a terminal state carries a completion_reason — the bot’s own account of why the run ended. The field is sealed in the lifecycle.v1 contract and there are ten values, no more. This page is the reference for all ten. Troubleshooting is the companion: it starts from a symptom you can see and walks to a fix. Start here when a run has already ended and you want to know what ended it.
Despite the name, completion_reason is carried on failed meetings as well as completed ones. On a failed row it arrives alongside failure_stage (requested · joining · awaiting_admission · active), which records how far the run got.

Where to read it

completion_reason and failure_stage are top-level fields on every meeting the API returns — no digging into data:
The same two fields ride the signed meeting.completed and bot.failed webhooks under data.meeting, so you can route on the reason without polling. GET /meetings/{id} carries them too.

The ten reasons

Whose side is the column that matters. Two of the ten point at Vexa; the rest are your own request, your own policy, or the ordinary friction of putting a bot into someone else’s calendar.

Tuning the timeouts

Four of the reasons above are a clock running out, and every one of those clocks is yours to set. Pass automatic_leave on POST /bots (all values in milliseconds):
A bot that keeps timing out in the lobby of hosts who admit slowly is a budget you can change today, not a defect you have to wait on.

Reading a reason honestly

”Did not complete” is not a fault

A run that ended in anything other than completed has not, by that fact, told you anything about whether Vexa worked. The user stopping the bot, nobody being in the room, a host declining it, a lobby nobody watched — these are ordinary meeting outcomes, and eight of the ten values above describe one. Only the reason separates them from the two that point at us, so a count of non-completions with the reasons thrown away is not a reliability measure of anything — ours or anyone’s.

join_failure is currently two failures under one name

join_failure today covers both a platform refusing the bot outright and a bot that was reaped before any admission verdict arrived. They have different causes and different fixes, and the value does not yet distinguish them. Tracked in Vexa-ai/vexa#1058 — open. Until it is split, treat a join_failure as “the bot did not get in”, not as a specific diagnosis.

left_alone is a silence verdict, not a headcount

The bot decides it is alone from the audio reaching it, not from a participant list. A meeting where nobody spoke for the whole window ends this way with people still present — and so does a meeting where the capture path failed and the bot genuinely heard nothing. If you see left_alone on a call you know was busy, check whether any transcript segments were produced; zero segments points at capture, not at an empty room. Tracked in Vexa-ai/vexa#1192 — open.

completed is not proof of a transcript

A run can reach completed with a benign reason and still have produced no segments. The completion reason describes how the bot’s run ended; it does not assert that transcription worked. Check the transcript itself before treating a completed meeting as a delivered one. Tracked in Vexa-ai/vexa#844 — open.

There is no per-reason rollup endpoint

The API serves the reason per meeting. It does not serve a breakdown across your meetings — if you want to know how your runs are ending in aggregate, page GET /bots and tally the field yourself.

The OpenAPI schema lists two values the product no longer emits

If you generate a client from the gateway’s OpenAPI document, its MeetingCompletionReason enum still carries stopped_before_admission and stopped_with_no_audio — 0.10-era values that the 0.12 codebase never produces. The ten above are the live set. Generated clients should tolerate the two extras rather than depend on them.

Still stuck?

Troubleshooting covers the symptom-first path — a bot that will not join, a join with no transcript, a stack that will not come up. If a reason above does not match what you observed, that mismatch is worth reporting: tell us at Support with the meeting id, the completion_reason you got, and what you saw in the call.