Webhooks
Los eventos se entregan por HTTP POST a sus endpoints activos.
Catálogo de eventos
| type | cuándo |
|---|---|
payment.succeeded | pago confirmado |
payment.settled | pago liquidado — planeado, aún no se dispara (hoy no hay confirmación automática de liquidación; ver Liquidación) |
payment.cancelled | pago cancelado |
payment.failed | pago rechazado |
Internamente, payment.captured se entrega públicamente como payment.succeeded. payment.authorized no genera webhook.
Envelope
{
"id": "evt_9f8c...", "type": "payment.succeeded", "created": 1789990000,
"data": {
"payment": {
"id": "pay_9f8c...",
"amount": 510000,
"currency": "BRL",
"gross_amount": 510000,
"gross_currency": "BRL",
"fee_amount": 25000,
"fee_currency": "BRL",
"net_amount": 95000,
"net_currency": "USD",
"payment_method": "pix",
"status": "succeeded",
"reference": "pedido-123",
"failure_code": null,
"fx": {
"rate": 5.1,
"base": "USD",
"quote": "BRL",
"locked_at": 1800000000,
"valid_until": null,
"rate_history_id": 123
},
"settlement": {
"estimated_date": "2026-09-08",
"reason": [
{ "type": "cutoff", "note": "..." },
{ "type": "weekend", "date": "2026-09-05" },
{ "type": "holiday", "date": "2026-09-07", "country": "BR", "name": "..." }
]
}
}
}
}El payload público nunca incluye spot_rate, cobertura_centibps, source, median_3_stablecoin, percent_centibps ni porcentajes de regla.
Liquidación (settlement)
Presente solo en payment.succeeded. Dice cuándo debe caer el dinero y por qué esa fecha — para que usted cierre la caja sin recalcular el calendario bancario (BR/Febraban + US/Fedwire), el corte de las 15h (America/Sao_Paulo) y el rodaje de fin de semana por su cuenta.
estimated_date(AAAA-MM-DD, America/Sao_Paulo) es una ESTIMACIÓN, no una garantía. Refleja las reglas de calendario vigentes al momento de la confirmación.reason[]es el rastro cronológico de lo que movió la fecha:cutoff(pagado después de las 15h),weekendyholiday(concountryyname). Un array vacío significa que la fecha cayó directo, sin ajustes.- No hay campo de "D+N". Use
estimated_date; elreasonexplica el camino. No sume días por su cuenta. - Fuera de la cobertura del calendario, el objeto
settlementse omite — nunca enviamos una fecha sin cobertura. Trate la ausencia como "sin previsión", no como error. - No existe evento de confirmación de liquidación.
payment.succeededtrae la previsión; la plataforma no emite hoy un evento que confirme que el crédito efectivamente ocurrió enestimated_date. No asuma confirmación automática — si necesita conciliar el crédito real, hágalo desde su extracto bancario.
Firma
Cada entrega lleva el header:
Pagooz-Signature: t=1789990000,v1=<hex>v1 es el HMAC-SHA256, en hex, de "{t}.{body}" (el timestamp, un punto y el cuerpo bruto de la solicitud), usando el secret del endpoint. Verifique y rechace entregas cuyo t esté a más de 5 minutos del momento actual (tolerancia de replay).
Ejemplo funcional (Node):
const crypto = require("crypto");
function verifyPagoozSignature(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((kv) => kv.split("=")));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false; // 5 min
const expected = crypto.createHmac("sha256", secret)
.update(`${t}.${rawBody}`).digest("hex");
const a = Buffer.from(expected), b = Buffer.from(parts.v1 || "");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}Entrega, retry e idempotencia del consumidor
- Una entrega solo tiene éxito con una respuesta
2xx. - Una falla dispara 5 intentos con backoff: inmediato, 1 minuto, 5 minutos, 30 minutos y 2 horas.
- Después de la 5ª falla, la entrega queda
dead_letteredy visible en el cockpit. - Entrega al menos una vez. El mismo evento puede llegar más de una vez (retry, renotificación, reentrega). Su consumidor debe ser idempotente por
event.id— procese cadaiduna sola vez.
Configuración en el cockpit
Una URL por tenant por entorno (sandbox y live). El cockpit lee el modo global de la sesión y guarda la URL de ese entorno. El secret de firma se muestra una sola vez, al crear/rotar. La tabla webhook_endpoints no se usa en esta versión; queda reservada para múltiples destinos en el futuro.
Conciliación y renotificación
Reenvío manual en el cockpit
El merchant puede reenviar una entrega fallida desde el cockpit. El payload reenviado mantiene el mismo event_id; el nuevo intento se registra en webhook_deliveries con origin = "manual".
Historial de entregas
El cockpit muestra las últimas entregas por entorno. Cada objeto:
{
"id": "whd_9f8c...", "event_id": "evt_9f8c...",
"attempt": 3, "status": "failed", "http_status": 503,
"response_body": "...",
"origin": "auto", "created": 1789990000, "next_attempt_at": 1789990300
}Le permite diagnosticar por su cuenta por qué una notificación no llegó.