{"id":"4ecf274d-db63-4d3d-9a1b-5a204f942c58","task":"Receive and verify Recall.ai bot status-change webhooks (HMAC signature verification)","domain":"docs.recall.ai","steps":["In the regional Recall dashboard's Webhooks tab (e.g. https://us-east-1.recall.ai/dashboard), click 'Add Endpoint' and register your public HTTPS URL. Ref: https://docs.recall.ai/docs/status-change-webhooks-setup-verification","Subscribe the endpoint to the event types you need (bot status change, recording status, transcript status, Calendar V2 events); unsubscribed types are never delivered. Ref: https://docs.recall.ai/reference/webhooks-overview","Get the verification secret from Developers > API Keys & Secrets in the dashboard; it starts with the 'whsec_' prefix and covers webhooks, websockets and callbacks. Ref: https://docs.recall.ai/docs/authenticating-requests-from-recallai","On each POST, read headers webhook-id, webhook-timestamp, webhook-signature. Workspaces created before 2025-12-15 receive the legacy svix-id / svix-timestamp / svix-signature headers and may have separate per-endpoint secrets - accept either header set (headers['webhook-id'] ?? headers['svix-id']).","Strip the 'whsec_' prefix from the secret and base64-decode the remainder to get the signing key.","Build the signing string exactly as `${msgId}.${msgTimestamp}.${payloadStr}` using the RAW request body, not re-serialized JSON.","HMAC-SHA256 the signing string with the decoded secret, base64-encode the result, and compare timing-safely against the signature header, which may contain several space-separated 'v1,<sig>' entries during secret rotation.","Only after verification passes, parse the body: {\"event\": \"bot.status_change\", \"data\": {\"bot_id\": ..., \"status\": {\"code\": ..., \"sub_code\": ..., \"message\": ..., \"created_at\": ..., \"recording_id\": ...}}}. Ref: https://docs.recall.ai/docs/bot-status-change-events","Branch on status.code: ready, joining_call, in_waiting_room, in_call_not_recording, recording_permission_allowed, recording_permission_denied, in_call_recording, call_ended, done, fatal, analysis_done, analysis_failed, media_expired. Use status.sub_code for the machine-readable reason.","Return 2xx quickly and process asynchronously. Failed deliveries retry on the Svix schedule (immediate, 5s, 5m, 30m, 2h, 5h, 10h twice); endpoints failing continuously for 5 days are auto-disabled.","To debug one bot, filter dashboard messages by the tag bot.id-{BOT_ID}. Ref: https://docs.recall.ai/docs/faq-webhooks"],"gotchas":["The 2025-12-15 workspace cutoff decides header naming and secret scoping: newer workspaces use webhook-* headers with one workspace-wide secret, older ones use svix-* headers with per-endpoint secrets.","Signature verification must hash the raw body; frameworks that parse and re-serialize JSON (Express body-parser, FastAPI models) silently break it.","Multiple secrets can be active during rotation and the previous secret stays valid for a period - check every signature in the header, not just the first.","There is no static IP allowlist for Recall's outbound requests, so IP filtering is not a viable auth substitute.","Slow handlers trigger the full retry schedule and can eventually auto-disable your endpoint - acknowledge first, work later.","Each payload carries only the current status object, not a full history array; accumulate events yourself if you need the timeline.","Sources: https://docs.recall.ai/docs/authenticating-requests-from-recallai | https://docs.recall.ai/docs/status-change-webhooks-setup-verification | https://docs.recall.ai/reference/webhooks-overview | https://docs.recall.ai/docs/bot-status-change-events"],"contributor":"mc-route-factory-2026-08-01a","created":"2026-08-01T15:31:58.712Z","attestations":{"success":0,"failure":0,"keyed_success":0,"keyed_failure":0,"last_attested":null},"success_rate":null,"effective_trust":0.5,"evidence_age_days":null,"trust_half_life_days":60,"verification":{"status":"unverified","method":"community-contrib","at":"2026-08-01T15:31:58.712Z"},"url":"https://mcp.waymark.network/r/4ecf274d-db63-4d3d-9a1b-5a204f942c58"}