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

# Webhooks

> Receba notificações em tempo real sobre eventos de pagamento

## Visão Geral

Webhooks permitem que você receba notificações HTTP automáticas quando eventos importantes acontecem na sua conta, como pagamentos confirmados, reembolsos processados ou saques concluídos.

## Eventos Disponíveis

| Evento                   | Descrição                                              |
| ------------------------ | ------------------------------------------------------ |
| `payment_completed`      | Pagamento foi confirmado com sucesso                   |
| `payment_expired`        | Pagamento expirou sem confirmação                      |
| `refund_completed`       | Reembolso foi processado                               |
| `withdrawal_completed`   | Saque foi processado com sucesso                       |
| `withdrawal_failed`      | Saque foi rejeitado ou falhou                          |
| `withdrawal_reversed`    | Saque foi estornado pelo PSP                           |
| `balance_block_created`  | Bloqueio de saldo criado (MED/judicial/administrativo) |
| `balance_block_approved` | Bloqueio aprovado — valor devolvido ao pagador         |
| `balance_block_rejected` | Bloqueio rejeitado — valor retorna ao lojista          |

## Estrutura do Payload

Todos os webhooks seguem a mesma estrutura base:

```json theme={null}
{
  "event": "payment_completed",
  "eventId": "a0b78f10-c7f4-4f5d-98dd-3e36eafeb812",
  "timestamp": "2026-01-11T19:03:28.280Z",
  "data": {
    // Dados específicos do evento
  }
}
```

| Campo       | Tipo   | Descrição                                         |
| ----------- | ------ | ------------------------------------------------- |
| `event`     | string | Tipo do evento                                    |
| `eventId`   | string | ID único do evento (geralmente o ID da transação) |
| `timestamp` | string | Data/hora do evento em formato ISO 8601           |
| `data`      | object | Dados específicos do evento                       |

## Headers Enviados

Cada requisição de webhook inclui os seguintes headers:

| Header                  | Descrição                                            |
| ----------------------- | ---------------------------------------------------- |
| `Content-Type`          | `application/json`                                   |
| `User-Agent`            | `Misespay-Webhook/1.0`                               |
| `X-Misespay-Event`      | Tipo do evento (ex.: `payment_completed`)            |
| `X-Misespay-Webhook-ID` | ID único desta entrega (UUID)                        |
| `X-Misespay-Signature`  | Assinatura do payload: `sha256=<HMAC-SHA256 em hex>` |
| `X-Misespay-Timestamp`  | Timestamp da entrega em milliseconds (Unix epoch)    |

## Validando a Assinatura

Cada webhook é assinado com HMAC-SHA256 usando o **secret do seu endpoint**
(exibido uma única vez na criação). Siga estes passos para validar:

1. Calcule o **HMAC-SHA256** do body cru da requisição usando o seu secret como chave
2. Prefixe o resultado em hex com `sha256=`
3. Compare com o header `X-Misespay-Signature` usando comparação em tempo constante

```typescript Node.js (crypto) theme={null}
import { createHmac, timingSafeEqual } from 'node:crypto';

app.post('/webhook', (req, res) => {
  const signature = req.headers['x-misespay-signature'] as string; // "sha256=<hex>"
  const rawBody = req.body; // raw string body

  // 1. HMAC-SHA256 do payload com o SEU secret (não hasheie o secret)
  // 2. Prefixar com "sha256="
  const expected = `sha256=${createHmac('sha256', 'seu_webhook_secret')
    .update(rawBody)
    .digest('hex')}`;

  // 3. Comparar em tempo constante
  const isValid =
    typeof signature === 'string' &&
    signature.length === expected.length &&
    timingSafeEqual(Buffer.from(expected), Buffer.from(signature));

  if (!isValid) {
    return res.status(401).send('Assinatura inválida');
  }

  // Processar o evento...
  res.status(200).send('OK');
});
```

<Warning>
  Sempre use comparação em tempo constante (`timingSafeEqual`) para evitar ataques de timing.
  Nunca compare assinaturas com `===`.
</Warning>

## Exemplos de Payload

### payment\_completed

Enviado quando um pagamento PIX é confirmado.

```json theme={null}
{
  "event": "payment_completed",
  "eventId": "a0b78f10-c7f4-4f5d-98dd-3e36eafeb812",
  "timestamp": "2026-01-11T19:03:28.280Z",
  "data": {
    "transactionId": "a0b78f10-c7f4-4f5d-98dd-3e36eafeb812",
    "environment": "sandbox",
    "amount": 100.21,
    "feeAmount": 0.5,
    "netAmount": 99.71,
    "currency": "BRL",
    "paymentMethod": "pix",
    "status": "completed",
    "completedAt": "2026-01-11T19:03:28.277Z",
    "externalReference": "pedido-12345",
    "e2eId": "E18189547202603160145ZYFfVx3jP8D",
    "counterpartName": "Maria Silva",
    "counterpartDocument": "12345678900",
    "metadata": {
      "orderId": "ORDER-12345",
      "customerId": "CUST-67890"
    }
  }
}
```

| Campo                 | Tipo   | Descrição                                                               |
| --------------------- | ------ | ----------------------------------------------------------------------- |
| `transactionId`       | string | ID único da transação                                                   |
| `environment`         | string | Ambiente (`production` ou `sandbox`)                                    |
| `amount`              | number | Valor total do pagamento                                                |
| `feeAmount`           | number | Taxa cobrada                                                            |
| `netAmount`           | number | Valor líquido (amount - feeAmount)                                      |
| `currency`            | string | Moeda (BRL)                                                             |
| `paymentMethod`       | string | Método de pagamento (pix)                                               |
| `status`              | string | Status do pagamento (completed)                                         |
| `completedAt`         | string | Data/hora da conclusão em ISO 8601                                      |
| `externalReference`   | string | Sua referência externa (opcional)                                       |
| `e2eId`               | string | ID fim-a-fim da rede PIX (opcional, presente quando disponível)         |
| `counterpartName`     | string | Nome do pagador conforme retornado pelo PSP (opcional)                  |
| `counterpartDocument` | string | CPF/CNPJ do pagador conforme retornado pelo PSP (opcional, sem máscara) |
| `metadata`            | object | Metadados customizados enviados na criação do pagamento (opcional)      |

### payment\_expired

Enviado quando um pagamento PIX expira sem confirmação.

```json theme={null}
{
  "event": "payment_expired",
  "eventId": "payment_expired_d5e6f7a8-1234-5678-9abc-def012345678",
  "timestamp": "2026-01-11T19:30:00.000Z",
  "data": {
    "transactionId": "d5e6f7a8-1234-5678-9abc-def012345678",
    "environment": "sandbox",
    "amount": 75.50,
    "feeAmount": 0,
    "netAmount": 0,
    "currency": "BRL",
    "paymentMethod": "pix",
    "status": "expired",
    "expiredAt": "2026-01-11T19:30:00.000Z",
    "externalReference": "pedido-12345",
    "metadata": {
      "orderId": "ORDER-12345",
      "customerId": "CUST-67890"
    }
  }
}
```

| Campo               | Tipo   | Descrição                                                          |
| ------------------- | ------ | ------------------------------------------------------------------ |
| `transactionId`     | string | ID único da transação                                              |
| `environment`       | string | Ambiente (`production` ou `sandbox`)                               |
| `amount`            | number | Valor do pagamento                                                 |
| `feeAmount`         | number | Taxa cobrada (sempre 0 para pagamentos expirados)                  |
| `netAmount`         | number | Valor líquido (sempre 0 para pagamentos expirados)                 |
| `currency`          | string | Moeda (BRL)                                                        |
| `paymentMethod`     | string | Método de pagamento (pix)                                          |
| `status`            | string | Status do pagamento (expired)                                      |
| `expiredAt`         | string | Data/hora da expiração em ISO 8601                                 |
| `externalReference` | string | Sua referência externa (opcional)                                  |
| `metadata`          | object | Metadados customizados enviados na criação do pagamento (opcional) |

### refund\_completed

Enviado quando um reembolso é processado.

```json theme={null}
{
  "event": "refund_completed",
  "eventId": "c92d45e6-8b33-4f12-a789-2e56f8901def",
  "timestamp": "2026-01-11T19:22:15.456Z",
  "data": {
    "refundTransactionId": "c92d45e6-8b33-4f12-a789-2e56f8901def",
    "originalTransactionId": "a0b78f10-c7f4-4f5d-98dd-3e36eafeb812",
    "environment": "sandbox",
    "amount": 50.00,
    "feeAmount": 0.25,
    "netAmount": 50.00,
    "currency": "BRL",
    "paymentMethod": "pix",
    "status": "completed",
    "refundedAt": "2026-01-11T19:22:15.400Z",
    "externalReference": "pedido-12345",
    "metadata": {
      "orderId": "ORDER-12345",
      "customerId": "CUST-67890"
    }
  }
}
```

<Note>
  **Convenção do `netAmount` em refund**: representa o que o destinatário do evento (o cliente final) efetivamente recebe — ou seja, o valor cheio do reembolso. A taxa de reembolso é debitada separadamente do saldo do lojista. O custo total do refund para o lojista é `amount + feeAmount`.
</Note>

| Campo                   | Tipo   | Descrição                                                    |
| ----------------------- | ------ | ------------------------------------------------------------ |
| `refundTransactionId`   | string | ID único da transação de reembolso                           |
| `originalTransactionId` | string | ID da transação original que foi reembolsada                 |
| `environment`           | string | Ambiente (`production` ou `sandbox`)                         |
| `amount`                | number | Valor do reembolso                                           |
| `feeAmount`             | number | Taxa do reembolso cobrada do lojista                         |
| `netAmount`             | number | Valor líquido recebido pelo cliente final (igual a `amount`) |
| `currency`              | string | Moeda (BRL)                                                  |
| `paymentMethod`         | string | Método de pagamento da transação original                    |
| `status`                | string | Status do reembolso (completed)                              |
| `refundedAt`            | string | Data/hora do reembolso em ISO 8601                           |
| `externalReference`     | string | Sua referência externa do pagamento original (opcional)      |
| `metadata`              | object | Metadados customizados do pagamento original (opcional)      |

### withdrawal\_completed

Enviado quando um saque é processado com sucesso.

```json theme={null}
{
  "event": "withdrawal_completed",
  "eventId": "e73775b5-70ee-4bad-be4c-4acff9890e27",
  "timestamp": "2026-01-11T19:08:21.953Z",
  "data": {
    "withdrawalId": "e73775b5-70ee-4bad-be4c-4acff9890e27",
    "environment": "sandbox",
    "amount": 500.00,
    "feeAmount": 2.50,
    "netAmount": 497.50,
    "currency": "BRL",
    "status": "completed",
    "completedAt": "2026-01-11T19:08:21.939Z",
    "externalReference": "saque-empresa-001",
    "e2eId": "E18189547202603160145ZYFfVx3jP8D",
    "counterpartName": "João da Silva",
    "counterpartDocument": "12345678900",
    "metadata": {
      "batchId": "BATCH-001"
    }
  }
}
```

| Campo                 | Tipo   | Descrição                                                             |
| --------------------- | ------ | --------------------------------------------------------------------- |
| `withdrawalId`        | string | ID único do saque                                                     |
| `environment`         | string | Ambiente (`production` ou `sandbox`)                                  |
| `amount`              | number | Valor do saque                                                        |
| `feeAmount`           | number | Taxa do saque                                                         |
| `netAmount`           | number | Valor líquido transferido                                             |
| `currency`            | string | Moeda (BRL)                                                           |
| `status`              | string | Status do saque (completed)                                           |
| `completedAt`         | string | Data/hora da conclusão em ISO 8601                                    |
| `externalReference`   | string | Sua referência externa enviada na criação do saque (opcional)         |
| `e2eId`               | string | ID fim-a-fim da rede PIX (opcional, presente após confirmação do PSP) |
| `counterpartName`     | string | Nome do destinatário no banco de destino (opcional)                   |
| `counterpartDocument` | string | CPF/CNPJ do destinatário (opcional, sem máscara)                      |
| `metadata`            | object | Metadados customizados do saque (opcional)                            |

### withdrawal\_failed

Enviado quando um saque é rejeitado ou falha.

```json theme={null}
{
  "event": "withdrawal_failed",
  "eventId": "b84f12c3-9a21-4e67-bc88-1d45f6789abc",
  "timestamp": "2026-01-11T19:15:42.123Z",
  "data": {
    "withdrawalId": "b84f12c3-9a21-4e67-bc88-1d45f6789abc",
    "environment": "sandbox",
    "amount": 1000.00,
    "feeAmount": 5.00,
    "netAmount": 995.00,
    "currency": "BRL",
    "status": "failed",
    "failedAt": "2026-01-11T19:15:42.100Z",
    "failureReason": "insufficient_funds",
    "externalReference": "saque-empresa-001",
    "e2eId": "E18189547202603160145ZYFfVx3jP8D",
    "counterpartName": "João da Silva",
    "counterpartDocument": "12345678900",
    "metadata": {
      "batchId": "BATCH-001"
    }
  }
}
```

| Campo                 | Tipo   | Descrição                                                                                        |
| --------------------- | ------ | ------------------------------------------------------------------------------------------------ |
| `withdrawalId`        | string | ID único do saque                                                                                |
| `environment`         | string | Ambiente (`production` ou `sandbox`)                                                             |
| `amount`              | number | Valor do saque                                                                                   |
| `feeAmount`           | number | Taxa do saque                                                                                    |
| `netAmount`           | number | Valor líquido que seria transferido                                                              |
| `currency`            | string | Moeda (BRL)                                                                                      |
| `status`              | string | Status do saque (failed)                                                                         |
| `failedAt`            | string | Data/hora da falha em ISO 8601                                                                   |
| `failureReason`       | string | Motivo da falha (insufficient\_funds, invalid\_account, etc.)                                    |
| `externalReference`   | string | Sua referência externa enviada na criação do saque (opcional)                                    |
| `e2eId`               | string | ID fim-a-fim da rede PIX (opcional, presente apenas se o saque chegou a ser processado pelo PSP) |
| `counterpartName`     | string | Nome do destinatário no banco de destino (opcional)                                              |
| `counterpartDocument` | string | CPF/CNPJ do destinatário (opcional, sem máscara)                                                 |
| `metadata`            | object | Metadados customizados do saque (opcional)                                                       |

### withdrawal\_reversed

Enviado quando um saque é estornado pelo PSP.

```json theme={null}
{
  "event": "withdrawal_reversed",
  "eventId": "f12a34b5-6c78-9d01-ef23-456789abcdef",
  "timestamp": "2026-01-11T20:00:00.000Z",
  "data": {
    "reversalTransactionId": "f12a34b5-6c78-9d01-ef23-456789abcdef",
    "originalTransactionId": "e73775b5-70ee-4bad-be4c-4acff9890e27",
    "environment": "sandbox",
    "amount": 500.00,
    "feeAmount": 0,
    "netAmount": 500.00,
    "currency": "BRL",
    "paymentMethod": "pix",
    "status": "completed",
    "reversedAt": "2026-01-11T20:00:00.000Z",
    "metadata": {
      "batchId": "BATCH-001"
    }
  }
}
```

| Campo                   | Tipo   | Descrição                                           |
| ----------------------- | ------ | --------------------------------------------------- |
| `reversalTransactionId` | string | ID único da transação de estorno                    |
| `originalTransactionId` | string | ID do saque original que foi estornado              |
| `environment`           | string | Ambiente (`production` ou `sandbox`)                |
| `amount`                | number | Valor do estorno                                    |
| `feeAmount`             | number | Taxa do estorno                                     |
| `netAmount`             | number | Valor líquido do estorno                            |
| `currency`              | string | Moeda (BRL)                                         |
| `paymentMethod`         | string | Método de pagamento (pix)                           |
| `status`                | string | Status do estorno (completed)                       |
| `reversedAt`            | string | Data/hora do estorno em ISO 8601                    |
| `externalReference`     | string | Sua referência externa do saque original (opcional) |
| `metadata`              | object | Metadados customizados do saque original (opcional) |

### balance\_block\_created

Enviado quando um bloqueio de saldo é criado (MED, judicial ou administrativo).

```json theme={null}
{
  "event": "balance_block_created",
  "eventId": "d4e5f6a7-8b9c-0d1e-2f3a-456789abcdef",
  "timestamp": "2026-01-11T19:30:00.000Z",
  "data": {
    "blockId": "d4e5f6a7-8b9c-0d1e-2f3a-456789abcdef",
    "transactionId": "a0b78f10-c7f4-4f5d-98dd-3e36eafeb812",
    "environment": "production",
    "amount": 1500.00,
    "currency": "BRL",
    "blockType": "med",
    "referenceNumber": "MED-2026-001234",
    "reason": "Notificação de infração Pix recebida",
    "status": "awaiting_response",
    "createdAt": "2026-01-11T19:30:00.000Z",
    "externalReference": "pedido-12345",
    "e2eId": "E18189547202603160145ZYFfVx3jP8D"
  }
}
```

| Campo               | Tipo   | Descrição                                                 |
| ------------------- | ------ | --------------------------------------------------------- |
| `blockId`           | string | ID único do bloqueio                                      |
| `transactionId`     | string | ID da transação original bloqueada                        |
| `environment`       | string | Ambiente (`production` ou `sandbox`)                      |
| `amount`            | number | Valor bloqueado em reais                                  |
| `currency`          | string | Moeda (BRL)                                               |
| `blockType`         | string | Tipo do bloqueio (`med`, `judicial` ou `administrative`)  |
| `referenceNumber`   | string | Número de referência do bloqueio                          |
| `reason`            | string | Motivo do bloqueio                                        |
| `status`            | string | Status inicial (`awaiting_response`)                      |
| `createdAt`         | string | Data/hora da criação em ISO 8601                          |
| `externalReference` | string | Referência externa da transação original (opcional)       |
| `e2eId`             | string | ID fim-a-fim da rede PIX da transação original (opcional) |

### balance\_block\_approved

Enviado quando um bloqueio de saldo é aprovado e o valor é devolvido ao pagador original.

```json theme={null}
{
  "event": "balance_block_approved",
  "eventId": "d4e5f6a7-8b9c-0d1e-2f3a-456789abcdef",
  "timestamp": "2026-01-12T14:00:00.000Z",
  "data": {
    "blockId": "d4e5f6a7-8b9c-0d1e-2f3a-456789abcdef",
    "transactionId": "a0b78f10-c7f4-4f5d-98dd-3e36eafeb812",
    "environment": "production",
    "amount": 1500.00,
    "currency": "BRL",
    "blockType": "med",
    "referenceNumber": "MED-2026-001234",
    "reason": "Notificação de infração Pix recebida",
    "resolutionReason": "Devolução confirmada pelo BACEN",
    "status": "approved",
    "createdAt": "2026-01-11T19:30:00.000Z",
    "resolvedAt": "2026-01-12T14:00:00.000Z",
    "externalReference": "pedido-12345",
    "e2eId": "E18189547202603160145ZYFfVx3jP8D"
  }
}
```

| Campo               | Tipo   | Descrição                                                 |
| ------------------- | ------ | --------------------------------------------------------- |
| `blockId`           | string | ID único do bloqueio                                      |
| `transactionId`     | string | ID da transação original bloqueada                        |
| `environment`       | string | Ambiente (`production` ou `sandbox`)                      |
| `amount`            | number | Valor bloqueado em reais                                  |
| `currency`          | string | Moeda (BRL)                                               |
| `blockType`         | string | Tipo do bloqueio (`med`, `judicial` ou `administrative`)  |
| `referenceNumber`   | string | Número de referência do bloqueio                          |
| `reason`            | string | Motivo original do bloqueio                               |
| `resolutionReason`  | string | Motivo da resolução (opcional)                            |
| `status`            | string | Status do bloqueio (`approved`)                           |
| `createdAt`         | string | Data/hora da criação em ISO 8601                          |
| `resolvedAt`        | string | Data/hora da resolução em ISO 8601                        |
| `externalReference` | string | Referência externa da transação original (opcional)       |
| `e2eId`             | string | ID fim-a-fim da rede PIX da transação original (opcional) |

### balance\_block\_rejected

Enviado quando um bloqueio de saldo é rejeitado e o valor retorna ao saldo disponível do lojista.

```json theme={null}
{
  "event": "balance_block_rejected",
  "eventId": "e5f6a7b8-9c0d-1e2f-3a4b-567890abcdef",
  "timestamp": "2026-01-12T14:00:00.000Z",
  "data": {
    "blockId": "e5f6a7b8-9c0d-1e2f-3a4b-567890abcdef",
    "transactionId": "b1c89f20-d8e5-5f6a-99ee-4f47eafeb923",
    "environment": "production",
    "amount": 250.00,
    "currency": "BRL",
    "blockType": "med",
    "referenceNumber": "MED-2026-005678",
    "reason": "Notificação de infração Pix recebida",
    "resolutionReason": "Defesa aceita - transação legítima comprovada",
    "status": "rejected",
    "createdAt": "2026-01-11T19:30:00.000Z",
    "resolvedAt": "2026-01-12T14:00:00.000Z",
    "externalReference": "pedido-67890",
    "e2eId": "E4071059520260316020613919677838"
  }
}
```

| Campo               | Tipo   | Descrição                                                 |
| ------------------- | ------ | --------------------------------------------------------- |
| `blockId`           | string | ID único do bloqueio                                      |
| `transactionId`     | string | ID da transação original bloqueada                        |
| `environment`       | string | Ambiente (`production` ou `sandbox`)                      |
| `amount`            | number | Valor bloqueado em reais                                  |
| `currency`          | string | Moeda (BRL)                                               |
| `blockType`         | string | Tipo do bloqueio (`med`, `judicial` ou `administrative`)  |
| `referenceNumber`   | string | Número de referência do bloqueio                          |
| `reason`            | string | Motivo original do bloqueio                               |
| `resolutionReason`  | string | Motivo da resolução (opcional)                            |
| `status`            | string | Status do bloqueio (`rejected`)                           |
| `createdAt`         | string | Data/hora da criação em ISO 8601                          |
| `resolvedAt`        | string | Data/hora da resolução em ISO 8601                        |
| `externalReference` | string | Referência externa da transação original (opcional)       |
| `e2eId`             | string | ID fim-a-fim da rede PIX da transação original (opcional) |

## Boas Práticas

<AccordionGroup>
  <Accordion title="Responda rapidamente">
    Retorne um status `200 OK` o mais rápido possível. Processe o webhook de forma assíncrona se necessário.
  </Accordion>

  <Accordion title="Implemente idempotência">
    Use o `eventId` para evitar processar o mesmo evento duas vezes. Webhooks podem ser reenviados em caso de falha.
  </Accordion>

  <Accordion title="Use HTTPS">
    Configure seu endpoint apenas com HTTPS para garantir a segurança dos dados.
  </Accordion>
</AccordionGroup>

## Retentativas

Se o seu endpoint não responder com status `2xx`, tentaremos reenviar o webhook:

* **5 tentativas** com backoff exponencial
* Intervalo inicial: 2 segundos
* Intervalo máximo: \~30 segundos entre tentativas

Após 5 tentativas sem sucesso, o webhook é marcado como falho.
