Idempotencia
La API soporta idempotencia para reintentar solicitudes de forma segura, sin ejecutar la misma operación dos veces. Útil cuando una llamada se interrumpe en tránsito y no recibes respuesta — por ejemplo, una solicitud para crear una suscripción que falla por un error de red puede reintentarse con la misma clave, garantizando que solo se cree una suscripción.
Cómo usarlo
Envía la cabecera X-Idempotency-Key: <clave> en cualquier solicitud POST, PUT o PATCH. La clave es opaca y la generas en tu lado. Recomendamos UUID v4 u otra cadena aleatoria con entropía suficiente para evitar colisiones.
curl -i \
-H "Authorization: Bearer $KOBANA_TOKEN" \
-H 'Content-Type: application/json' \
-H 'X-Idempotency-Key: 8c0f5d6e-3f8b-4cb5-9a47-d8f5b15e9b21' \
-H 'User-Agent: Kevin Mitnick <kmitnick@kobana.com.br>' \
-d '{
"subscription": {
"billing_account_id": "ba_01HXY123",
"plan_id": "plan_01HPRO",
"billing_cycle": "monthly"
}
}' \
-X POST 'https://api.billing.sandbox.kobana.com.br/v1/subscriptions'
Cómo funciona
Cuando el servidor recibe una solicitud con X-Idempotency-Key:
- Reserva la clave de forma atómica, en el ámbito de tu organización. Dos organizaciones distintas pueden usar la misma clave sin conflicto.
- Calcula la huella de la solicitud — SHA-256 de
método + ruta + cuerpo en bruto. Ese hash se almacena junto a la clave. - Ejecuta el handler. Si devuelve una respuesta, el servidor registra
status_code, cuerpo y cabeceras relevantes vinculados a la clave. - Marca la clave como completada. Cualquier nueva solicitud que reutilice la clave dentro de 24 horas recibe la respuesta original verbatim — mismo
status_code, mismo cuerpo, mismas cabeceras — más la cabeceraX-Idempotent-Replay: trueque indica que se trata de una reejecución.
La capa guarda el resultado independientemente de éxito o fracaso. Los reintentos con la misma clave devuelven el mismo resultado, incluyendo errores 5xx.
Límites
- Longitud máxima de la clave: 255 caracteres.
- Retención: las claves expiran automáticamente 24 horas después de su creación. Tras expirar, una nueva solicitud con la misma clave genera un resultado nuevo.
- Ámbito: por organización. Una clave usada en una organización no interfiere con otra.
- Caracteres aceptados: cualquier cadena UTF-8 imprimible.
Cuándo el resultado no se guarda
La capa solo guarda el resultado cuando el handler ha comenzado a ejecutarse. No se registra nada en los siguientes casos — es seguro reenviar la solicitud:
- La clave está vacía o supera los 255 caracteres → respuesta
400 bad_request. - El parser del envoltorio de la API rechaza la solicitud antes del handler (JSON inválido, autenticación ausente, etc.).
- El handler lanza una excepción durante la ejecución — la reserva se libera para que el siguiente reintento pueda correr limpio.
- Otra solicitud con la misma clave está ejecutándose en paralelo → respuesta
409 conflictcondetails.reason = "idempotency_request_in_progress".
Conflictos
Si la misma clave se reutiliza con una solicitud diferente (cualquier cambio en método, ruta o cuerpo), la API responde 409 conflict con details.reason = "idempotency_key_reused". Esto protege contra reuso accidental — por ejemplo, persistir la clave junto a la operación en tu lado y luego intentar crear dos recursos diferentes con el mismo identificador.
Respuesta de error
{
"error": {
"code": "conflict",
"message": "X-Idempotency-Key was already used with different request parameters. Reuse the original parameters or generate a new key.",
"details": { "reason": "idempotency_key_reused" }
}
}
Otras respuestas posibles
| Escenario | Status | error.code | details.reason |
|---|---|---|---|
| Otra solicitud con la misma clave en ejecución | 409 | conflict | idempotency_request_in_progress |
| Misma clave reutilizada con un cuerpo distinto | 409 | conflict | idempotency_key_reused |
| Clave vacía o mayor que 255 caracteres | 400 | bad_request | invalid_idempotency_key |
Métodos aceptados
| Método | ¿Acepta X-Idempotency-Key? |
|---|---|
POST | ✅ Sí |
PUT | ✅ Sí |
PATCH | ✅ Sí |
GET | ❌ No (ya es idempotente por definición) |
DELETE | ❌ No (ya es idempotente por definición) |
Enviar la clave en GET o DELETE no tiene efecto — la API procesa la solicitud normalmente sin registrar nada.
Identificando una reejecución
Toda respuesta servida desde el registro lleva la cabecera:
X-Idempotent-Replay: true
Puedes usar esta cabecera en tu lado para distinguir la primera ejecución de una reejecución hecha por el servidor.
Buenas prácticas
- Una clave por operación lógica. No reutilices la misma clave para crear dos recursos diferentes — recibirás
409 conflict. - Persiste la clave junto al estado de la operación en tu lado, para que el reintento tras un crash o reinicio use la misma clave.
- Combínalo con backoff exponencial ante
5xxo timeout — la clave garantiza que el reintento no duplica el recurso. Como los fallos también se registran, puedes detener los reintentos en cuanto llegue el status code esperado. - Descarta la clave a las 24 horas. No tiene sentido mantenerla en tu lado después: el servidor ya ha descartado el registro.
- No envíes la clave en métodos que no la aceptan. En
GET/DELETEse ignora silenciosamente — preferimos que el cliente la omita para dejar claro qué es idempotente por contrato y qué depende de la clave.