Webhooks

Clearslot sends a signed HTTPS request to your endpoint when something changes in your business: a booking is made, moved or cancelled, a customer record changes, or a booking link is edited. Every Pro business and every business with a complimentary grant can use them.

Use webhooks to react within seconds. Use GET /api/v1/events to catch up after downtime or to check that you saw everything.

Events

TypeWhen
booking.createdA booking exists. Requests that await approval arrive with status pending. Reservations that still wait for email verification, online payment or a waitlist answer are never sent.
booking.confirmedA booking request was approved.
booking.rescheduledA booking moved. previous holds the old start and end.
booking.cancelledA booking was cancelled by the host, the guest or a connected calendar, or a request was declined.
booking.no_show_marked, booking.no_show_clearedAttendance changed.
booking.payment_succeeded, booking.payment_refundedAn online payment settled or was refunded.
customer.created, customer.updated, customer.erasedThe customer register changed.
booking_link.created, booking_link.updated, booking_link.deletedA booking link's own settings changed.
routing_form.submittedA routing form was submitted.
webhook.pingVerification and test requests. Every endpoint receives it.

Subscribe to explicit types. New types never reach an endpoint that did not ask for them. Subscribing to a family needs its read scope: bookings:read, customers:read, or booking_links:read for booking links and routing forms.

The request

POST /your/path HTTP/1.1
Content-Type: application/json
webhook-id: 7d0f3c1e-5b8a-4c0e-9a51-2f6c1d3e8b90
webhook-timestamp: 1793180000
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=
{
  "id": "7d0f3c1e-5b8a-4c0e-9a51-2f6c1d3e8b90",
  "object": "event",
  "type": "booking.rescheduled",
  "apiVersion": 1,
  "occurredAt": "2026-11-02T09:14:03.120Z",
  "subject": { "object": "booking", "id": "…" },
  "details": { "rescheduledBy": "guest", "trigger": "move" },
  "previous": { "startTime": "…", "endTime": "…" },
  "data": { "object": "booking", "id": "…", "status": "confirmed", "startTime": "…" },
  "timestamp": "2026-11-02T09:14:05.480Z"
}

The event feed

GET /api/v1/events lists the business's events from the last 30 days, newest first, with cursor paging. Without expand, each event has everything above except data. Filter with type, one type or several separated by commas. expand=data renders each subject as a webhook delivery does; limit is then at most 25. A key sees an event only if it holds the read scope of the event's family, so a key with only bookings:read lists booking events alone.

Verify the signature

Requests follow the Standard Webhooks specification. The signature is HMAC-SHA256 over webhook-id, webhook-timestamp and the raw body, joined with dots, using your endpoint's whsec_… secret. Verify the raw body before parsing it, and reject timestamps more than 5 minutes old.

Node.js, with the standardwebhooks package:

import { Webhook } from 'standardwebhooks';

const webhook = new Webhook(process.env.CLEARSLOT_WEBHOOK_SECRET);
// rawBody: the exact bytes received, as a string.
const event = webhook.verify(rawBody, {
  'webhook-id': request.headers['webhook-id'],
  'webhook-timestamp': request.headers['webhook-timestamp'],
  'webhook-signature': request.headers['webhook-signature']
});

Python, with the standardwebhooks package:

from standardwebhooks.webhooks import Webhook

event = Webhook(secret).verify(raw_body, headers)

Without a library:

import { createHmac, timingSafeEqual } from 'node:crypto';

const key = Buffer.from(secret.slice('whsec_'.length), 'base64');
const expected = createHmac('sha256', key).update(`${id}.${timestamp}.${rawBody}`).digest('base64');
const valid = signatureHeader.split(' ').some(part => {
  const [version, value] = part.split(',');
  return version === 'v1' && value.length === expected.length && timingSafeEqual(Buffer.from(value), Buffer.from(expected));
});

Rotating the secret keeps the old one valid for 24 hours. During that time webhook-signature carries two signatures separated by a space, so a receiver that still has the old secret keeps working until you deploy the new one.

Answer quickly

Return any 2xx status within 10 seconds. Do the work afterwards. Clearslot reads at most 64 KB of your response and ignores its body.

Verification, retries and failures

The delivery log in Settings → Developers → Webhooks shows every attempt from the last 30 days: status, attempts, response status, latency and an error class (timeout, dns, tls, refused, blocked_address, http_status, too_large). Request and response bodies are never stored.

Where an endpoint can live

Filters

An endpoint can be limited to some team members, some booking links, or both. Filters apply to booking and booking-link events; endpoints with filters can't subscribe to customer or routing-form events. A staff member's own integration can receive only their bookings this way.

Authentication header

Some receivers can check a fixed header but not a signature. Give the endpoint one custom header, for example X-Clearslot-Token, and it is sent with every request. The value is encrypted, never shown again after saving and never logged. Host, Content-*, webhook-* and connection headers are reserved. Prefer the signature when your receiver can check it.

Endpoints through the API (REST Hooks)

Automation platforms create one endpoint per workflow when it is turned on, and delete it when it is turned off:

POST /api/v1/webhook-endpoints
{ "url": "https://hooks.example.com/abc", "eventTypes": ["booking.created"] }

The response is { "endpoint": { … }, "secret": "whsec_…" }. The secret is returned once; store it to verify signatures. An Idempotency-Key is ignored on this request, because the response carries the secret.

RequestDoes
GET /api/v1/webhook-endpoints, GET /api/v1/webhook-endpoints/{id}List or show endpoints (webhooks:read)
PATCH /api/v1/webhook-endpoints/{id}Change url, description, eventTypes, filters or authHeader (null removes it), or turn it off and on with enabled
DELETE /api/v1/webhook-endpoints/{id}Unsubscribe
POST /api/v1/webhook-endpoints/{id}/testSend webhook.ping now; a pending endpoint that answers becomes active
POST /api/v1/webhook-endpoints/{id}/rotate-secretReturn a new secret once; the old one stays valid for 24 hours
GET /api/v1/webhook-endpoints/{id}/deliveriesThe delivery log, newest first
POST /api/v1/webhook-deliveries/{id}/retrySend a finished event delivery again with a fresh body

An API-created endpoint that answers 410 is deleted, and one that never verifies is deleted after 24 hours, so a platform that misses an unsubscribe does not leave endpoints behind. A changed URL is verified again before events resume. A business can have 100 endpoints. Creating them needs webhooks:write plus the read scope of each event family. An API key or OAuth app sees and manages only the endpoints it created. Revoking a key removes its endpoints, and once a key expires its endpoints receive nothing; a rolled key keeps them. Signing secrets are shown again, or replaced, in the dashboard only to owners and admins who confirm their password or two-factor code.

Hand-built automations

Until the Zapier, Make and n8n apps are published, or if you prefer building the flow yourself, register the platform's catch URL as an endpoint in Settings → Developers → Webhooks, and call the API with an API key in later steps (Authorization: Bearer cs_live_…).

n8n

  1. Add a Webhook node with HTTP method POST. Copy its production URL.
  2. Under Authentication, choose Header Auth and create a credential

with a header name such as X-Clearslot-Token and a long random value.

  1. In Clearslot, add an endpoint with that URL, choose the events, and enter

the same header name and value as the authentication header.

  1. Activate the workflow before saving the endpoint, so the verification ping

gets a 2xx. The ping has type webhook.ping; add an If node that stops the workflow for it.

  1. To check signatures instead, set the Webhook node's Raw Body option and

verify in a Code node with the snippet above.

  1. Call the API with an HTTP Request node and a Header Auth credential

(Authorization, Bearer cs_live_…).

Make

  1. Start a scenario with Webhooks → Custom webhook and copy its address.
  2. Add the address as an endpoint in Clearslot. Make answers the ping with

200 while the scenario listens, which verifies the endpoint.

  1. Filter out webhook.ping with a filter on type after the trigger.
  2. Make's custom webhook can't check signatures; use the authentication header

and a filter on its value. Under the webhook's advanced settings, enable Get request headers to read it.

  1. Call the API with HTTP → Make a request, adding the Authorization

header.

Zapier (Webhooks by Zapier)

  1. Create a Zap with Webhooks by Zapier → Catch Hook (use **Catch Raw

Hook** if you want to verify signatures in a Code step) and copy the URL.

  1. Add it as an endpoint in Clearslot, then use Test trigger; Zapier keeps

the ping as sample data. Add a Filter step that continues only when type is not webhook.ping.

  1. Catch Hook can't check signatures. Use the authentication header and a

Filter on the header value, or use Catch Raw Hook with a Code by Zapier step that verifies the signature.

  1. Call the API with Webhooks by Zapier → Custom Request and the

Authorization header.

For sample data while building, GET /api/v1/events?expand=data&type=booking.created&limit=1 returns a recent event in the same shape as a webhook body.