Webhooks de reservas
Los webhooks de reservas entregan un POST firmado a un endpoint HTTPS que tú controlas cada vez que una reserva cambia de estado en tu workspace — creada, confirmada, cancelada, reprogramada, rechazada, reasignada o marcada como inasistencia (no-show). Esta página cubre la lista de eventos, el payload, los encabezados, la verificación de firma y el contrato de entrega/reintentos. Para conocer la API de Bookings de solo lectura y una orientación rápida, empieza por la introducción a Bookings.
Las suscripciones a webhooks se configuran desde los ajustes de Bookings de la app de Copera por un administrador del workspace — no se crean ni se gestionan a través de la Public API. Cada suscripción tiene una URL receptora, un conjunto de eventos y un secreto de firma.
Eventos
| Evento | Se dispara cuando |
|---|---|
booking.created | Se crea una reserva (pendiente o confirmada). |
booking.confirmed | Una reserva se confirma. |
booking.cancelled | Una reserva se cancela. |
booking.rescheduled | Una reserva se reprograma a una nueva hora. |
booking.declined | Una reserva pendiente es rechazada por el host. |
booking.reassigned | El host de una reserva se reasigna (round-robin). |
booking.no_show | Se registra una inasistencia para el host o el booker. |
Una suscripción recibe solo los eventos que incluye, y solo mientras está activa.
Payload
Cada entrega es un POST con un cuerpo 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" }
}
}
El bloque data son los datos de reserva de tu propio workspace, por lo que se incluyen los datos de contacto del booker. Los identificadores internos (el token de gestión de la reserva, la clave de idempotencia, los internos de ICS) nunca se envían.
Encabezados
| Encabezado | Valor |
|---|---|
X-Copera-Event | La cadena del evento (p. ej. booking.confirmed). |
X-Copera-Signature | sha256=<hex> — HMAC-SHA256 del cuerpo de la solicitud sin procesar (raw) usando el secreto de tu suscripción. |
X-Copera-Webhook-Version | La versión del contrato del payload (actualmente 1). |
Verificar la firma
Calcula el HMAC-SHA256 del cuerpo de la solicitud sin procesar (raw) (antes de parsear el JSON) con el secreto de tu suscripción y compáralo en tiempo constante con el hex de 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 y reintentos
- Responde con un
2xxpara confirmar la recepción. - Un
4xxse trata como un rechazo permanente — Copera no reintenta. - Un
5xx, un error de red o un timeout se reintenta con backoff exponencial (3 intentos en total). Haz que tu receptor sea idempotente — el mismo evento puede entregarse más de una vez. - Las URLs receptoras deben ser endpoints HTTPS públicos; las direcciones privadas o internas se rechazan.
Referencia
- Introducción a Bookings — orientación, inicio rápido, paridad y la API de Bookings de solo lectura.
- Autenticación — alcances de token para los endpoints de lectura de Bookings.