Skip to main content

Booking Webhooks

Booking webhooks deliver a signed POST to an HTTPS endpoint you control every time a booking transitions in your workspace — created, confirmed, cancelled, rescheduled, declined, reassigned, or flagged as a no-show. This page covers the event list, the payload, the headers, signature verification, and the delivery/retry contract. For the read-only Bookings API and a quick orientation, start with the Bookings introduction.

note

Webhook subscriptions are configured from the Copera app's Bookings settings by a workspace administrator — they are not created or managed through the Public API. Each subscription has a receiver URL, a set of events, and a signing secret.

Events

EventFires when
booking.createdA booking is created (pending or confirmed).
booking.confirmedA booking is confirmed.
booking.cancelledA booking is cancelled.
booking.rescheduledA booking is rescheduled to a new time.
booking.declinedA pending booking is declined by the host.
booking.reassignedA booking's host is reassigned (round-robin).
booking.no_showA no-show is recorded for the host or booker.

A subscription receives only the events it includes, and only while it is active.

Payload

Each delivery is a POST with a JSON body:

{
"event": "booking.confirmed",
"createdAt": "2026-07-06T09:00:00.000Z",
"data": {
"id": "665f…",
"status": "CONFIRMED",
"bookingTypeId": "665f…",
"bookingTypeTitle": "Intro Call",
"start": "2026-07-08T15:00:00.000Z",
"end": "2026-07-08T15:30:00.000Z",
"durationMinutes": 30,
"timezoneAtBooking": "America/Sao_Paulo",
"hosts": [{ "userId": "665f…", "role": "ORGANIZER" }],
"booker": {
"name": "Alice Booker",
"email": "[email protected]",
"phone": "+15551234567",
"timezone": "America/Sao_Paulo",
"locale": "en"
},
"guests": [],
"answers": [{ "questionId": "q1", "label": "Topic", "value": "Pricing" }],
"location": { "type": "MEETING_CHANNEL" }
}
}

The data block is your workspace's own booking data, so the booker's contact details are included. Internal handles (the booking's manage token, idempotency key, ICS internals) are never sent.

Headers

HeaderValue
X-Copera-EventThe event string (e.g. booking.confirmed).
X-Copera-Signaturesha256=<hex> — HMAC-SHA256 of the raw request body using your subscription secret.
X-Copera-Webhook-VersionThe payload contract version (currently 1).

Verifying the signature

Compute the HMAC-SHA256 of the raw request body (before JSON parsing) with your subscription secret and compare it in constant time to the hex in X-Copera-Signature:

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

function isValidSignature(rawBody, header, secret) {
const expected =
"sha256=" + createHmac("sha256", secret).update(rawBody, "utf8").digest("hex");
const a = Buffer.from(header);
const b = Buffer.from(expected);
return a.length === b.length && timingSafeEqual(a, b);
}

Delivery and retries

  • Respond with a 2xx to acknowledge receipt.
  • A 4xx is treated as a permanent rejection — Copera does not retry.
  • A 5xx, network error, or timeout is retried with exponential backoff (3 attempts total). Make your receiver idempotent — the same event may be delivered more than once.
  • Receiver URLs must be public HTTPS endpoints; private or internal addresses are rejected.

Reference