> For the complete documentation index, see [llms.txt](https://docs.barte.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.barte.com/guias/webhooks/estornos-charge.md).

# Transações (CHARGE)

Webhook do domínio CHARGE: notificações por cobrança no formato v2, hoje cobrindo estornos — eventos, payload, casos de erro, ordem de entrega e ativação.

O domínio `CHARGE` do Webhook v2 notifica o ciclo de vida de **estornos** no nível da **cobrança** (charge), em 3 eventos: `charge.refund.processing`, `charge.refund.succeeded` e `charge.refund.failed`. Diferente do webhook de pedidos, ele cobre também estornos **parciais** que não zeram o saldo da cobrança.

{% hint style="info" %}
**Quais estornos geram eventos.** O domínio `CHARGE` cobre os estornos processados pelo fluxo de estorno assíncrono da Barte, o mesmo descrito na Referência de API. Estornos processados fora desse fluxo não geram eventos deste domínio.

Hoje o domínio cobre apenas eventos de estorno; outros eventos de cobrança podem ser adicionados no futuro, e ativá-lo agora significa receber os de estorno.

Ativar o `CHARGE` não altera os webhooks que você já recebe nos demais domínios.
{% endhint %}

***

## Como ativar

No [Dashboard do AppBarte](https://app.barte.com), em [**Configurações → Integração → Web Hooks**](https://app.barte.com/integracao) (a mesma tela de [Configurando Webhooks](/guias/passo-a-passo-do-vendedor/1o-preparacao/configurando-webhooks.md)):

* Cadastre um novo webhook ou edite um existente;
* Selecione a versão **2.0**. Enquanto a versão for 1.0, a opção fica desabilitada;
* Marque a caixa **Transação**.

{% hint style="info" %}
**Na tela, o domínio se chama "Transação". No payload, ele chega como `"domain": "CHARGE"`.** É o mesmo domínio: a caixa "Transação" é o que ativa os eventos descritos nesta página.
{% endhint %}

Os **três eventos vêm juntos**, sem seleção individual por evento.

***

## Os eventos

| eventType                  | Quando dispara                                                                                                                   |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `charge.refund.processing` | Um pedido de estorno foi aceito e está em processamento. Emitido **uma única vez por estorno**, no momento da criação.           |
| `charge.refund.succeeded`  | Um estorno foi confirmado. Pode ocorrer de segundos a dias após o `processing`, dependendo do provedor e do método de pagamento. |
| `charge.refund.failed`     | Um estorno não pôde ser concluído, por exemplo por rejeição do provedor.                                                         |

Garantias do contrato:

* **Exatamente um** `charge.refund.processing` por estorno. Reprocessamentos internos **não** geram novo `processing`;
* **No máximo um** evento terminal por estorno: `succeeded` **ou** `failed`, nunca ambos;
* Um pedido de estorno rejeitado na criação (validação síncrona da API) **não gera evento**, porque o estorno não chega a existir. Esses casos aparecem como erro na resposta da requisição, descritos em [Erros da solicitação](#erros-da-solicitacao);
* A mesma notificação pode ser **reenviada** (entrega *at-least-once*). Veja [Entrega, reenvio e idempotência](#entrega-reenvio-e-idempotencia).

### Quanto tempo até o evento terminal

Na maioria dos casos o estorno conclui em poucos segundos. Em cartão, quando a cobrança ainda não liquidou, o estorno é um cancelamento da autorização e não depende do prazo do adquirente. Em PIX, a devolução é enviada ao provedor assim que o pedido é aceito.

Quando o provedor não confirma de primeira, a Barte **retenta com espaçamento crescente por até 48 horas**. O desfecho depende do que o provedor responde:

| Situação                                                | O que você recebe                                                                                      |
| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| Rejeição confirmada pelo provedor                       | `charge.refund.failed`, com o motivo em `errorReason`                                                  |
| Falha incerta, por exemplo indisponibilidade momentânea | **Nenhum evento terminal ainda**. O estorno permanece em `PROCESSING` até haver confirmação definitiva |

{% hint style="info" %}
**O estorno só conclui com confirmação real do provedor.** Um estorno parado em `PROCESSING` por mais tempo que o habitual não foi perdido nem falhou em silêncio: significa que a Barte ainda não tem resposta definitiva. Não trate ausência de evento terminal como falha, e não recrie o estorno por isso.
{% endhint %}

***

## O payload

{% hint style="info" %}
**O evento é um retrato (snapshot) da cobrança.** O `eventData` descreve o **estado da cobrança no momento do envio**, com valores atualizados e o **histórico completo de estornos** em `refunds[]`, e **não aponta qual estorno motivou o evento**. Consuma o evento reconciliando o estado: compare cada item de `refunds[]` (pelo `uuid`) com o que você tem registrado.
{% endhint %}

Exemplo de um `charge.refund.processing` em uma cobrança de R$ 100,00 com R$ 30,00 e R$ 20,00 já confirmados e um terceiro estorno de R$ 50,00 recém-aceito:

```json
{
  "version": "2.0",
  "domain": "CHARGE",
  "eventType": "charge.refund.processing",
  "eventId": "CHARGE_REFUND_PROCESSING-3f7c1d90-5b42-4e18-9a63-2c8f0b7d1a55",
  "eventDatetime": "2026-08-19T13:42:11.874512309Z",
  "sellerId": 123,
  "metadata": null,
  "eventData": {
    "uuid": "b1e7a204-9c3d-4f81-8a52-6d0f3e9c7b14",
    "originalValue": 100.0,
    "currentValue": 0.0,
    "createdAt": "2026-08-19T11:05:47Z",
    "paidDate": "2026-08-19",
    "status": "PAID",
    "order": { "uuid": "7d2c8e51-3a94-4b67-9e08-1f5a6c2d9b30" },
    "refunds": [
      {
        "uuid": "a4f0c8d2-71b5-4e93-8c17-9f2e5a3b6d48",
        "amount": 30.0,
        "status": "SUCCESS",
        "createdAt": "2026-08-19T12:18:03Z",
        "idempotencyKey": null,
        "errorReason": null
      },
      {
        "uuid": "c9b3e716-2d40-4a58-b6f1-8e07d4c95a21",
        "amount": 20.0,
        "status": "SUCCESS",
        "createdAt": "2026-08-19T12:54:39Z",
        "idempotencyKey": null,
        "errorReason": null
      },
      {
        "uuid": "3f7c1d90-5b42-4e18-9a63-2c8f0b7d1a55",
        "amount": 50.0,
        "status": "PROCESSING",
        "createdAt": "2026-08-19T13:42:11Z",
        "idempotencyKey": "8c2d5f71-4e09-4b3a-9d16-6a0e3c8b5f42",
        "errorReason": null
      }
    ],
    "buyer": { "uuid": "e58d1c47-0b62-4f39-a714-5c93b8e2d06f", "email": "comprador@exemplo.com" }
  }
}
```

Os três eventos usam a **mesma estrutura**. O que muda é o `eventType` e o estado refletido no `eventData`.

{% hint style="info" %}
**Campos sem valor chegam como `null`, não são omitidos.** Todas as chaves do contrato estão presentes em toda notificação `CHARGE`. No exemplo acima, `metadata`, `idempotencyKey` e `errorReason` chegam explicitamente como `null`. Trate `null` como "sem valor", não como campo ausente.
{% endhint %}

Dois detalhes de formato para acertar no parser:

* **Valores monetários são decimais em reais** (`100.0`), não centavos. A quantidade de casas decimais varia entre eventos, então leia o campo como número decimal de escala livre. Em linguagens com ponto flutuante, use um tipo decimal (`BigDecimal`, `decimal.Decimal`) para não perder centavos no arredondamento;
* **Timestamps são ISO 8601 UTC com sufixo `Z`**. Use um parser ISO 8601. Já `paidDate` é **data pura**, sem hora.

### Campos do envelope

| Campo             | Tipo                                                       | Descrição                                                                                                                                                              |
| ----------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **version**       | `string`                                                   | Versão do formato do webhook. Valor fixo: `"2.0"`.                                                                                                                     |
| **domain**        | `string`                                                   | Domínio do evento. Valor fixo: `"CHARGE"`.                                                                                                                             |
| **eventType**     | `string`                                                   | Tipo do evento. Um dos 3 valores de [Os eventos](#os-eventos).                                                                                                         |
| **eventId**       | `string`                                                   | Identificador único do **evento**. String **opaca**, não interprete o formato. **Estável entre reenvios**: a mesma notificação reentregue chega com o mesmo `eventId`. |
| **eventDatetime** | `string` (ISO 8601 UTC, sufixo `Z`)                        | Data e hora **da transição de estado** que originou o evento. É o campo de ordenação. Veja [Ordem de entrega](#ordem-de-entrega-nao-e-garantida).                      |
| **sellerId**      | `integer`                                                  | Identificador interno do vendedor.                                                                                                                                     |
| **metadata**      | `array` de `{ "key": string, "value": string }`, ou `null` | Metadata do **pedido** ao qual a cobrança pertence. `null` quando a cobrança não tem pedido associado ou o pedido não tem metadata.                                    |
| **eventData**     | `object`                                                   | Snapshot da cobrança. Veja abaixo.                                                                                                                                     |

### Campos do `eventData`

O `eventData` é a **cobrança**:

| Campo             | Tipo                                | Descrição                                                                                                                                                                        |
| ----------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **uuid**          | `string` (UUID)                     | Identificador da cobrança.                                                                                                                                                       |
| **originalValue** | `number` (decimal em reais)         | Valor original da cobrança.                                                                                                                                                      |
| **currentValue**  | `number` (decimal em reais)         | Valor da cobrança que **ainda pode ser estornado** no momento do envio. Desconta os estornos confirmados **e os em processamento**. **Nunca negativo**: o valor tem piso em `0`. |
| **createdAt**     | `string` (ISO 8601 UTC, sufixo `Z`) | Data e hora de criação da cobrança.                                                                                                                                              |
| **paidDate**      | `string` (`YYYY-MM-DD`), ou `null`  | Data do pagamento da cobrança, sem hora. `null` quando a cobrança não foi paga.                                                                                                  |
| **status**        | `string`                            | Status atual da cobrança no momento do envio. Veja [Status da cobrança](#status-da-cobranca).                                                                                    |
| **order**         | `object`                            | Pedido ao qual a cobrança pertence, no formato `{ "uuid": string }`. O `uuid` **pode ser `null`**: cobranças de assinatura, por exemplo, não têm pedido associado.               |
| **refunds**       | `array`                             | **Histórico completo** de estornos da cobrança, em todos os status, inclusive falhas. Veja abaixo.                                                                               |
| **buyer**         | `object`                            | Comprador. Veja abaixo.                                                                                                                                                          |

#### Status da cobrança

Nos eventos de estorno, os valores esperados de `eventData.status` são:

| Valor    | Significado                                                                                   |
| -------- | --------------------------------------------------------------------------------------------- |
| `PAID`   | Cobrança paga e **não** totalmente estornada. Inclui o caso de estorno parcial já confirmado. |
| `REFUND` | Cobrança **totalmente** estornada.                                                            |

O campo reflete o status real da cobrança, e o conjunto de status da cobrança é maior que esses dois. Trate valores fora dessa lista de forma genérica, sem quebrar o processamento.

{% hint style="warning" %}
**Duas situações em que o `status` costuma ser lido como inconsistência, e não é.**

**1. No estorno parcial, `status` permanece `PAID`.** A cobrança só passa a `REFUND` quando estiver **totalmente** estornada. Um `charge.refund.succeeded` com `status = "PAID"` é o comportamento normal de um estorno parcial.

**2. A cobrança só muda de status no evento terminal, não no `processing`.** No estorno que zera o saldo, os dois eventos do **mesmo** estorno chegam com `status` diferente:

| Evento                     | `eventData.status`                                         |
| -------------------------- | ---------------------------------------------------------- |
| `charge.refund.processing` | `PAID`, porque o estorno foi aceito mas ainda não concluiu |
| `charge.refund.succeeded`  | `REFUND`, porque a cobrança passou a totalmente estornada  |

Continuando o exemplo acima, é isso que muda no `charge.refund.succeeded` do estorno de R$ 50,00:

```json
{
  "eventType": "charge.refund.succeeded",
  "eventId": "CHARGE_REFUND_SUCCESS-3f7c1d90-5b42-4e18-9a63-2c8f0b7d1a55",
  "eventDatetime": "2026-08-19T13:44:52.093184000Z",
  "eventData": {
    "status": "REFUND",
    "refunds": [
      { "uuid": "3f7c1d90-5b42-4e18-9a63-2c8f0b7d1a55", "amount": 50.0, "status": "SUCCESS", "...": "..." }
    ]
  }
}
```

Repare que o `eventId` do terminal usa o token interno `CHARGE_REFUND_SUCCESS`, enquanto o `eventType` público é `charge.refund.succeeded`. É mais uma razão para tratar o `eventId` como string opaca.
{% endhint %}

#### Itens de `refunds[]`

| Campo              | Tipo                                    | Descrição                                                                                                                                                                                                                       |
| ------------------ | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **uuid**           | `string` (UUID)                         | Identificador do estorno, o mesmo devolvido na criação. Use-o para reconciliar com o que você já registrou.                                                                                                                     |
| **amount**         | `number` (decimal em reais)             | Valor do estorno.                                                                                                                                                                                                               |
| **status**         | `string`                                | Status do estorno: `PROCESSING`, `SUCCESS` ou `FAILED`.                                                                                                                                                                         |
| **createdAt**      | `string` (ISO 8601 UTC, sufixo `Z`)     | Data e hora de criação do estorno.                                                                                                                                                                                              |
| **idempotencyKey** | `string` (até 63 caracteres), ou `null` | Chave que você enviou no header `x-idempotency-key` ao criar o estorno, devolvida aqui para correlacionar o evento com a sua própria requisição. `null` em estornos que não foram criados pela API, como os feitos pelo painel. |
| **errorReason**    | `string`, ou `null`                     | Motivo da falha, como código estável definido pela Barte. `null` sempre que o estorno não falhou. Veja [Motivos de falha](#motivos-de-falha-errorreason).                                                                       |

O array vem ordenado **do estorno mais antigo para o mais recente** (por `createdAt`) e contém **todos** os estornos da cobrança, incluindo os que falharam. Em um evento de estorno há sempre pelo menos um item.

{% hint style="info" %}
**Use o `idempotencyKey` para ligar o evento ao seu próprio pedido de estorno.** O evento é um snapshot da cobrança e não aponta qual estorno o disparou. O `uuid` resolve isso no caminho feliz, mas é gerado pela Barte: se a resposta da criação se perdeu num timeout, você não o tem. Já o `idempotencyKey` é o valor que **você** enviou no header `x-idempotency-key`, o único campo do payload sob seu controle e o único elo que sobrevive a uma resposta perdida.

A chave é obrigatória ao criar um estorno pela API, então no consumo basta casar o item de `refunds[]` por ela. Vem `null` em estornos que não nasceram da sua integração, como os feitos pelo painel, então um mesmo array pode misturar itens com e sem chave.

O comportamento da chave na criação, incluindo o que acontece ao reenviá-la, está na **Referência de API**, em **Estorno → Estornar Cobrança**.
{% endhint %}

{% hint style="warning" %}
**Como derivar os valores de estorno:** o total **confirmado** estornado é a soma dos itens de `refunds[]` com `status = "SUCCESS"`. O `currentValue` desconta também os estornos **em processamento**. Estornos com `status = "FAILED"` **não** entram em nenhuma das duas contas.

**Sempre derive os totais a partir de `refunds[]`, nunca por subtração.** O `currentValue` tem **piso em zero**: se dois estornos forem aceitos em concorrência e juntos passarem do saldo da cobrança, o campo chega `0`, não negativo. Nesse caso `originalValue` menos `currentValue` deixa de equivaler à soma dos estornos em voo.

Os valores são um **snapshot do momento do envio da notificação**, não do instante da transição: uma notificação entregue com atraso pode chegar já refletindo estornos posteriores.

Para saber se a cobrança está totalmente estornada, use `status = "REFUND"`. O `currentValue = 0` **não** significa isso, porque inclui estornos ainda em processamento e o caso do piso descrito acima.
{% endhint %}

#### Motivos de falha (`errorReason`)

Quando um estorno falha, `errorReason` traz um **código estável definido pela Barte**, nunca a mensagem crua do provedor de pagamento.

| Valor                                | O que aconteceu                                                                                                             | O que fazer                                                                                                                    |
| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `INSUFFICIENT_BALANCE`               | O saldo restante da cobrança não cobria o estorno solicitado no momento do processamento.                                   | Conferir o saldo em `currentValue` antes de solicitar. Ocorre também quando dois estornos concorrentes disputam o mesmo saldo. |
| `ISSUER_INSUFFICIENT_FUNDS`          | A rede ou o emissor recusou o estorno por insuficiência de fundos.                                                          | Não é resolvível pela sua integração. Encaminhe ao comprador ou fale com o suporte.                                            |
| `REJECTED_BY_PROVIDER_HTTP_<status>` | O provedor rejeitou a solicitação, e o sufixo traz o status HTTP da rejeição (por exemplo `REJECTED_BY_PROVIDER_HTTP_422`). | Registrar o código recebido e acionar o suporte com o `uuid` do estorno.                                                       |
| `REJECTED_BY_PROVIDER`               | O provedor reportou a falha sem um motivo mais específico.                                                                  | Mesmo tratamento do anterior.                                                                                                  |
| `REFUND_NOT_DISPATCHED`              | O estorno não chegou a ser despachado ao provedor dentro da janela de retentativas.                                         | Acionar o suporte com o `uuid` do estorno; um novo pedido pode ser necessário.                                                 |

{% hint style="warning" %}
**O campo é&#x20;*****best-effort*****&#x20;e a lista é aberta.**

* `errorReason` pode vir `null` **mesmo com `status = "FAILED"`**, quando a falha chega pelo provedor sem informação classificável, ou em estornos anteriores ao registro do motivo. Ausência de motivo não é erro da sua integração;
* **novos códigos podem ser adicionados** sem aviso prévio, de forma aditiva. Trate valores desconhecidos de forma genérica, registrando o código recebido, em vez de rejeitar a notificação.
  {% endhint %}

Exemplo de um `charge.refund.failed`: dois estornos de R$ 60,00 pedidos em concorrência na mesma cobrança de R$ 100,00, e o segundo não tinha saldo.

```json
{
  "version": "2.0",
  "domain": "CHARGE",
  "eventType": "charge.refund.failed",
  "eventId": "CHARGE_REFUND_FAILED-6c41a8f3-9e27-4d05-b83a-27f9e4c15b6d",
  "eventDatetime": "2026-08-19T15:07:26.442810000Z",
  "sellerId": 123,
  "metadata": null,
  "eventData": {
    "uuid": "f402d95b-8c71-4e63-a09d-5b8e1f7c3a26",
    "originalValue": 100.0,
    "currentValue": 40.0,
    "createdAt": "2026-08-19T14:30:12Z",
    "paidDate": "2026-08-19",
    "status": "PAID",
    "order": { "uuid": "2b7e6d18-45c9-4a30-8f16-9d0c3e5b7a42" },
    "refunds": [
      {
        "uuid": "9a5f2c74-1d68-4b39-8e02-4c7d6a1f3b95",
        "amount": 60.0,
        "status": "PROCESSING",
        "createdAt": "2026-08-19T15:07:25Z",
        "idempotencyKey": "5e8b1a37-2c94-4f60-8d25-7b3f9c4e1a68",
        "errorReason": null
      },
      {
        "uuid": "6c41a8f3-9e27-4d05-b83a-27f9e4c15b6d",
        "amount": 60.0,
        "status": "FAILED",
        "createdAt": "2026-08-19T15:07:25Z",
        "idempotencyKey": null,
        "errorReason": "INSUFFICIENT_BALANCE"
      }
    ],
    "buyer": { "uuid": "e58d1c47-0b62-4f39-a714-5c93b8e2d06f", "email": "comprador@exemplo.com" }
  }
}
```

O estorno que falhou **continua no array** e **deixa de consumir saldo**: com um dos R$ 60,00 em processamento e o outro em falha, o `currentValue` volta a refletir apenas o estorno vivo.

#### `buyer`

| Campo     | Tipo                       | Descrição                   |
| --------- | -------------------------- | --------------------------- |
| **uuid**  | `string` (UUID), ou `null` | Identificador do comprador. |
| **email** | `string`, ou `null`        | E-mail do comprador.        |

Os dados do comprador são resolvidos por consulta ao cadastro do vendedor e são *best-effort*: se não houver cadastro correspondente, a notificação é enviada com os campos de `buyer` nulos em vez de ser retida.

***

## Erros da solicitação

Nem toda falha de estorno vira evento. Quando a Barte **recusa o pedido na criação**, a rejeição volta na própria resposta da requisição e **nenhum evento `CHARGE` é emitido**, porque o estorno não chega a existir.

Saber disso muda o desenho da integração: esses casos precisam ser tratados no retorno da chamada, e ficar esperando um `charge.refund.failed` que nunca vem é um erro comum.

| Código     | O que aconteceu                                                                                                                                                                      |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `BAR-0500` | Parâmetro inválido na requisição, por exemplo `amount` ausente, zero ou negativo, ou chave de idempotência vazia ou com mais de 63 caracteres.                                       |
| `BAR-0424` | A cobrança não existe, ou não pertence à conta autenticada.                                                                                                                          |
| `BAR-0450` | O status atual da cobrança não permite estorno, por exemplo cancelada, em disputa ou em chargeback. Uma cobrança já totalmente estornada pode responder este código ou o `BAR-0451`. |
| `BAR-0451` | O valor solicitado é maior que o saldo restante da cobrança.                                                                                                                         |
| `BAR-0452` | O método de pagamento não suporta estorno, por exemplo boleto.                                                                                                                       |
| `BAR-0434` | Fora do prazo de estorno, ou a cobrança ainda não tem data de pagamento confirmada.                                                                                                  |
| `BAR-0460` | O saldo da conta é insuficiente para cobrir o estorno.                                                                                                                               |
| `BAR-0463` | Estorno bloqueado por política, por exemplo risco, fraude ou suspensão administrativa.                                                                                               |
| `BAR-0466` | A chave de idempotência já está associada a um estorno de valor diferente naquela cobrança.                                                                                          |
| `BAR-0467` | Já existe uma requisição em andamento com a mesma chave de idempotência para aquela cobrança.                                                                                        |

O formato do corpo de erro e os detalhes de cada resposta estão na **Referência de API**, em **Estorno → Estornar Cobrança**.

{% hint style="info" %}
**Como distinguir os dois tipos de falha:**

* **Recusa na criação**: chega como erro na resposta da requisição, com um dos códigos acima, e **não** gera evento. O estorno nunca existiu;
* **Falha no processamento**: o estorno foi criado, você recebeu `charge.refund.processing`, e depois recebe `charge.refund.failed` com o motivo em [`errorReason`](#motivos-de-falha-errorreason).

Um mesmo motivo de fundo pode aparecer nos dois lugares. Saldo insuficiente, por exemplo, é `BAR-0451` quando detectado na criação e `INSUFFICIENT_BALANCE` quando detectado no processamento, o que acontece quando dois estornos concorrentes disputam o mesmo saldo.
{% endhint %}

***

## Entrega, reenvio e idempotência

A notificação é um **HTTP POST** na URL cadastrada. Seu endpoint deve responder **2xx em até 20 segundos**. Fora disso, a entrega é considerada falha e será **reenviada com o mesmo `eventId`**.

### Headers da requisição

| Header                  | Tipo                                  | Descrição                                                                                    |
| ----------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------- |
| **authorization**       | `string`                              | Token de autenticação configurado no cadastro do webhook.                                    |
| **idempotency-key**     | `string`                              | Chave de idempotência. Para eventos `CHARGE`, é igual ao `eventId` e estável entre reenvios. |
| **x-webhook-timestamp** | `integer`                             | Unix epoch em segundos, no momento do envio.                                                 |
| **x-webhook-nonce**     | `string` (32 caracteres hexadecimais) | Único por requisição.                                                                        |

### Idempotência

A entrega é *at-least-once*: a mesma notificação pode chegar mais de uma vez. Use o `eventId` (ou o header `idempotency-key`, que carrega o mesmo valor) para deduplicar. Guarde os identificadores já processados e ignore repetições.

{% hint style="warning" %}
**Não confunda os dois headers de idempotência.** Os nomes são parecidos e as direções são opostas:

| Header              | Direção                            | Para que serve                                                                                                                  |
| ------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `idempotency-key`   | A Barte envia, **na notificação**  | Deduplicar reentregas da mesma notificação. Carrega o `eventId`                                                                 |
| `x-idempotency-key` | Você envia, ao **criar** o estorno | Evitar criar o estorno duas vezes e correlacionar o evento com a sua requisição. Veja [Itens de `refunds[]`](#itens-de-refunds) |
| {% endhint %}       |                                    |                                                                                                                                 |

### Ordem de entrega não é garantida

Eventos da mesma cobrança podem chegar **fora de ordem**. Um `charge.refund.succeeded` pode chegar **antes** do `charge.refund.processing` correspondente. Para consumir corretamente, aplique duas regras:

1. **Ordene por `eventDatetime`**, que registra o instante da transição de estado. Descarte eventos com `eventDatetime` **anterior** ao do último estado que você registrou para aquela cobrança (`eventData.uuid`);
2. **Status terminal vence.** `SUCCESS` e `FAILED` são estados finais de um estorno. Nunca sobrescreva o estado final de um estorno já registrado com `PROCESSING`, independentemente do timestamp do evento.

***

## Verificação de autenticidade

Em **produção**, as notificações partem de um IP de saída fixo: **`3.213.198.81`**. Restringir seu endpoint a esse endereço é a verificação de origem disponível hoje, e vale combinar com as validações abaixo.

Valide também toda notificação recebida antes de processá-la:

* **`authorization`**: confira que o valor é exatamente o token configurado no cadastro do webhook. Rejeite requisições com token ausente ou diferente;
* **`x-webhook-timestamp`**: rejeite requisições com timestamp fora de uma janela de tolerância de 5 minutos;
* **`x-webhook-nonce`**: verifique a unicidade do nonce para bloquear replay de requisições.

***

## Relação com o webhook de pedidos (ORDER)

O domínio `ORDER` continua funcionando como hoje: quando um estorno **zera** o saldo restante da cobrança, o pedido transiciona para `REFUND` e o evento `order.refund` dispara com o `refunds[]` do pedido.

A diferença está na cobertura: um estorno **parcial** que não zera o saldo **não gera evento `ORDER`**. A visibilidade em tempo real de parciais vem do domínio `CHARGE`.

Se você já consome `ORDER`, ativar o `CHARGE` não muda nada no que você recebe hoje. Os dois domínios convivem, e uma cobrança totalmente estornada pode gerar eventos nos dois.

***

<i class="fa-tickets">:tickets:</i> Em caso de dúvidas, entre em contato com o nosso time através do suporte disponibilizado.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.barte.com/guias/webhooks/estornos-charge.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
