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.
Transferência bancária (TED) — confirmação por conferência
Uma cobrança paga por transferência bancária não é confirmada por sistema: o pagador envia o comprovante no checkout e a Poolvo confere o recebimento antes de dar a cobrança por paga. Isso muda o que você deve esperar como integrador:
- O
payment.succeededde uma transferência chega depois da conferência, não no momento em que o pagador diz que pagou. Entre uma coisa e outra podem passar horas, e a conferência acontece em horário comercial. Não trate a demora como falha nem reenvie a cobrança. - Enquanto aguarda conferência, o pagamento existe e não conta como pago: não some no seu fechamento de período e não gera webhook.
- Recusa na conferência (comprovante que não confere) chega como
payment.failed. - Quando o pagamento é confirmado, o webhook é o mesmo
payment.succeededdo PIX e do cartão, com os mesmos campos. Você não precisa de tratamento próprio por método.
payment_methods em POST /v1/payment_links aceita pix e card. Cobrança por transferência bancária é criada pelo fluxo de cotação (POST /v1/quotes com payment_method: "ted", depois aceitando a cotação), e o objeto Payment devolve payment_method: "ted" normalmente.
Liquidação é confirmada fora da API
A previsão de liquidação vem no payment.succeeded (ver Webhooks → Liquidação). A confirmação de que o crédito ocorreu é registrada pela Poolvo por conferência manual, e não é exposta na API hoje: não existe evento, nem campo de status de liquidação no objeto Payment. payment.settled segue planejado. Se você precisa conciliar o crédito real, use o seu extrato bancário — não espere uma notificação nossa.