[MARCA] Documentação

Webhooks

Eventos assinados, verificação com os SDKs, novas tentativas e boas práticas.

Cadastre um endpoint https público com os tipos de evento que interessam:

curl -X POST "https://api.exemplo.com.br/v1/webhook_endpoints" \
  -H "Authorization: Bearer $PIX_API_KEY" -H "Idempotency-Key: $(uuidgen)" -H "Content-Type: application/json" \
  -d '{"url": "https://loja.exemplo.com.br/webhooks/pix", "event_types": ["charge.paid", "refund.succeeded"]}'

A resposta traz o secret (whsec_…) uma única vez: guarde para verificar as assinaturas.

Eventos

charge.created, charge.paid, charge.expired, charge.canceled, charge.refunded, payment.received, payment.unmatched, refund.created, refund.succeeded, refund.failed, med.blocked, med.released, unit.suspended, unit.reactivated, settlement.closed (fechamento do dia) e ping (teste). Use "*" para todos.

{ "id": "evt_01J9Z…", "object": "event", "type": "charge.paid", "created_at": "2026-09-29T12:00:00Z",
  "livemode": true, "data": { "unit_id": "…", "object": { "id": "ch_…", "status": "paid", "amount": "149.90" } } }

Verifique a assinatura

Seguimos o padrão Standard Webhooks: cabeçalhos webhook-id, webhook-timestamp e webhook-signature (v1,<base64> — durante a rotação do segredo chegam duas). Use o corpo cru, exatamente como chegou.

app.post("/webhooks/pix", express.raw({ type: "application/json" }), (req, res) => {
  const event = webhooks.verify(req.body, req.headers, process.env.PIX_WEBHOOK_SECRET!);
  // …
  res.sendStatus(204);
});
event = webhooks.verify(request.body, request.headers, os.environ["PIX_WEBHOOK_SECRET"])
$event = \GatewayPix\Webhooks::verify(file_get_contents('php://input'), getallheaders(), getenv('PIX_WEBHOOK_SECRET'));

Assinatura inválida ou webhook-timestamp com mais de 5 minutos de diferença: responda 400 e descarte.

Entrega

  • Pelo menos uma vez: o mesmo evento pode chegar mais de uma vez — use o webhook-id para não processar duas vezes.
  • Sem garantia de ordem: charge.paid pode chegar antes de charge.created. Decida pelo estado atual (consulte a cobrança).
  • Responda 2xx em até 10 segundos; processe o resto de forma assíncrona.
  • Sem 2xx, tentamos de novo por cerca de 3,5 dias (0 s, 5 s, 30 s, 2 min, 10 min, 30 min, 1 h, 2 h, 4 h, 8 h, 12 h, 24 h, 24 h). Endpoint que falha seguidamente é desativado e você recebe um e-mail.
  • Reenvie manualmente com POST /v1/events/{id}/redeliver ou pelo painel.

Rotação do segredo

POST /v1/webhook_endpoints/{id}/rotate_secret devolve o segredo novo; por 24 horas assinamos com os dois. Passe os dois para o verify durante a troca (os SDKs aceitam uma lista).

Próximo: Cobrança com vencimento →