Webhooks
Receba eventos em tempo real e valide a assinatura.
Webhooks avisam seu servidor quando algo acontece. Cadastre endpoints HTTPS no painel, em Desenvolvedores, na tela Webhooks, e escolha os eventos de cada um. Em cobranças e saques, o campo opcional webhookUrl envia os eventos daquele recurso para mais uma URL, com o mesmo segredo.
O id identifica o evento, e data é o recurso completo no momento do evento, no mesmo formato da API.
{
"id": "3f0a7d7c-52ce-4c2b-8f0e-1a6d9b2e4c11",
"type": "charge.paid",
"livemode": true,
"createdAt": "2026-11-01T15:06:12.000Z",
"data": {
"id": "7c1e4f2a-3b58-4e0d-9a6c-2f8d1b7e5a10",
"object": "charge",
"livemode": true,
"status": "paid",
"amount": 15000,
"fee": 299,
"net": 14701,
"currency": "BRL",
"reference": "PEDIDO-0001",
"description": "Plano anual",
"customer": {
"name": "Ana Souza",
"document": "12345678901",
"email": "ana@example.com"
},
"metadata": {
"pedido": "0001"
},
"pix": {
"copyPaste": "0002...",
"qrCodeBase64": "iVBOR..."
},
"checkoutUrl": "https://painel.example.com/pay/7c1e4f2a-3b58-4e0d-9a6c-2f8d1b7e5a10",
"payer": null,
"failureReason": null,
"paidAt": "2026-11-01T15:06:12.000Z",
"refundedAt": null,
"createdAt": "2026-11-01T15:04:05.000Z"
}
}Eventos
| Evento | Quando |
|---|---|
charge.created | Cobrança criada, com o Pix disponível. |
charge.paid | Pagamento confirmado e saldo creditado. |
charge.refunded | Pix estornado e saldo debitado. |
payout.created | Saque criado e valor reservado. |
payout.paid | Saque concluído. |
payout.failed | Saque falhou, foi rejeitado ou cancelado, e a reserva voltou ao saldo. Veja status e failureReason. |
test.ping | Disparado pelo botão de teste no painel. Tem data vazio. |
Verificar a assinatura
Todo envio traz estes cabeçalhos. Valide a assinatura antes de usar o conteúdo, porque sua URL é pública e qualquer um pode chamá-la.
| Cabeçalho | Conteúdo |
|---|---|
X-Webhook-Id | Id do evento. É igual nas retentativas. |
X-Webhook-Signature | t=<timestamp>,v1=<assinatura> |
A assinatura é o HMAC-SHA256 em hexadecimal de <t>.<corpo cru>, usando o segredo whsec_ do ambiente, disponível no painel.
- Calcule sobre o corpo exatamente como chegou, antes de qualquer parse.
- Compare em tempo constante.
- Recuse
tcom mais de 5 minutos de diferença para evitar repetição de mensagens capturadas. Cada tentativa traz um novot.
import express from 'express';
import { Gateway } from '@gateway/sdk';
const app = express();
// O corpo precisa chegar cru: JSON re-serializado invalida a assinatura.
app.post('/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
let event;
try {
event = Gateway.webhooks.verify(
req.body,
req.headers['x-webhook-signature'],
process.env.WEBHOOK_SECRET,
);
} catch {
return res.sendStatus(400);
}
res.sendStatus(200);
enqueue(event); // processe depois de responder
});Retentativas
Uma entrega só conta como recebida com resposta 2xx em até 10 segundos. Redirecionamentos não são seguidos e contam como falha. Depois de uma falha, enviamos de novo nestes intervalos:
| Retentativa | 1ª | 2ª | 3ª | 4ª | 5ª | 6ª |
|---|---|---|---|---|---|---|
| Espera | 1 min | 5 min | 30 min | 2 h | 6 h | 24 h |
Depois da 6ª retentativa a entrega para. O painel mostra cada entrega e permite reenviar manualmente.
Boas práticas
- Responda 200 imediatamente e processe em uma fila.
- Use o
iddo evento para ignorar duplicatas: a entrega é pelo menos uma vez. - Não dependa da ordem de chegada. Antes de liberar um pedido, se quiser garantia extra, consulte GET /v1/charges/{id}.
- Confira o campo livemode para não misturar eventos de teste com produção.
- Endpoints precisam usar HTTPS e não podem apontar para endereços internos.