factadesarrolladores API 0.1.0-esqueleto

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.

curlTerminal
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

Escriba para buscar Por ejemplo: idempotencia, contingencia, 422, emitir.

moverse Enter abrir Esc cerrar