Skip to main content
Quando algo acontece com o seu dinheiro, a gente faz um POST na sua URL com o evento em JSON. É assim que você sabe que o PIX caiu sem ficar consultando a API.

Duas configurações diferentes

callbackUrl, por cobrança

Você manda no corpo da requisição ao criar. Vale só para aquela cobrança.Usado por pagamento PIX, cartão, assinatura e split.

URL da conta, no painel

Configurada uma vez em API e Desenvolvedores. Vale para tudo.Usado por pedido da loja e saque.

O que a sua URL precisa ter

URL inválida devolve 400 na criação da cobrança, com callbackUrl must be a valid, public HTTPS URL. O erro vem na hora, não na entrega.
Em desenvolvimento, use um túnel (ngrok http 3000, cloudflared tunnel) para expor o seu localhost com HTTPS público. Passar http://localhost:3000 não funciona por design.

Headers

X-Webhook-Signature vem em todo webhook, sem opt-in. Se você não valida, a sua URL aceita qualquer POST da internet dizendo que um pagamento foi aprovado.

Reentrega

Se a entrega falhar (timeout, erro de rede ou status fora de 2xx), o evento entra na fila e é reenviado. São até 7 tentativas, cobrindo pouco mais de 20 horas. O corpo reenviado é idêntico ao original e o X-Webhook-Id não muda.
É exatamente por causa da reentrega que o seu handler precisa ser idempotente. Deduplique pelo X-Webhook-Id antes de fazer qualquer coisa que não dá pra desfazer, como creditar saldo ou enviar licença.

Eventos

Em sandbox, os eventos de saque saem como withdrawal.test.requested e withdrawal.test.completed.

O jeito certo de tratar

1

Leia o corpo cru

Antes de qualquer parse de JSON. A assinatura é calculada sobre os bytes exatos que chegaram.
2

Valide a assinatura

Com comparação timing-safe. Se não bater, responda 401 e pare por aí.
3

Deduplique pelo X-Webhook-Id

Já processou esse id? Responda 200 e não faça nada.
4

Grave e responda 200

Salve o evento e responda imediatamente. O processamento pesado vai para uma fila.
5

Confira o valor antes de entregar

amountCents precisa bater com o que você esperava. Status paid sozinho não basta.
Não conte com a ordem de chegada. Se a ordem importa para você, confirme o estado atual com um GET na API antes de decidir.

Validando a assinatura

Código pronto em Node, PHP, Python e Go.