Fac-360

Errores, idempotencia y cuotas

El sobre de error, qué significa cada código y qué es seguro reintentar.

El sobre

Todo error de /api/v2 tiene la misma forma:

{
  "schemaVersion": "2.0",
  "requestId": "…",
  "error": {
    "code": "INVALID_DOCUMENT",
    "message": "…",
    "details": []
  }
}

requestId viaja también en la cabecera X-Request-Id, y es lo que hay que dar al pedir soporte: identifica la petición en los registros sin exponer nada del cuerpo.

Programa contra code, nunca contra message. El código es el contrato; el mensaje está escrito para una persona y puede mejorar sin previo aviso.

Qué significa cada familia

EstadoQué pasó¿Reintentar?
400La petición está mal formadaNo, sin cambiarla
401Credencial inválida o inservibleNo
403Falta un scopeNo
404No existe, o no es tuyoNo
409Conflicto con un estadoDepende del código
422Bien formada, inaceptableNo, sin corregir el contenido
429Cuota o límite de frecuenciaSí, respetando Retry-After
5xxFallo nuestroSí, con la misma Idempotency-Key

Un 404 no siempre significa que el recurso no existe: también responde así un recurso que existe pero pertenece a otra empresa. Es intencionado — distinguirlos convertiría la ruta en una sonda para averiguar qué RUC están registrados.

Códigos que verás a menudo

CódigoEstadoQué hacer
INVALID_TOKEN401La credencial no se reconoce en este edge
INSUFFICIENT_SCOPE403La credencial es válida pero no tiene ese permiso
INVALID_IDEMPOTENCY_KEY400Falta o no tiene la forma exigida
IDEMPOTENCY_CONFLICT409Misma clave, cuerpo distinto. Ver abajo
INVALID_DOCUMENT422El comprobante no pasa las validaciones. Mira details
SUPPLIER_RUC_MISMATCH422El emisor del cuerpo no es la empresa de la credencial
QUOTA_EXCEEDED429Sin cuota en el periodo. Espera a Retry-After
DOCUMENT_NOT_SUBMITTABLE409El estado actual no admite un envío
SUBMISSION_ALREADY_PENDING409Ya hay un envío en curso: espera, no lo repitas
ARTIFACT_NOT_READY409El artefacto existe pero aún se está escribiendo
TENANT_CAPABILITY_DISABLED403Esa capacidad no está habilitada para esta empresa

Idempotencia

Todas las operaciones que crean o mueven algo exigen la cabecera Idempotency-Key. Las lecturas no la aceptan, y POST /api/v2/documents/validations tampoco: no crea nada, así que no hay nada que repetir.

La regla es simple y estricta:

  • Misma clave, mismo cuerpo → te devuelve el resultado original. No se crea un segundo documento, no se consume cuota otra vez, no se manda un segundo envío a SUNAT.
  • Misma clave, cuerpo distinto → 409 IDEMPOTENCY_CONFLICT. No se toca nada.

Esa segunda regla es lo que hace la cabecera útil. Sin ella, un reintento con un cuerpo modificado sobrescribiría en silencio, y no habría forma de saber cuál de las dos versiones quedó.

Genera la clave antes del primer intento y guárdala junto a tu comprobante. Una clave nueva por reintento no protege de nada: es una petición nueva a todos los efectos.

Cuando no sabes si llegó

Si tu petición se corta sin respuesta, repítela con la misma Idempotency-Key. O bien nunca llegó y ahora se procesa, o bien llegó y te devuelve el resultado original. Es exactamente el caso para el que existe la cabecera.

Cuotas

Se cobra una unidad por documento aceptado, en la misma transacción que lo hace durable. No hay una capa de cuota aparte, y eso importa: no existe una ventana en la que el documento esté creado y la cuota no, ni al revés.

De ahí se siguen tres cosas que conviene tener claras:

  • Un replay no cobra dos veces. Lo impide una restricción única en la base de datos, no una rama de código.
  • El cargo es por documento, no por petición ni por operación. Un mismo documento no puede generar dos cargos aunque se llegue a él por caminos distintos.
  • Un comprobante rechazado por SUNAT ya consumió su unidad. Se cobra por aceptar y persistir, no por el veredicto fiscal. Recorrió el sistema entero.

QUOTA_EXCEEDED responde 429 con Retry-After apuntando al cierre del periodo, no a un retraso fijo: reintentar antes solo puede volver a fallar. Y cuando llega, no se ha persistido nada.

Tu consumo está en GET /api/v2/organizations/usage (scope organizations:read), y en la consola en la vista de uso de cada empresa.

Límites de frecuencia

POST /api/v2/documents/validations está limitada a 60 peticiones por minuto y credencial, en su propio espacio de nombres: el uso de una empresa no consume el presupuesto de otra. Es una llamada barata y sin efectos, pero no es gratis de servir.

El edge tiene además su propio límite, que responde 429 antes de que la petición llegue a la aplicación.

Validar antes de emitir

La forma más barata de no encontrarse un 422 es no llegar a él:

POST /api/v2/documents/validations

Corre exactamente las mismas validaciones que la creación —no una versión reducida ni parecida: la misma implementación— y devuelve valid, findings y los totals calculados. No crea documento, no toma correlativo, no consume cuota y no acepta Idempotency-Key.

Pide el scope documents:validate, que no está implicado por documents:create. Una credencial de desarrollo puede ensayar sin poder emitir.

On this page