Idempotency-Key
Un Idempotency-Key es un identificador único que envías en
tus POST. Si tu cliente recibe un timeout y reintenta, el segundo POST
con la misma key no crea un comprobante duplicado — recibe la misma
respuesta que el primero.
¿Por qué importa?
Emitoo es async: el POST responde en milisegundos con la factura en
PENDIENTE. Pero entre que sale tu request y vuelve la
respuesta pueden pasar cosas: el cliente se desconecta, hay un timeout
en tu proxy, una excepción de red. Sin idempotency, el reintento crea
un segundo comprobante — y dos facturas por la misma venta nunca es
aceptable.
Cómo usarlo
Envía el header Idempotency-Key con un string único por
operación lógica. Para una orden de checkout, usa el ID de la orden:
import { Emitoo } from "emitoo";
const client = new Emitoo(); // lee la API key de EMITOO_API_KEY
const ordenId = "order-90210";
// Con idempotencyKey, el SDK además reintenta el POST solo cuando es seguro.
const factura = await client.facturas.crear(
{
tipo_identificacion: "04",
identificacion: "0991234567001",
razon_social: "ACME S.A.",
detalles: [{ codigo_principal: "LIC-1", descripcion: "Licencia",
cantidad: 1, precio_unitario: 75.0, tarifa_iva: 15 }],
},
{ idempotencyKey: ordenId },
);from emitoo import EmitooClient
client = EmitooClient() # lee la API key de EMITOO_API_KEY
# Con idempotency_key, el SDK además reintenta el POST solo cuando es seguro.
factura = client.facturas.crear(
{
"tipo_identificacion": "04",
"identificacion": "0991234567001",
"razon_social": "ACME S.A.",
"detalles": [{"codigo_principal": "LIC-1", "descripcion": "Licencia",
"cantidad": 1, "precio_unitario": 75.0, "tarifa_iva": 15}],
},
idempotency_key="order-90210",
)client, err := emitoo.New("") // lee la API key de EMITOO_API_KEY
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
ordenID := "order-90210"
// Con WithIdempotencyKey, el SDK además reintenta el POST solo cuando es seguro.
factura, err := client.Facturas.Crear(ctx, emitoo.M{
"tipo_identificacion": "04",
"identificacion": "0991234567001",
"razon_social": "ACME S.A.",
"detalles": []emitoo.M{
{"codigo_principal": "LIC-1", "descripcion": "Licencia",
"cantidad": 1, "precio_unitario": 75.0, "tarifa_iva": 15},
},
}, emitoo.WithIdempotencyKey(ordenID))
if err != nil {
log.Fatal(err)
}curl -X POST https://api.emitoo.io/api/v1/facturas \
-H "X-API-Key: $EMITOO_API_KEY" \
-H "Idempotency-Key: order-90210" \
-H "Content-Type: application/json" \
-d '{ ... }'Reintentos de los SDKs oficiales
429 y errores de red. Pero
un POST (crear un comprobante) solo lo reintentan si le
pasas la idempotency key (idempotency_key /
idempotencyKey / WithIdempotencyKey): sin ella,
un reintento podría duplicar el comprobante, así que el SDK prefiere
fallar antes que arriesgarse.
Reglas
- El key puede tener hasta 255 caracteres. Recomendamos 36–64.
- Si reusas la misma key con un body distinto, recibes
409 idempotency_conflict. - El server guarda el resultado en cache por 24 horas; pasado ese tiempo, el mismo key se considera una nueva petición.
- El key se asocia a tu API key: keys distintas con el mismo key no chocan entre sí.
Usa IDs de tu sistema
Qué evitar
- ❌ No uses timestamps ni contadores: cada reintento con un valor nuevo crea un duplicado.
- ❌ No aleatorices con
Math.random(): las colisiones (aunque improbables) duplican el efecto. - ❌ No reuses el mismo key para dos operaciones distintas: aunque conceptualmente sean "lo mismo", el server las verá como un conflicto.
¿Qué responde en un reintento?
El segundo POST con la misma key devuelve exactamente la misma respuesta que el primero, incluyendo status code. Esto incluye errores: si el primer POST dio 422, el reintento también. Eso es lo que hace que la idempotencia sea útil — no tienes que manejar dos flujos.