Skip to content

Webhooks -- Visão Geral

Webhooks permitem que sua aplicação receba notificações em tempo real sobre eventos na plataforma Minha Konta. Quando um evento ocorre, a Minha Konta envia um HTTP POST para a URL cadastrada.

Como Funciona

  1. Cadastre uma URL de webhook na sua conta
  2. Quando um evento ocorrer (ex: PIX recebido), a Minha Konta envia um HTTP POST para sua URL
  3. Sua aplicação processa a notificação e responde com status 2xx (200, 201 ou 204)

Eventos Disponíveis

A Minha Konta entrega eventos de PIX, TEF entre contas Minha Konta e teste operacional de webhook. Outros produtos (boleto, contas e STA) não estão em escopo. Qualquer tentativa de assinar eventos fora da tabela abaixo é rejeitada com events: contains invalid events: ....

EventoStatus bodyDescriçãoDisparo
pix.charge.createdcreatedQR code gerado ou cash-in iniciadoAtivo
pix.charge.paidpaidPIX recebido e liquidadoAtivo
pix.charge.expiredexpiredQR code expirou sem pagamentoAtivo
pix.charge.cancelledcancelledQR code cancelado antes de pagamentoRegistrado, ainda não disparado
pix.payout.queuedqueuedPIX enviado enfileirado por limite operacional. Retry automático com TTL máximo de 2hAtivo
pix.payout.processingprocessingPIX enviado, aguardando confirmação BACENAtivo
pix.payout.heldprocessingPIX enviado retido para análise no agente de liquidação (alçada/anti-fraude). Liquida ou rejeita em seguida — não reenviarAtivo
pix.payout.confirmedsettledPIX enviado e confirmado (terminal)Ativo
pix.payout.failedrejectedPIX enviado rejeitado pelo SPI (terminal)Ativo
pix.payout.returnedreturnedPIX enviado devolvidoAtivo
pix.refund.requestedrequestedPedido de devolução recebido (infração BACEN); bloqueio cautelar criado no saldo do clienteAtivo
pix.refund.completedsettled / completedAnálise da defesa finalizada e devolução executada (ou liberada)Ativo
pix.return.receivedsettledDevolução PIX recebida (crédito)Ativo
pix.infraction.createdACKNOWLEDGEDInfração PIX reportada pela contraparte via BACEN DICT; pode exigir análise e defesa MEDAtivo
pix.infraction.resolvedCLOSED / CANCELLEDInfração resolvida por decisão final ou cancelamento da contraparteAtivo
pix.infraction.defense_submitteddefense_submittedDefesa submetida pelo merchant (portal ou API); aguarda análise BACENAtivo
tef.transfer.sentsettledTEF entre contas Minha Konta liquidada para a conta de origemAtivo
tef.transfer.receivedsettledTEF entre contas Minha Konta liquidada para a conta de destinoAtivo
tef.transfer.failedfailedTEF entre contas Minha Konta rejeitada ou não liquidadaAtivo
webhook.testtestTeste manual. Disponível apenas via Admin/Merchant portal - a External API não expõe endpoint para disparar testeDisparo manual (não External API)

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:

HeaderDescrição
X-Minha Konta-SignatureAssinatura HMAC-SHA256 do payload (prefixo sha256=). Em casos raros (webhook cadastrado sem secret), o valor literal é unsigned - veja nota abaixo
X-Minha Konta-TimestampUnix timestamp em segundos do envio
X-Minha Konta-Event-IdUUID único da delivery (para deduplicação)
X-Minha Konta-Event-TypeTipo do evento (ex: pix.charge.paid)
Content-TypeSempre application/json
User-AgentSempre Minha Konta-Webhook/1.0 - use para whitelisting em firewalls/WAF. Evolução futura seguirá o padrão Minha Konta-Webhook/{version}; filtre por prefixo Minha Konta-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-Minha Konta-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 Minha Konta usam HMAC-SHA256 na assinatura X-Minha Konta-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 Minha Konta:

javascript
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 Minha Konta 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 Minha Konta.

⚠️ 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 Minha Konta 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-Minha Konta-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-Minha Konta-Event-Id (proteção contra retries).

Retry Policy

Se sua URL retornar um status diferente de 2xx (ou timeout após 30 s), a Minha Konta realiza até 8 tentativas com backoff exponencial. O total de tempo entre a primeira e a oitava tentativa é aproximadamente 7h45min:

TentativaDelay desde tentativa anteriorTempo acumulado
1a- (imediato)~50-200 ms
2a30 segundos~30 s
3a2 minutos~2,5 min
4a10 minutos~12,5 min
5a30 minutos~42,5 min
6a1 hora~1,75 h
7a2 horas~3,75 h
8a4 horas~7,75 h

Após 8 tentativas sem sucesso, o webhook_delivery é marcado com status failed e não é reenviado automaticamente. Você pode solicitar replay manual ao suporte Minha Konta fornecendo o X-Minha Konta-Event-Id (ou ter um operador com acesso admin replayar pelo portal).

Status de uma delivery

Cada delivery passa pelos status: pending (criada, aguardando entrega) → delivered (2xx recebido) OU failed (8 tentativas exauridas) OU expired (replay protection).

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 Minha Konta 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-Minha Konta-Event-Id), deve processá-lo sem duplicar efeitos.

Replay manual via admin

Se uma delivery falhou e você precisa re-enviar, o time Minha Konta pode executar replay manual via admin dashboard. 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-Minha Konta-Event-Id - é o mesmo event, não um retry.

Para evitar impacto duplicado:

  • Dedupe por X-Minha Konta-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_type quando fizer sentido para o evento

Isso é comportamento esperado, não erro. Retries legítimos (após 5xx/timeout) também reusam o mesmo X-Minha Konta-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: true no cadastro)
  • Deve responder com status 2xx em até 30 segundos
  • O body da resposta é ignorado
  • Recomendado responder rápido (200 OK imediato) e processar o evento de forma assíncrona no seu lado; delays longos reduzem o throughput e aumentam chance de retries

Próximos Passos

Minha Konta Instituição de Pagamento - ISPB 39929224