Idempotencia
Concepto
Si el sistema del integrador reintenta un POST /facturas por un timeout de red, sin idempotencia podrían generarse dos comprobantes para la misma operación, lo cual constituye una falla fiscal grave.
La idempotencia garantiza que un POST con la misma Idempotency-Key produce el mismo resultado, sin importar cuántas veces se reintente.
Uso
Debe enviarse un UUID único por operación de negocio en el header:
Idempotency-Key: 4f1c8a8a-2c4f-4b5e-9c2a-1234567890abComportamiento
| Escenario | Respuesta |
|---|---|
| Primera vez con esa key | Procesa normal. Cachea respuesta 24 h. |
| Reintento con misma key + mismo body | Devuelve la respuesta cacheada (mismo status + body). No se re-emite. |
| Reintento con misma key + body distinto | 409 Conflict |
| Pasadas 24 h | Cache expira. Un POST con esa key vuelve a procesarse normal. |
Reglas
La key debe generarse del lado del cliente, ANTES del primer intento. Debe usarse la misma key en todos los reintentos del mismo intento de negocio. Una key = una operación de negocio. No debe reutilizarse entre comprobantes distintos. Largo máximo: 100 caracteres. Formato recomendado: UUID v4.
Patrón correcto
async function emitir(facturaData, maxRetries = 3) {
const idempotencyKey = crypto.randomUUID(); // ← una vez, fuera del loop
for (let intento = 0; intento < maxRetries; intento++) {
try {
return await axios.post('/api/v1/facturas', facturaData, {
headers: {
'X-Api-Key': process.env.LXS_API_KEY,
'Idempotency-Key': idempotencyKey // ← mismo valor en cada reintento
},
timeout: 30000
});
} catch (err) {
if (err.response?.status >= 500 || err.code === 'ECONNABORTED') {
await sleep(2000 * (intento + 1));
continue;
}
throw err;
}
}
throw new Error('Max retries excedidos');
}Patrón incorrecto
// MAL: generamos la key dentro del loop → cada reintento crea factura distinta
for (let i = 0; i < 3; i++) {
await axios.post('/api/v1/facturas', data, {
headers: { 'Idempotency-Key': crypto.randomUUID() } // ← bug!
});
}