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

# Idempotencia

> Cómo reintentar una escritura sin crear el recurso dos veces.

Una integración reintenta: se cae la red, expira un timeout, el proceso se reinicia a mitad de una sincronización. Sin idempotencia, cada reintento crea otro estudiante.

Por eso los `POST` de creación del Client API **exigen** la cabecera `Idempotency-Key`.

```http theme={null}
POST /api/client/v1/students
Authorization: Bearer {token}
Idempotency-Key: sis-student-1001-create-2026-05-09
Content-Type: application/json
```

Sin la cabecera, la respuesta es `400 IDEMPOTENCY_KEY_REQUIRED`. La clave admite hasta **160 caracteres**.

## Cómo se comporta

EdTools guarda, por API key y por clave, un hash estable del cuerpo junto con el método, el path y la respuesta que dio.

<CardGroup cols={2}>
  <Card title="Misma clave, misma petición" icon="rotate-right">
    Se devuelve **la respuesta original**, con su mismo código de estado. No se crea nada nuevo. Reintenta tranquilo.
  </Card>

  <Card title="Misma clave, petición distinta" icon="triangle-exclamation">
    `409 IDEMPOTENCY_CONFLICT`. Cambió el cuerpo, el método o el path: EdTools se niega a adivinar cuál de las dos querías.
  </Card>
</CardGroup>

<Warning>
  El alcance de la clave es **por API key**. Dos tokens distintos pueden usar la misma cadena sin pisarse — y un token rotado empieza con la memoria en blanco.
</Warning>

## Los seis endpoints que la exigen

| Endpoint                                         | Crea                                   |
| ------------------------------------------------ | -------------------------------------- |
| `POST /v1/students`                              | Un estudiante                          |
| `POST /v1/admissions/applications`               | Una postulación                        |
| `POST /v1/admissions/applications/{id}/contacts` | Un contacto vinculado a la postulación |
| `POST /v1/payments`                              | Un pago                                |
| `POST /v1/vault/documents/upload-intents`        | Una intención de carga de documento    |
| `POST /v1/vault/requests`                        | Una solicitud de documentos            |

Los demás `POST` del Vault (`upload-url`, `complete`, `links`) no la piden: operan sobre un recurso que ya existe y son seguros de repetir por su propia forma.

## Cómo elegir la clave

Que sea **determinista por operación de negocio**, no aleatoria. Si la generas con un UUID nuevo en cada intento, no tienes idempotencia: tienes duplicados con nombres distintos.

```text theme={null}
sis-student-1001-create-2026-05-09     ✅ derivada del id externo y la operación
pago-factura-A-00412                   ✅ derivada del documento de origen
550e8400-e29b-41d4-a716-446655440000   ❌ nueva en cada reintento
crear                                  ❌ colisiona con todo
```

<Warning>
  No reutilices una clave para otro endpoint, método o cuerpo. Eso no es un atajo: es el `409`.
</Warning>

## Cuando tienes un identificador estable, mejor el upsert

Si el sistema externo ya tiene su propio id, no crees: sincroniza. Los `PUT /external/{externalId}` son idempotentes por naturaleza — el identificador *es* la clave.

| Endpoint                                                | Efecto                                            |
| ------------------------------------------------------- | ------------------------------------------------- |
| `PUT /v1/students/external/{externalId}`                | Crea o actualiza el estudiante con ese id externo |
| `PUT /v1/admissions/applications/external/{externalId}` | Crea o actualiza la postulación                   |
| `PUT /v1/payments/external/{externalId}`                | Crea o actualiza el pago                          |

<Tip>
  Esta es la forma recomendada de sincronizar desde un SIS. Te evita llevar un mapa de ids de EdTools, sobrevive a reintentos sin bookkeeping, y hace que volver a correr la sincronización completa sea inofensivo.
</Tip>
