Ir al contenido

Idempotency-Key

Todo método no seguro (POST, PUT, PATCH, DELETE) en las operaciones de /api/v1 con contexto de organización — es decir, las que se ejecutan dentro de una organización activa, como proyectos, defectos, casos de prueba, ejecuciones y planes — acepta una cabecera OPCIONAL Idempotency-Key. Envía la misma clave en un reintento y Probara reproduce la respuesta original en lugar de volver a ejecutar la mutación — útil para reintentos de red, timeouts del cliente y esos dobles clics donde no sabes si la petición llegó a enviarse. Las peticiones GET/HEAD ignoran la cabecera por completo.

Los endpoints de autenticación (/api/v1/auth/*), perfil de usuario (/api/v1/profile, /api/v1/profile/avatar) y subida de avatar de proyecto (/api/v1/projects/{projectId}/avatar) no admiten esta cabecera — se ignora en silencio.

Las claves están limitadas por organización y por el miembro que actúa: el mismo valor de clave usado por otra organización, o por otro miembro dentro de la misma organización, se trata como una clave independiente y nunca reproduce una respuesta entre esos límites.

  • No vacía
  • No mayor de 255 caracteres ASCII visibles (sin espacios en blanco, caracteres de control ni puntos de código fuera de ASCII)

Una clave inválida responde 400 y el handler nunca se ejecuta.

Ventana de terminal
# Primer intento — se ejecuta con normalidad y la respuesta queda registrada bajo la clave.
curl -sS -X POST \
-H "Authorization: Bearer $PROBARA_API_TOKEN" \
-H "Idempotency-Key: 8f14e45f-ceea-4a3c-b9a8-1a6b3d2f0e91" \
-H "Content-Type: application/json" \
-d '{"name":"Flujo de checkout"}' \
"https://probara.net/api/v1/projects"
HTTP/1.1 201 Created
Content-Type: application/json
{ "ulid": "01J...PROJ", "name": "Flujo de checkout", ... }

Reintentar la petición idéntica con la misma clave y el mismo cuerpo dentro de la ventana de retención devuelve el mismo estado y cuerpo — sigue siendo 01J...PROJ, sin un segundo proyecto creado — con una cabecera adicional en la respuesta:

HTTP/1.1 201 Created
Idempotency-Replayed: true
Content-Type: application/json
{ "ulid": "01J...PROJ", "name": "Flujo de checkout", ... }
SituaciónRespuesta
Primera petición con una clave nunca vistaSe ejecuta con normalidad; la respuesta se registra para reproducirla (JSON, estado < 500, sin ser 204 ni binaria)
Reintento idéntico dentro de la ventana de retenciónMismo estado y cuerpo que el original, más Idempotency-Replayed: true
Llega un duplicado mientras la primera petición sigue en curso409 con el envoltorio de error estándar (code: "conflict") y una cabecera Retry-After
Se reutiliza la misma clave con un método, ruta o cuerpo distinto422 con code: "validation_failed"; no se ejecuta nada
Clave inválida (vacía o de más de 255 caracteres ASCII visibles)400; no se ejecuta nada
La primera petición fallaSe libera la reserva de la clave; un reintento posterior con la misma clave se ejecuta con normalidad
La respuesta es 204, no es JSON o es binariaPasa sin registrarse — un reintento con la misma clave vuelve a ejecutar el handler

Los registros de clave se conservan durante 24 horas. Pasada esa ventana, la misma clave se comporta como una clave nueva, nunca vista: la petición idéntica vuelve a ejecutar el handler y Idempotency-Replayed no aparece en esa respuesta.

EstadoCódigoSignificado
400validation_failedIdempotency-Key está vacía o supera los 255 caracteres ASCII visibles
409conflictHay una petición en curso con la misma clave; incluye una cabecera Retry-After
422validation_failedLa clave ya se usó con un método, ruta o cuerpo de petición distinto