Skip to content

Webhooks

Eventos são entregues por HTTP POST aos seus endpoints ativos.

Catálogo de eventos

typequando
payment.succeededpagamento confirmado
payment.settledpagamento liquidado — planejado, ainda não é disparado (não há confirmação automática de liquidação hoje; ver Liquidação)
payment.cancelledpagamento cancelado
payment.failedpagamento recusado

Internamente, payment.captured é entregue publicamente como payment.succeeded. payment.authorized não gera webhook.

Envelope

json
{
  "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, e holiday (com country e name). Array vazio significa que a data caiu direto, sem ajustes.
  • Não há campo de "D+N". Use estimated_date; o reason explica 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.succeeded traz a previsão; a plataforma não emite hoje um evento que confirme que o crédito efetivamente ocorreu na estimated_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):

js
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_lettered e 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 cada id uma 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:

json
{
  "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.