Agent skill
Demodesk Webhooks Skill
Receive and authenticate Demodesk webhooks. Use when setting up a Demodesk webhook endpoint, checking the custom header you add to it, debugging 401s, or handling meeting and recording events like demo.scheduled, demo.ended and recording.uploaded.
Install this skill
npx skills add hookdeck/webhook-skills --skill demodesk-webhooks
Demodesk sends a JSON webhook when a sales meeting or its recording changes: a demo is scheduled, rescheduled, canceled, started or ended, or a recording is uploaded. Webhooks are not signed. Instead, you add a custom header (for example Authorization: Bearer abc123) to the webhook endpoint in Demodesk, and your receiver compares that header with the value you stored.
When to Use This Skill
- How do I receive Demodesk webhooks?
- Does Demodesk sign webhook payloads? (No: use a custom header as a shared secret)
- How do I authenticate Demodesk webhook requests?
- How do I handle
demo.scheduled,demo.endedorrecording.uploadedevents? - Why does my Demodesk webhook endpoint return 401?
Verification (core)
const crypto = require('crypto');
// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
const ha = crypto.createHash('sha256').update(String(a)).digest();
const hb = crypto.createHash('sha256').update(String(b)).digest();
return crypto.timingSafeEqual(ha, hb);
}
function verifyDemodeskRequest(headers, name = process.env.DEMODESK_WEBHOOK_HEADER || 'Authorization',
secret = process.env.DEMODESK_WEBHOOK_SECRET) {
if (!secret) return false; // never accept when unconfigured
const received = headers[name.toLowerCase()]; // Node lowercases header names
return Boolean(received) && safeEqual(received, secret);
}
- The header name is yours to choose (default
Authorization), set inDEMODESK_WEBHOOK_HEADER. The expected value isDEMODESK_WEBHOOK_SECRET: the exact, whole header value, including anyBearerprefix you typed. Look the header up case-insensitively, and compare the whole value; don't strip or parse aBearerprefix. - Python:
hmac.compare_on bytes (adigest(received.encode(), secret.encode()) strargument raises on non-ASCII input). - Return
401for a missing header or a mismatch,400for a body that isn't a JSON object,200once handled. - Read the body raw (
express.raw({ type: '*/*' })) and authenticate before parsing it.
A static shared credential authenticates the sender, not the body. There is no signature and no timestamp, so it gives no replay protection and cannot detect a tampered payload. Serve the endpoint over HTTPS only, and rotate the header value (edit the webhook in Demodesk and update
DEMODESK_WEBHOOK_SECRET) if it leaks.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Event Types
The event name is in meta.event.action. Each webhook endpoint subscribes to one event.
| Event | Sent When |
|---|---|
demo.scheduled | A meeting is scheduled |
demo.rescheduled | A meeting is moved |
demo.event_ | An invitee's response to the meeting changes |
demo.handovered | A meeting is handed over to another host |
demo.canceled | A meeting is canceled |
demo.started | A meeting starts |
demo.call_note_updated | A call note is updated |
demo.ended | A meeting ends (can fire again if it is restarted) |
recording.uploaded | A recording is uploaded |
recording.transcription_ | A transcript finishes post-processing |
Environment Variables
DEMODESK_WEBHOOK_HEADER=Authorization # custom header name you added in Demodesk
DEMODESK_WEBHOOK_SECRET=Bearer your-long-random-secret # exact whole header value
Local Development
Demodesk can't reach localhost. Tunnel with the Hookdeck CLI (no account required):
npx hookdeck-cli listen 3000 demodesk --path /webhooks/demodesk
Use the printed URL as the Webhook URL in Demodesk.
Resources
- references/overview.md - What Demodesk webhooks are, events and payload shape
- references/setup.md - Add a webhook endpoint with a custom header
- references/verification.md - Header check details, gotchas, debugging 401s
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests