# Idempotencia (/es/guides/idempotency)

Reintenta una solicitud y habrás enviado el mismo correo dos veces. Deja que un formulario de checkout se envíe dos veces y el cliente recibe dos recibos. La solución es el encabezado `Idempotency-Key`, que garantiza que una solicitud se ejecuta **como máximo una vez**.

Cuando Bitelio ve una clave por primera vez, la solicitud sigue su curso normal. Cuando la misma clave vuelve a aparecer dentro de tu proyecto, la solicitud es **rechazada** con un `409` en lugar de ejecutarse una segunda vez.

El encabezado es compatible en ambos endpoints de escritura de la API pública:

* `POST /v1/track`
* `POST /v1/send`

## Enviar una clave

Cualquier cadena de 1 a 255 caracteres ASCII imprimibles funciona como clave. Lo que importa es que cada operación lógica tenga la suya propia — ya sea un UUID, o algo que derives de tus propios registros, como `receipt-order-1234`.

import {Tab, Tabs} from 'fumadocs-ui/components/tabs';

<Tabs items={['cURL', 'JavaScript', 'Python']}>
  <Tab value="cURL">
    ```bash
    curl -X POST https://api.bitelio.com/v1/send \
      -H "Authorization: Bearer sk_your_secret_key" \
      -H "Idempotency-Key: receipt-order-1234" \
      -H "Content-Type: application/json" \
      -d '{
        "to": "customer@example.com",
        "subject": "Your receipt",
        "body": "<p>Thanks for your order!</p>"
      }'
    ```
  </Tab>

  <Tab value="JavaScript">
    ```javascript
    await fetch('https://api.bitelio.com/v1/send', {
      method: 'POST',
      headers: {
        Authorization: 'Bearer sk_your_secret_key',
        'Idempotency-Key': 'receipt-order-1234',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        to: 'customer@example.com',
        subject: 'Your receipt',
        body: '<p>Thanks for your order!</p>',
      }),
    });
    ```
  </Tab>

  <Tab value="Python">
    ```python
    import requests

    requests.post(
        "https://api.bitelio.com/v1/send",
        headers={
            "Authorization": "Bearer sk_your_secret_key",
            "Idempotency-Key": "receipt-order-1234",
        },
        json={
            "to": "customer@example.com",
            "subject": "Your receipt",
            "body": "<p>Thanks for your order!</p>",
        },
    )
    ```
  </Tab>
</Tabs>

## Qué ocurre en una reutilización

Una clave repetida vuelve como un `409` con el código de error `IDEMPOTENCY_KEY_REUSED`. Dentro de `details` encontrarás información sobre la solicitud que reclamó la clave primero:

```json
{
  "success": false,
  "error": {
    "code": "IDEMPOTENCY_KEY_REUSED",
    "message": "Idempotency-Key \"receipt-order-1234\" has already been used",
    "statusCode": 409,
    "details": {
      "key": "receipt-order-1234",
      "originalRequest": "POST /v1/send",
      "originalRequestAt": "2025-01-15T10:30:00.000Z",
      "originalStatusCode": 200
    }
  }
}
```

Si `originalStatusCode` es `null`, la primera solicitud aún no ha terminado — ambas solicitudes con la misma clave llegaron simultáneamente y esta perdió la carrera.

<Callout type="warn">
  Ante una clave reutilizada, Bitelio **rechaza** — no reproduce la primera respuesta. El `409` garantiza que nada se ejecutó dos veces, pero no te entregará el correo o el ID de evento original. Captura ese ID tú mismo cuando la primera solicitud tenga éxito, si lo vas a necesitar después.
</Callout>

## Qué fallos liberan la clave

Una solicitud fallida no siempre consume la clave.

| Resultado de la primera solicitud | La clave queda | Por qué                                                                                                                                                           |
| --------------------------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Éxito `2xx`                       | **Retenida**   | La operación ocurrió. Un reintento la duplicaría.                                                                                                                 |
| Error de cliente `4xx`            | **Liberada**   | Los fallos de validación y permisos se rechazan antes de que ocurra cualquier escritura, así que corregir la solicitud y reintentar con la misma clave es seguro. |
| Error de servidor `5xx`           | **Retenida**   | Parte del trabajo puede haber ocurrido ya. Bloquear el reintento es exactamente para lo que existe la clave.                                                      |

Si un `5xx` consumió una clave pero tienes la certeza de que la operación nunca tuvo efecto, reintenta con una clave nueva.

## Alcance y expiración

Una clave pertenece a tu **proyecto** en su conjunto, no a un único endpoint — una vez gastada en `/v1/track`, no puede reproducirse contra `/v1/send`. Proyectos separados, en cambio, pueden usar cada uno la misma cadena de clave sin interferir entre sí.

Las claves reclamadas expiran después de **24 horas** y pueden volver a usarse a partir de ese momento.

## Múltiples destinatarios

Cuando pasas un arreglo de destinatarios a `POST /v1/send`, se procesan secuencialmente — pero la clave de idempotencia protege la solicitud como una unidad, no destinatario por destinatario. Si la solicitud muere a mitad de camino con un `5xx`, algunos destinatarios pueden ya haber recibido su correo; la clave permanece reclamada exactamente por esa razón, así que un reintento ingenuo no puede alcanzarlos dos veces.

Si necesitas control a nivel de destinatario, emite una solicitud por destinatario, cada una con su propia clave.
