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-idpara não processar duas vezes. - Sem garantia de ordem:
charge.paidpode chegar antes decharge.created. Decida pelo estado atual (consulte a cobrança). - Responda
2xxem 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}/redeliverou 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).