Webhooks
Subscribe to real-time events from Reservory. Webhooks enable you to sync bookings to your CRM, email platform, analytics, or custom systems.
Creating a Webhook Subscription
Create subscriptions at /dashboard/settings/webhooks in the operator dashboard.
POST /api/webhooks/endpoints
Authorization: Bearer <session_jwt>
Content-Type: application/json
{
"url": "https://your-server.com/webhooks/reservory",
"events": [
"booking.created",
"booking.confirmed",
"booking.cancelled",
"refund.created",
"waiver.signed"
]
}The response contains your signing_secret — store this securely. It will not be shown again.
Event Taxonomy
Reservory currently emits these events:
A customer has created a booking (hold placed)
A booking has been confirmed and paid
A booking was cancelled (by customer or operator)
A customer checked in at the venue
A booking was marked as no-show
A refund was issued for a booking
A refund attempt failed
A customer signed their waiver
A new customer record was created
A gift card was created
A gift card was used on a booking
Slot capacity was exceeded (safety net)
Webhook Payload
Every webhook POST includes:
POST https://your-server.com/webhooks/reservory
X-Reservory-Timestamp: 1716170400
X-Reservory-Signature: v1,abc123def...
Content-Type: application/json
{
"event_id": "evt_abc123",
"event": "booking.confirmed",
"created_at": "2026-05-20T12:00:00Z",
"data": {
"booking_id": "bk_456",
"status": "confirmed",
"slot_id": "slot_123",
"customer_email": "alice@example.com",
"customer_name": "Alice",
"seat_count": 2,
"total_cents": 3999,
"created_at": "2026-05-20T11:55:00Z",
"confirmed_at": "2026-05-20T12:00:00Z"
}
}Signature Verification
Verify webhook authenticity using HMAC-SHA256. The signature is computed over ${timestamp}.${rawBody}:
// Node.js example
import crypto from 'crypto';
function verifyWebhook(request, signingSecret) {
const timestamp = request.headers['x-reservory-timestamp'];
const signature = request.headers['x-reservory-signature'];
const rawBody = request.rawBody; // buffer before JSON parse
// Reject old timestamps (> 5 minutes)
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - parseInt(timestamp)) > 300) {
throw new Error('Webhook timestamp too old');
}
// Compute HMAC
const message = `${timestamp}.${rawBody.toString()}`;
const computed = crypto
.createHmac('sha256', signingSecret)
.update(message)
.digest('hex');
// Compare (constant-time)
if (!crypto.timingSafeEqual(
Buffer.from(computed),
Buffer.from(signature.split(',')[1])
)) {
throw new Error('Webhook signature invalid');
}
}
Retry Policy
If your endpoint returns a non-2xx status or times out (10s), Reservory retries with exponential backoff:
- Attempt 1: immediate
- Attempt 2: 5 minutes
- Attempt 3: 30 minutes
- Attempt 4: 2 hours
- Attempt 5: 12 hours
- Attempt 6: 24 hours (final)
After 6 failed attempts, the delivery is marked failed and logged to your /dashboard/settings/webhooks page.
Signing Secret Rotation
When you rotate a signing secret at /dashboard/settings/webhooks, the old secret remains valid for 7 days. This allows you to deploy your verification code before shutting off the old key.
Best Practices
- Always verify the signature before processing.
- Reject timestamps older than 5 minutes to prevent replay attacks.
- Return 200 quickly; process heavy tasks asynchronously.
- Store your signing secret in environment variables, never in code.
- Log received webhook events for audit purposes.
- Monitor your webhook endpoint logs at
/dashboard/settings/webhooksto catch delivery failures.