Skip to content

Infrações PIX (fluxo completo)

Visão end-to-end do ciclo de vida de uma infração PIX na Owem Pay — como ela nasce, como é processada automaticamente, quando bloqueia saldo, como você se defende e como reconciliar no seu sistema.

Resumo em uma frase

Uma infração PIX é uma contestação reportada pela contraparte contra um PIX que você recebeu. Ela pode gerar bloqueio cautelar do valor contestado até a resolução. Assine os webhooks pix.infraction.* para agir dentro do prazo.


1. O que é uma infração PIX?

Uma infração (do tipo REFUND_REQUEST no vocabulário BACEN) é um relato formal registrado no DICT pela instituição do pagador alegando que o PIX recebido por você apresenta indício de fraude, golpe, erro operacional ou solicitação de reembolso.

O canal oficial é o BACEN DICT — nenhum cliente pode abrir infração diretamente em você; ela sempre passa pela instituição pagadora e pelo BACEN antes de chegar na Owem.

Campo técnicoDescrição
infraction_typeTipo da infração no BACEN. Valores: REFUND_REQUEST (pedido de devolução) e REFUND_CANCELLED (cancelamento de pedido anterior)
statusEstado no BACEN. Valores em UPPERCASE: ACKNOWLEDGED (reconhecida, em análise), CLOSED (finalizada), CANCELLED (cancelada pela contraparte antes da análise)
amountValor contestado em subcentavos (não necessariamente o valor total do PIX original)
e2e_idEnd-to-End ID do PIX original que foi contestado
defense_deadlinePrazo BACEN para submissão de defesa (ISO 8601 UTC)
counterpart_ispbISPB da instituição do pagador que abriu a infração
analysis_resultDecisão final ao fechar (CLOSED): AGREED (devolução executada) ou DISAGREED (defesa aceita, sem devolução)

Fontes: schemas/pix_compliance/pix_infraction.ex.

Não confunda com MED

MED (Mecanismo Especial de Devolução) é o mecanismo regulatório que executa a devolução quando uma infração com fraude confirmada precisa ser ressarcida — ele é consequência de uma infração aceita, não o evento inicial. Ver a seção Relação infração ↔ MED abaixo.


2. Como a Owem recebe infrações

A Owem não recebe infrações via push. O backend faz polling ativo no DICT OnZ/BACEN a cada 15 minutos via o worker Fluxiq.Workers.ComplianceSyncWorker.

Fluxo de descoberta:

BACEN DICT

  │ GET /v3/dict/infracao/list (paginado 1000/call)

ComplianceSyncWorker (*/15 * * * *)

  ├─ upsert_infraction/2 → persiste em pix_infractions
  │     ├─ Se nova (is_new=true): dispatch "pix.infraction.created"
  │     └─ Se atualização (status/analysis_result mudou): dispatch "pix.infraction.resolved"

  └─ maybe_auto_deny_or_block/2 → roteia decisão (ver seção 3)

Fontes: workers/compliance_sync_worker.ex, use_cases/pix_compliance/pix_compliance.ex.

Latência BACEN → Webhook

Uma infração aberta na contraparte chega na Owem em até 15 min (próximo ciclo do ComplianceSyncWorker). O webhook pix.infraction.created é disparado assim que a linha é inserida/atualizada em pix_infractions.


3. Classificação e ações automáticas

Assim que uma infração REFUND_REQUEST com status ACKNOWLEDGED ou OPEN é detectada, o backend classifica em três caminhos automáticos sem intervenção manual:

3.1 E2E inexistente nas suas transações → auto-deny

Se a infração referencia um e2e_id que não está na sua tabela transactions (nenhum PIX IN com aquele E2E chegou ao seu sistema), o backend assume que é um erro operacional da contraparte e faz auto-deny imediato.

  • Ação OnZ: POST /v3/dict/infracao/{id} com AnalysisResult=DISAGREED
  • Atualiza pix_infractionsstatus=CLOSED, analysis_result=DISAGREED, resolved_at=now
  • Webhook pix.infraction.resolved é disparado
  • Nenhum bloqueio cautelar é criado
  • Nenhuma devolução é executada

3.2 Valor ≤ R$ 1.000 (threshold configurável) → auto-deny

Infrações de baixo valor são automaticamente negadas. O limiar padrão é R$ 1.000,00 (10_000_000 subcentavos, constante @default_auto_deny_threshold), mas pode ser configurado por conta ou por merchant na tabela med_configurations (campo min_threshold_amount).

  • A mesma ação de auto-deny do item 3.1 é aplicada
  • A justificativa padronizada enviada ao BACEN é: "Verificado pelo time de compliance e sem evidencias concretas nao temos como fazer devolucao"
  • Webhook pix.infraction.resolved é disparado
  • Nenhum bloqueio cautelar é criado
  • O valor do PIX recebido continua disponível no saldo

Threshold customizado por conta

Contas que trabalham com tickets altos legítimos (ex: marketplaces de alto valor) podem solicitar aumento do threshold via admin Owem. Contas que preferem defesa manual para qualquer valor podem solicitar threshold=0 (nada é auto-negado).

3.3 Valor > R$ 1.000 E E2E existe → bloqueio cautelar MED

Este é o caminho que impacta seu saldo. Ao detectar uma infração elegível, o backend:

  1. Atualiza pix_infractions.status para PROCESSING (evita re-entrada no próximo ciclo do sync)
  2. Cria uma linha em med_cautelar_blocks com o valor contestado
  3. Cria um phantom hold no TigerBeetle — transferência pendente contra a wallet da conta afetada, que reduz o saldo disponível (available em Saldo) no valor contestado
  4. Dispara o webhook pix.refund.requested (ver payload)
  5. Dispara o webhook pix.infraction.created (ver payload)

O valor fica bloqueado até que:

  • O prazo de defesa BACEN expire (ver seção 6)
  • Você submeta uma defesa via portal (ver seção 5)
  • O BACEN resolva a infração (seção 7)
  • A contraparte cancele (status=CANCELLED) antes da análise

Fontes: use_cases/pix_compliance/pix_compliance.ex:495-537, use_cases/pix_compliance/med/processor.ex.


4. Relação infração ↔ MED

A infração e o MED (Mecanismo Especial de Devolução) são camadas distintas do mesmo ciclo:

CamadaO que éTabelaWebhook principal
InfraçãoRelato formal no BACEN DICTpix_infractionspix.infraction.created
Bloqueio cautelar MEDReserva de saldo em TigerBeetle durante análisemed_cautelar_blockspix.refund.requested
Devolução MEDPACS.004 executado após decisão AGREEDtransactions (type=pix_return)pix.refund.completed + pix.payout.returned
[Infração criada no BACEN]


  ComplianceSyncWorker detecta (15 min polling)

          ├─ pix.infraction.created (sempre, quando is_new=true)

          │ Se > R$ 1.000 E E2E existe:

  MED Processor → cautelar block + TB phantom hold

          ├─ pix.refund.requested (bloqueio criado)

          │ Aguarda decisão (merchant defende OU prazo vence OU auto-accept session 135)

  [Resolução]

          ├─ AGREED → PACS.004 dispatched
          │     ├─ pix.refund.completed
          │     └─ pix.payout.returned (prefixo D no E2E)

          └─ DISAGREED → bloqueio liberado
                └─ BlockRelease.release_for_e2e — void do TB phantom + release do saldo


  pix.infraction.resolved (sempre ao fechar — AGREED ou DISAGREED)

A relação é 1-para-1 por E2E, não 1-para-1 por ID

Uma mesma transação PIX (mesmo e2e_id) não pode ter duas infrações ativas ao mesmo tempo — o ComplianceSyncWorker faz guard em cautelar_block_exists_for_infraction?/1 para evitar duplicação de bloqueios. Se a contraparte cancela e abre nova infração com o mesmo E2E, o novo bloqueio substitui o anterior.

Ver também: med-list e med-detail para consultar os bloqueios cautelares via External API.


5. Como o merchant defende

Defesa NÃO está disponível via External API

A submissão de defesa (upload de evidências + justificativa) só é possível via merchant portal (https://merchant.owem.com.br). Não há endpoint External API POST /api/external/infractions/:id/defense hoje. Esta é uma limitação conhecida — ver seção 8.

Fluxo de defesa no merchant portal:

  1. O operador autenticado acessa Compliance → Infrações no portal (/compliance)
  2. Vê a lista de infrações em ACKNOWLEDGED com defense_deadline aproximando
  3. Clica em uma infração e preenche:
    • Texto da defesa (até ~5000 caracteres, livre — será enviado ao BACEN como AnalysisDetails)
    • Anexos (até 5 arquivos, 10MB cada — evidências como prints, contratos, histórico de conversa)
  4. Submete → backend:
    • Faz upload dos anexos para storage GCS
    • Chama OnZ POST /v3/dict/infracao/{id}/defense com o texto + URLs dos anexos
    • Atualiza pix_infractions.status=defense_submitted localmente
    • Dispara webhook pix.infraction.defense_submitted

Fontes: controllers/merchant/infractions/defense_controller.ex.

Quem pode defender

Qualquer usuário com permissão infractions:write no merchant portal. Subconta operators (sem ser primary holder) também podem submeter defesa desde que a conta afetada esteja nas suas account_ids. Primary holders defendem qualquer conta do merchant.


6. Prazos e auto-expiração

6.1 defense_deadline (prazo BACEN)

O BACEN define um prazo para o merchant responder. Ele vem no campo defense_deadline (ISO 8601 UTC) do webhook pix.infraction.created e é propagado para pix_infractions.defense_deadline.

Tipicamente, esse prazo é de 7 dias corridos a partir da criação da infração, mas pode variar conforme o tipo (REFUND_REQUEST vs fraude grave).

6.2 Auto-expiração pelo worker MedDefenseExpiration

Para evitar exposição regulatória da instituição (multa BACEN por não responder no prazo), a Owem roda o worker Fluxiq.Workers.MedDefenseExpiration a cada 5 minutos via Oban cron:

  • Busca bloqueios cautelares com deadline nos próximos 30 minutos e analysis_status ainda em pending ou defense_submitted
  • Para cada bloqueio, força o veredito founded (aceita a contestação) e dispara Med.execute_return/1 → PACS.004 é enviado ao BACEN
  • Libera o saldo bloqueado (ver TB phantom void)
  • O actor do audit log é system, não um admin

Resultado: se você não defender a tempo, o sistema auto-aceita a contestação e devolve o valor antes do prazo BACEN expirar.

Fontes: workers/med_defense_expiration.ex (cron */5 * * * *).

Auto-aceite é conservador

O comportamento padrão é proteger a instituição do risco regulatório — por isso o default é aceitar a contestação e devolver. Se você precisa de lógica diferente (ex: sempre defender automaticamente), contate compliance@owem.com.br.


7. Resolução e webhook final

A infração sempre termina em um destes três estados terminais:

Status finalAnalysis resultO que aconteceu com o saldoWebhook disparado
CLOSEDAGREEDValor devolvido ao counterparty via PACS.004pix.infraction.resolved + pix.refund.completed + pix.payout.returned
CLOSEDDISAGREEDBloqueio liberado, valor volta para o availablepix.infraction.resolved
CANCELLEDnullContraparte cancelou antes da análise — bloqueio liberadopix.infraction.resolved

Em todos os casos, o bloqueio cautelar TB é liberado via Fluxiq.UseCases.PixCompliance.Med.BlockRelease.release_for_e2e/2, que faz o void da pending transfer no TigerBeetle e atualiza o med_cautelar_blocks.status.

Fontes: use_cases/pix_compliance/med/block_release.ex.

Diferença entre AGREED e DISAGREED

  • AGREED: você concordou (explicitamente ou via auto-accept) com a devolução. O PIX OUT (pacs.004) é executado, o valor SAI da sua conta, você recebe pix.payout.returned com status="returned".
  • DISAGREED: você defendeu e o BACEN acatou a defesa, OU a infração foi auto-negada (≤R$1k ou E2E inexistente). O valor fica com você, nenhum PIX OUT é executado.

8. Limitações atuais (External API)

Feature gap — operações via External API

Hoje, apenas consulta indireta via MED está disponível no namespace /api/external/*:

  • Existe GET /api/external/med (Listar MED) e GET /api/external/med/:id (Detalhe MED) — ambos retornam bloqueios MED, não infrações diretamente
  • NÃO existe GET /api/external/infractions nem POST /api/external/infractions/:id/defense
  • A lista completa de infrações e o fluxo de defesa estão disponíveis apenas via merchant portal (JWT-based, não API Key)
  • Webhooks pix.infraction.* são a forma oficial de notificação automática

Se o seu caso de uso exige automação via External API (ex: submeter defesa programaticamente), contate compliance@owem.com.br para priorização.


9. Webhooks relacionados

Os três eventos abaixo são seus olhos e ouvidos no ciclo de infração. Assine todos no Cadastrar Webhook:

EventoQuando disparaPayload completo
pix.infraction.createdNova infração detectada E bloqueio cautelar criado (>R$1k com E2E existente)webhooks-payloads#pix-infraction-created
pix.infraction.resolvedStatus passou para CLOSED ou CANCELLED (AGREED, DISAGREED, auto-deny, auto-accept)webhooks-payloads#pix-infraction-resolved
pix.infraction.defense_submittedDefesa submetida via portal (merchant ou admin)webhooks-payloads#pix-infraction-defense-submitted

Eventos correlatos (camada MED):

EventoQuando dispara
pix.refund.requestedBloqueio cautelar foi criado em med_cautelar_blocks (conta teve saldo reservado)
pix.refund.completedDevolução PACS.004 foi executada (AGREED ou auto-accept)
pix.payout.returnedPIX devolvido com E2E prefixo D — seu saldo foi debitado para ressarcir o pagador

Auto-deny silencioso em ≤R$1k

O webhook pix.infraction.created não é disparado no caminho de auto-deny ≤R$1k/E2E inexistente (caminhos 3.1 e 3.2 na seção 3). Apenas pix.infraction.resolved é disparado ao fechar. Isso evita ruído para o merchant em casos onde não há decisão a tomar.

Se você quer visibilidade total (incluindo auto-denies), consulte via merchant portal /compliance — lá aparecem todas as infrações independente do caminho.


10. Reconciliação no seu sistema

Recomendações para conciliar infrações no seu DRE / ERP:

10.1 Assine os webhooks

  • pix.infraction.created → marca PIX IN original como "contestado" (flag interna)
  • pix.refund.requested → reserva o valor no seu ledger como "disputado"
  • pix.infraction.resolved:
    • Se analysis_result=DISAGREED → libera reserva, PIX IN permanece definitivo
    • Se analysis_result=AGREED → marca PIX IN como "devolvido", cria lançamento de saída
  • pix.refund.completed → confirma a saída do PIX OUT de devolução

10.2 Use o e2e_id como chave de correlação

Toda infração carrega e2e_id apontando para o PIX IN original. Use esse campo como FK no seu sistema para:

10.3 Mantenha histórico do analysis_details

O campo analysis_details vem com a justificativa enviada ao BACEN. Em casos de auto-deny, vem a mensagem padronizada. Em casos de defesa submetida, vem o texto que o seu operador escreveu. Esses dados são auditáveis e devem ser preservados para rastreabilidade regulatória (LGPD Art. 46, BCB Resolução 4893).


Referências técnicas


FAQ

Posso abrir uma infração contra outro merchant via API Owem?

Não. Infrações são abertas apenas pela instituição do pagador, não pelo recebedor. Se você é o pagador de um PIX e quer contestar, use o seu próprio canal de atendimento (app do banco, ouvidoria). A Owem só atua no lado recebedor.

O que acontece se eu não assinar os webhooks pix.infraction.*?

O sistema continua funcionando — auto-deny e auto-accept ocorrem automaticamente. Mas você só descobrirá o bloqueio olhando o available em /balance (vai estar menor do que o balance seguro). Para listar bloqueios ativos, consulte GET /api/external/med.

Por que meu saldo caiu sem explicação?

Se você vê available menor que o balance, provavelmente há bloqueios cautelares MED ativos (> R$1k + E2E válido). Consulte GET /api/external/med para ver todos. Assine pix.refund.requested para ser notificado em tempo real quando um novo bloqueio é criado.

Posso aumentar ou desativar o threshold de R$1.000?

Sim. Contate compliance@owem.com.br. O threshold é armazenado em med_configurations.min_threshold_amount e pode ser configurado:

  • Por merchant (aplica em todas as contas)
  • Por conta específica (override do merchant)
  • Default do sistema: R$1.000 (10.000.000 subcentavos)

Aumentar o threshold reduz o número de bloqueios cautelares (mais auto-denies). Zerar o threshold elimina auto-deny e faz com que qualquer valor disputado passe para análise manual.

A Owem já tomou decisões em meu nome?

Sim, em dois cenários explicitamente automáticos:

  1. Auto-deny ≤R$1k ou E2E inexistente — o sistema nega automaticamente em nome do merchant. A justificativa padrão enviada ao BACEN é "Verificado pelo time de compliance e sem evidencias concretas nao temos como fazer devolucao".
  2. Auto-accept perto do prazo BACEN — se o bloqueio cautelar estiver a <30 min do defense_deadline e o merchant não tiver submetido defesa, o worker MedDefenseExpiration aceita a contestação como verdict founded e dispara a devolução.

Ambos os comportamentos são conservadores para proteger a instituição de multa regulatória BACEN.

Histórico do merchant é visível via API?

Parcialmente — via GET /api/external/med (bloqueios) e GET /api/external/transactions/ref/:external_id (transações originais e devoluções). O histórico completo de infrações com todos os campos do BACEN (incluindo analysisDetails, fraudType, situationType) hoje está disponível apenas no merchant portal (/compliance).

Owem Pay Instituição de Pagamento — ISPB 37839059