Agent skill
Recall.ai Webhooks Skill
Receive and verify Recall.ai webhooks (meeting bot / recording API). Use when setting up Recall.ai webhook handlers, debugging webhook-signature or legacy svix-signature verification (Standard Webhooks, base64 HMAC-SHA256 over "<id>.<timestamp>.<raw body>" with a whsec_ secret), or handling events like bot.done, bot.fatal, bot.call_ended, recording.done, transcript.done, calendar.sync_events, participant_events.join or transcript.data.
Install this skill
npx skills add hookdeck/webhook-skills --skill recall-ai-webhooks
When to Use This Skill
- How do I receive Recall.ai webhooks for meeting bot status changes?
- How do I verify Recall.ai webhook signatures (
webhook-signatureorsvix-signature)? - Why are the
webhook-id/webhook-timestamp/webhook-signatureheaders missing? - Handling
bot.done,recording.doneortranscript.doneto download media and transcripts - Handling real-time endpoint events like
participant_events.joinandtranscript.data - Keeping bot scheduling in sync with Calendar V2 (
calendar.update,calendar.sync_events)
Verification (core)
Recall.ai uses Standard Webhooks. With a workspace verification secret (Developers → API Keys & Secrets) every request carries webhook-id, webhook-timestamp and webhook-signature. Accounts created before 2025-12-15 get svix-id, svix-timestamp and svix-signature on Svix webhooks, signed with the per-endpoint Svix secret. The algorithm is the same, so accept either prefix:
const prefix = headers['webhook-signature'] !== undefined ? 'webhook' : 'svix';
const id = headers[`${prefix}-id`];
const timestamp = headers[`${prefix}-timestamp`];
const signature = headers[`${prefix}-signature`]; // "v1,<b64> v1,<b64>"
// Key = base64-decoded part after "whsec_"; signed content = id.timestamp.rawBody
const key = Buffer.from(secret.slice('whsec_'.length), 'base64');
const expected = crypto.createHmac('sha256', key)
.update(`${id}.${timestamp}.`).update(rawBody).digest();
// Several signatures arrive after a secret rotation: accept any v1 match
const valid = signature.split(' ').some((entry) => {
const [version, sig] = entry.split(',');
if (version !== 'v1' || !sig) return false;
const bytes = Buffer.from(sig, 'base64');
return bytes.length === expected.length && crypto.timingSafeEqual(bytes, expected);
});
Verify the raw body, return 400 on failure, and optionally reject timestamps more than 5 minutes old (the examples do; Recall.ai's sample doesn't). There's no Recall.ai SDK for this; standardwebhooks or svix libraries also work.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Common Event Types
The event name is in the body's top-level event field (not type).
| Event | Delivery | Description |
|---|---|---|
bot.joining_call | Svix | Bot is connecting to the meeting |
bot.in_waiting_room | Svix | Bot is in the waiting room |
bot.in_ | Svix | Bot joined but isn't recording |
bot.in_call_recording | Svix | Bot is recording |
bot.call_ended | Svix | Bot left the call; media may not be ready yet |
bot.done | Svix | Bot shut down; recorded media is available |
bot.fatal | Svix | Bot failed (data.data.sub_code has the reason) |
recording.done / recording.failed | Svix | Recording artifact finished or failed |
transcript.done / transcript.failed | Svix | Transcript artifact finished or failed |
calendar.update | Svix | Calendar changed (e.g. disconnected); re-fetch it |
calendar.sync_events | Svix | Calendar events changed since data.last_updated_ts |
participant_events.join / participant_events.leave | Real-time | Participant joined or left |
transcript.data | Real-time | Transcript utterance generated during the call |
For the full list (media artifacts like
audio_mixed.done, breakout room events, all participant events), see references/overview.md.
Environment Variables
# Workspace verification secret, or the legacy Svix endpoint signing secret
RECALL_WEBHOOK_SECRET=whsec_xxxxx
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 recall-ai --path /webhooks/recall-ai
Add the tunnel URL as an endpoint in your region's Recall.ai Webhooks dashboard (us-east-1, us-west-2, eu-central-1 or ap-northeast-1), or as a realtime_endpoints URL on Create Bot.
Reference Materials
- references/overview.md - Svix vs. real-time webhooks, full event list, payloads, retry behaviour
- references/setup.md - Regional dashboards, which secret to use, endpoint and real-time configuration
- references/verification.md - Signature verification details, gotchas and debugging