Webhooks
Eventos são entregues por HTTP POST aos seus endpoints ativos.
Catálogo de eventos
| type | quando |
|---|---|
payment.succeeded | pagamento confirmado |
payment.settled | pagamento liquidado — planejado, ainda não é disparado (não há confirmação automática de liquidação hoje; ver Liquidação) |
payment.cancelled | pagamento cancelado |
payment.failed | pagamento recusado |
Internamente, payment.captured é entregue publicamente como payment.succeeded. payment.authorized não gera 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": "..." }
]
}
}
}
}O payload público nunca inclui spot_rate, cobertura_centibps, source, median_3_stablecoin, percent_centibps nem percentuais de regra.
Liquidação (settlement)
Presente apenas em payment.succeeded. Diz quando o valor deve cair e por quê aquela data — para você fechar o caixa sem recalcular o calendário bancário (BR/Febraban + US/Fedwire), o corte de 15h (America/Sao_Paulo) e o rolamento de fim de semana por conta.
estimated_date(AAAA-MM-DD, America/Sao_Paulo) é uma ESTIMATIVA, não uma garantia. Reflete as regras vigentes de calendário no momento da confirmação.reason[]é o rastro cronológico do que moveu a data:cutoff(pago após as 15h),weekend, eholiday(comcountryename). Array vazio significa que a data caiu direto, sem ajustes.- Não há campo de "D+N". Use
estimated_date; oreasonexplica o caminho. Não some dias por conta. - Fora da cobertura do calendário, o objeto
settlementé omitido — nunca enviamos uma data sem cobertura. Trate a ausência como "sem previsão", não como erro. - Não existe evento de confirmação de liquidação.
payment.succeededtraz a previsão; a plataforma não emite hoje um evento que confirme que o crédito efetivamente ocorreu naestimated_date. Não assuma confirmação automática — se você precisa conciliar o crédito real, faça-o pelo seu extrato bancário.
Assinatura
Cada entrega carrega o header:
Pagooz-Signature: t=1789990000,v1=<hex>v1 é o HMAC-SHA256, em hex, de "{t}.{body}" (o timestamp, um ponto, e o corpo bruto do request), usando o secret do endpoint. Verifique e rejeite entregas com t a mais de 5 minutos do agora (tolerância de replay).
Exemplo 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 idempotência do consumidor
- Uma entrega é bem-sucedida somente com resposta
2xx. - Falha aciona 5 tentativas com backoff: imediato, 1 minuto, 5 minutos, 30 minutos e 2 horas.
- Depois da 5ª falha, a entrega fica
dead_letterede visível no cockpit. - Entrega ao menos uma vez. O mesmo evento pode chegar mais de uma vez (retry, renotificação, reentrega). Seu consumidor deve ser idempotente por
event.id— processe cadaiduma só vez.
Configuração no cockpit
Uma URL por tenant por ambiente (sandbox e live). O cockpit lê o modo global da sessão e salva a URL daquele ambiente. O secret de assinatura é exibido uma única vez na criação/rotação. A tabela webhook_endpoints não é usada nesta versão; fica reservada para múltiplos destinos futuros.
Conciliação e renotificação
Reenvio manual no cockpit
O merchant pode reenviar uma entrega falhada pelo cockpit. O payload reenviado mantém o mesmo event_id; a nova tentativa é registrada em webhook_deliveries com origin = "manual".
Histórico de entregas
O cockpit mostra as últimas entregas por ambiente. 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
}Permite diagnosticar sozinho por que uma notificação não chegou.