Agent skill

SavvyCal Webhooks Skill

Receive and verify SavvyCal webhooks. Use when setting up SavvyCal webhook handlers, debugging x-savvycal-signature verification, or handling scheduling events like event.created, event.rescheduled, event.canceled, event.changed, or event.checkout.completed.

Install this skill

npx skills add hookdeck/webhook-skills --skill savvycal-webhooks


When to Use This Skill

  • How do I receive SavvyCal webhooks?
  • How do I verify the SavvyCal x-savvycal-signature header?
  • How do I handle event.created, event.rescheduled, or event.canceled events?
  • Why is my SavvyCal webhook signature verification failing?
  • Setting up a SavvyCal webhook handler for bookings, approvals, or paid-event checkout

Verification (core)

SavvyCal signs the raw request body with HMAC-SHA256, keyed with the webhook's secret, and sends the result in the x-savvycal-signature header. There is no timestamp and no SDK helper, so verify manually.

The header is sha256= followed by uppercase hex, and most HMAC libraries output lowercase. Strip the sha256= prefix and compare the hex case-insensitively.

const crypto = require('crypto');

function verifySavvyCalSignature(rawBody, header, secret) {
  if (!header || !secret) return false;

  const received = header.trim().replace(/^sha256=/i, '').toLowerCase();
  if (!/^[0-9a-f]{64}$/.test(received)) return false;

  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(received, 'hex'), Buffer.from(expected, 'hex'));
}

Read the body as raw bytes whatever the content type (in Express, express.raw({ type: '*/*' })), and parse JSON only after verification passes.

For complete handlers with route wiring, event dispatch, and tests, see:

Common Event Types

The payload is JSON with a top-level type (the event name) and id (payload_...).

EventTriggered When
event.createdSomeone books a meeting (or an organizer approves a pending one)
event.requestedSomeone books a meeting that requires approval
event.approved / event.declinedAn organizer approves or declines a requested event
event.rescheduledAn existing event is rescheduled
event.changedAn event is updated in any way, with details of the change
event.canceledAn event is canceled
event.checkout.pendingA paid event is booked and checkout is still pending
event.checkout.expiredA paid event's checkout expires unpaid
event.checkout.completedA paid event's checkout succeeds
event.attendee.added / event.attendee.canceled / event.attendee.rescheduledAttendee changes on an event or group event
poll.response.created / poll.response.updatedA poll response is created or updated
workflow.action.triggeredA workflow action fires

For the full event reference, see references/overview.md and SavvyCal's webhook documentation.

Environment Variables

# Secret shown next to the webhook in Settings > Integrations
SAVVYCAL_WEBHOOK_SECRET=your_webhook_secret_here

Local Development

# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 savvycal --path /webhooks/savvycal

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 5, 2026

View on GitHub →