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

# Retentativas e idempotência

> Chaves de idempotência evitam operações duplicadas nos endpoints de transações do Midaz ao reexecutar falhas transitórias.

Um aspecto fundamental da confiabilidade do Midaz é garantir que as operações sejam **seguras para retentativa** e nunca processadas mais de uma vez.

Seja ao lidar com transações ou criar entidades, sua integração deve ser resiliente, mesmo diante de falhas de rede, timeouts ou interrupções temporárias. Esta página explica como o Midaz protege contra duplicações usando **chaves de idempotência**, e como você pode implementá-las para lidar com retentativas no seu sistema com confiança.

### Tornando as retentativas seguras

Em sistemas reais, falhas em requisições de API são comuns. Talvez ocorra um timeout de rede. Talvez seu serviço caia logo após enviar uma requisição. Nesses casos, é natural tentar novamente, mas como você pode ter certeza de que o Midaz não processará a mesma operação duas vezes?

É aí que as **chaves de idempotência** entram em ação.

Ao anexar uma chave única a cada requisição, você está dizendo ao Midaz: "Esta é a mesma operação. Se você já a processou, não faça novamente."

O Midaz armazena essa chave temporariamente e a utiliza para determinar se a requisição é nova, já foi concluída ou ainda está sendo processada. Isso protege seu sistema contra duplicatas enquanto oferece controle total sobre sua estratégia de retentativa.

## Idempotência no Midaz

***

O Midaz usa **chaves de idempotência** para garantir que as operações de transação sejam seguras para retentativa e nunca processadas mais de uma vez. Este mecanismo está disponível em todos os **endpoints de transações**: `/transactions/json`, `/transactions/dsl`, `/transactions/inflow`, `/transactions/outflow`, `/transactions/annotation` e `/transactions/{id}/revert`.

<Note>
  Outros produtos da Lerian também suportam idempotência através de seus próprios headers — veja a seção [Idempotência nos produtos Lerian](#idempotência-nos-produtos-lerian) abaixo. Esta página foca na idempotência para a API do Midaz Ledger.
</Note>

<Note>
  Os endpoints `commit` e `cancel` de transações usam um lock baseado em Redis para prevenir processamento concorrente da mesma transação, mas não suportam idempotência completa (sem respostas em cache ou header `X-Idempotency-Replayed`).
</Note>

Para usá-lo, sua requisição pode incluir dois headers:

* `X-Idempotency`: a chave única que identifica a requisição.
* `X-TTL`: o tempo de vida (em segundos) que o Midaz deve armazenar essa chave em cache.

<Note>
  Se você não enviar o header `X-Idempotency`, o Midaz gera um automaticamente calculando um hash SHA-256 do corpo da requisição. Isso significa que corpos de requisição idênticos enviados para a mesma organização e ledger são automaticamente deduplicados.
</Note>

Veja o que acontece nos bastidores:

1. Quando uma nova chave chega, o Midaz a marca como `pending`, processa a requisição e armazena a resposta completa em cache.
2. Se a mesma chave for usada novamente dentro da janela de TTL:
   1. Se a operação ainda estiver em execução, o Midaz retorna um `409 Conflict` (código de erro `0084`) com `X-Idempotency-Replayed: false`.
   2. Se estiver concluída, o Midaz retorna exatamente a mesma resposta com código de status `201 Created` e `X-Idempotency-Replayed: true`.
3. Se a transação falhar por erros de validação ou saldo insuficiente, o Midaz **deleta a chave de idempotência**, permitindo que você retente com a mesma chave após corrigir o problema.

No Midaz, as chaves de idempotência são escopadas por **organização e ledger**. Isso significa que o mesmo valor de chave pode ser usado independentemente em diferentes organizações ou ledgers sem conflito. Outros produtos da Lerian utilizam estratégias de escopo diferentes — consulte a [tabela comparativa](#idempotência-nos-produtos-lerian) para mais detalhes.

### Resumo do fluxo

A *Figura 1* mostra o ciclo de vida completo de uma requisição idempotente:

<Frame caption="Figura 1. Ciclo de vida de uma requisição idempotente no Midaz.">
  <img src="https://mintcdn.com/lerian-49cb71fc/ZrZBZTM4DWnrahSd/images/pt/d2/idempotency.svg?fit=max&auto=format&n=ZrZBZTM4DWnrahSd&q=85&s=5bd8140bf8f2e6d888504a2d923736f2" alt="Ciclo de vida completo de uma requisição idempotente no Midaz, da recepção da chave de idempotência até o retorno da resposta em cache ou recém-processada" width="1115" height="1454" data-path="images/pt/d2/idempotency.svg" />
</Frame>

**Como funciona:**

* Se a requisição **não incluir uma chave de idempotência existente**, o Midaz cria uma nova (ou gera uma automaticamente a partir do hash do corpo da requisição), processa a requisição, armazena a resposta e a retorna com `X-Idempotency-Replayed: false`.
* Se a chave **já existir**:
  * Se a operação ainda estiver em execução, o Midaz retorna um `409 Conflict` com `X-Idempotency-Replayed: false`.
  * Se a operação estiver concluída, o Midaz ignora a execução e retorna a resposta em cache com código de status `201 Created` e `X-Idempotency-Replayed: true`.
* Se a requisição original **falhou por erros de validação ou saldo**, a chave é limpa automaticamente, permitindo que você retente com a mesma chave de forma segura.

Esse comportamento garante que cada requisição seja tratada de forma segura, previsível e sem duplicação, mesmo quando retentada.

### Exemplo de requisição

Veja como enviar uma requisição idempotente para criar uma transação:

<CodeGroup>
  ```http HTTP theme={null}
  POST /v1/organizations/{organization_id}/ledgers/{ledger_id}/transactions/json HTTP/1.1
  X-Idempotency: 7fb8e1d098cd4730bb932d038b3b8651
  X-TTL: 60
  Content-Type: application/json

  {
    "description": "Pagamento de assinatura mensal",
    "code": "SUB-2025-001",
    "send": {
      "asset": "USD",
      "value": "1500",
      "source": {
        "from": [
          {
            "accountAlias": "customer-usd-1",
            "amount": {
              "asset": "USD",
              "value": "1500"
            }
          }
        ]
      },
      "distribute": {
        "to": [
          {
            "accountAlias": "merchant-usd-1",
            "amount": {
              "asset": "USD",
              "value": "1500"
            }
          }
        ]
      }
    }
  }
  ```
</CodeGroup>

Se a requisição for bem-sucedida e você enviá-la novamente dentro de 60 segundos, o Midaz retornará o resultado em cache com:

<CodeGroup>
  ```http HTTP theme={null}
  HTTP/1.1 201 Created
  X-Idempotency-Replayed: true
  ```
</CodeGroup>

Se você enviá-la novamente enquanto a original ainda estiver sendo processada, você receberá:

<CodeGroup>
  ```http HTTP theme={null}
  HTTP/1.1 409 Conflict
  X-Idempotency-Replayed: false
  ```
</CodeGroup>

## Geração de chaves

***

Você pode fornecer sua própria chave `X-Idempotency` ou deixar o Midaz gerar uma automaticamente.

### Geração automática de chaves

Se você omitir o header `X-Idempotency`, o Midaz calcula um hash SHA-256 do corpo da requisição e o usa como chave de idempotência. Isso significa que enviar o mesmo corpo JSON exato para a mesma organização e ledger será automaticamente deduplicado — sem trabalho adicional.

Isso é suficiente para a maioria dos cenários de retentativa onde o corpo da requisição não muda entre tentativas.

### Geração personalizada de chaves

Use uma chave personalizada quando precisar:

* Correlacionar a chave de idempotência com um ID no seu próprio sistema (ex: um ID de pedido).
* Retentar com um corpo de requisição modificado e ainda assim deduplicar (ex: após corrigir um campo).
* Controlar o formato da chave para fins de log ou auditoria.

A chave deve ser determinística: se a mesma operação lógica for retentada, a chave permanece a mesma. Uma abordagem comum é usar um UUID ou um hash baseado na sua referência interna:

<CodeGroup>
  ```python Python theme={null}
  import uuid

  # Opção 1: Usar um ID de negócio existente como chave
  idempotency_key = f"order-{order_id}"

  # Opção 2: Gerar um UUID e armazená-lo para retentativas
  idempotency_key = str(uuid.uuid4())
  ```
</CodeGroup>

## Boas práticas

***

### Sempre valide o header `X-Idempotency-Replayed`

Quando seu sistema recebe uma resposta de um endpoint de transação, **sempre verifique o header de resposta `X-Idempotency-Replayed`** antes de processar o resultado. Esse header indica se a resposta é de uma operação nova ou um replay do cache:

* `X-Idempotency-Replayed: false` — Esta é uma resposta nova. A transação acabou de ser processada.
* `X-Idempotency-Replayed: true` — Esta é uma resposta em cache. A transação já foi processada anteriormente.

<Warning>
  Não verificar esse header é um erro de integração comum. Sem ele, seu sistema pode interpretar uma resposta repetida como uma nova transação, levando a processamento duplicado do seu lado — mesmo que o Midaz tenha executado apenas uma vez.
</Warning>

Por exemplo, se seu sistema liquida boletos com base nas respostas de transação, você deve verificar `X-Idempotency-Replayed` para evitar liquidar o mesmo boleto duas vezes.

### Use chaves de idempotência explícitas para fluxos críticos

Embora o Midaz gere chaves automaticamente a partir do corpo da requisição, para fluxos financeiros críticos (liquidações, pagamentos, transferências), sempre forneça uma chave `X-Idempotency` explícita vinculada ao ID do seu processo de negócio. Isso oferece:

* Controle total sobre a deduplicação, mesmo se o corpo da requisição mudar ligeiramente entre retentativas.
* Um rastro de auditoria claro vinculando as transações do Midaz às suas operações internas.
* Proteção contra casos extremos onde a serialização da requisição pode diferir.

### Defina valores de TTL apropriados

Escolha valores de TTL que correspondam à sua janela de retentativa:

* Para operações síncronas com retentativas rápidas: **60–120 segundos**.
* Para fluxos assíncronos com possíveis atrasos: **300–600 segundos**.
* Para processamento em lote com janelas de retentativa longas: considere TTLs maiores e chaves explícitas.

## Estratégia de retentativas

***

Quando uma requisição falha, como você retenta é importante. Aqui estão os padrões recomendados para lidar com diferentes cenários de falha:

### Falhas retentáveis

Estas falhas são seguras para retentar com a mesma chave de idempotência:

| Cenário                                            | O que fazer                                                                                                                    |
| -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Timeout de rede ou erro de conexão                 | Retente com a mesma chave e corpo. Se a requisição original foi processada, você receberá a resposta em cache.                 |
| Erro `5xx` do servidor                             | Retente com backoff exponencial. O servidor pode estar temporariamente sobrecarregado.                                         |
| `409 Conflict` com `X-Idempotency-Replayed: false` | A requisição anterior ainda está sendo processada. Aguarde e retente após uma breve pausa.                                     |
| Erro de validação ou saldo                         | Corrija o problema na sua requisição, depois retente com a mesma chave de idempotência (o Midaz deleta a chave nessas falhas). |

### Falhas não retentáveis

Estas falhas requerem uma abordagem diferente:

| Cenário                              | O que fazer                                                                 |
| ------------------------------------ | --------------------------------------------------------------------------- |
| `400 Bad Request` (erro de esquema)  | Corrija o formato da requisição. Não retente o mesmo payload.               |
| `401 Unauthorized` / `403 Forbidden` | Verifique suas credenciais de autenticação. Retentar não vai ajudar.        |
| `404 Not Found`                      | Verifique os IDs de organização, ledger ou conta no path da sua requisição. |

### Backoff exponencial

Para erros transitórios, use backoff exponencial com jitter para evitar sobrecarregar o servidor:

<CodeGroup>
  ```python Python theme={null}
  import time
  import random
  import requests

  def create_transaction_with_retry(idempotency_key, payload, max_retries=5, base_delay=1.0):
      """Retenta com a mesma chave de idempotência em cada tentativa."""
      for attempt in range(max_retries):
          try:
              response = requests.post(
                  f"{BASE_URL}/v1/organizations/{org_id}/ledgers/{ledger_id}/transactions/json",
                  headers={
                      "X-Idempotency": idempotency_key,
                      "X-TTL": "300",
                      "Content-Type": "application/json",
                  },
                  json=payload,
              )
              response.raise_for_status()
              return response.json()
          except requests.exceptions.RequestException:
              if attempt == max_retries - 1:
                  raise
              delay = base_delay * (2 ** attempt) + random.uniform(0, 1)
              time.sleep(delay)
  ```
</CodeGroup>

Uma sequência típica de retentativas com esse padrão seria: 1s, 2s, 4s, 8s, 16s (mais jitter aleatório em cada tentativa).

## Prevenindo duplicação de entidades

***

Para alguns endpoints, você não precisa de chaves de idempotência para evitar duplicação. O Midaz aplica **restrições de unicidade** em recursos críticos. Se você tentar criar uma entidade que conflite com uma existente, o sistema bloqueia a requisição e retorna um `409 Conflict` com um erro descritivo:

| Recurso      | Campo único                                     | Código de erro |
| ------------ | ----------------------------------------------- | -------------- |
| Ledger       | Nome (dentro da organização)                    | `0002`         |
| Asset        | Nome ou código (dentro do ledger)               | `0003`         |
| Segment      | Nome (dentro do ledger)                         | `0015`         |
| Account      | Alias (dentro da organização e ledger)          | `0020`         |
| Account Type | Valor da chave (dentro da organização e ledger) | `0108`         |

Isso garante que seus dados permaneçam limpos, sem ambiguidade e fáceis de gerenciar, mesmo quando vários serviços estão operando em paralelo ou quando retentativas ocorrem automaticamente. Diferente das chaves de idempotência, essas restrições são permanentes e não expiram.

## Idempotência nos produtos Lerian

***

Múltiplos produtos da Lerian suportam idempotência, cada um com sua própria convenção de headers. A maioria dos produtos retorna o header de resposta `X-Idempotency-Replayed` para indicar se a resposta é um replay do cache. O Midaz sempre inclui este header (`false` para requisições novas, `true` para replays), enquanto os demais serviços só o adicionam quando a resposta é um replay — se o header estiver ausente, a requisição foi processada como nova. O Pix Direto não retorna este header.

| Produto          | Header de requisição                                  | Aceita `X-TTL`    | `X-Idempotency-Replayed` | Escopo                                             | Endpoints cobertos                                                       |
| ---------------- | ----------------------------------------------------- | ----------------- | ------------------------ | -------------------------------------------------- | ------------------------------------------------------------------------ |
| **Midaz**        | `X-Idempotency`                                       | Sim (padrão 300s) | Sempre (`false`/`true`)  | Por organização e ledger                           | Criação, anotação e reversão de transações (6 endpoints)                 |
| **Matcher**      | `X-Idempotency-Key` (também aceita `Idempotency-Key`) | Não               | Apenas em replay         | Por tenant, método e rota                          | Todos os endpoints POST/PUT/PATCH (\~33 endpoints via middleware global) |
| **TED**          | `X-Idempotency-Key`                                   | Não               | Apenas em replay         | Por chave de idempotência (sem scoping por tenant) | Envio, devolução e cancelamento de mensagens (3 endpoints)               |
| **Pix Indireto** | `X-Idempotency`                                       | Sim               | Apenas em replay         | Por conta                                          | Início de cashout, processamento de cashout e reembolso (3 endpoints)    |
| **Pix Direto**   | `Idempotency-Key`                                     | Não               | Não                      | Por chave                                          | Criação de pagamento e início de devolução (2 endpoints)                 |
| **Reporter**     | `X-Idempotency`                                       | Não               | Apenas em replay         | Por chave                                          | Criação de relatórios e templates (2 endpoints)                          |

<Note>
  O Midaz sempre inclui o header `X-Idempotency-Replayed` na resposta (`false` para requisições novas, `true` para replays). Os demais serviços (Matcher, TED, Indirect Pix e Reporter) só adicionam este header quando a resposta é um replay — se o header estiver ausente, a requisição foi processada como nova. O Pix Direto não retorna este header; seu middleware de idempotência faz replay da resposta completa de forma transparente, sem um indicador de replay.
</Note>

<Note>
  Fees Engine, Tracer, Auth e CRM atualmente não suportam headers de idempotência. As validações do **Tracer** são idempotentes por `requestId` — POST `/v1/validations` faz deduplicação pelo campo do body e retorna o resultado em cache com HTTP 200 (HTTP 201 na primeira chamada). As operações de tokens do Auth são inerentemente idempotentes. CRM e entidades de onboarding dependem de [restrições de unicidade](#prevenindo-duplicação-de-entidades) em vez disso.
</Note>

## FAQ

***

<AccordionGroup>
  <Accordion title="O que acontece se eu não enviar uma chave de idempotência?">
    O Midaz gera uma automaticamente calculando um hash SHA-256 do corpo da requisição. Isso significa que corpos de requisição idênticos enviados para a mesma organização e ledger são automaticamente deduplicados. Você só precisa fornecer uma chave personalizada se quiser controlar a deduplicação independentemente do corpo da requisição.
  </Accordion>

  <Accordion title="Posso reutilizar uma chave em diferentes organizações ou ledgers?">
    Sim. As chaves de idempotência são escopadas por organização e ledger, então o mesmo valor de chave usado em diferentes organizações ou ledgers não entrará em conflito. Porém, dentro da mesma organização e ledger, cada chave deve ser única por operação.
  </Accordion>

  <Accordion title="Posso reutilizar uma chave em diferentes endpoints?">
    No Midaz, as chaves de idempotência são escopadas por organização e ledger, não por endpoint. Se você reutilizar a mesma chave em um endpoint diferente dentro da mesma organização e ledger, receberá a resposta em cache do endpoint original. Sempre use chaves únicas para cada operação distinta.
  </Accordion>

  <Accordion title="O que acontece se eu alterar o TTL em uma retentativa?">
    Apenas o TTL da primeira requisição é utilizado. Alterá-lo depois não tem efeito.
  </Accordion>

  <Accordion title="A resposta repetida será sempre idêntica?">
    Sim. O Midaz repete a resposta completa, incluindo código de status (`201 Created`), headers e corpo, para requisições concluídas.
  </Accordion>

  <Accordion title="Qual é o TTL padrão se eu não enviar X-TTL?">
    A janela padrão é de **300 segundos** (5 minutos).
  </Accordion>

  <Accordion title="O que acontece se a requisição original falhar?">
    Se a transação falhar por erros de validação ou saldo insuficiente, o Midaz deleta a chave de idempotência do cache. Isso permite que você corrija o problema e retente com a mesma chave.
  </Accordion>
</AccordionGroup>
