> ## Documentation Index
> Fetch the complete documentation index at: https://docs.safefypay.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks de MED

> Eventos enviados quando um caso de MED (disputa) é aberto e quando ele termina

Quando um pagamento PIX recebe uma MED (Mecanismo Especial de Devolução) ou quando o caso termina, a Safefy envia um
evento `dispute.*` para os webhooks da sua organização que assinam esse evento. O campo `type` diz em que etapa o caso
está e o objeto `data` traz o retrato do caso naquele momento.

## Como receber

Os eventos de MED saem pelos webhooks cadastrados no painel, não pelo `callbackUrl` do pagamento:

1. No painel da Safefy, abra **Webhooks** e clique em **Novo webhook**.
2. Informe a URL que vai receber os eventos.
3. No grupo **Dispute**, marque os eventos `dispute.*` que você quer receber.
4. Salve e guarde o **segredo** que aparece na tela. Ele assina os eventos desse webhook e só é mostrado uma vez.

## Eventos

| `type` | Quando é enviado | `data.status` | Efeito no saldo |
| - | - | - | - |
| `dispute.opened` | Uma MED foi aberta para um pagamento da sua organização | `Open` | O valor contestado sai do disponível e fica bloqueado |
| `dispute.won` | A MED terminou a seu favor: o valor fica com você | `Won` | O bloqueio volta para o saldo disponível |
| `dispute.lost` | A MED terminou contra você: o valor foi devolvido ao pagador | `Lost` | O valor bloqueado é debitado definitivamente |
| `dispute.cancelled` | O caso foi encerrado sem decisão (cancelado ou com prazo expirado) | `Cancelled`, `Expired` | O bloqueio volta para o saldo disponível |

> [!NOTE]
>
> A MED pode ter uma taxa, conforme as condições da sua organização. O valor cobrado no caso vem em `data.medFee`.

Um caso que já chega com desfecho (por exemplo, uma MED aberta e perdida no mesmo aviso da processadora) gera os dois
eventos, nesta ordem: `dispute.opened` e depois o do desfecho.

## Headers

| Header | Descrição |
| - | - |
| `X-Safefy-Event` | Tipo do evento (`dispute.opened`, `dispute.won`, `dispute.lost`, `dispute.cancelled`) |
| `X-Safefy-Delivery` | ID único da entrega |
| `X-Safefy-Attempt` | Número da tentativa (`1`, `2`, `3`...) |
| `X-Safefy-Timestamp` | Momento do envio, em segundos Unix |
| `X-Safefy-Signature-V2` | Assinatura `t={timestamp},v1={assinatura}` com o segredo do webhook cadastrado |

A assinatura é validada do mesmo jeito descrito em [Webhooks → Assinatura](https://docs.safefypay.com.br/webhooks.md#assinatura), usando o segredo do
webhook cadastrado (e não o segredo dos callbacks de pagamento e saque).

## Payload

Valores em centavos. Datas em UTC (ISO 8601).

```json dispute.opened theme={null}
{
  "id": "0193f0a2-6c1e-7b8a-9d41-2f6a1c0e9b77",
  "type": "dispute.opened",
  "createdAt": "2026-10-29T14:03:12Z",
  "data": {
    "id": "0193f0a2-6a90-7c55-8e2b-41d7a9c3f001",
    "status": "Open",
    "reason": "Scam",
    "reasonDescription": "Pagador informa que não reconhece a compra.",
    "amount": 4000,
    "currency": "BRL",
    "environment": "Production",
    "openedAt": "2026-10-29T14:03:11Z",
    "responseDeadline": "2026-11-05T23:59:59Z",
    "closedAt": null,
    "holdState": "Held",
    "holdAmount": 4000,
    "heldAmount": 4000,
    "medFee": 0,
    "payment": {
      "id": "8f8fce31-5c83-42c4-b90b-c29636b595e3",
      "externalId": "pedido-1234",
      "amount": 10000,
      "status": "Disputed",
      "endToEndId": "E00000000202610291400abcdef12345",
      "txId": "safefy8f8fce315c8342c4b90bc29636"
    }
  }
}
```

```json dispute.lost theme={null}
{
  "id": "0193f5d1-2b44-7f10-a3c9-77e0b1d2c3a4",
  "type": "dispute.lost",
  "createdAt": "2026-11-04T10:20:45Z",
  "data": {
    "id": "0193f0a2-6a90-7c55-8e2b-41d7a9c3f001",
    "status": "Lost",
    "reason": "Scam",
    "reasonDescription": "Pagador informa que não reconhece a compra.",
    "amount": 4000,
    "currency": "BRL",
    "environment": "Production",
    "openedAt": "2026-10-29T14:03:11Z",
    "responseDeadline": "2026-11-05T23:59:59Z",
    "closedAt": "2026-11-04T10:20:44Z",
    "holdState": "Debited",
    "holdAmount": 4000,
    "heldAmount": 0,
    "medFee": 300,
    "payment": {
      "id": "8f8fce31-5c83-42c4-b90b-c29636b595e3",
      "externalId": "pedido-1234",
      "amount": 10000,
      "status": "Refunded",
      "endToEndId": "E00000000202610291400abcdef12345",
      "txId": "safefy8f8fce315c8342c4b90bc29636"
    }
  }
}
```

### Campos

| Campo | Tipo | Descrição |
| - | - | - |
| `id` | string (uuid) | ID do evento. Use para descartar reentregas |
| `type` | string | Etapa do caso (tabela de eventos acima) |
| `createdAt` | string | Momento em que o evento foi gerado |
| `data.id` | string (uuid) | ID da disputa. É o mesmo de [Buscar disputa](https://docs.safefypay.com.br/api-reference/disputes/get.md) |
| `data.status` | string | `Open`, `Won`, `Lost`, `Cancelled` ou `Expired` |
| `data.reason` | string | Categoria da MED (tabela abaixo) |
| `data.reasonDescription` | string ou null | Detalhe do motivo |
| `data.amount` | integer | Valor contestado |
| `data.currency` | string | Moeda (`BRL`) |
| `data.environment` | string | `Production` ou `Sandbox` |
| `data.openedAt` | string | Abertura do caso |
| `data.responseDeadline` | string ou null | Prazo para enviar a defesa |
| `data.closedAt` | string ou null | Encerramento do caso |
| `data.holdState` | string | Situação do bloqueio: `None`, `Held` (retido), `Released` (liberado) ou `Debited` (debitado) |
| `data.holdAmount` | integer | Valor que o bloqueio movimentou |
| `data.heldAmount` | integer | Valor ainda retido agora (`0` quando o bloqueio foi liberado ou debitado) |
| `data.medFee` | integer | Taxa de MED cobrada no caso (`0` quando não houve cobrança) |
| `data.payment` | object ou null | Pagamento contestado: `id`, `externalId`, `amount`, `status`, `endToEndId` e `txId` |

> [!NOTE]
>
> `holdAmount` pode ser menor que `amount` quando parte do pagamento já tinha sido estornada ao pagador antes da MED: a
> Safefy só bloqueia o que ainda está com você.

### Categorias (`data.reason`)

| Valor | Descrição |
| - | - |
| `Fraud` | Fraude |
| `Scam` | Golpe (engenharia social) |
| `ProductNotReceived` | Produto ou serviço não recebido |
| `ProductNotAsDescribed` | Produto ou serviço diferente do anunciado |
| `DuplicateCharge` | Cobrança duplicada |
| `UnrecognizedTransaction` | Transação não reconhecida pelo pagador |
| `CoercionOrThreat` | Coação ou ameaça |
| `OperationalError` | Erro operacional |
| `Other` | Outro motivo |

## Entrega

* Responda com `2xx` para confirmar. Sem confirmação, a Safefy tenta de novo: são até 3 tentativas, ou o número
  configurado no webhook.
* Todas as entregas (com corpo enviado e resposta recebida) aparecem no painel, em **Webhooks**.

## Boas práticas

* **Idempotência:** guarde o `id` do evento e ignore um evento que você já processou. Não conte com a ordem de chegada:
  o `data.status` é a fonte da verdade do caso.
* **`dispute.opened`:** marque a venda como contestada e segure a entrega do produto ou serviço. Envie a defesa e as
  evidências antes de `data.responseDeadline`, pelas rotas [Enviar resposta](https://docs.safefypay.com.br/api-reference/disputes/respond.md) e
  [Adicionar evidência](https://docs.safefypay.com.br/api-reference/disputes/add-evidence.md).
* **`dispute.won` e `dispute.cancelled`:** a venda volta ao fluxo normal. O valor continua com você.
* **`dispute.lost`:** registre a devolução ao pagador e, se fizer sentido, revogue o acesso ao produto.
* **`data.reason`:** use a categoria para automatizar regras internas, como revisar vendas de categorias que se
  repetem.
* Para ver o caso completo (defesa enviada, evidências e linha do tempo), use
  [Buscar disputa](https://docs.safefypay.com.br/api-reference/disputes/get.md) com o `data.id`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.