VerifikaDocs
Guías

Idempotencia

Cómo reintentar sin registrar la misma venta dos veces.

El problema

Tu bot manda un pago. La red se corta después de que lo procesamos pero antes de que te llegue la respuesta. Tu bot reintenta. Sin protección, el negocio termina con la misma venta dos veces y culpando a Verifika.

La solución

Manda una cabecera Idempotency-Key única por operación en cada escritura (POST, PATCH, DELETE) — sobre todo en POST /v1/verifications, donde un reintento sin ella puede registrar el mismo comprobante dos veces si no trae referencia:

curl -X POST https://api.verifika.tech/v1/incomes \
  -H "Authorization: Bearer vk_live_..." \
  -H "Idempotency-Key: 8f14e45f-ea28-4b4c-9f2a-0b1d3c5e7a90" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 45000, "reference": "M1A2B3C4" }'

Si repites la petición con la misma llave y el mismo cuerpo, no ejecutamos nada: te devolvemos la respuesta original, con la cabecera Idempotent-Replay: true.

SituaciónQué pasa
Llave nuevaSe ejecuta y se guarda la respuesta
Llave repetida, mismo cuerpo, ya respondidaRespuesta original + Idempotent-Replay: true
Llave repetida, mismo cuerpo, todavía ejecutando409 IDEMPOTENCY_IN_PROGRESS — reintenta en unos segundos
Llave repetida, cuerpo distinto409 IDEMPOTENCY_KEY_REUSED

Cómo generar la llave

Un UUID v4 por operación de negocio, no por intento HTTP. Es decir: se genera una vez, antes del primer envío, y se reusa en todos los reintentos de esa misma operación.

const idempotencyKey = crypto.randomUUID(); // una vez, fuera del bucle de reintentos

for (let intento = 1; intento <= 3; intento++) {
  const res = await fetch('https://api.verifika.tech/v1/incomes', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${key}`,
      'Idempotency-Key': idempotencyKey,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(pago),
  });

  if (res.ok) return res.json();
  if (res.status < 500 && res.status !== 429) throw new Error(await res.text());
  await new Promise((r) => setTimeout(r, 2 ** intento * 500));
}

Un error no se guarda

Solo cacheamos respuestas exitosas. Si te devolvimos un 400, puedes corregir el cuerpo y reintentar con la misma llave: la reserva se libera. Si guardáramos los errores, un problema pasajero se volvería permanente.

En una verificación, un rechazo no es un error

POST /v1/verifications responde 200 con status: DUPLICATE o REJECTED cuando el comprobante está repetido o no es un comprobante. Es una respuesta exitosa, así que se guarda: repetir con la misma llave devuelve el mismo veredicto. En cambio, un 503 AI_UNAVAILABLE no se guarda: puedes reintentarlo con la misma llave.

Las llaves se recuerdan 24 horas. Después de eso, la misma llave se trata como nueva.

Dos detalles más: la llave se cuenta por API key (dos llaves distintas pueden usar el mismo valor sin pisarse) y admite hasta 255 caracteres; con más, 400.

En esta página