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. Campos internos sensíveis nunca são enviados.
Correlacionando um evento de webhook com a API pública
O payload do webhook inclui data.id. Passe esse valor diretamente para GET /public/v1/bookings/{id} para buscar os detalhes completos do booking:
curl -H "Authorization: Bearer $TOKEN" \
"https://api.copera.ai/public/v1/bookings/${data.id}"
Use o mesmo valor data.id com uma requisição autenticada da API de Bookings quando precisar correlacionar eventos de webhook com os detalhes completos do booking.
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.