Skip to navigation

Webhooks and Events

Every event a MeetStream bot sends, the payload format, and how a bot's ending is reported
View as Markdown

MeetStream sends an HTTP POST with a JSON body to your server every time a bot changes state and when its recordings and transcripts are ready. This page is the reference for what arrives and how to handle it.

The one rule to remember: branch on bot_event. Every payload carries both event and bot_event. They are equal for every event except a bot’s ending, where event is always bot.stopped and bot_event tells you why the bot ended.

Where events are delivered

Workspace endpointPer-bot callback_url
Set up inDashboard: Configure > Webhooks (guide)callback_url on create_bot
SignedYesNo
Which eventsThe events the endpoint subscribes toEvery event for that bot

Both receive the same payloads. If you use both with the same URL, each event is delivered once, signed.

curl -X POST "https://api.meetstream.ai/api/v1/bots/create_bot" \
-H "Authorization: Token <YOUR_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"meeting_link": "<YOUR_MEETING_LINK>",
"video_required": false,
"callback_url": "https://your-domain.com/webhooks/meetstream",
"custom_attributes": { "customer_id": "cus_123" }
}'

Payload format

Bot status events look like this:

{
"event": "bot.inmeeting",
"bot_event": "bot.inmeeting",
"bot_id": "6667fd0c-0165-471a-a880-06a1180be377",
"bot_status": "InMeeting",
"message": "Bot successfully joined the meeting",
"status_code": 200,
"timestamp": "2026-09-18T08:10:12.000000+00:00",
"custom_attributes": { "customer_id": "cus_123" }
}
FieldDescription
bot_eventThe event name. Branch on this field.
eventSame as bot_event, except for bot endings, where it is always bot.stopped (kept for integrations built before bot_event existed).
bot_idThe bot this event is about.
bot_statusThe bot’s status at this point, for example InWaitingRoom, InMeeting, Stopped, NotAllowed. Compare it case-insensitively: failure values include Error, ERROR, Failed and FAILED.
messageHuman-readable detail. Failures start with Error: (unexpected errors) or Failed: (expected outcomes such as a timeout or denial).
status_code200 or 500. Useful for alerting, but do not use it as the only success signal (see How a bot ends).
timestampISO 8601 time the event was generated. Present on status events; some post-call events omit it.
custom_attributesEcho of the custom_attributes you set when creating the bot, when present.
scheduled_join_timeOnly on bot.scheduled.
failure_reasonGoogle Meet only, on bot.failed when the meeting itself blocked the join. See How a bot ends.

Post-call events use the same envelope but carry an artifact status instead of bot_status; see Post-call events.

The life of a bot

bot_eventbot_statusWhen it is sent
bot.scheduledScheduledA bot created with join_at was accepted. Includes scheduled_join_time.
bot.joiningJoiningMeetStream accepted the request and dispatched the bot. For scheduled bots, when the scheduled dispatch runs (shortly before the join time). It can arrive more than once for scheduled bots; treat it as informational.
bot.in_waiting_roomInWaitingRoomThe bot asked to join. Sent even when the bot is admitted straight away; it lasts until the host admits the bot when there is a lobby or waiting room.
bot.inmeetingInMeetingThe bot is in the meeting. Sent at most once per bot.
bot.recording_permission_allowedRecordingPermissionAllowedZoom only. The host allowed the bot to record.
bot.recording_permission_deniedRecordingPermissionDeniedZoom only. The host denied recording (“Failed: Recording permission denied by host”) or did not answer in time (“Failed: Recording permission timeout”). status_code is 500. The bot then leaves and ends with bot.stopped.
bot.recordingRecordingCapture started. On Zoom this follows the recording permission. On Google Meet it is only sent when the bot was created with a callback_url.
bot.leavingLeavingThe bot is about to end. Sent before the ending event.
Endingsee belowExactly one per bot.
bot.doneDone (or the ending status, see below)Everything for this bot, including post-call processing, is finished.
data_deletionn/aThe bot’s media was deleted (retention window or DELETE /api/v1/bots/{bot_id}/delete).

Platform details, such as how each platform’s lobby works, are in the Zoom, Google Meet and Microsoft Teams guides.

How a bot ends

A bot ends with exactly one ending event (the one exception, a bot you remove before it joins, is listed below). On the wire, event is always bot.stopped; bot_event names the cause:

bot_eventbot_statusstatus_codeTypical causes
bot.stoppedStopped200The meeting ended, everyone left, automatic leave triggered, you removed the bot with the API, or Zoom recording permission was denied or timed out
bot.kickedStopped200A host or participant removed the bot from the meeting
bot.deniedDenied500The host rejected the bot’s request to join, or the platform refused it
bot.notallowedNotAllowed500Nobody admitted the bot before waiting_room_timeout ran out
bot.failedError, ERROR, FAILED or Failed500The bot could not join or hit an error

Things that trip up handlers:

  • A kick reports bot_status: "Stopped". Only bot_event: "bot.kicked" tells a kick apart from a normal stop.
  • Google Meet join blockers carry a reason. When the meeting itself prevents the join, a Google Meet bot ends with bot_event: "bot.failed", bot_status: "Failed", status_code: 500 and a failure_reason: invalid_meeting_link, meeting_not_started, meeting_ended, meeting_restricted, sign_in_required, google_sign_in_failed or cant_join_call.
  • A Zoom recording-permission denial ends with bot.stopped, after a bot.recording_permission_denied event.
  • Removing a bot before it joins sets its status to Stopped without an ending webhook on Zoom and Google Meet.
  • Workspace endpoints only receive the ending events they subscribe to. Subscribe to all five, or you will miss kicks, denials and lobby timeouts.

Every bot_status, error message and fix is listed in the status and error code reference.

Ending payloads

{
"event": "bot.stopped",
"bot_event": "bot.kicked",
"bot_id": "6667fd0c-0165-471a-a880-06a1180be377",
"bot_status": "Stopped",
"message": "Bot was removed from the meeting by a participant",
"status_code": 200,
"timestamp": "2026-09-18T08:45:00.000000+00:00"
}
{
"event": "bot.stopped",
"bot_event": "bot.notallowed",
"bot_id": "6667fd0c-0165-471a-a880-06a1180be377",
"bot_status": "NotAllowed",
"message": "Failed: Not admitted to meeting",
"status_code": 500,
"timestamp": "2026-09-18T08:20:00.000000+00:00"
}
{
"event": "bot.stopped",
"bot_event": "bot.failed",
"bot_id": "6667fd0c-0165-471a-a880-06a1180be377",
"bot_status": "Failed",
"message": "Failed: Invalid meeting link",
"status_code": 500,
"failure_reason": "invalid_meeting_link",
"timestamp": "2026-09-18T08:10:40.000000+00:00"
}

Post-call events

After the ending event, MeetStream processes the recording in the background. Audio, video and transcript processing run in parallel, so these events can arrive in any order. bot.done always comes last.

bot_eventArtifact fieldstatus_codeMeaning
audio.processedaudio_status: "Success"200Mixed audio is ready: GET /api/v1/bots/{bot_id}/get_audio
audio.skippedaudio_status: "Skipped"200No audio was captured
audio.failedaudio_status: "Failed"400 or 500Audio processing failed
video.processedvideo_status: "Success"200The recording is ready: GET /api/v1/bots/{bot_id}/get_video
video.failedvideo_status: "Failed"500Video processing failed
transcription.processedtranscript_status: "Success"200The post-call transcript is ready
transcription.skippedtranscript_status: "Skipped"200Transcription was skipped, for example because there was no audio or no valid provider configuration
transcription.not_requiredtranscript_status: "Skipped"200The bot had no post-call transcription configured
transcription.failedtranscript_status: "Failed"500Transcription failed. Check the provider configuration, then re-run it with POST /api/v1/bots/{bot_id}/transcribe
bot.donebot_status200All processing for this bot is finished
{
"event": "audio.processed",
"bot_event": "audio.processed",
"bot_id": "5b0ff6e7-3cea-4c9f-a6b4-851c5f11cf4f",
"status": "success",
"audio_status": "Success",
"message": "Audio processing completed successfully",
"status_code": 200
}
{
"event": "bot.done",
"bot_event": "bot.done",
"bot_id": "6667fd0c-0165-471a-a880-06a1180be377",
"bot_status": "Done",
"message": "Post-call processing completed",
"status_code": 200,
"timestamp": "2026-09-18T09:07:30.000000+00:00"
}

About bot.done:

  • It is the last event for a bot and always has status_code: 200. It means processing finished, not that every step succeeded. Check the artifact events for that.
  • It is sent for bots that never got into the meeting too. For those, bot_status keeps the ending status (for example NotAllowed or Denied) instead of Done.
  • It is sent whether or not transcription was configured, including for live (streaming) transcription.

data_deletion is sent when a bot’s media is deleted, either when its retention window ends or when you call DELETE /api/v1/bots/{bot_id}/delete. The bot’s status becomes MediaExpired and media endpoints stop returning files.

{
"event": "data_deletion",
"bot_event": "data_deletion",
"bot_id": "5b0ff6e7-3cea-4c9f-a6b4-851c5f11cf4f",
"status": "success",
"message": "Bot data deleted successfully",
"deleted_objects": 5,
"status_code": 200,
"timestamp": "2026-09-18T14:30:00Z"
}

Event catalog

These are all the event names MeetStream sends. The first four groups match the Subscribe to events list in the dashboard exactly. Events in the other groups are not listed in the dashboard and are delivered to every active workspace endpoint.

GroupEvents
Schedulingbot.scheduled, bot.joining
Bot lifecyclebot.in_waiting_room, bot.inmeeting, bot.recording, bot.leaving, bot.stopped, bot.kicked, bot.denied, bot.notallowed, bot.failed
Zoom recording permissionbot.recording_permission_allowed, bot.recording_permission_denied
Post-call processingaudio.processed, transcription.processed, video.processed, bot.done, data_deletion
In-meeting errors (not in the dashboard list)bot.error (a non-fatal problem while the bot is in the meeting, such as a live transcription provider failing; bot_status stays InMeeting, and the bot keeps going), agent_error (a problem with a MIA voice agent)
Participants (not in the dashboard list)participant_events.join, participant_events.leave
More post-call results (not in the dashboard list)audio.failed, audio.skipped, video.failed, transcription.failed, transcription.skipped, transcription.not_required, manifest.completed, manifest.skipped, manifest.failed (audio manifest processing)
Account usage (not in the dashboard list)usage.milestone_1h, usage.milestone_5h, usage.milestone_16h, usage.milestone_50h, usage.milestone_100h, usage.inactive_3d, usage.inactive_7d, usage.inactive_14d, usage.inactive_30d, usage.inactive_60d, usage.weekly_summary (informational, sent to workspace endpoints)

Live transcription results are not webhook events. They are posted to the live_transcription_required.webhook_url you set on the bot; see Live Transcription.

Signing

Deliveries to workspace endpoints carry:

HeaderValue
X-MeetStream-Signaturesha256= followed by the hex HMAC-SHA256 of the raw request body, keyed with the endpoint’s secret
X-MeetStream-TimestampISO 8601 time of the delivery. It is not part of the signed content.
User-AgentMeetStream-Webhook/1.0

Deliveries to a per-bot callback_url are not signed. Verification code for Python and Node is in Verifying Webhook Signatures.

Delivery rules

  • One attempt, no automatic retries. A timeout, connection error or non-2xx response is logged as a failed delivery and is not retried. You can resend it from the dashboard: Webhooks > Logs > RESEND.
  • Respond with 2xx quickly and process asynchronously. Some deliveries time out after 10 seconds, and a slow response can delay the next events for the same bot.
  • At most once per bot: bot.inmeeting, the ending event and each post-call event. bot.joining can repeat for scheduled bots.
  • No ordering guarantee between post-call events. Wait for bot.done if you need everything.
  • Duplicates are possible when you resend from the Logs tab. De-duplicate on bot_id plus bot_event.
  • Unknown event names can appear. Ignore events you do not handle rather than returning an error.

Handler checklist

  1. Accept POST requests and read the raw body before parsing it, so you can verify the signature.
  2. Verify X-MeetStream-Signature (workspace endpoints).
  3. Return 2xx immediately; queue the work.
  4. Branch on bot_event.
  5. On any ending event (bot.stopped, bot.kicked, bot.denied, bot.notallowed, bot.failed), mark the session ended and record the cause.
  6. On audio.processed, video.processed and transcription.processed, fetch the artifact. Or wait for bot.done and fetch everything at once.
  7. On data_deletion, drop your stored references to the bot’s media.

Related: Workspace Webhook Endpoints, Set Up Local Server for Webhook, Debugging Bots, Bot status and error code reference.