Idempotencia y reintentos
Idempotency-Key es obligatoria en toda ruta que no se pueda deshacer: las tres que gastan correlativo (emitir, prepare, sign) y la anulación. Sin ella, un reintento por timeout quemaría un segundo número, o mandaría un segundo evento contra un documento que el primero ya anuló.
Elíjala usted, y que signifique algo
Es un identificador que usted elige, no que el servidor genera. Si su sistema ya tiene uno para esta venta —el número de pedido, el ticket de caja—, ese es mejor que uno aleatorio: hace la operación idempotente entre reinicios de su proceso, no solo entre reintentos de una misma llamada en memoria.
curl -X POST "$FACTA_API_BASE_URL/v1/dte" \
-H "X-Facta-Key: $FACTA_API_KEY" \
-H "X-Facta-Sign-Key: $FACTA_SIGN_KEY" \
-H "Idempotency-Key: pedido-00417" \
-H "Content-Type: application/json" \
-d '{ "tipoDte": "01", "items": [{ "descripcion": "Café", "cantidad": 2, "precioUni": 1.5 }] }'Cuatro reglas, todas verificables
- Alcance
(llave, Idempotency-Key). Dos integradores pueden usar la misma cadena sin chocar, y ninguno puede leer la respuesta del otro. - La misma llave con el mismo cuerpo devuelve la respuesta guardada, con
Idempotency-Replayed: true. Eso incluye un rechazo del Ministerio: el reintento devuelve el mismo 422, sin quemar otro número. - La misma llave con otro cuerpo es
422 idempotency_key_reuse. Devolver la factura de ayer para la venta de hoy sería peor que fallar. - Mientras la primera está en vuelo, la segunda es
409 idempotency_in_flight. No se encola: el cliente reintenta. - Dura 24 horas. Pasado eso la cadena queda libre: un reintento un día después es una venta nueva, no un reintento.
Dónde reintentar es seguro, y dónde no
Solo cinco condiciones se arreglan reintentando, y las cinco son casos donde nadie tomó todavía una decisión irreversible sobre su venta:
409 idempotency_in_flight— la primera petición sigue trabajando.503 service_unavailable/503 correlative_unavailable— algo de Facta no contestó.502 mh_unreachable— reservado; la versión actual nunca lo devuelve (ver abajo).- El fallo de red del propio cliente, antes de recibir cualquier respuesta.
Nunca reintente un 4xx que no sea el 409 de arriba, y nunca reintente un rechazo (422 mh_rejected). Un rechazo es un veredicto sobre sus datos, no una caída — reintentar el mismo cuerpo solo repite el mismo veredicto guardado.
Por qué mh_unreachable está reservado y nunca aparece
Un 502 invitaría a reintentar, y reintentar una emisión que YA está firmada firmaría un segundo documento. Por eso todo fallo de transporte hacia el Ministerio —caída, timeout, una respuesta que no se puede leer— sale como 202 contingencia en vez de un 502: para cuando el fallo ocurre, el documento ya existe y se le debe a Hacienda. La guía Contingencia y resultados inciertos cuenta qué hacer con ese 202.
La anulación es la única ruta donde un 502 mh_unreachable sí puede ocurrir de verdad: ahí nada se firmó todavía cuando el transporte falla, así que reintentar con la misma Idempotency-Key es seguro y no anula dos veces.
Otras guías
Emitir un DTE
Cómo llamar a POST /v1/dte, qué llega firmado por dentro y las tres respuestas que puede recibir.
Leer →Preparar y firmar por separado
Por qué separar la reserva del correlativo de la firma, el prepareToken y sus 15 minutos, y qué pasa si el documento cambia entre las dos llamadas.
Leer →