> ## 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.

# Reintentos e idempotencia

> Las claves de idempotencia evitan operaciones duplicadas en los endpoints de transacciones de Midaz al reintentar fallos transitorios.

Un aspecto clave de la confiabilidad de Midaz es asegurar que las operaciones sean **seguras de reintentar** y nunca se procesen más de una vez.

Ya sea que estés manejando transacciones o creando entidades, tu integración debe ser resiliente, incluso cuando enfrenta problemas de red, tiempos de espera o interrupciones temporales. Esta página explica cómo Midaz protege contra la duplicación usando **claves de idempotencia**, y cómo puedes implementarlas para manejar reintentos en tu sistema con confianza.

### Hacer que los reintentos sean seguros

En sistemas del mundo real, las solicitudes de API fallidas son comunes. Tal vez ocurra un tiempo de espera de red. Tal vez tu servicio se caiga justo después de enviar una solicitud. En estos casos, es natural reintentar, pero ¿cómo puedes estar seguro de que Midaz no procesará la misma operación dos veces?

Ahí es donde entran las **claves de idempotencia**.

Al adjuntar una clave única a cada solicitud, le estás diciendo a Midaz: "Esta es la misma operación. Si ya la has procesado, no la hagas de nuevo."

Midaz almacena esta clave temporalmente y la usa para determinar si la solicitud es nueva, ya completada o todavía en proceso. Esto protege tu sistema de duplicados mientras te da control total sobre tu estrategia de reintentos.

## Idempotencia en Midaz

***

Midaz usa **claves de idempotencia** para asegurar que las operaciones de transacción sean seguras de reintentar y nunca se procesen más de una vez. Este mecanismo está disponible en todos los **endpoints de transacciones**: `/transactions/json`, `/transactions/dsl`, `/transactions/inflow`, `/transactions/outflow`, `/transactions/annotation` y `/transactions/{id}/revert`.

<Note>
  Otros productos de Lerian también soportan idempotencia a través de sus propios encabezados — consulta la sección [Idempotencia en los productos Lerian](#idempotencia-en-los-productos-lerian) a continuación. Esta página se enfoca en la idempotencia para la API del Midaz Ledger.
</Note>

<Note>
  Los endpoints `commit` y `cancel` de transacciones usan un bloqueo basado en Redis para prevenir el procesamiento concurrente de la misma transacción, pero no soportan idempotencia completa (sin respuestas en caché ni encabezado `X-Idempotency-Replayed`).
</Note>

Para usarlo, tu solicitud puede incluir dos encabezados:

* `X-Idempotency`: la clave única que identifica la solicitud.
* `X-TTL`: el tiempo de vida (en segundos) que Midaz debe almacenar esta clave en caché.

<Note>
  Si no envías el encabezado `X-Idempotency`, Midaz genera uno automáticamente calculando un hash SHA-256 del cuerpo de la solicitud. Esto significa que cuerpos de solicitud idénticos enviados a la misma organización y ledger se deduplicarán automáticamente.
</Note>

Esto es lo que sucede detrás de escena:

1. Cuando llega una nueva clave, Midaz la marca como `pending`, procesa la solicitud y almacena la respuesta completa en caché.
2. Si la misma clave se usa nuevamente dentro de la ventana TTL:
   1. Si la operación todavía se está ejecutando, Midaz devuelve un `409 Conflict` (código de error `0084`) con `X-Idempotency-Replayed: false`.
   2. Si está terminada, Midaz devuelve exactamente la misma respuesta con un código de estado `201 Created` y `X-Idempotency-Replayed: true`.
3. Si la transacción falla por errores de validación o saldo insuficiente, Midaz **elimina la clave de idempotencia**, permitiéndote reintentar con la misma clave después de corregir el problema.

En Midaz, las claves de idempotencia están delimitadas por **organización y ledger**. Esto significa que el mismo valor de clave se puede usar independientemente en diferentes organizaciones o ledgers sin conflicto. Otros productos de Lerian utilizan diferentes estrategias de alcance — consulta la [tabla comparativa](#idempotencia-en-los-productos-lerian) para más detalles.

### Resumen del flujo de trabajo

La *Figura 1* muestra el ciclo de vida completo de una solicitud idempotente:

<Frame caption="Figura 1. Ciclo de vida de solicitud idempotente en Midaz.">
  <img src="https://mintcdn.com/lerian-49cb71fc/SEOef3JqTInYAAau/images/es/d2/idempotency.svg?fit=max&auto=format&n=SEOef3JqTInYAAau&q=85&s=5fc5213eec66e31b5bb7f07de13adf80" alt="Ciclo de vida completo de una solicitud idempotente en Midaz, desde la recepción de la clave de idempotencia hasta la devolución de la respuesta en caché o recién procesada" width="1112" height="1454" data-path="images/es/d2/idempotency.svg" />
</Frame>

**Cómo funciona:**

* Si la solicitud **no incluye una clave de idempotencia existente**, Midaz crea una nueva (o genera una automáticamente a partir del hash del cuerpo de la solicitud), procesa la solicitud, almacena la respuesta y la devuelve con `X-Idempotency-Replayed: false`.
* Si la clave **ya existe**:
  * Si la operación todavía se está ejecutando, Midaz devuelve un `409 Conflict` con `X-Idempotency-Replayed: false`.
  * Si la operación está completa, Midaz omite la ejecución y devuelve la respuesta en caché con un código de estado `201 Created` y `X-Idempotency-Replayed: true`.
* Si la solicitud original **falló por errores de validación o saldo**, la clave se limpia automáticamente, para que puedas reintentar con la misma clave de forma segura.

Este comportamiento garantiza que cada solicitud se maneje de manera segura, predecible y sin duplicación, incluso cuando se reintenta.

### Ejemplo de solicitud

Aquí se muestra cómo enviar una solicitud idempotente para crear una transacción:

<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": "Pago de suscripción mensual",
    "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>

Si la solicitud tiene éxito y la envías nuevamente dentro de 60 segundos, Midaz devolverá el resultado en caché con:

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

Si la envías nuevamente mientras la original todavía se está procesando, recibirás:

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

## Generación de claves

***

Puedes proporcionar tu propia clave `X-Idempotency` o dejar que Midaz genere una automáticamente.

### Generación automática de claves

Si omites el encabezado `X-Idempotency`, Midaz calcula un hash SHA-256 del cuerpo de la solicitud y lo usa como clave de idempotencia. Esto significa que enviar el mismo cuerpo JSON exacto a la misma organización y ledger se deduplicará automáticamente, sin trabajo adicional.

Esto es suficiente para la mayoría de escenarios de reintento donde el cuerpo de la solicitud no cambia entre intentos.

### Generación personalizada de claves

Usa una clave personalizada cuando necesites:

* Correlacionar la clave de idempotencia con un ID en tu propio sistema (por ejemplo, un ID de pedido).
* Reintentar con la misma clave después de corregir un payload que falló validación o saldo, ya que Midaz elimina la clave en esos casos.
* Mantener la misma clave cuando el payload sea semánticamente el mismo, aunque su serialización cambie entre intentos.

La clave debe ser determinística: si se reintenta la misma operación lógica, la clave permanece igual. Un enfoque común es usar un UUID o un hash basado en tu referencia interna:

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

  # Opción 1: Usar un ID de negocio existente como clave
  idempotency_key = f"order-{order_id}"

  # Opción 2: Generar un UUID y almacenarlo para reintentos
  idempotency_key = str(uuid.uuid4())
  ```
</CodeGroup>

## Mejores prácticas

***

### Siempre valida el header `X-Idempotency-Replayed`

Cuando tu sistema recibe una respuesta de un endpoint de transacción, **siempre verifica el header de respuesta `X-Idempotency-Replayed`** antes de procesar el resultado. Este header te indica si la respuesta es de una operación nueva o una reproducción en caché:

* `X-Idempotency-Replayed: false` — Esta es una respuesta nueva. La transacción acaba de procesarse.
* `X-Idempotency-Replayed: true` — Esta es una respuesta en caché. La transacción ya fue procesada anteriormente.

<Warning>
  No verificar este header es un error de integración común. Sin él, tu sistema puede interpretar una respuesta reproducida como una nueva transacción, lo que lleva a procesamiento duplicado de tu lado — aunque Midaz solo la ejecutó una vez.
</Warning>

Por ejemplo, si tu sistema liquida boletos bancarios basándose en las respuestas de transacción, debe verificar `X-Idempotency-Replayed` para evitar liquidar el mismo boleto dos veces.

### Usa claves de idempotencia explícitas para flujos críticos

Si bien Midaz genera claves automáticamente a partir del cuerpo de la solicitud, para flujos financieros críticos (liquidaciones, pagos, transferencias), siempre proporciona una clave `X-Idempotency` explícita vinculada al ID de tu proceso de negocio. Esto te brinda:

* Control total sobre la deduplicación, incluso si el cuerpo de la solicitud cambia ligeramente entre reintentos.
* Un rastro de auditoría claro que vincula las transacciones de Midaz con tus operaciones internas.
* Protección contra casos extremos donde la serialización de la solicitud podría diferir.

### Establece valores TTL apropiados

Elige valores TTL que coincidan con tu ventana de reintentos:

* Para operaciones sincrónicas con reintentos rápidos: **60–120 segundos**.
* Para flujos de trabajo asíncronos con posibles retrasos: **300–600 segundos**.
* Para procesamiento por lotes con ventanas de reintento largas: considera TTLs más largos y claves explícitas.

## Estrategia de reintentos

***

Cuando una solicitud falla, cómo reintentas es importante. Aquí están los patrones recomendados para manejar diferentes escenarios de fallo:

### Fallos reintentables

Estos fallos son seguros de reintentar con la misma clave de idempotencia:

| Escenario                                          | Qué hacer                                                                                                                         |
| -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| Timeout de red o error de conexión                 | Reintenta con la misma clave y cuerpo. Si la solicitud original fue procesada, obtendrás la respuesta en caché.                   |
| Error `5xx` del servidor                           | Reintenta con retroceso exponencial. El servidor puede estar temporalmente sobrecargado.                                          |
| `409 Conflict` con `X-Idempotency-Replayed: false` | La solicitud anterior todavía se está procesando. Espera y reintenta después de una breve pausa.                                  |
| Error de validación o saldo                        | Corrige el problema en tu solicitud, luego reintenta con la misma clave de idempotencia (Midaz elimina la clave en estos fallos). |

### Fallos no reintentables

Estos fallos requieren un enfoque diferente:

| Escenario                            | Qué hacer                                                                     |
| ------------------------------------ | ----------------------------------------------------------------------------- |
| `400 Bad Request` (error de esquema) | Corrige el formato de la solicitud. No reintentes el mismo payload.           |
| `401 Unauthorized` / `403 Forbidden` | Verifica tus credenciales de autenticación. Reintentar no ayudará.            |
| `404 Not Found`                      | Verifica los IDs de organización, ledger o cuenta en la ruta de tu solicitud. |

### Retroceso exponencial

Para errores transitorios, usa retroceso exponencial con jitter para evitar sobrecargar el 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):
      """Reintenta con la misma clave de idempotencia en cada intento."""
      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>

Una secuencia típica de reintentos con este patrón sería: 1s, 2s, 4s, 8s, 16s (más jitter aleatorio en cada intento).

## Prevención de duplicación de entidades

***

Para algunos endpoints, no necesitas claves de idempotencia para evitar la duplicación. Midaz aplica **restricciones de unicidad** en recursos críticos. Si intentas crear una entidad que entra en conflicto con una existente, el sistema bloquea la solicitud y devuelve un `409 Conflict` con un error descriptivo:

| Recurso      | Campo único                                         | Código de error |
| ------------ | --------------------------------------------------- | --------------- |
| Ledger       | Nombre (dentro de la organización)                  | `0002`          |
| Asset        | Nombre o código (dentro del ledger)                 | `0003`          |
| Segment      | Nombre (dentro del ledger)                          | `0015`          |
| Account      | Alias (dentro de la organización y ledger)          | `0020`          |
| Account Type | Valor de clave (dentro de la organización y ledger) | `0108`          |

Esto garantiza que tus datos permanezcan limpios, sin ambigüedades y fáciles de gestionar, incluso cuando múltiples servicios están operando en paralelo o cuando los reintentos ocurren automáticamente. A diferencia de las claves de idempotencia, estas restricciones son permanentes y no expiran.

## Idempotencia en los productos Lerian

***

Múltiples productos de Lerian soportan idempotencia, cada uno con su propia convención de headers. La mayoría de los productos retornan el header de respuesta `X-Idempotency-Replayed` para indicar si la respuesta es un replay del caché. Midaz siempre incluye este header (`false` para solicitudes nuevas, `true` para replays), mientras que los demás servicios solo lo agregan cuando la respuesta es un replay — si el header está ausente, la solicitud fue procesada como nueva. Pix Directo no retorna este header.

| Producto          | Encabezado de solicitud                                | Acepta `X-TTL`           | `X-Idempotency-Replayed` | Alcance                                            | Endpoints cubiertos                                                       |
| ----------------- | ------------------------------------------------------ | ------------------------ | ------------------------ | -------------------------------------------------- | ------------------------------------------------------------------------- |
| **Midaz**         | `X-Idempotency`                                        | Sí (predeterminado 300s) | Siempre (`false`/`true`) | Por organización y ledger                          | Creación, anotación y reversión de transacciones (6 endpoints)            |
| **Matcher**       | `X-Idempotency-Key` (también acepta `Idempotency-Key`) | No                       | Solo en replay           | Por tenant, método y ruta                          | Todos los endpoints POST/PUT/PATCH (\~33 endpoints vía middleware global) |
| **TED**           | `X-Idempotency-Key`                                    | No                       | Solo en replay           | Por clave de idempotencia (sin scoping por tenant) | Envío, devolución y cancelación de mensajes (3 endpoints)                 |
| **Pix Indirecto** | `X-Idempotency`                                        | Sí                       | Solo en replay           | Por cuenta                                         | Inicio de cashout, proceso de cashout y reembolso (3 endpoints)           |
| **Pix Directo**   | `Idempotency-Key`                                      | No                       | No                       | Por clave                                          | Creación de pago e inicio de devolución (2 endpoints)                     |
| **Reporter**      | `X-Idempotency`                                        | No                       | Solo en replay           | Por clave                                          | Creación de reportes y plantillas (2 endpoints)                           |

<Note>
  Midaz siempre incluye el header `X-Idempotency-Replayed` en la respuesta (`false` para solicitudes nuevas, `true` para replays). Los demás servicios (Matcher, TED, Pix Indirecto y Reporter) solo agregan este header cuando la respuesta es un replay — si el header está ausente, la solicitud fue procesada como nueva. PIX Direct no retorna este header; su middleware de idempotencia hace replay de la respuesta completa de forma transparente sin un indicador de replay.
</Note>

<Note>
  Fees Engine, Tracer, Auth y CRM actualmente no soportan encabezados de idempotencia. Las validaciones de **Tracer** son idempotentes por `requestId` — POST `/v1/validations` deduplica por el campo del body y devuelve el resultado en caché con HTTP 200 (HTTP 201 en la primera llamada). Las operaciones de tokens de Auth son inherentemente idempotentes. CRM y las entidades de onboarding dependen de [restricciones de unicidad](#prevención-de-duplicación-de-entidades) en su lugar.
</Note>

## Preguntas frecuentes

***

<AccordionGroup>
  <Accordion title="¿Qué sucede si no envío una clave de idempotencia?">
    Midaz genera una automáticamente calculando un hash SHA-256 del cuerpo de la solicitud. Esto significa que cuerpos de solicitud idénticos enviados a la misma organización y ledger se deduplicarán automáticamente. Solo necesitas proporcionar una clave personalizada si deseas controlar la deduplicación independientemente del cuerpo de la solicitud.
  </Accordion>

  <Accordion title="¿Puedo reutilizar una clave en diferentes organizaciones o ledgers?">
    Sí. Las claves de idempotencia están delimitadas por organización y ledger, por lo que el mismo valor de clave usado en diferentes organizaciones o ledgers no entrará en conflicto. Sin embargo, dentro de la misma organización y ledger, cada clave debe ser única por operación.
  </Accordion>

  <Accordion title="¿Puedo reutilizar una clave en diferentes endpoints?">
    En Midaz, las claves de idempotencia están delimitadas por organización y ledger, no por endpoint. Si reutilizas la misma clave en un endpoint diferente dentro de la misma organización y ledger, recibirás la respuesta en caché del endpoint original. Siempre usa claves únicas para cada operación distinta.
  </Accordion>

  <Accordion title="¿Qué sucede si cambio el TTL en un reintento?">
    Solo se usa el TTL de la primera solicitud. Cambiarlo más tarde no tiene efecto.
  </Accordion>

  <Accordion title="¿La respuesta reproducida siempre será idéntica?">
    Sí. Midaz reproduce la respuesta completa, incluyendo código de estado (`201 Created`), encabezados y cuerpo, para solicitudes completadas.
  </Accordion>

  <Accordion title="¿Cuál es el TTL predeterminado si no envío X-TTL?">
    La ventana predeterminada es de **300 segundos** (5 minutos).
  </Accordion>

  <Accordion title="¿Qué sucede si la solicitud original falla?">
    Si la transacción falla por errores de validación o saldo insuficiente, Midaz elimina la clave de idempotencia del caché. Esto te permite corregir el problema y reintentar con la misma clave.
  </Accordion>
</AccordionGroup>
