Skip to main content

Webhooks

Create webhooks under Settings > API & Webhooks. URLs must use HTTPS unless a developer explicitly enables the insecure-URL filter for a controlled environment.

Events

  • reservation.created
  • reservation.updated
  • reservation.rescheduled
  • reservation.status_changed
  • reservation.cancelled
  • reservation.deleted
  • payment.registered
  • payment.refunded
  • customer.created
  • customer.updated
  • customer.deleted
  • service.created
  • service.updated
  • service.deleted

The administration test action sends webhook.test.

Payload

{
"id": "evt_xxx",
"event": "reservation.created",
"created": 1779190000,
"site": {
"url": "https://example.com",
"timezone": "Europe/Rome",
"pluginVersion": "4"
},
"actor": {
"type": "api",
"id": "abc123...wxyz"
},
"data": {}
}

Headers

  • X-TBK-Event
  • X-TBK-Delivery
  • X-TBK-Timestamp
  • X-TBK-Site
  • X-TBK-Signature: sha256=<hex digest>

The signed bytes are:

<X-TBK-Timestamp>.<raw request body>

Always verify the raw body before parsing JSON and use a timing-safe comparison.

Verify in PHP

$timestamp = $_SERVER['HTTP_X_TBK_TIMESTAMP'] ?? '';
$provided = $_SERVER['HTTP_X_TBK_SIGNATURE'] ?? '';
$rawBody = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $rawBody, $secret);

if (!hash_equals($expected, $provided)) {
http_response_code(401);
exit;
}

Verify in Node.js

import crypto from 'node:crypto';

function verifyWebhook({timestamp, rawBody, signature, secret}) {
const expected = `sha256=${crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex')}`;

const left = Buffer.from(expected);
const right = Buffer.from(signature);
return left.length === right.length && crypto.timingSafeEqual(left, right);
}

Delivery behavior

Delivery is synchronous with a default timeout of five seconds. A failure does not roll back the original booking command. The webhook record stores delivery and failure counters plus the latest status.

Version 4 does not include an asynchronous retry queue. Respond with a 2xx quickly, deduplicate by X-TBK-Delivery, and run reconciliation for critical integrations.