Webhooks

Complete guide to webhooks in Meeting BaaS v2

Webhooks allow you to receive real-time notifications about bot and calendar events. Instead of polling the API, you can configure webhook endpoints that will receive HTTP POST requests when events occur.

Overview

Meeting BaaS v2 uses SVIX for webhook delivery, ensuring reliable delivery with retries and delivery status tracking.

Webhook Configuration

Webhooks are configured at the account level. You can set up webhook endpoints in your account settings to receive notifications for all bot and calendar events.

Callbacks vs Webhooks

Meeting BaaS v2 supports two notification mechanisms:

  1. Webhooks (Account-level): Configured in your account settings, sent via SVIX. All events for all bots are sent to your configured webhook endpoints.

  2. Callbacks (Bot-specific): Configured per-bot when creating a bot using the callback_config parameter. Callbacks are direct HTTP requests (POST or PUT) sent to your specified URL when that specific bot completes or fails.

Key Differences:

  • Webhooks: Account-level, sent via SVIX with signature verification, all events
  • Callbacks: Bot-specific, direct HTTP requests, only for bot completion/failure events

You can use both webhooks and callbacks together - they serve different purposes and complement each other.

Webhook Security

All webhooks are signed using SVIX's signature verification. The signature is included in the svix-id, svix-timestamp, and svix-signature headers.

To verify webhooks, use SVIX's verification libraries or verify the signature manually using your webhook signing secret.

Bot Webhooks

bot.status_change

Triggered whenever a bot's status changes (e.g., from queued to joining_call, from joining_call to in_call_recording, etc.).

Use Cases:

  • Track bot progress in real-time
  • Update UI to show current bot state
  • Trigger actions based on status changes
  • Monitor bot lifecycle

Payload:

{
  "event": "bot.status_change",
  "data": {
    "bot_id": "123e4567-e89b-12d3-a456-426614174000",
    "event_id": "789e0123-e45b-67c8-d901-234567890abc",
    "status": {
      "code": "in_call_recording",
      "created_at": "2025-01-15T10:30:00Z",
      "start_time": 1736941800
    },
    "extra": {
      "customer_id": "12345"
    }
  }
}

Status object fields:

  • code: The status code (see list below)
  • created_at: ISO 8601 timestamp when this status change occurred
  • start_time (optional): Unix timestamp in seconds when recording started. Only present on in_call_recording
  • error_message (optional): Human-readable description of the failure. Present on recording_failed and meeting_error, and may appear on other error states

Status Codes

The status.code field can contain any of the values below, grouped here by where they occur in the bot's lifecycle.

Lifecycle:

  • queued: Bot is queued and waiting to join
  • pickup_delayed: The bot has stayed in the queued status longer than the expected pickup window. No action is required on your end — this is informational. The bot may still proceed normally to joining_call, and our team is automatically notified to investigate persistent occurrences
  • transcribing: Bot has exited and transcription is in progress
  • completed: Bot has finished successfully (terminal state)
  • failed: Bot has failed (terminal state)

In-call progress:

  • joining_call: Bot is attempting to join the meeting
  • in_waiting_room: Bot is in the meeting's waiting room / lobby
  • in_waiting_for_host: Bot is waiting for the host to start or admit it (Zoom only)
  • in_call_not_recording: Bot has joined the call but recording has not yet started
  • in_call_recording: Bot is in the meeting and recording. The payload includes start_time
  • recording_paused: Recording has been paused (e.g., via the pause-recording endpoint)
  • recording_resumed: Recording has resumed after a pause
  • call_ended: Bot has left the meeting
  • recording_succeeded: Recording finished and artifacts were captured successfully
  • recording_failed: Recording could not be produced. The payload includes error_message

Intermediary signals (Google Meet / Microsoft Teams) — these indicate why a recording is about to fail, and are followed by a recording_failed status and, ultimately, a bot.failed webhook:

  • api_request_stop: Bot was stopped via the leave-bot endpoint
  • bot_rejected: Bot was denied entry to the meeting (e.g., host rejected the join request)
  • bot_removed: Bot was removed from the meeting by a participant after joining
  • bot_removed_too_early: Bot was removed before recording could start
  • waiting_room_timeout: Bot timed out waiting to be admitted from the waiting room
  • invalid_meeting_url: The meeting URL was invalid or could not be opened
  • meeting_error: A general meeting-related error occurred. The payload includes error_message

If you only care about terminal outcomes, watch for completed and failed (or subscribe to the bot.completed and bot.failed webhooks instead). The other codes are useful for live progress tracking but are not required reading.

bot.completed

Triggered when a bot successfully completes recording and processing.

Use Cases:

  • Download meeting recordings automatically
  • Process transcriptions
  • Trigger post-meeting workflows
  • Update meeting records in your system

Payload:

{
  "event": "bot.completed",
  "data": {
    "bot_id": "123e4567-e89b-12d3-a456-426614174000",
    "event_id": "789e0123-e45b-67c8-d901-234567890abc",
    "transcription": "https://s3.amazonaws.com/.../transcription.json",
    "mp4": "https://s3.amazonaws.com/.../video.mp4",
    "audio": "https://s3.amazonaws.com/.../audio.mp3",
    "diarization": "https://s3.amazonaws.com/.../diarization.jsonl",
    "chat_messages": "https://s3.amazonaws.com/.../chat_messages.json",
    "duration_seconds": 3600,
    "participants": [...],
    "speakers": [...],
    "extra": {
      "customer_id": "12345"
    }
  }
}

Note: All artifact URLs (transcription, mp4, audio, diarization) are presigned S3 URLs valid for 4 hours. For detailed information about each artifact type, see the Artifacts documentation.

bot.chat_message

Triggered in real-time when a chat message is received in the meeting. This allows you to process chat messages as they happen, without waiting for the meeting to end.

Use Cases:

  • Build real-time chat integrations
  • Respond to participant questions or commands
  • Log chat messages for compliance
  • Trigger workflows based on chat content

Payload:

{
  "event": "bot.chat_message",
  "data": {
    "bot_id": "123e4567-e89b-12d3-a456-426614174000",
    "event_id": "789e0123-e45b-67c8-d901-234567890abc",
    "message_id": "spaces/XxM_aNTpzGkB/messages/1773539019302815",
    "sender_name": "John Doe",
    "sender_id": 2,
    "text": "Can we discuss the Q4 roadmap?",
    "sent_at": "2025-01-15T10:32:15Z"
  },
  "extra": {
    "customer_id": "12345"
  }
}

Fields:

  • message_id: Unique identifier for the chat message (format varies by platform)
  • sender_name: Display name of the message sender
  • sender_id: Participant ID of the sender. May be null if the sender could not be resolved to a participant
  • text: Text content of the chat message
  • sent_at: ISO 8601 timestamp when this webhook was dispatched by the server (not when the message was sent in the meeting)

Note: The bot's own messages (sent via the send chat message endpoint) do not trigger this webhook — only messages from meeting participants are delivered. For a complete record of all chat messages (including bot-sent messages), use the chat_messages artifact available in the bot.completed webhook and bot details endpoint. See the Artifacts documentation for the artifact structure.

bot.failed

Triggered when a bot fails to complete successfully.

Use Cases:

  • Handle errors gracefully
  • Retry bot creation if appropriate
  • Log failures for analysis
  • Notify users of failures

Payload:

{
  "event": "bot.failed",
  "data": {
    "bot_id": "123e4567-e89b-12d3-a456-426614174000",
    "event_id": "789e0123-e45b-67c8-d901-234567890abc",
    "error_code": "BOT_NOT_ACCEPTED",
    "error_message": "Bot was not accepted into the meeting",
    "extra": {
      "customer_id": "12345"
    }
  }
}

Common Error Codes:

  • BOT_NOT_ACCEPTED: Bot was not accepted into the meeting
  • TIMEOUT_WAITING_TO_START: Meeting didn't start within the timeout period
  • INSUFFICIENT_TOKENS: Not enough tokens to create the bot
  • DAILY_BOT_CAP_REACHED: Daily bot creation limit reached
  • INVALID_MEETING_PLATFORM: Could not determine meeting platform from URL
  • TRANSCRIPTION_ERROR: Error occurred during transcription

Calendar Webhooks

calendar.connection_created

Triggered when a new calendar connection is created.

Use Cases:

  • Confirm calendar integration success
  • Initialize calendar-specific workflows
  • Track calendar connections

Payload:

{
  "event": "calendar.connection_created",
  "data": {
    "calendar_id": "123e4567-e89b-12d3-a456-426614174000",
    "platform": "google",
    "account_email": "user@example.com",
    "calendar_name": "Primary"
  }
}

calendar.connection_updated

Triggered when a calendar connection is updated (e.g., OAuth credentials refreshed).

Use Cases:

  • Track credential updates
  • Monitor connection health
  • Update connection status in your system

Payload:

{
  "event": "calendar.connection_updated",
  "data": {
    "calendar_id": "123e4567-e89b-12d3-a456-426614174000",
    "platform": "google",
    "status": "active"
  }
}

calendar.connection_deleted

Triggered when a calendar connection is deleted.

Use Cases:

  • Clean up calendar-related data
  • Notify users of disconnection
  • Update UI to reflect removal

Payload:

{
  "event": "calendar.connection_deleted",
  "data": {
    "calendar_id": "123e4567-e89b-12d3-a456-426614174000",
    "platform": "google"
  }
}

calendar.connection_error

Triggered when a calendar connection encounters an error (e.g., OAuth token refresh failed).

Use Cases:

  • Alert users to connection issues
  • Trigger automatic reconnection attempts
  • Log errors for troubleshooting

Payload:

{
  "event": "calendar.connection_error",
  "data": {
    "calendar_id": "123e4567-e89b-12d3-a456-426614174000",
    "platform": "google",
    "error": "OAuth token refresh failed",
    "status": "error"
  }
}

calendar.events_synced

Triggered after a calendar sync operation completes (initial sync).

Use Cases:

  • Confirm sync completion
  • Process newly synced events
  • Update event cache

Payload:

{
  "event": "calendar.events_synced",
  "data": {
    "calendar_id": "123e4567-e89b-12d3-a456-426614174000",
    "events_synced": 42,
    "sync_type": "full"
  }
}

calendar.event_created

Triggered when a new event is created in a connected calendar.

Use Cases:

  • Automatically schedule bots for new events
  • Update event calendars
  • Trigger event-specific workflows

Payload:

{
  "event": "calendar.event_created",
  "data": {
    "calendar_id": "123e4567-e89b-12d3-a456-426614174000",
    "event_type": "one_off",
    "series_id": "789e0123-e45b-67c8-d901-234567890abc",
    "series_bot_scheduled": false,
    "instances": [
      {
        "event_id": "abc123...",
        "title": "Team Meeting",
        "start_time": "2025-01-20T10:00:00Z",
        "end_time": "2025-01-20T11:00:00Z",
        "meeting_url": "https://meet.google.com/...",
        "bot_scheduled": false
      }
    ]
  }
}

calendar.event_updated

Triggered when an existing event is updated in a connected calendar.

Use Cases:

  • Update bot schedules if meeting time changes
  • Sync event changes to your system
  • Handle event modifications

Payload:

{
  "event": "calendar.event_updated",
  "data": {
    "calendar_id": "123e4567-e89b-12d3-a456-426614174000",
    "event_type": "recurring",
    "series_id": "789e0123-e45b-67c8-d901-234567890abc",
    "series_bot_scheduled": true,
    "affected_instances": [
      {
        "event_id": "abc123...",
        "title": "Team Meeting",
        "start_time": "2025-01-20T10:00:00Z",
        "end_time": "2025-01-20T11:00:00Z",
        "meeting_url": "https://meet.google.com/...",
        "bot_scheduled": true
      }
    ]
  }
}

calendar.event_cancelled

Triggered when an event is cancelled in a connected calendar.

Use Cases:

  • Cancel scheduled bots for cancelled events
  • Update event status
  • Clean up event-related data

Payload:

{
  "event": "calendar.event_cancelled",
  "data": {
    "calendar_id": "123e4567-e89b-12d3-a456-426614174000",
    "event_type": "one_off",
    "series_id": "789e0123-e45b-67c8-d901-234567890abc",
    "series_bot_scheduled": false,
    "cancelled_instances": [
      {
        "event_id": "abc123...",
        "title": "Team Meeting",
        "start_time": "2025-01-20T10:00:00Z"
      }
    ]
  }
}

Webhook Delivery

Retry Schedule

Failed webhook deliveries are automatically retried with increasing delays:

AttemptDelayCumulative Time
1 (initial)0s
25 seconds~5s
310 seconds~15s
45 minutes~5 min
515 minutes~20 min
630 minutes~50 min
72 hours~3 hours

All retries complete within approximately 3 hours. This is intentional — bot.completed webhooks include signed download URLs (for video, audio, transcription) that are valid for 4 hours. Keeping retries within this window ensures that URLs in the payload remain usable.

A small amount of random jitter (±20%) is added to each retry delay to prevent thundering herd issues.

Timeouts and Overload Protection

  • Successful acknowledgement: Only HTTP 2xx responses are treated as successful delivery. Any other status code — including 3xx redirects, 4xx client errors, and 5xx server errors — is considered a failure and will be retried. Failed deliveries contribute to the retry schedule and, over time, to the auto-disable behavior.
  • Request timeout: Your endpoint must respond within 30 seconds. Requests that exceed this limit are treated as failures and retried.
  • Overload penalty: If your endpoint returns a 429 Too Many Requests status or times out, a minimum 60-second delay is applied before the next retry, regardless of the scheduled delay.

Idempotency and Ordering

  • Idempotency: Webhooks may be delivered multiple times — ensure your handler is idempotent
  • Ordering: Webhooks are generally delivered in order, but network issues or retries may cause out-of-order delivery

Endpoint Auto-Disable

If your webhook endpoint fails continuously for an extended period, it will be automatically disabled to prevent unnecessary retry traffic:

  1. After all retry attempts fail for a message, a webhook_delivery_exhausted operational alert is emitted (if you have alert rules configured for this type)
  2. After 5 days of continuous failure (no successful delivery across any message), the endpoint is automatically disabled
  3. When auto-disabled, the team owner receives an email notification with the endpoint details and common failure causes
  4. Your endpoint status is updated in the dashboard — you'll see it marked as disabled
  5. No new deliveries are attempted to a disabled endpoint until you manually re-enable it from the dashboard

Once an endpoint is auto-disabled, all subsequent webhook messages are silently dropped. Configure a Webhook Delivery Exhausted alert rule to get notified per-message as retries fail, well before the 5-day auto-disable threshold. See Alerts for setup instructions.

A single successful delivery at any point resets the failure clock — the 5-day timer only applies to endpoints that fail every delivery attempt without any success.

Testing Webhooks

You can test your webhook endpoint using tools like:

Callbacks

Callbacks are bot-specific HTTP requests sent directly to a URL you provide when creating a bot. Unlike webhooks (which are account-level and sent via SVIX), callbacks are:

  • Bot-specific: Configured per-bot using callback_config when creating a bot
  • Direct HTTP: Sent directly to your URL (not via SVIX)
  • Limited events: Only sent for bot.completed and bot.failed events
  • Same payload: Uses the same payload structure as webhooks

Configuring Callbacks

When creating a bot, include the callback_config in your request:

{
  "bot_name": "My Bot",
  "meeting_url": "https://meet.google.com/...",
  "callback_enabled": true,
  "callback_config": {
    "url": "https://your-server.com/webhook",
    "method": "POST",
    "secret": "your-secret-key"
  }
}

Callback Security

If you provide a secret in callback_config, it will be included in the x-mb-secret header of all callback requests. Use this to verify that callbacks are coming from Meeting BaaS.

Callback Delivery and Retries

Callbacks are delivered with automatic retries on failure:

AttemptDelay
1 (initial)
21 second
35 seconds
415 seconds
  • Only retries on network errors and 5xx server responses
  • Does not retry on 4xx client errors (e.g., 400, 401, 403, 404)
  • 30-second request timeout per attempt

If all 4 attempts fail, you can manually retry using the POST /v2/bots/:bot_id/retry-callback endpoint. This generates a fresh payload with new signed download URLs.

Resending Webhooks

If a webhook delivery fails, you can resend it using the POST /v2/bots/:bot_id/resend-webhook endpoint.

On this page