Webhooks
O que são Webhooks?
Webhooks são requisições HTTP POST enviadas automaticamente para uma URL de sua escolha sempre que eventos específicos ocorrem na plataforma. Isso permite que sua aplicação seja notificada instantaneamente sobre mudanças no status de pagamentos e saques, sem a necessidade de fazer polling constante na API.
Como Configurar Webhooks
Você pode gerenciar webhooks pelo painel (Menu Lateral → Desenvolvedor →
Webhooks) ou via API com sua chave (POST /webhook — veja
Referência da API). Criar, atualizar e excluir também estão
disponíveis pelo painel; atualizar e excluir via API ainda não são suportados com
X-API-KEY (exigem sessão do painel) — liste e crie webhooks pela API
normalmente, mas gerencie edição/remoção pelo painel por enquanto.
Importante
Sua URL de webhook precisa ser pública (HTTPS recomendado) e aceitar requisições POST. URLs apontando para localhost, IPs privados (10.x, 172.16-31.x, 192.168.x) ou domínios reservados (example.com, .test, .local) são rejeitadas na criação.
Campos do Webhook
- URL do Endpoint: URL onde você receberá as notificações
- Nome: identificador do webhook
- Eventos: lista de eventos que você quer receber (veja abaixo)
- Secret: gerado na criação, mostrado uma única vez — use pra validar a assinatura de cada entrega (veja "Verificando a Assinatura")
Eventos Disponíveis
Consulte a lista completa e atualizada em GET /webhook/events
(Referência da API). Os eventos hoje disponíveis:
| Evento | Quando dispara |
|---|---|
charge.pending | Cobrança criada, aguardando pagamento |
charge.processing | Pagamento em processamento na adquirente |
charge.paid | Pagamento confirmado |
charge.expired | Cobrança expirou sem ser paga |
charge.received | Reservado para uso futuro — nenhum fluxo atual dispara este evento |
subscription.authorized | Assinatura Pix Automático autorizada pelo cliente |
subscription.rejected | Autorização de assinatura recusada |
subscription.charge.paid | Cobrança recorrente de uma assinatura paga |
subscription.charge.failed | Cobrança recorrente de uma assinatura falhou |
withdraw.completed | Saque confirmado pela adquirente — veja Saldo e Saques |
dispute.chargeback | Chargeback recebido numa cobrança |
Sobre charge.received
Esse evento existe na lista de eventos selecionáveis, mas nenhum fluxo do sistema
o dispara atualmente. Não configure lógica que dependa dele. Pra saber quando um
valor fica disponível pra saque, consulte GET /account-balance — não existe hoje
uma notificação push pra liberação de reserva financeira.
Formato do Payload
Toda entrega é um POST com este formato:
{
"event": "charge.paid",
"data": {
"chargeId": "chg_abc123",
"companyId": "cmp_xyz789",
"paymentStatus": "PAID",
"previousStatus": "PROCESSING",
"total": 10000,
"externalId": null,
"customer": {
"id": "cli_abc123",
"name": "Nome do Cliente",
"email": "cliente@exemplo.com",
"phone": "+5511999999999"
},
"paidAt": "2026-08-07T10:30:00.000Z",
"expiresAt": null
},
"timestamp": "2026-08-07T10:30:00.000Z"
}total está em centavos. paymentStatus reflete o status real da cobrança
(PENDING, PROCESSING, PAID, EXPIRED, REFUNDED). customer e previousStatus
podem vir null/ausentes dependendo do evento.
Verificando a Assinatura
Cada entrega inclui os headers:
X-Webhook-Event: nome do evento (ex.charge.paid)X-Webhook-Delivery: ID único da entregaX-Webhook-Timestamp: timestamp ISO da entregaX-Webhook-Signature:sha256=<hex>— HMAC-SHA256 do corpo bruto (JSON serializado exatamente como enviado), usando osecretdo webhook como chave
const crypto = require('crypto');
function isValid(rawBody, signatureHeader, secret) {
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));
}Use o corpo bruto (antes de qualquer JSON.parse) pra calcular o HMAC — corpos
re-serializados podem mudar a ordem das chaves e invalidar a comparação.
Implementação do Endpoint
Seu endpoint deve:
- Aceitar POST com
Content-Type: application/json - Responder com status 200-299 pra confirmar recebimento
- Responder rápido — timeout de 10 segundos por tentativa
- Validar a assinatura antes de processar (seção acima)
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
if (!isValid(req.body, req.headers['x-webhook-signature'], WEBHOOK_SECRET)) {
return res.status(401).send('invalid signature');
}
const event = JSON.parse(req.body);
switch (event.event) {
case 'charge.paid':
// ...
break;
case 'charge.expired':
// ...
break;
case 'withdraw.completed':
// ...
break;
}
res.status(200).send('OK');
});Tentativas de Entrega
- Tentativas: até 5
- Intervalo: backoff exponencial a partir de 5s (≈5s, 10s, 20s, 40s, 80s entre tentativas)
- Timeout por tentativa: 10 segundos
- Status de sucesso: 200-299
Atenção
Depois de esgotar as 5 tentativas de uma entrega, o webhook é desativado
automaticamente (não é só a entrega que falha — o webhook inteiro para de
receber novas entregas até você reativá-lo pelo painel). Monitore os logs de
entrega (GET /webhook/{id}/logs) se seu endpoint tiver instabilidade.
Logs de Webhook
Consulte GET /webhook/{id}/logs (Referência da API) pra ver o
histórico de entregas (sucesso/falha, status HTTP, duração). Pra testar sem esperar
um evento real, use POST /webhook/{id}/test.
Perguntas Frequentes
Sim, você pode criar múltiplos webhooks com URLs diferentes, cada um com sua própria lista de eventos.
O sistema tenta reenviar seguindo a política de retry acima. Após 5 tentativas sem sucesso, o webhook é desativado automaticamente — reative pelo painel depois de corrigir o endpoint.
Use POST /webhook/{id}/test pra disparar uma entrega de teste, ou ferramentas
como ngrok pra expor um servidor local com uma URL pública.
Ainda não — criar e listar funcionam com X-API-KEY, mas atualizar e remover
exigem sessão do painel por enquanto. Gerencie esses dois pelo painel.
Suporte
Problemas com webhooks ou dúvidas de implementação: fale com o suporte da sua instância pelos canais disponíveis no painel de controle.