Webhooks
En vez de hacer polling, registra un endpoint HTTPS y recibe un POST por cada evento de tu operación — cobro pagado, acuerdo aceptado, disputa resuelta.
Registro
Gestiona endpoints en Configuración → Webhooks (o vía POST /api/v1/webhook-endpoints), eligiendo los eventos suscritos — una lista vacía suscribe todos, incluidos eventos futuros. Cada recurso emite *.created, *.updated, *.deleted y eventos de ciclo de vida (charge.paid, agreement.accepted, dispute.resolved...). El catálogo completo, con payload de ejemplo por evento, está en Eventos.
Entrega
Cada evento dispara un POST con el payload JSON:
POST /tu-endpoint HTTP/1.1
Content-Type: application/json
User-Agent: Kobana-Dunning-Webhooks/1.0
X-Webhook-Event: charge.paid
X-Webhook-Event-Id: 3d1f0c9a-5b2e-4c7d-9e1f-8a6b4c2d0e9f
X-Webhook-Delivery-Id: 9b2e4a10-7c3f-4d8e-a1b2-c3d4e5f60718
X-Webhook-Attempt: 1
X-Webhook-Timestamp: 2026-07-23T12:00:00.000Z
X-Webhook-Signature: t=1753272000,v1=<hmac-sha256 de "t.body" con tu secret>
{
"event": "charge.paid",
"timestamp": "2026-07-23T12:00:00.000Z",
"organizationId": "cmd8k0a1b0001ef5g",
"data": { ...recurso serializado en el formato de la API v1... }
}
Tu endpoint puede exigir autenticación propia (Basic, Bearer o header custom), configurada en el registro. Deduplica por el X-Webhook-Event-Id (estable entre intentos y reenvíos).
Verificando la firma
X-Webhook-Signature viene en el formato t=<unix>,v1=<hex>: v1 es el HMAC-SHA256 de "{t}.{body crudo}" con el secret del endpoint. Verifica la tolerancia del timestamp (recomendado: 5 minutos — anti-replay) y compara en tiempo constante:
const crypto = require('crypto');
const TOLERANCE_SECONDS = 300; // 5 minutos
function verify(rawBody, signatureHeader, secret) {
const t = /(?:^|,)t=(\d+)(?:,|$)/.exec(signatureHeader || '')?.[1];
const v1 = /(?:^|,)v1=([0-9a-f]+)(?:,|$)/i.exec(signatureHeader || '')?.[1];
if (!t || !v1) return false;
if (Math.abs(Math.floor(Date.now() / 1000) - Number(t)) > TOLERANCE_SECONDS) return false;
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const a = Buffer.from(expected, 'hex');
const b = Buffer.from(v1.toLowerCase(), 'hex');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Rechaza (con 401) cualquier entrega cuya firma no coincida.
Evento de prueba
POST /api/v1/webhook-endpoints/{id}/test entrega un evento webhook.test real por el mismo pipeline (cola, firma HMAC, captura, reintento) — valida el consumidor de punta a punta antes del primer evento de negocio.
Contrato del consumidor
- Responde
2xxde inmediato y procesa de forma asíncrona — el timeout es de 10 segundos por intento. - ¿Evento desconocido? Responde
200e ignóralo — nuevos eventos pueden empezar a emitirse sin aviso. - Deduplica: la entrega es "al menos una vez"; un
200perdido en la red genera un reenvío.
Las entregas fallidas se reintentan 8 veces con backoff exponencial (base 4 minutos, ventana total ≈ 8h30); el historial completo, con request y response capturados y reenvío manual, queda en la página del endpoint. Detalles del cronograma y del contrato en Entregas y reintentos y verificación de firma en Seguridad.