Disputas — Mecanismo Especial de Devolução (MED)

O MED é o processo pelo qual o pagador de um PIX pede a devolução do valor, normalmente por suspeita de fraude. Quando uma disputa é aberta contra uma conta sua, você recebe um webhook, tem 48 horas para apresentar sua defesa e nós a encaminhamos ao time que conduz a disputa.

Três coisas definem como este módulo funciona, e vale saber delas antes de integrar:

  • A disputa não é decidida por esta API. Não existe rota para devolver o PIX, reter saldo ou recusar a devolução. O que você faz aqui é registrar sua versão do caso; a condução no arranjo acontece fora da API.
  • A API não mostra status da disputa. O liquidante não oferece consulta de relato — o que sabemos é o que o último webhook trouxe, e não há como confirmar se ainda vale. Em vez de exibir um estado que pode estar dias velho, a API expõe o que é verificável: se você respondeu (answered) e qual é o seu prazo (clientAnswerDeadline).
  • A defesa aceita anexo. Diferente do texto puro de antes, você pode subir documentos (comprovante de entrega, nota, print de conversa) e eles seguem junto com a sua resposta.

Prazo de resposta

clientAnswerDeadline é sempre abertura + 48 horas, e é o único prazo que sai na API.

Nada é rejeitado automaticamente pelo tempo. Vencer o prazo não encerra a disputa nem gera devolução por si: significa que o caso segue sem a sua versão dele. Monitore clientAnswerDeadline — ou o painel, que avisa quando falta menos de 12 horas.

Recebendo disputas por webhook

pix.med.opened na abertura, pix.med.updated a cada mudança que o liquidante comunicar. Assine os dois.

{
"id": "pix-med-opened-204cc938-da3d-4f04-baf3-0b2e6a2f1283-OPEN",
"type": "pix.med.opened",
"occurredAt": "2026-02-18T19:34:14.000000000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-suaempresa",
"accountId": "773107de-...",
"data": {
"medId": "204cc938-da3d-4f04-baf3-0b2e6a2f1283",
"tenantId": "tenant-suaempresa",
"accountId": "773107de-...",
"originalEndToEnd": "E303062942026021812490000005QLMv",
"amount": 1000.00,
"reasonCode": "unauthorized-transaction",
"claimMessage": "Não reconheço esta transação",
"openedAtIso": "2026-02-18T19:34:14.000000000Z",
"clientAnswerDeadlineIso": "2026-02-20T19:34:14.000000000Z",
"status": "OPEN"
}
}

O status no webhook é o estado no instante daquele evento — nisso ele é confiável, porque é a própria notificação do liquidante. É justamente por isso que ele não aparece no GET: lá seria um valor guardado, sem consulta que o confirme. Se o seu sistema precisa acompanhar o estado da disputa, acumule os eventos pix.med.updated; eles são a única fonte.

Reentrega do mesmo estado não gera evento novo, e evento atrasado (com carimbo do liquidante anterior ao que já registramos) é descartado — você não vê a disputa voltar no tempo.

reasonCode é o código do arranjo, repassado como vem (unauthorized-transaction, fraud, scam-or-fraud, …). Não normalizamos para um enum nosso.

O relato traz o banco do reclamante, não o nome ou documento dele. Essa informação não é disponibilizada ao recebedor.

Consultando disputas

GET /v1/accounts/{accountId}/pix/med?limit=50&offset=0

Paginada por limit (máximo 200) e offset. Não há filtro por status.

{
"accountId": "773107de-...",
"tenantId": "tenant-suaempresa",
"items": [
{
"medId": "204cc938-da3d-4f04-baf3-0b2e6a2f1283",
"accountId": "773107de-...",
"tenantId": "tenant-suaempresa",
"endToEndId": "E303062942026021812490000005QLMv",
"transactionId": "tx-8f2c...",
"amount": 1000.00,
"reasonCode": "unauthorized-transaction",
"claimMessage": "Não reconheço esta transação",
"claimantBank": "Banco do reclamante",
"openedAt": "2026-02-18T19:34:14Z",
"clientAnswerDeadline": "2026-02-20T19:34:14Z",
"answered": false,
"updatedAt": "2026-02-18T19:34:16Z"
}
],
"count": 1,
"limit": 50,
"offset": 0
}

transactionId liga a disputa ao lançamento do PIX no seu extrato, quando ele existe na plataforma. Depois de responder, answered fica true e aparecem answeredAt, answerResult e answer (a justificativa que você enviou).

Anexando evidências

Suba os arquivos antes de responder: a resposta é o que fecha o pacote e dispara o encaminhamento, e anexo posterior não entra nele.

São três passos. O arquivo vai direto do seu lado para o armazenamento, sem passar pelo corpo da nossa API.

1. Peça a URL de upload

POST /v1/accounts/{accountId}/pix/med/{medId}/evidence/upload-url
{
"filename": "comprovante-entrega.pdf",
"contentType": "application/pdf",
"sizeBytes": 184320
}
{
"medId": "204cc938-...",
"evidenceId": "medev_9f1c...",
"key": "med-evidences/tenant-suaempresa/204cc938-.../medev_9f1c...-comprovante-entrega.pdf",
"uploadUrl": "https://...",
"method": "PUT",
"contentType": "application/pdf",
"sizeBytes": 184320,
"expiresIn": 900
}

sizeBytes é o tamanho real do arquivo, não uma estimativa: ele entra na assinatura da URL. Declarar um número e enviar outro faz o armazenamento recusar o PUT.

2. Envie o arquivo na URL assinada

PUT direto na uploadUrl, com o mesmo Content-Type e exatamente os sizeBytes que você declarou. Sem cabeçalho de autenticação nosso — a assinatura já autoriza, e um header a mais faz o armazenamento recusar.

3. Registre o anexo

POST /v1/accounts/{accountId}/pix/med/{medId}/evidence/add
{
"key": "med-evidences/tenant-suaempresa/204cc938-.../medev_9f1c...-comprovante-entrega.pdf",
"evidenceId": "medev_9f1c...",
"filename": "comprovante-entrega.pdf",
"contentType": "application/pdf",
"sizeBytes": 184320
}

Sem este passo o arquivo fica no armazenamento e não entra na defesa: é o registro que liga o binário à disputa. Tipo e tamanho são conferidos de novo aqui, com os mesmos limites e os mesmos errorCode da tabela abaixo — as duas chamadas são independentes.

Todo upload passa por verificação de tipo real, antivírus e sanitização antes de ficar disponível — alguns segundos. scanStatus acompanha isso: PENDING em processamento, APPROVED liberado, REJECTED recusado (com scanReason). Arquivo recusado não segue com a defesa.

Limites

LimiteValor
Tipos aceitosapplication/pdf, image/jpeg, image/png, image/webp, text/plain, text/csv
Tamanho por arquivo5 MB
Total por disputa6 MB
Arquivos por disputa10
HTTPerrorCodeQuando
413file_too_largeArquivo acima de 5 MB.
413payload_too_largeO conjunto da disputa passaria de 6 MB.
422unsupported_media_typecontentType fora da lista.
422too_many_filesDécimo primeiro arquivo na mesma disputa.
409conflictDisputa já respondida — anexo tem que vir antes.

Listando o que está anexado

GET /v1/accounts/{accountId}/pix/med/{medId}/evidence/download

Devolve os anexos da disputa com downloadUrl assinada (curta duração) para cada um que passou na verificação. Com ?evidenceId= retorna apenas um.

Respondendo a uma disputa

POST /v1/accounts/{accountId}/pix/med/{medId}/answer
{
"result": "DISAGREE",
"reason": "Venda legítima: cliente cadastrado há 14 meses, produto entregue e rastreio confirmado em 19/02."
}
CampoObrigatórioDescrição
resultsimAGREE reconhece a fraude; DISAGREE contesta o relato.
reasonem DISAGREESua defesa, em texto livre.

Resposta 202:

{
"medId": "204cc938-...",
"result": "DISAGREE",
"answered": true,
"answeredAt": "2026-02-19T14:02:31Z",
"message": "resposta registrada e encaminhada ao time de análise; a CorpX não responde nem decide MED junto ao liquidante"
}

A resposta e os anexos seguem para o time que conduz a disputa. Nada é enviado ao liquidante por esta chamada, e nenhum status muda: responder é registrar sua versão, não encerrar o caso.

Erros específicos desta rota:

HTTPerrorCodeQuando
400invalid_payloadresult fora de AGREE/DISAGREE.
409conflictA disputa já foi respondida e o encaminhamento já saiu.
422invalid_payloadDISAGREE sem reason.
404not_foundA disputa não existe ou não é desta conta.

Se a disputa já estiver respondida mas o encaminhamento ainda não tiver saído, repetir a chamada devolve 202 e reenvia — o conteúdo gravado não é sobrescrito.

Taxa de MED

GET /v1/backoffice/tenants/{tenantId}/med-stats?days=30&accountId={accountId}

Devolve os contadores de prazo e a série diária da taxa:

CampoDescrição
totalTotal de disputas registradas.
awaitingAnswerDisputas ainda sem resposta sua.
dueSoonSem resposta e com menos de 12 horas de prazo.
overdueSem resposta e com prazo vencido.
openAmountValor somado das disputas ainda não encerradas.
nextDeadlineO prazo mais apertado entre as pendentes (vazio se não há pendência).
stats[]Série diária: date, pixInCount, pixInAmount, medCount, medAmount, quantityRatePercent, amountRatePercent.

A listagem equivalente por tenant é GET /v1/backoffice/tenants/{tenantId}/meds, com os filtros accountId, limit e offset.

Faixas de referência

Referência operacional, usada no painel e no acompanhamento comercial. Nenhum erro ou bloqueio decorre automaticamente delas — ações automáticas por taxa se configuram em Políticas e Regras.

TaxaSituação
< 0,4%Saudável
0,4% – 1,0%Atenção, monitorar
> 1,0%Elevada, ações podem ser necessárias

Políticas de MED

Veja Políticas e Regras para:

  • Auto-block: reter saldo automaticamente em disputa acima de um valor.
  • Taxa máxima: teto de proporção MED/PIX-in com ações automáticas.