Booking Webhooks
Os booking webhooks entregam um POST assinado a um endpoint HTTPS sob seu controle toda vez que um booking muda de estado no seu workspace — criado, confirmado, cancelado, reagendado, recusado, reatribuído ou marcado como no-show. Esta página cobre a lista de eventos, o payload, os headers, a verificação de assinatura e o contrato de entrega/retry. Para a Bookings API somente leitura e uma orientação rápida, comece pela introdução de Bookings.
As inscrições de webhook são configuradas nas configurações de Bookings do app Copera por um administrador do workspace — elas não são criadas nem gerenciadas pela Public API. Cada inscrição tem uma URL de recebimento, um conjunto de eventos e um signing secret.
Eventos
| Evento | Dispara quando |
|---|---|
booking.created | Um booking é criado (pending ou confirmed). |
booking.confirmed | Um booking é confirmado. |
booking.cancelled | Um booking é cancelado. |
booking.rescheduled | Um booking é reagendado para um novo horário. |
booking.declined | Um booking pendente é recusado pelo host. |
booking.reassigned | O host de um booking é reatribuído (round-robin). |
booking.no_show | Um no-show é registrado para o host ou para o booker. |
Uma inscrição recebe apenas os eventos que ela inclui, e somente enquanto estiver ativa.
Payload
Cada entrega é um POST com um corpo JSON:
{
"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" }
}
}
O bloco data contém os dados de booking do seu próprio workspace, então os dados de contato do booker estão incluídos. Handles internos (o manage token do booking, a idempotency key, internos do ICS) nunca são enviados.
Headers
| Header | Valor |
|---|---|
X-Copera-Event | A string do evento (ex.: booking.confirmed). |
X-Copera-Signature | sha256=<hex> — HMAC-SHA256 do corpo bruto da requisição usando o secret da sua inscrição. |
X-Copera-Webhook-Version | A versão do contrato de payload (atualmente 1). |
Verificando a assinatura
Calcule o HMAC-SHA256 do corpo bruto da requisição (antes do parse do JSON) com o secret da sua inscrição e compare-o em tempo constante com o hex em 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);
}
Entrega e retries
- Responda com um
2xxpara confirmar o recebimento. - Um
4xxé tratado como uma rejeição permanente — o Copera não faz retry. - Um
5xx, erro de rede ou timeout sofre retry com backoff exponencial (3 tentativas no total). Faça seu receiver ser idempotente — o mesmo evento pode ser entregue mais de uma vez. - As URLs de recebimento precisam ser endpoints HTTPS públicos; endereços privados ou internos são rejeitados.
Referência
- Introdução de Bookings — orientação, Quick Start, paridade e a Bookings API somente leitura.
- Autenticação — escopos de token para os endpoints de leitura de Bookings.