Bot Lifecycle
A meeting bot is an ephemeral worker that moves through a well-defined set of states. Understanding these states and why they transition is the key to building a robust integration.
State Machine Overview
The following diagram represents the typical path of a bot:
scheduled → booting → joining → recording → leaving → processing → transcribing → finished
Standard Statuses
| Status | Description |
|---|---|
scheduled | The bot has been created with a scheduled_start_time in the future. You can force a scheduled bot to join immediately by updating its scheduled_start_time to null. |
booting | Skribby is spinning up a dedicated worker instance and initializing the browser environment. |
joining | The bot is navigating to the meeting URL and attempting to enter. This includes the "Waiting Room" phase. |
recording | The bot is inside the meeting, capturing audio/video and streaming transcription (if enabled). |
leaving | The bot is exiting the meeting (brief transitional state). |
processing | The meeting has ended. The bot is uploading raw assets and preparing for transcription. |
transcribing | The audio is being processed by the selected AI model (e.g., Whisper, Deepgram). |
finished | All data is ready. Transcript and Recording URLs are available. |
Terminal "Failed" Statuses
If a bot cannot reach the finished state, it will land in one of these terminal error states:
| Status | Description |
|---|---|
not_admitted | The bot reached the meeting but was never allowed inside. See Stop Reasons for details. |
bot_detected | The platform blocked the bot. Note: This is not always terminal. Skribby automatically retries up to 3 times with new credentials/IPs. You may see the status toggle between bot_detected and booting during this process. |
auth_required | The meeting is locked to specific users and the bot was not provided with Authentication. |
invalid_credentials | Authentication was provided, but the platform rejected the email/password or ZAK token. |
invalid_api_key | Transcription credentials (BYOK) were provided, but the transcription provider rejected the API key. See Transcription Credentials. |
failed | A rare internal error occurred. Our team is automatically notified of these. |
Stop Reasons
When a bot's status becomes finished or not_admitted, the stop_reason field provides the "Why."
Configuring Stop Timers
Stop timers are configured when you create a bot with the stop_options object. All durations are in minutes.
| Option | Default | Disable with 0? | Description |
|---|---|---|---|
stop_options.empty_meeting_timeout | 10 | Yes | How long the bot waits after it joins an empty meeting and no other participants arrive. |
stop_options.last_person_detection | 2 | Yes | How long the bot waits after it becomes the only participant after other participants were present. |
stop_options.waiting_room_timeout | 10 | No | How long the bot waits to be admitted before stopping as not_admitted. Values above 10 minutes can add waiting-room charges. |
stop_options.time_limit | 720 | No | The hard ceiling for how long the bot may stay active. The maximum is 720 minutes (12 hours). |
stop_options.silence_detection | 15 | Yes | How long the bot waits without hearing speech before leaving. This helps when other bots remain in the call and last-person detection cannot trigger. |
{
"transcription_model": "openai/whisper-large-v3",
"meeting_url": "https://meet.google.com/osk-sbwe-nff",
"service": "gmeet",
"bot_name": "My Meeting Bot",
"stop_options": {
"empty_meeting_timeout": 10,
"last_person_detection": 5,
"waiting_room_timeout": 10,
"time_limit": 120,
"silence_detection": 0
}
}
Stop option fields must be nested under stop_options. Fields such as empty_meeting_timeout, last_person_detected, last_person_detection, waiting_room_timeout, time_limit, or silence_detection at the top level of the create-bot payload are ignored, except for the deprecated top-level time_limit field kept for backwards compatibility.
Reasons for not_admitted
invalid_meeting_url: The URL provided doesn't point to a valid meeting.call_already_finished: The meeting had already ended before the bot could join.waiting_room_timeout: No one admitted the bot within the configured timeout (seewaiting_room_timeoutinstop_options).request_denied: A human in the meeting explicitly clicked "Deny" when the bot asked to join.host_in_another_meeting: The host is currently in another meeting and has not started this one yet (common in Zoom).manually_stopped: You called thePOST /bot/{id}/stopendpoint while the bot was in the waiting room.
Reasons for finished
kicked: A participant explicitly removed the bot from the active meeting.meeting_ended: The host ended the meeting or everyone left.empty_meeting_timeout: The bot left because it joined the meeting, but no other participants arrived within the configuredempty_meeting_timeoutduration.last_person_detected: The bot left because it was the only participant remaining after other participants had been present (configured withlast_person_detection).manually_stopped: You called thePOST /bot/{id}/stopendpoint while the bot was in the meeting.silence_detection_triggered: No one spoke for the configured duration.
Handling State Changes
We recommend using Webhooks to react to these changes. Specifically, listen for the status_update event to drive your UI:
// Example: Nudge user if bot is waiting
if (webhook.type === 'status_update' && webhook.data.new_status === 'joining') {
sendUserPushNotification('Your bot is waiting to be admitted!');
}