Skip to main content

Webhooks

Receive real-time notifications for bot events.

Overview

Configure a callbackUrl when creating a bot to receive event notifications:

{
"meetingInfo": {
"platform": "google",
"meetingUrl": "https://meet.google.com/abc-defg-hij"
},
"callbackUrl": "https://your-server.com/webhooks/meetbot"
}

Webhook Payload

All webhooks are sent as POST requests with JSON body:

{
"event": "bot.joined",
"botId": 1,
"timestamp": "2024-01-15T10:30:00Z",
"data": {}
}

Events

Bot Lifecycle

EventDescription
bot.deployingBot container is starting
bot.joiningBot is joining the meeting
bot.joinedBot successfully joined
bot.in_waiting_roomBot is in waiting room
bot.leftBot left the meeting
bot.errorAn error occurred

Recording

EventDescription
recording.startedRecording began
recording.stoppedRecording ended
recording.readyRecording is available for download

Participants

EventDescription
participant.joinedSomeone joined the meeting
participant.leftSomeone left the meeting

Event Payloads

bot.joined

{
"event": "bot.joined",
"botId": 1,
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"meetingTitle": "Team Standup",
"platform": "google"
}
}

bot.error

{
"event": "bot.error",
"botId": 1,
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"error": "Failed to join meeting",
"code": "JOIN_FAILED"
}
}

recording.ready

{
"event": "recording.ready",
"botId": 1,
"timestamp": "2024-01-15T11:30:00Z",
"data": {
"durationSeconds": 3600,
"fileSizeBytes": 157286400
}
}

participant.joined

{
"event": "participant.joined",
"botId": 1,
"timestamp": "2024-01-15T10:35:00Z",
"data": {
"participantName": "John Doe"
}
}

Webhook Security

Headers

Every webhook delivery includes the following headers:

HeaderExamplePurpose
X-Webhook-Signaturesha256={hmac}HMAC-SHA256 of {timestamp}.{body}
X-Webhook-Timestamp1705312200Delivery time (Unix seconds), included in signature
X-Webhook-Idwhdel_a1b2c3...Unique delivery ID for idempotency
X-Webhook-Eventbot.joinedEvent type string

Signature Verification

The signature is computed over {timestamp}.{body} using your webhook secret. Always verify the signature and reject deliveries older than 5 minutes to prevent replay attacks.

import hmac
import hashlib
import time

def verify_webhook(payload: bytes, signature: str, timestamp: str, secret: str) -> bool:
# Reject deliveries older than 5 minutes
if abs(time.time() - int(timestamp)) > 300:
return False
signed_content = f"{timestamp}.{payload.decode()}"
expected = hmac.new(
secret.encode(),
signed_content.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
const crypto = require('crypto');

function verifyWebhook(payload, signature, timestamp, secret) {
// Reject deliveries older than 5 minutes
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
return false;
}
const signedContent = `${timestamp}.${payload}`;
const expected = crypto
.createHmac('sha256', secret)
.update(signedContent)
.digest('hex');
return signature === `sha256=${expected}`;
}

Webhook Management API

You can create and manage webhooks programmatically.

Create Webhook

POST /webhooks

curl -X POST https://api.meetbot.dev/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Production Webhook",
"url": "https://your-server.com/webhooks/meetbot",
"events": ["bot.joined", "bot.ended", "recording.available"],
"isActive": true
}'

Response includes a secret field (only shown once):

{
"id": 1,
"name": "Production Webhook",
"url": "https://your-server.com/webhooks/meetbot",
"events": ["bot.joined", "bot.ended", "recording.available"],
"secret": "whsec_abc123...",
"isActive": true,
"createdAt": "2024-01-15T10:30:00Z"
}
caution

The full secret is only shown once on creation. Store it securely!

List Webhooks

GET /webhooks

Get Webhook

GET /webhooks/{id}

Includes delivery statistics.

Update Webhook

PATCH /webhooks/{id}

Delete Webhook

DELETE /webhooks/{id}

Test Webhook

POST /webhooks/{id}/test

Sends a test payload to the webhook URL and returns the result.

Delivery History

GET /webhooks/{id}/deliveries

Returns paginated delivery logs with status codes, response times, and payloads.

Webhook Secret

Use the secret returned when creating a webhook to verify delivery signatures.

Retry Policy

Failed webhooks are retried with exponential backoff:

AttemptDelayCumulative Time
1st retry30 seconds30s
2nd retry1 minute1m 30s
3rd retry2 minutes3m 30s
4th retry4 minutes7m 30s
5th retry8 minutes15m 30s

Webhooks are considered failed if:

  • Response status is not 2xx
  • Connection timeout (10 seconds)
  • No response within 30 seconds

Best Practices

  1. Respond quickly: Return 200 immediately, process async
  2. Handle duplicates: Events may be sent multiple times
  3. Verify signatures: Always validate webhook authenticity
  4. Use HTTPS: Only use secure endpoints in production
  5. Log events: Keep records for debugging

Testing Webhooks

Use tools like ngrok for local development:

# Start ngrok
ngrok http 3000

# Use the ngrok URL as callback
{
"callbackUrl": "https://abc123.ngrok.io/webhooks/meetbot"
}