Saltearse al contenido

Empezar

Errores

Todos los errores de la API tienen la misma forma, sea cual sea el endpoint:

{
"error": {
"code": "totals_mismatch",
"message": "El valor declarado en totales.importe_total (115.00) no coincide con el calculado (115.50).",
"details": { "campo": "totales.importe_total", "declarado": 115.00, "calculado": 115.50 },
"request_id": "c0a8f3e2-4b91-4d77-8a1e-9f2c6b5d0e31",
"doc_url": "https://olympus.atharix.com/docs/empezar/errores#totals_mismatch"
}
}
CampoQué es
codeIdentificador estable. Programa contra esto, no contra el mensaje
messageExplicación en español, pensada para que una persona la lea
detailsDatos del error: el campo afectado, los valores en conflicto, lo que falta
request_idIdentificador de la petición. También viene en la cabecera X-Request-Id
doc_urlEnlace a la sección de esta página que explica ese código

401. Falta la cabecera X-API-Key o la clave no existe.

Comprueba que envías la cabecera, que la clave está completa (empieza por dlt_ y tiene 44 caracteres) y que no se coló un espacio o un salto de línea al leerla del entorno.

401. La clave fue revocada desde Configuración → Integraciones. La revocación es definitiva: hay que crear una clave nueva.

401. La clave pasó su fecha de caducidad. Si venía de una rotación, terminó el periodo de gracia y tu sistema sigue usando la vieja: despliega la nueva.

401. La clave está desactivada (no revocada). Se vuelve a activar desde la web.

403. La clave tiene lista de IPs y la tuya no está. details.ip dice desde qué dirección llegó la petición; añádela a la clave o quita la restricción.

403. La clave no tiene el permiso necesario. details.required dice cuál falta y details.granted los que tiene. Añádelo desde la web.

403. El negocio no puede emitir todavía. details.requisitos_faltantes dice qué falta y details.como_resolverlo, dónde se arregla:

RequisitoQué falta
verifactu_enabled(España) Activar VERI*FACTU en Configuración → VERI*FACTU
electronic_signatureSubir el certificado en Configuración → Fiscal
signature_passwordRegistrar la contraseña del certificado
software_producer(España) Configuración de la plataforma; contacta con soporte

Esto no se resuelve por la API: el negocio lo configura en la aplicación web.

403. El negocio agotó los comprobantes que incluye su plan este mes. El cupo se renueva el día 1, o el negocio puede mejorar el plan.

403. El país del negocio no tiene facturación electrónica en Olympus. La API emite para Ecuador (SRI) y España (VERI*FACTU).

Salvo agent_required, solo los recibe la clave de un agente. Ninguno se arregla reintentando la misma petición, y más de 20 respuestas 403 en 10 minutos pausan al agente.

403. Usaste una clave de integración en un endpoint solo para agentes: KPIs, listados de datos o reportes contables. Crea un agente en Configuración → Equipo → Agentes y usa su clave. Ver Migrar desde claves.

403. Al agente le falta el módulo, o el nivel, que pide la operación. details.modulo dice cuál (o cuáles, si vale cualquiera de varios) y details.nivel, cuál hace falta (view u operate):

{
"error": {
"code": "insufficient_permissions",
"message": "El agente no tiene permiso para esta operación. Se lo da su responsable en Configuración → Equipo y sucursales → Agentes.",
"details": { "modulo": "inventario.stock", "nivel": "view" }
}
}

Si lo que falta es un permiso sensible, details.permiso dice cuál. Pasa con datos.personales, que hace falta para descargar el PDF o el XML de un comprobante y para buscar por correo, teléfono o identificación en /contacts, /purchase-invoices y /quotes (un buscar con @ o con 6 cifras o más).

También llega, sin details, si un KPI pide con sucursal una sucursal que no es del agente. Su responsable le da el permiso en el editor, dentro del techo de los agentes.

403. El agente está limitado a algunas sucursales y el establecimiento del payload no es de ninguna de ellas. details.establecimiento dice el código que enviaste. Emite desde uno de sus establecimientos, o no envíes establecimiento y se usará el principal de los suyos. Ver Sucursales y ventas de otros.

400. Un agente emitió sin la cabecera Idempotency-Key, que para él es obligatoria al emitir. No se emitió nada. Envíala con una clave que identifique la operación en tu sistema (venta-SO-2026-0912), no una al azar en cada intento. Ver Idempotencia.

403. El agente ya no es miembro del negocio, aunque su clave siga viva. Revisa el agente en Configuración → Equipo → Agentes; si se dio de baja, crea uno nuevo.

403. El agente está pausado: lo pausó una persona o la pausa automática. details.motivo dice por qué, si se indicó. Lo reanuda su responsable desde la ficha del agente; antes, revisa su actividad.

403. Su responsable ya no puede responder por él: salió del negocio o perdió algún permiso que el agente tiene. Se reanuda en la ficha del agente tras asignarle otro responsable.

401. El agente fue revocado. Es definitivo: crea uno nuevo. Al revocar un agente se revocan también sus claves, así que lo normal es recibir api_key_revoked.

403. El propietario del negocio pausó todos sus agentes. Solo él los reanuda. Las claves de integración siguen funcionando.

503. Olympus desactivó los agentes en toda la plataforma, normalmente por un incidente. Las claves de integración siguen funcionando. Reintenta más tarde, espaciando los intentos (minutos, no segundos).

403. La autonomía del agente tiene prohibida esta acción. details.accion dice cuál (invoice.issue, document.issue, document.send_email). La cambia quien administra las integraciones. Ver Autonomía y límites.

403. La acción está en modo con aprobación. Hoy eso significa que el agente no la ejecuta: no se crea nada. details.accion y details.modo dicen cuál. Que la haga una persona desde Olympus, o que el responsable la pase a automático con topes. Ver «Con aprobación», hoy.

429. El agente llegó a uno de sus topes del día. details.limite dice cuál, details.permitido cuánto es y details.actual lo que lleva hoy; en el importe diario, además, details.este_documento:

{
"error": {
"code": "agent_limit_exceeded",
"message": "El agente alcanzó su límite «Documentos al día». Lo cambia su responsable en Configuración → Equipo y sucursales → Agentes.",
"details": { "limite": "max_daily_documents", "permitido": 500, "actual": 500 }
}
}

En el tope de correos, details.esta_peticion dice cuántos destinatarios llevaba la petición: cuentan todos. No trae Retry-After: los contadores vuelven a cero al día siguiente (00:00 de Ecuador). Si el importe de un comprobante pasa su tope, el comprobante no se emite. Ver Límites del día.

422. Pediste a un KPI o a un reporte más de 366 días. Cuenta el periodo efectivo: si solo enviaste desde, se mide hasta hoy. Los listados no tienen este límite. details.maximo_dias dice el máximo. Divide la consulta en periodos de un año como mucho.

403. El plan del negocio no incluye los datos que pediste. El negocio lo cambia en Configuración → Plan. Crear agentes también exige un plan con API y webhooks; eso lo avisa la propia web.

400. El payload tiene campos que faltan, sobran o no valen. details reproduce la estructura del payload con el problema en el sitio exacto:

{
"error": {
"code": "validation_error",
"message": "La petición tiene campos inválidos o incompletos.",
"details": {
"items": [
{
"impuestos": [
{ "codigo_porcentaje": ["«99» no es un código de IVA del SRI. Consulta los válidos en GET /api/v1/catalogs/tax-rates."] }
]
}
]
}
}
}

Reintentar sin cambiar el payload da exactamente el mismo error.

400. El cuerpo no es JSON válido. Revisa que envías Content-Type: application/json y que el JSON está bien formado (comas de más, comillas sin cerrar).

409. Ya existe una factura con esa referencia_externa en el negocio. details.id y details.secuencial identifican la que ya se emitió.

Casi siempre significa que tu sistema reintentó una emisión que en realidad salió bien. Trátalo como éxito: la factura existe, consúltala con el id que te devuelve.

409. (Ecuador) Enviaste un secuencial que ya está usado en ese punto de emisión. Si controlas la numeración tú, avanza el contador; si no te hace falta, deja de enviar secuencial y que Olympus lo asigne.

409. Reutilizaste una Idempotency-Key con un cuerpo distinto. Cada operación diferente necesita su propia clave. Ver Idempotencia.

409. Otra petición con la misma Idempotency-Key se está procesando ahora mismo. Espera un segundo y consulta el resultado; no la repitas en bucle.

409. Pediste el PDF, el XML o un reenvío de correo de un comprobante que aún no está listo. details.estado dice en qué estado está.

En Ecuador hay que esperar a que el SRI lo autorice. En España solo ocurre si la factura no llegó a registrarse.

409. La operación no cabe en el estado actual del comprobante: por ejemplo, reintentar una factura ya autorizada o que sigue en curso. details.estado dice el estado real.

409. (Ecuador) La factura que referencias en factura_modificada (notas de crédito y débito) o en destinatarios[].factura (guías de remisión) está anulada o el SRI todavía no la autorizó. details.estado dice en qué estado está; espera a que sea autorizado y vuelve a intentarlo.

422. Los totales que declaraste no coinciden con lo que Olympus calcula a partir de las líneas. details trae declarado, calculado y el campo afectado.

Esta comprobación existe para que nunca se emita algo distinto de lo que tu sistema contabilizó. Si no cuadra, revisa tus redondeos línea a línea. Si prefieres que Olympus calcule sin comparar, omite el bloque totales.

422. Alguna línea enlaza con un producto del negocio que no tiene existencias suficientes. details.faltantes lista los productos y las cantidades.

Repón stock, permite stock negativo en la configuración del negocio, o factura sin enlazar el producto (usa un codigo_principal que no coincida con ningún SKU).

422. (Ecuador) El establecimiento o el punto de emisión que indicaste no existe o no está activo. Consulta GET /establishments para ver los códigos reales.

422. Una línea lleva un impuesto que esta versión no admite (ICE, IRBPNR, o varios impuestos en la misma línea). Por ahora cada línea admite un solo IVA.

422. (España) Una línea no declara su IVA, o lo declara al 0 % sin decir por qué. Exenta, no sujeta e inversión del sujeto pasivo se declaran distinto ante la AEAT: hay que elegir. details.codigos lista los válidos. Ver IVA, exenciones e ISP.

422. Enviaste un campo que no existe en la facturación del país del negocio: establecimiento o secuencial a un negocio español, retencion o recargo_equivalencia a uno ecuatoriano. details.campos_de_este_pais dice cuáles sí puedes usar.

422. (España) La factura no cumple una regla de VERI*FACTU y no llegó a expedirse: comprador extranjero sin pais, factura simplificada por encima del límite legal, o total cero.

422. El negocio no puede facturar en esa moneda. El SRI solo admite dólares; España acepta las divisas de referencia del BCE. details.permitidas lista algunas válidas.

422. (España) Pediste el XML de una factura española. VERI*FACTU no genera un XML descargable: lo que acredita la factura es su registro encadenado. Usa el PDF y urls.verificacion.

422. (España) Usaste GET /documents/{clave_acceso} en un negocio español. Allí no hay clave de acceso: busca la factura en GET /invoices por referencia_externa o secuencial.

422. (España) Pediste reintentar el envío. No hace falta: el registro ya existe y Olympus lo reenvía a la AEAT por su cuenta.

422. (Ecuador) La factura que referencias por id o secuencial no existe en este negocio. details.campo dice dónde la enviaste. Comprueba el número completo (001-001-000000123) o usa el id que devolvió POST /invoices/issue.

422. Pediste emitir o consultar una nota de crédito, nota de débito, retención, guía de remisión o liquidación de compra en un negocio español. Esos comprobantes existen solo en el SRI; ver Otros comprobantes.

429. Superaste el límite de peticiones por minuto de tu clave. details.retry_after_seconds y la cabecera Retry-After dicen cuánto esperar. Las cabeceras X-RateLimit-Limit y X-RateLimit-Remaining van en todas las respuestas para que lo veas venir. Ver Límites y paginación.

404. El recurso no existe o no pertenece a tu negocio. Por seguridad no distinguimos entre ambos casos: una clave nunca puede averiguar si existe una factura de otro negocio.

500. Algo falló en Olympus. La respuesta siempre trae request_id; envíalo a soporte.

Es seguro reintentar con la misma Idempotency-Key: si la factura llegó a emitirse recibirás la respuesta guardada, y si no, se emitirá una sola vez.

import time
import httpx
REINTENTABLES = {"rate_limited", "internal_error", "idempotency_in_progress"}
def emitir(client, payload, idempotency_key, intentos=3):
for intento in range(intentos):
respuesta = client.post(
"/invoices/issue", json=payload, headers={"Idempotency-Key": idempotency_key}
)
if respuesta.is_success:
return respuesta.json()
error = respuesta.json()["error"]
# Ya estaba emitida: no es un fallo, es el mismo resultado por otro camino.
if error["code"] == "external_id_conflict":
return client.get(f"/invoices/{error['details']['id']}").json()
if error["code"] not in REINTENTABLES:
raise ErrorDeFacturacion(error["code"], error["message"], error.get("request_id"))
time.sleep(float(respuesta.headers.get("Retry-After", 2**intento)))
raise ErrorDeFacturacion("sin_respuesta", "Se agotaron los reintentos", None)

La regla: reintenta lo transitorio, no lo que depende de que corrijas el payload. Un validation_error reintentado da igual resultado.