Webhooks -- Visão Geral
Webhooks permitem que sua aplicação receba notificações em tempo real sobre eventos na plataforma Monetarie. Quando um evento ocorre, a Monetarie envia um HTTP POST para a URL cadastrada.
Como Funciona
- Cadastre uma URL de webhook na sua conta
- Quando um evento ocorrer (ex: PIX recebido), a Monetarie envia um HTTP POST para sua URL
- Sua aplicação processa a notificação e responde com status
2xx(200, 201 ou 204)
Eventos Disponíveis
A Monetarie entrega ao cliente final eventos transacionais de PIX, TED, TEF entre contas Monetarie, tarifa e teste operacional de webhook. Eventos de provisionamento da Partner API (account.*, pix.key.*, pix.claim.* e pix.med.*) não pertencem a este contrato. Qualquer tentativa de assiná-los nesta API é rejeitada com events: contains invalid events: ....
| Evento | Status body | Descrição | Disparo |
|---|---|---|---|
pix.charge.created | created | QR code gerado ou cash-in iniciado | Ativo |
pix.charge.paid | paid | PIX recebido e liquidado | Ativo |
pix.charge.expired | expired | QR code expirou sem pagamento | Ativo |
pix.charge.cancelled | cancelled | QR code cancelado antes de pagamento | Registrado, ainda não disparado |
pix.payout.queued | queued | PIX enviado enfileirado por limite operacional. Retry automático com TTL máximo de 2h | Ativo |
pix.payout.processing | processing | PIX enviado, aguardando confirmação BACEN | Ativo |
pix.payout.held | processing | PIX enviado aceito pelo SPI e ainda sem desfecho. Liquida ou rejeita em seguida - não reenviar | Ativo |
pix.payout.confirmed | settled | PIX enviado e confirmado (terminal) | Ativo |
pix.payout.failed | rejected | PIX enviado rejeitado pelo SPI (terminal) | Ativo |
pix.payout.returned | returned | PIX enviado devolvido | Ativo |
pix.payout.return.failed | rejected | Tentativa de devolução do PIX enviado rejeitada; nenhum valor retornou | Ativo |
pix.refund.requested | requested | Pedido de devolução recebido (infração BACEN); bloqueio cautelar criado no saldo do cliente | Ativo |
pix.refund.completed | settled / completed | Análise da defesa finalizada e devolução executada (ou liberada) | Ativo |
pix.refund.failed | failed | Devolução rejeitada pelo SPI/liquidante (terminal); traz reason_code e reason_description do motivo | Ativo |
pix.return.received | - | ALIAS de assinatura de pix.payout.returned (o evento canônico é o entregue; status: "returned", direction: "credit") | Alias |
pix.infraction.created | OPEN | Infração PIX recebida via BACEN DICT; pode exigir análise e defesa MED | Ativo |
pix.infraction.resolved | CLOSED / CANCELLED | Infração resolvida por decisão final ou cancelamento da contraparte | Ativo |
pix.infraction.defense_submitted | defense_submitted | Defesa submetida pelo merchant (portal ou API); aguarda análise BACEN | Ativo |
tef.transfer.sent | settled | TEF entre contas Monetarie liquidada para a conta de origem | Ativo |
tef.transfer.received | settled | TEF entre contas Monetarie liquidada para a conta de destino | Ativo |
tef.transfer.failed | failed | TEF entre contas Monetarie rejeitada ou não liquidada | Ativo |
webhook.test | test | Teste manual. Disponível apenas via Admin/Merchant portal - a External API não expõe endpoint para disparar teste | Disparo manual (não External API) |
Recebimento, tarifa e TED
| Evento | Status body | Descrição | Disparo |
|---|---|---|---|
pix.received | settled | PIX recebido sem QR code seu (chave avulsa, copia e cola de terceiro) | Ativo |
fee.charged | charged | Tarifa cobrada da sua conta | Ativo |
ted.received | settled | TED recebida (crédito) | Ativo |
ted.confirmed | settled | TED enviada e confirmada (terminal) | Ativo |
ted.failed | rejected | TED enviada rejeitada ou expirada (terminal) | Ativo |
ted.refund.requested | requested | Devolução de TED recebida solicitada | Ativo |
ted.refund.completed | completed | Devolução de TED concluída | Ativo |
ted.refund.failed | failed | Devolução de TED rejeitada | Ativo |
O catálogo em tempo real
GET /api/external/webhooks/events devolve exatamente a lista aceita no cadastro, e é a fonte da verdade: se divergir desta página, vale a resposta do endpoint.
Partner API é um contrato separado
Integrações que provisionam clientes e contas pela Partner API possuem catálogo e autenticação próprios. Eventos como account.created não devem ser configurados no portal do cliente final.
pix.charge.cancelled ainda não é disparado
O evento está no enum e pode ser assinado, mas o sistema não possui fluxo de cancelamento de QR code hoje. Se você assinar, o POST /webhooks responde 201 normalmente - porém nenhuma notificação chegará. Continue monitorando pix.charge.expired para o ciclo natural de vida do QR.
Segurança
Cada notificação inclui headers de segurança e identificação para validação:
| Header | Descrição |
|---|---|
X-Monetarie-Signature | Assinatura HMAC-SHA256 do payload (prefixo sha256=). Em casos raros (webhook cadastrado sem secret), o valor literal é unsigned - veja nota abaixo |
X-Monetarie-Timestamp | Unix timestamp em segundos do envio |
X-Monetarie-Event-Id | UUID único da delivery (para deduplicação) |
X-Monetarie-Event-Type | Tipo do evento (ex: pix.charge.paid) |
Content-Type | Sempre application/json |
User-Agent | Sempre Monetarie-Webhook/1.0 - use para whitelisting em firewalls/WAF. Evolução futura seguirá o padrão Monetarie-Webhook/{version}; filtre por prefixo Monetarie-Webhook/ se quiser ficar imune a novas versões |
Signature unsigned quando webhook não tem secret
Se o webhook foi cadastrado sem campo secret em um registro legado, o header X-Monetarie-Signature pode vir como unsigned. Isso desabilita a validação HMAC do seu lado. Se você receber unsigned, cadastre um novo webhook com secret explícito e remova o antigo.
SHA256 nos webhooks vs SHA512 na API
A API usa HMAC-SHA512 para autenticar requisições que você envia. Os webhooks enviados pela Monetarie usam HMAC-SHA256 na assinatura X-Monetarie-Signature. São algoritmos diferentes -- cada um no seu contexto.
Validando a Assinatura
Valide a assinatura para garantir que a notificação foi enviada pela Monetarie:
const crypto = require('crypto');
function validateWebhook(rawBody, timestamp, signature, secret) {
// rawBody is the RAW request body string (before any JSON parse)
// timestamp is unix seconds (e.g., 1712160000)
const message = `${timestamp}.${rawBody}`;
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(message)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}Use o body RAW, não re-serializado
Você deve usar o corpo exato da requisição HTTP como os bytes chegaram na sua aplicação. Se fizer JSON.parse e depois JSON.stringify, os bytes resultantes não serão idênticos ao que a Monetarie usou para assinar, e a validação falhará.
Em Express/Node: use express.raw({ type: 'application/json' }) ou guarde o body antes de qualquer middleware de parse.
Em outras frameworks: configure para capturar o raw body antes do middleware JSON.
Ordenação de chaves em WEBHOOKS: NÃO é necessária
Para validação de webhooks (HMAC-SHA256) você NÃO precisa ordenar as chaves - use o body raw como recebido no HTTP request do Monetarie.
⚠️ Atenção - diferença vs envio de requisições: Na assinatura HMAC-SHA512 de REQUISIÇÕES que você envia, a ordenação alfabética das chaves É obrigatória (o servidor Monetarie reordena antes de validar). Não confunda os dois cenários:
- Webhook recebido (HMAC-SHA256): valide o body raw sem reordenar
- Request enviada (HMAC-SHA512): ordene suas chaves alfabeticamente antes de assinar
Valide sempre
Nunca processe um webhook sem validar a assinatura. Isso protege contra requisições falsificadas.
Adicionalmente, valide que o X-Monetarie-Timestamp está dentro de ± 5 minutos da hora atual (proteção anti-replay - o servidor não rejeita webhooks "antigos" por padrão; essa verificação cabe ao seu endpoint como defense-in-depth) e deduplique eventos por X-Monetarie-Event-Id (proteção contra retries).
Ordem dos Eventos
Os eventos de um mesmo pagamento podem chegar ao seu endpoint fora de ordem em janelas curtas (entregas HTTP são independentes e um PIX pode liquidar em menos de um segundo). Trate os status conforme a tabela:
| Tipo de evento | Semântica |
|---|---|
*.processing / intermediários | Informativo. Nunca deve sobrescrever um status terminal já recebido. |
*.confirmed, *.paid, *.completed, *.failed | Terminal (absorvente): depois de receber um destes, ignore qualquer evento intermediário do mesmo pagamento que chegue depois. |
Regras práticas para o seu consumidor:
- Aplique transições de estado pelo conteúdo do evento (campo
status), não pela ordem de chegada. - Um pagamento em estado terminal nunca regride: descarte
processingrecebido apósconfirmed/failed. - Deduplique por
X-Monetarie-Event-Id(retries reenviam o mesmo evento com o mesmo id).
Política de Retry
Se sua URL retornar erro transitório ou encerrar a conexão antes de responder, a Monetarie faz um ciclo inicial de 8 tentativas com backoff curto:
| Tentativa | Delay desde tentativa anterior | Tempo acumulado |
|---|---|---|
| 1a | - (imediato) | ~50-200 ms |
| 2a | 2 segundos | ~2 s |
| 3a | 5 segundos | ~7 s |
| 4a | 15 segundos | ~22 s |
| 5a | 30 segundos | ~52 s |
| 6a | 1 minuto | ~1,9 min |
| 7a | 2 minutos | ~3,9 min |
| 8a | 5 minutos | ~8,9 min |
Se o erro continuar transitório após o ciclo inicial, a delivery permanece pending e o reconciliador retoma automaticamente as tentativas em intervalos de até 5 minutos, preservando o mesmo X-Monetarie-Event-Id. Respostas HTTP permanentes de cliente, como 400 ou 401, encerram a entrega como failed; replay manual continua disponível pelo portal com trilha de auditoria.
Status de uma delivery
Cada delivery passa pelos status: pending (criada, aguardando ou recuperando entrega) → delivered (2xx recebido) OU failed (erro permanente) OU expired (proteção contra a primeira tentativa excessivamente antiga).
Sobre expired: quando uma tentativa de entrega fica antiga demais antes do envio, ela pode ser descartada e marcada como expired. Isso impede que reprocessamentos tardios disparem notificações antigas. Replays manuais solicitados ao suporte Monetarie seguem um fluxo controlado e podem reenviar o evento ao cliente.
Durabilidade
Antes de tentar a primeira entrega, o evento é persistido para retry. Se houver falha durante a entrega, o sistema retoma automaticamente no próximo retry - nenhum evento é perdido.
Idempotência
Sua aplicação deve ser idempotente: se receber o mesmo evento mais de uma vez (identificado pelo X-Monetarie-Event-Id), deve processá-lo sem duplicar efeitos.
Replay manual via admin
Se uma delivery falhou e você precisa re-enviar, o time Monetarie pode executar replay manual pelo painel administrativo. Contate o suporte com o event_id da delivery.
Entregas duplicadas (race condition conhecida)
O sistema pode usar mais de um caminho de entrega para acelerar a primeira notificação e manter retry durável. Em cenários de alta concorrência, você pode receber o mesmo payload 2 vezes via HTTP, mas com o mesmo X-Monetarie-Event-Id - é o mesmo evento, não um retry.
Para evitar impacto duplicado:
- Dedupe por
X-Monetarie-Event-Id(recomendado - UUID único por delivery, estável em retries e na race condition acima) - Ou alternativamente dedupe por
end_to_end_id+event_typequando fizer sentido para o evento
Isso é comportamento esperado, não erro. Retries legítimos (após 5xx/timeout) também reusam o mesmo X-Monetarie-Event-Id.
External ID nos Webhooks
Quando uma transação foi criada com external_id, esse campo é incluído no payload do webhook dentro do objeto data. Use-o para correlacionar o evento com o pedido no seu sistema sem precisar fazer uma consulta adicional.
Requisitos do Endpoint
- A URL deve usar HTTPS (a menos que
allow_insecure: trueno cadastro) - Deve responder com status
2xxem até 30 segundos - O body da resposta é ignorado
- Recomendado responder rápido (
200 OKimediato) e processar o evento de forma assíncrona no seu lado; delays longos reduzem o throughput e aumentam chance de retries
Próximos Passos
- Cadastrar Webhook -- criar, listar e remover webhooks
- Payloads dos Eventos -- exemplos de cada tipo de evento