Webhooks
Webhooks let Reservly push real-time notifications to your app when events happen — new bookings, payments, cancellations, etc. Instead of polling our API, your server receives an HTTP POST automatically.
Setting Up Webhooks
Configure webhooks from your dashboard in Settings → Integrations → Webhooks.
- Enter your endpoint URL. It must use HTTPS.
- Select the events you want to receive.
- Click Add.
After adding a webhook, use the Test button to send a sample payload and verify your endpoint is reachable.
Event Types
Subscribe to any combination of the following events:
| Event | Description |
|---|---|
booking.created | A new booking is made |
booking.confirmed | A booking is confirmed by the business owner |
booking.cancelled | A booking is cancelled |
booking.rescheduled | A booking's date or time is changed |
payment.received | A payment is successfully completed |
payment.refunded | A refund is processed |
customer.created | A new customer profile is created |
Payload Format
Every webhook delivery is a POST request with a JSON body. The top-level structure includes the event type, a timestamp, your business identity, and the event-specific data.
Booking event example
{
"event": "booking.created",
"timestamp": "2026-04-15T10:30:00.000Z",
"business": {
"id": "uuid",
"slug": "luxe-salon"
},
"data": {
"booking_id": "uuid",
"date": "2026-04-15",
"start_time": "10:00",
"end_time": "11:00",
"status": "pending",
"customer_name": "Jane Smith",
"customer_email": "jane@example.com",
"services": [
{
"name": "Haircut",
"duration_minutes": 60,
"price": 45
}
]
}
}jsonPayment event example
{
"event": "payment.received",
"timestamp": "2026-04-15T10:31:00.000Z",
"business": {
"id": "uuid",
"slug": "luxe-salon"
},
"data": {
"booking_id": "uuid",
"amount_cents": 4500,
"currency": "USD",
"provider": "stripe",
"status": "paid",
"customer_name": "Jane Smith",
"customer_email": "jane@example.com"
}
}jsonHeaders
Every webhook request includes the following headers:
| Header | Description |
|---|---|
X-Reservly-Signature | HMAC-SHA256 signature of the payload |
X-Reservly-Event | The event type (e.g., booking.created) |
X-Reservly-Timestamp | ISO 8601 timestamp of the delivery attempt |
Content-Type | application/json |
User-Agent | Reservly-Webhooks/1.0 |
Signature Verification
To verify that a webhook was genuinely sent by Reservly, compute an HMAC-SHA256 hash of the raw request body using your signing secret, then compare it to the X-Reservly-Signature header.
Your signing secret is the SHA-256 hash of your first API key. Generate an API key in Settings → Integrations before setting up webhooks.
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your Express handler:
app.post('/webhook', (req, res) => {
const signature = req.headers['x-reservly-signature'];
const isValid = verifyWebhook(
JSON.stringify(req.body),
signature,
YOUR_SECRET
);
if (!isValid) return res.status(401).send('Invalid signature');
// Process the event...
res.status(200).send('OK');
});javascriptRetry Behavior
If your endpoint does not respond with a 2xx status code, Reservly retries delivery up to 3 times with exponential backoff:
- 1st retry— 1 second after the initial attempt
- 2nd retry— 5 seconds after the 1st retry
- 3rd retry— 25 seconds after the 2nd retry
Each attempt has a 15-second timeout.
Only 5xx errors and network failures trigger retries. 4xx errors are treated as permanent failures and are not retried. You can view the full delivery history in Settings → Integrations.
Best Practices
- Respond with 200 quickly. Acknowledge the webhook immediately and process the event asynchronously (e.g., via a queue). Long-running handlers risk hitting the 15-second timeout.
- Verify signatures on every request. Always check the
X-Reservly-Signatureheader to confirm the payload was not tampered with. - Handle duplicate deliveries. Network issues can cause the same event to be delivered more than once. Use the
timestampanddatafields to implement idempotency in your handler. - Monitor delivery history. Check Settings → Integrations for failed deliveries and address any endpoint issues promptly.