Horato firma eventos salientes, conserva entregas y permite replay para que los consumidores puedan recuperarse de fallos.
Firmas
Cada entrega es un POST con el evento JSON como cuerpo y dos headers: horato-event (el tipo de evento) y horato-signature con la forma t=<timestamp>,v1=<firma>. La firma es HMAC-SHA256(secret, "<timestamp>.<cuerpo_crudo>") en hex, usando el secreto de firma que se devuelve una sola vez al crear el webhook.
Verifica sobre el cuerpo crudo antes de parsear JSON, compara firmas en tiempo constante y rechaza timestamps de más de unos minutos para reducir el riesgo de replay. Los nombres antiguos x-okcal-event / x-okcal-signature se envían como alias para consumidores que verificaban contra los nombres previos al rebrand.
import crypto from "node:crypto";
function verifyHoratoWebhook(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto
.createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
const ok = crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return ok && fresh;
}
verifyHoratoWebhook(rawBody, req.headers["horato-signature"], process.env.HORATO_WEBHOOK_SECRET);Catálogo de eventos
Suscribe un webhook a tipos de evento específicos, u omite el filtro para recibirlos todos. Los eventos se nombran por dominio.
- Conexiones:
connection.reauth_required. - Email:
email.received,email.sent,email.draft.created,email.draft.updated. - Agendamiento:
scheduling.booking.created,scheduling.booking.rescheduled,scheduling.booking.cancelled. - Grabación:
recording.bot.started,recording.meeting.status_change,recording.transcript.updated,recording.recording.completed. - La lista completa y vigente está en
/docs/openapi.jsony en la columna de eventos de la referencia.
Reparación de entregas
Horato reintenta entregas fallidas con backoff exponencial y mueve los eventos agotados a una cola dead-letter. Usa el inspector cuando un receptor devuelva un estado distinto de 2xx: arregla el receptor, reproduce la entrega y luego limpia las colas dead-letter de forma intencional.
- Lista entregas con
/v1/webhooks/deliveries. - Reproduce una entrega con
/v1/webhooks/deliveries/{delivery_id}/replay. - Reencola eventos dead-letter con
/v1/webhooks/dead-letter/{event_id}/requeue.