Skip to main content

Webhooks

Let's Book sends real-time notifications to your server when events happen. No polling required.

How webhooks work

When a booking gets confirmed, a customer registers, or any other event occurs, we send an HTTP POST request to your webhook URL with event details. Your server processes the data and responds within 5 seconds with a 2xx status code to confirm receipt. If we don't get a 2xx response, we'll retry the webhooks a few times.

What you need to do

Webhook payload structure

Every webhook POST request contains:

{
"event": "booking.confirmed",
"occurredOn": "2025-11-17T18:34:11+00:00",
"data": {
...
}
}

See the API documentation for the complete list of webhook events and their payload schemas.

Verify webhook signatures

Webhook subscriptions have a secret. You should securely store this secret in your systems as you can use it to verify the request came from Let's Book and wasn't tampered with.

When we call your webhook URL, we send along a X-Webhook-Signature header. The signature is an HMAC SHA-256 hash of the request body using your webhook secret as the key. You should also calculate the signature based on the secret you have and verify it corresponds to the signature we sent.

How to verify

const express = require('express');
const crypto = require('crypto');
const path = require('path');

// Load environment variables via dotenv (most popular package)
require('dotenv').config({ path: path.resolve(__dirname, '.env') });

const app = express();

function verifyWebhookSignature(payload, signature, secret) {
if (!signature || typeof signature !== 'string') return false;

// Compute HMAC using the raw payload
const calculatedSignatureHex = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');

// Compare in constant time using 32-byte buffers
const expected = Buffer.from(calculatedSignatureHex, 'hex');
const provided = Buffer.from(signature, 'hex');

return crypto.timingSafeEqual(expected, provided);
}

// Express.js example
app.post(
'/webhooks/letsbook',
// Parse the body as raw Buffer so we can verify the signature
express.raw({ type: 'application/json' }),
(req, res) => {
const secret = process.env.LETSBOOK_WEBHOOK_SECRET; // Place the secret in your environment
const signature = req.headers['x-webhook-signature'];

const payloadBuffer = req.body;
const payload = Buffer.isBuffer(payloadBuffer)
? payloadBuffer.toString()
: '';

if (!verifyWebhookSignature(payload, signature, secret)) {
return res.status(401).send('Invalid signature');
}

// ...parse JSON safely

return res.status(200).send('OK');
}
);

app.listen(3000, () => {
console.log(`Server is humming on http://localhost:3000`);
});

Testing webhooks

  1. In order to test your webhook while developing, you need to make it publicly accessible to the internet. You can use a tool like ngrok or Cloudflare Tunnels for this.
  2. Create a test webhook in the dashboard
  3. Trigger test events by creating bookings in your test environment
  4. Verify your signature validation works and your server responds correctly

Best practices checklist

  1. Always verify signatures - Check the X-Webhook-Signature header on every request. Don't process webhooks without verification.
  2. Return 2xx quickly - Respond fast. Process heavy operations in a background job after acknowledging receipt.
  3. Handle duplicates - Network issues can cause duplicate deliveries. Use the event data to detect and skip duplicates.
  4. Log everything - Keep webhook logs for debugging. Store the raw payload, headers, and your processing results.