Ir al contenido
En producción, en modo shadow: FingerTrackID recomienda, su sistema decide.Estado →

Idempotencia y reintento

Hay dos niveles de identidad, y no se mezclan:

  • Operación lógica: idempotency_key + external_event_id, en el cuerpo. Se mantienen iguales en cada reintento y nunca se reutilizan para otra operación, ni siquiera después de que los datos caduquen.
  • Intento de envío: X-FTID-Request-Id, X-FTID-Timestamp y la firma. Cambian en cada intento.
Situación Respuesta
Clave nueva 201, con la evaluación recién almacenada
Misma clave, mismo contenido, resultado aún dentro de su ventana de historial aprobada 200, con exactamente la evaluación ya almacenada, sin volver a calcularla, incluso si el occurred_at original ya está fuera de la ventana de ±300 s
Misma clave, mismo contenido, resultado fuera de su ventana de historial aprobada 410 assessment-result-expired, sin resultado histórico ni identificador de evaluación; sin recálculo ni uso nuevo
Mismo idempotency_key o mismo external_event_id con contenido distinto 409 idempotency-conflict, sin efecto parcial
Referencias de persona, cuenta o sesión incompatibles con vínculos ya almacenados, por ejemplo un account_ref enviado por primera vez con otro subject_ref 409 binding-conflict, sin almacenar nada; no reintentable

“Contenido” significa los diez campos del cuerpo distintos de deadline_ms. Cambiar solo deadline_ms mantiene la misma operación. Cambiar cualquier otro campo genera un conflicto, incluido reemplazar null por un token, cambiar el token o reescribir occurred_at con otra grafía (Z y +00:00 son grafías distintas).

El event_context_token es de un solo uso y se consume junto con la evaluación. Un reintento de la misma operación no lo vuelve a consumir; una operación nueva con un token ya usado recibe 400 event-context-invalid.

409 · misma clave, contenido distinto
{
"type": "https://fingertrackid.dev/problems/idempotency-conflict",
"title": "Chave de idempotência já usada com outro conteúdo",
"status": 409,
"correlation_id": "corr-problem-idempotency-0001",
"retryable": false
}
Resultado ¿Reintentar? Cómo
201 o 200 No Use la evaluación
401 timestamp-out-of-window Sí Corrija el reloj; nuevo intento
401 request-replayed Sí Nuevo intento, y corrija el código que reutilizó el request_id
429 rate-limited Sí Espere lo indicado en Retry-After (hoy 1 s; al menos 1 s si falta); nuevo intento
503 assessment-unavailable Sí Espere lo indicado en Retry-After (hoy 1 s; al menos 1 s si falta); nuevo intento. El estado puede ser indeterminado
410 assessment-result-expired No El resultado antiguo ya no está disponible. Detenga los reintentos de conciliación; conserve los IDs de operación originales y no regenere la operación
Error de red, tiempo de espera agotado, respuesta truncada o respuesta de más de 65536 bytes Sí Trátelo como indeterminado; nuevo intento con el mismo cuerpo
Cualquier otro 4xx, o 500 No Corrija la causa (vea errores)

Un nuevo intento es siempre: el mismo cuerpo, un request_id nuevo, la marca de tiempo actual y una firma nueva. Esto también vale para 429 y 503: el request_id del intento respondido puede ya estar consumido. Reintente un número acotado de veces, nunca en bucle.

Una operación, dos intentos
Attempt 1 same body bytes
X-FTID-Request-Id: <new UUIDv7>
X-FTID-Timestamp: <now>
X-FTID-Signature: <new>
→ 503 assessment-unavailable, Retry-After: 1
wait at least Retry-After
Attempt 2 the same body bytes
X-FTID-Request-Id: <another new UUIDv7>
X-FTID-Timestamp: <now>
X-FTID-Signature: <new>
→ 200 with the assessment already stored,
or 201 if the first attempt stored nothing

Tras un tiempo de espera agotado, la evaluación puede haberse almacenado sin que la respuesta le llegara. Un reintento con el mismo cuerpo lo resuelve: un resultado retenido devuelve 200; un resultado caducado devuelve un 410 no reintentable. Use una política de reintentos acotada: backoff, Retry-After y el plazo de su operación de negocio. Una vez agotado ese plazo, aplique su propio comportamiento de contingencia y concilie más tarde, con el mismo cuerpo.

Un cuerpo que lleva un event_context_token solo se concilia de esta forma si el primer intento se almacenó, o mientras aún no haya llegado el expiresAt del token (como máximo la ventana de evidencia de su aplicación, 60 o 300 segundos, después de su captura). De lo contrario, el mismo cuerpo devuelve 400 event-context-invalid; en ese caso, evalúe el evento como una operación nueva con null (guía de integración).

No existe un GET de recuperación: la recuperación es el mismo POST.