Webhook Guide
Receive real-time HTTP notifications when events happen in your studio.
How it works
When you register a webhook endpoint in Admin → Settings → Developer → Webhooks, BOOKING BIBLE will POST a JSON payload to your URL whenever a subscribed event occurs.
Every request includes an X-Webhook-Signature header — an HMAC-SHA256 hash of the request body, signed with your endpoint's secret. Verify this on every request to make sure the event is really from us.
Event types
| Event | Description |
|---|---|
| Bookings | |
| booking.created | A new booking was created |
| booking.cancelled | A booking was cancelled |
| booking.checked_in | A member checked in to a class |
| booking.no_show | A member was marked as no-show |
| booking.waitlist_promoted | A member was promoted from the waitlist |
| Classes | |
| class.created | A new class instance was generated |
| class.updated | A class instance was updated |
| class.cancelled | A class instance was cancelled |
| Members | |
| member.created | A new member signed up |
| member.updated | A member profile was updated |
| member.deleted | A member was removed |
| Passes | |
| pass.activated | A pass was activated |
| pass.expired | A pass expired |
| pass.paused | A pass was paused |
| pass.cancelled | A pass/subscription was cancelled |
| Payments | |
| payment.succeeded | A payment was completed |
| payment.failed | A payment failed |
| payment.refunded | A payment was refunded |
| Instructors | |
| instructor.substituted | An instructor was substituted for a class |
Payload format
Every webhook request POSTs a JSON envelope with a unique event id, type, timestamp, organization id, and an event-specific data object.
{
"id": "evt_a1b2c3d4",
"type": "booking.created",
"created_at": "2026-04-12T07:15:00Z",
"organization_id": "org_xxxx",
"data": {
"booking_id": "bk_xxxx",
"user_id": "usr_xxxx",
"class_instance_id": "ci_xxxx",
"class_name": "Hot Yoga",
"start_time": "2026-04-12T08:00:00Z",
"status": "confirmed",
"attendance_type": "physical"
}
}Request headers
| Header | Value |
|---|---|
| Content-Type | application/json |
| X-Webhook-Signature | sha256=xxxxxxxx... |
| X-Webhook-Event | booking.created |
| X-Webhook-Id | evt_a1b2c3d4 |
| X-Webhook-Timestamp | 2026-04-12T07:15:00Z |
| User-Agent | BookingBible-Webhooks/1.0 |
Verifying signatures
Compute an HMAC-SHA256 of the raw request body using your endpoint's secret, then compare the result with the X-Webhook-Signature header. Always use a constant-time comparison function — never === or ==.
const crypto = require('crypto');
function verifyWebhook(body, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(body, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express middleware
app.post('/webhooks/bookingbible', (req, res) => {
const signature = req.headers['x-webhook-signature'];
if (!verifyWebhook(req.rawBody, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = req.body;
console.log(`Received ${event.type}:`, event.data);
res.status(200).send('OK');
});Retry policy
If your endpoint returns a non-2xx status or times out (10s), we retry with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | immediate |
| 2 | after 1 minute |
| 3 | after 5 minutes |
| 4 | after 30 minutes |
| 5 | after 2 hours |
| 6 | after 12 hours |
After 10 consecutive failures, the endpoint is automatically disabled. You can re-enable it from the admin panel.
Best practices
- Respond with a 2xx within 5 seconds. Process events asynchronously.
- Use X-Webhook-Id to deduplicate — we may send the same event twice.
- Store the raw payload before processing for debugging.
- Verify the signature on every request — never skip this.
- Monitor your endpoint in Admin → Settings → Developer → Webhooks for delivery logs and failures.
Ready to subscribe?
Add a webhook endpoint from the admin panel and start receiving events in minutes.