The Bookable webhook system allows distributors to receive real-time notifications about key booking events — such as new bookings, updates, and cancellations. This documentation provides a complete guide to the integration process, including how to register webhooks and handle incoming notifications effectively.
- Register to Webhook Events: Use the Register to Webhook Events API to register your callback URL
- Store Secret Key: Save the returned secret key for data verification
- Implement the callback endpoint: Create an endpoint that follows the Handling Webhook Events API specification
- Handle Webhook Events: Process incoming booking events
- OAuth2 client credentials
- HTTPS endpoint capable of receiving POST requests
- Ability to store and manage secret keys securely
First, obtain an OAuth2 access token using the OAuth2 client credentials flow:
curl -X POST https://auth.bookabletech.com/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "client_credentials",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"audience": "api.bookabletech.com"
}'ℹ️ Info: In the following example, we assume
https://your-domain.com/webhooks/booking-notificationas thecallbackUrl.
curl -X POST https://api.bookabletech.com/webhooks \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"callbackUrl": "https://your-domain.com/webhooks/booking-notification"
}'Response:
{
"secretKey": "123e4567-e89b-12d3-a456-426655440000",
"callbackUrl": "https://your-domain.com/webhooks/booking-notification"
}⚠️ Important: Store the secretKey securely. You'll need it to verify incoming webhook data.
To update your callback URL:
curl -X PUT https://api.bookabletech.com/webhooks \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"callbackUrl": "https://your-new-domain.com/webhooks/booking-notification"
}'To remove your webhook registration:
curl -X DELETE https://api.bookabletech.com/webhooks \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"Your webhook endpoint must:
- Accept POST requests at the registered callback URL
- Handle JSON payloads
- Respond with appropriate HTTP status codes
- Implement proper error handling
booking.updated- Existing booking modified
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
// Your stored secret key from webhook registration
const WEBHOOK_SECRET = 'your-secret-key-here';
app.post('/webhooks/booking-notification', (req, res) => {
try {
// Verify webhook authenticity (implement your verification logic)
if (!verifyWebhookSignature(req)) {
return res.status(401).json({
success: false,
error: 'unauthorized',
message: 'Invalid webhook signature'
});
}
const { eventType, timestamp, booking } = req.body;
// Validate required fields
if (!eventType || !timestamp) {
return res.status(400).json({
success: false,
error: 'validation_error',
message: 'Missing required fields: eventType, timestamp'
});
}
// Process the booking notification
switch (eventType) {
case 'booking.updated':
handleBookingUpdated(booking);
break;
default:
console.warn(`Unknown event type: ${eventType}`);
}
// Return success response
res.status(204)..end();
} catch (error) {
console.error('Webhook processing error:', error);
res.status(500).json({
success: false,
error: 'internal_error',
message: 'Failed to process webhook notification'
});
}
});
function verifyWebhookSignature(req) {
// Verify webhook signature using HMAC-SHA256
// The signature is sent in the X-API-Key header
const signature = req.headers['x-api-key'];
if (!signature) {
return false;
}
const payload = JSON.stringify(req.body);
const expectedSignature = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expectedSignature, 'hex')
);
}
function handleBookingUpdated(booking) {
console.log('Booking updated:', booking[0]?.id);
// Implement your booking update logic
}
from flask import Flask, request, jsonify
import hashlib
import hmac
import json
app = Flask(__name__)
# Your stored secret key from webhook registration
WEBHOOK_SECRET = 'your-secret-key-here'
@app.route('/webhooks/booking-notification', methods=['POST'])
def handle_booking_notification():
try:
# Verify webhook authenticity
if not verify_webhook_signature(request):
return jsonify({
'success': False,
'error': 'unauthorized',
'message': 'Invalid webhook signature'
}), 401
data = request.get_json()
event_type = data.get('eventType')
timestamp = data.get('timestamp')
booking = data.get('booking')
# Validate required fields
if not event_type or not timestamp:
return jsonify({
'success': False,
'error': 'validation_error',
'message': 'Missing required fields: eventType, timestamp'
}), 400
# Process the booking notification
if event_type == 'booking.updated':
handle_booking_updated(booking)
else:
print(f"Unknown event type: {event_type}")
return '', 204
except Exception as e:
print(f"Webhook processing error: {e}")
return jsonify({
'success': False,
'error': 'internal_error',
'message': 'Failed to process webhook notification'
}), 500
def verify_webhook_signature(request, secret_key):
"""
Verify webhook signature using HMAC-SHA256.
The signature is sent in the X-API-Key header.
"""
signature = request.headers.get('X-API-Key')
if not signature:
return False
payload = json.dumps(request.get_json(), separators=(',', ':'))
expected_signature = hmac.new(
secret_key.encode('utf-8'),
payload.encode('utf-8'),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected_signature)
def handle_booking_updated(booking):
print(f"Booking updated: {booking[0]['id'] if booking else 'Unknown'}")
# Implement your booking update logic
{
"eventType": "booking.created|booking.updated|booking.cancelled",
"timestamp": "2025-06-17T10:30:00Z",
"booking": [
{
"id": "12345678-1234-1234-1234-123456789012",
"venueId": "87654321-4321-4321-4321-210987654321",
"date": "2025-06-25",
"time": "19:30:00",
"partySize": 4,
"status": "confirmed",
"reference": "REF-20250617-001",
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"phone": "+1234567890",
"company": "Acme Corp",
"location": {
"address": "123 Main Street",
"city": "New York",
"state": "NY",
"country": "US",
"postalCode": "10001"
},
"duration": 120,
"productType": "dinner",
"notes": "Window table preferred",
"createdDate": "2025-06-17T09:15:00Z",
"lastUpdate": "2025-06-17T10:30:00Z"
}
]
}pending- Booking is awaiting confirmationconfirmed- Booking has been confirmedcancelled- Booking has been cancelledcompleted- Booking has been completedno_show- Customer did not show up
The webhook system uses HMAC-SHA256 for payload verification. Here's how it works:
Signature Generation: The Bookings Group system generates a signature using:
- Payload: The complete JSON webhook payload
- Secret Key: Your webhook secret token
- Algorithm: HMAC-SHA256
Signature Delivery: The generated signature is sent in the
X-API-Keyheader with each webhook requestVerification Process: Your endpoint should:
- Extract the signature from the
X-API-Keyheader - Generate the expected signature using the same payload and secret
- Compare signatures using a timing-safe comparison function
- Extract the signature from the
The webhook secret key is used to generate an HMAC-SHA256 signature for payload verification. The Bookings Group system generates a signature using the JSON payload and your secret token, which is sent in the X-API-Key header.
Use the secret key provided during webhook registration to verify that incoming requests are legitimate:
function verifyWebhookSignature(req, secretKey) {
const signature = req.headers['x-api-key']; // Signature sent in X-API-Key header
const payload = JSON.stringify(req.body);
const expectedSignature = crypto
.createHmac('sha256', secretKey)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expectedSignature, 'hex')
);
}- HTTPS Only: Only use HTTPS endpoints for webhook URLs
- Validate Payloads: Always validate incoming data structure
- Idempotency: Handle duplicate notifications gracefully
- Error Handling: Implement proper error responses
- Logging: Log webhook events for debugging and monitoring
- Rate Limiting: Implement rate limiting on your webhook endpoint
Your webhook endpoint should return appropriate HTTP status codes:
204- Success: Notification processed successfully400- Bad Request: Invalid payload or missing required fields401- Unauthorized: Invalid authentication or signature500- Internal Server Error: Processing failed
The Bookings Group system will retry failed webhook deliveries with exponential backoff:
- Initial retry after 1 minute
- Subsequent retries at 2, 4, 8, 16, 32 minutes
- Maximum of 5 retry attempts
- Webhooks failing all retries will be logged for manual review
Create a simple test endpoint to verify your implementation:
curl -X POST https://your-domain.com/webhooks/booking-notification \
-H "Content-Type: application/json" \
-d '{
"eventType": "booking.created",
"timestamp": "2025-06-17T10:30:00Z",
"booking": [{
"id": "test-booking-id",
"venueId": "test-venue-id",
"date": "2025-06-25",
"time": "19:30:00",
"partySize": 4,
"status": "confirmed",
"reference": "TEST-REF-001",
"firstName": "Test",
"lastName": "User",
"email": "test@example.com",
"phone": "+1234567890",
"duration": 120,
"notes": "Test booking",
"createdDate": "2025-06-17T10:30:00Z",
"lastUpdate": "2025-06-17T10:30:00Z"
}]
}'Not Receiving Webhook Events
- Verify your callback URL is accessible from the internet
- Check that your endpoint returns correct HTTP status codes
- Ensure your server can handle HTTPS requests
Authentication Failures
- Verify your OAuth2 credentials are correct
- Ensure you're including the
audienceparameter in token requests
Payload Validation Errors
- Verify your endpoint accepts JSON content
- Check that required fields are being processed
- Validate the structure matches the specification
For technical support or questions about webhook integration:
- Email: hello@bookabletech.com
- Base URL:
https://api-staging.bookabletech.com - Auth URL:
https://bookabletech.uk.auth0.com/oauth/token
- Base URL:
https://api.bookabletech.com - Auth URL:
https://auth.bookabletech.com/oauth/token
| Method | Endpoint | Description |
|---|---|---|
| POST | /webhooks | Register a new webhook |
| PUT | /webhooks | Update existing webhook URL |
| DELETE | /webhooks | Delete webhook registration |
All webhook management endpoints require OAuth2 authentication with partner:create scope.