Ir al contenido
SimploSimplo Docs

Errores

Códigos de error de la API de Simplo, clases de error del SDK por status HTTP y las reglas de reintento automático.

Toda falla del SDK lanza una subclase de SimploError. Captura estrecho cuando puedas actuar sobre el caso, amplio en el resto:

import Simplo, { RateLimitError, SimploError, ValidationError } from '@simplohq/sdk';
const simplo = new Simplo();
try {
await simplo.dtes.emit(params);
} catch (error) {
if (error instanceof ValidationError) {
// Input malo — corrige la request, no la reintentes tal cual.
for (const issue of error.details ?? []) {
console.error(`${issue.field}: ${issue.code} — ${issue.message}`);
}
} else if (error instanceof RateLimitError) {
// El SDK ya reintentó; frena al llamador.
console.error(`Rate limit. Reintenta en ${error.retryAfter ?? '?'}s`);
} else if (error instanceof SimploError) {
console.error(error.status, error.code, error.message, error.requestId);
} else {
throw error; // error de programación — no lo tragues
}
}
Propiedad Tipo Descripción
status number | undefined Status HTTP (ausente en fallas de conexión)
code string | undefined Código de la API legible por máquina, ej. "VALIDATION_ERROR"
message string Descripción legible por humanos
requestId string | undefined ID de la request — inclúyelo al contactar soporte
details ValidationIssue[] Issues a nivel de campo (field, code, message) en errores de validación
action string | undefined Siguiente paso recomendado por la API, cuando existe
docUrl string Link a la documentación relevante
Status Clase ¿Reintentar? Causas típicas
400/422 ValidationError No — corrige la request Dígito verificador de RUT inválido, campos requeridos ausentes
401 AuthenticationError No — corrige credenciales API key errónea o revocada (Autenticación)
403 PermissionError No La key no tiene el permiso o rol requerido
404 NotFoundError No ID equivocado, o recurso de otra empresa
409 ConflictError Depende (ver abajo) Folio duplicado; idempotency key aún en vuelo
429 RateLimitError Automático Demasiadas requests (los límites son por empresa)
5xx ServerError Automático (si es seguro) Problema transitorio de la API
— ConnectionError Automático (si es seguro) Falla de red, DNS, timeout
— WebhookVerificationError No — rechaza la entrega Firma mala, timestamp vencido (Webhooks)

Los errores vienen de dos capas y sus sobres no son idénticos:

  • Plataforma (auth, allowlist, rate limit, idempotencia requerida): { code, message, error, severity, action?, context? }.
  • Motor de facturación (validaciones de emisión, folios, estados): pasa por el proxy con su propio shape — trae code, message, action y severity/context cuando aplica.

Reglas de manejo robustas: decide por status HTTP + code cuando existe, tolera campos desconocidos, y nunca parsees message como si fuera un contrato. Dos comportamientos reales a tener presentes:

  • Algunas validaciones del motor responden hoy con mensaje crudo y código genérico — p. ej. omitir ind_servicio en una boleta da 400 con "IndServicio is required for boleta", no un código de catálogo. Y en producción algunos errores tipados del motor pueden llegar como texto plano donde el sandbox devuelve el código estructurado (RUT_INVALIDO, CAF_NO_DISPONIBLE) — divergencia conocida, fix pendiente. Diseña tu manejo para funcionar aunque solo tengas el status.
  • Los largos de razon_social tienen dos capas: el motor valida el tope del SII (100 caracteres, receptor y emisor) y responde 400 RAZON_SOCIAL_INVALIDA nombrando el campo y el límite; strings todavía más largos (> 200) los corta antes la validación de shape de la plataforma con un 400 genérico {"code":"BAD_REQUEST","message":"invalid request"} sin nombrar el campo — si ves ese, revisa primero los largos de tus strings.
Código Descripción
401 Credencial inválida, revocada o expirada. La respuesta no distingue el motivo (anti-enumeración): revisa el header y el prefijo sk_simplo_.
409 IDEMPOTENCY_IN_PROGRESS La misma Idempotency-Key aún se está procesando. Espera y consulta el estado en vez de reintentar de inmediato.
409 (folio duplicado) Conflicto de folio al emitir.
422 IDEMPOTENCY_BODY_MISMATCH Reutilizaste una Idempotency-Key con un body distinto al original. Usa una key nueva por cada emisión distinta.
422 BILLING_METADATA_NOT_SUPPORTED Mandaste metadata con contenido en la emisión. El campo todavía no se persiste, así que lo rechazamos en vez de descartarlo en silencio — quítalo y correlaciona contra el id que devuelve la emisión. {} y null sí pasan. Ver Emisión.
429 Rate limit: 60 llamadas/min por empresa en la facturación por API key (120/min en MCP). Espera y reintenta — ver Límites.
503 (idempotencia) Servicio de idempotencia temporalmente no disponible al emitir. Reintenta con la misma key.

Códigos de validación de emisión (motor de facturación)

Sección titulada «Códigos de validación de emisión (motor de facturación)»

Validaciones que corren antes de asignar folio — el mismo camino de emisión en sandbox y producción, así que un 400 de estos en sandbox es exactamente lo que verías en producción (donde además evitan quemar un folio real en un documento que el SII rechazaría):

Código Status Qué hacer
EMISOR_INCOMPLETO 400 A tu empresa le falta giro, dirección o comuna (el context.missing_fields lista cuáles). El SII exige estos datos del emisor en el XML (GiroEmis/DirOrigen/CmnaOrigen) — complétalos en la app o vía API y reintenta. Aplica a todos los tipos, boletas incluidas.
RAZON_SOCIAL_INVALIDA 400 razon_social (del receptor o de tu empresa) supera los 100 caracteres del esquema del SII (RznSocLargaType). Acórtala; el mensaje trae el largo recibido.
MONTO_INVALIDO 400 Cantidad/precio negativos, o monto_total final ≤ 0 en un documento con montos. Corrige las líneas — el campo exacto viene en context.field.
REFERENCIA_INVALIDA 400 Una NC/ND (56/61 y también las de exportación 111/112) referencia un tipo+folio que no existe para tu empresa. Verifica tipo_doc_ref/folio_ref contra el documento original.
RECEPTOR_INCOMPLETO 400 En documentos tipo factura (33/34/43/46/56/61) el receptor necesita direccion y comuna — el SII real rechaza sin ellas (HED-3-845/HED-3-846). El context.missing_fields lista cuáles faltan. Boletas (39/41) y exportación (110/111/112) no las exigen.
DETALLE_DESCUADRADO 400 Enviaste un monto_item que no cuadra con cantidad × precio (± descuentos/recargos, tolerancia ±1 peso de redondeo). El error trae el valor esperado y el recibido por línea. Si quieres fijar el monto final directo, omite precio y manda solo monto_item.
IND_TRASLADO_REQUERIDO 400 Las guías de despacho (52) requieren ind_traslado según la tabla del SII (1=operación constituye venta … 9=venta para exportación). Nota: una guía de venta (1 o 9) además debe llevar montos — con precio 0 responde MONTO_INVALIDO.
EXPORT_TPO_CAMBIO_REQUERIDO 400 Documento de exportación en moneda extranjera sin tpo_cambio: el SII real rechaza el fallback 1:1 («Los Valores del Encabezado No Cuadran»). Manda tpo_cambio (> 0); mnt_export explícito o forma_pago_exp: 21 (sin pago) siguen siendo válidos sin tasa.

Códigos de la plataforma (API keys y sandbox)

Sección titulada «Códigos de la plataforma (API keys y sandbox)»

Estos códigos vienen de la plataforma Simplo (no del motor de facturación) y tienen siempre una salida clara:

Código Status Qué hacer
BILLING_API_KEY_ROUTE_NOT_ALLOWED 403 La ruta es solo-sesión. Una key billing:dte solo alcanza las 6 rutas DTE — ver la tabla del quickstart. No es una cuenta sin habilitar.
BILLING_API_KEY_IDEMPOTENCY_KEY_REQUIRED 400 Agrega el header Idempotency-Key a tu POST por key (/billing/dte y /billing/dte/send-boletas).
BILLING_API_KEY_NOT_ENABLED 403 Tu empresa no tiene habilitada la emisión en producción por key — escribe a hola@simplo.cl. El sandbox no necesita esto.
BILLING_SANDBOX_API_KEY_NOT_ENABLED 403 Tu plan no incluye el sandbox de facturación.
COMPANY_RUT_REQUIRED_FOR_SANDBOX 422 Al crear una key sandbox: tu empresa aún no tiene RUT — complétalo en la app y reintenta.
COMPANY_RAZON_SOCIAL_REQUIRED_FOR_SANDBOX 422 Al crear una key sandbox: falta la razón social — complétala en la app y reintenta.
SANDBOX_COMPANY_FLOW_REMOVED 410 Llamaste el flujo antiguo POST /api/v1/sandbox/companies, que ya no existe: el sandbox es un modo de tu propia empresa — crea una key sandbox en Desarrolladores.
SANDBOX_NOT_AVAILABLE 503 El sandbox no está disponible en este momento — reintenta más tarde.

El SDK reintenta en 429, 5xx y errores de red con backoff exponencial y jitter, respetando el header Retry-After. Por defecto: 2 reintentos; sobrescríbelo con maxRetries en el cliente o por request.

Regla de seguridad: un POST solo se reintenta cuando lleva una Idempotency-Key. dtes.emit genera una automáticamente (UUID v4), así que los reintentos de emisión nunca pueden duplicar un documento. Otros POSTs (folios.upload, webhooks.create) no se reintentan automáticamente — vuelve a llamarlos tú si hace falta; ambos son seguros de re-ejecutar (un CAF duplicado devuelve ConflictError).

const simplo = new Simplo({ maxRetries: 3, timeout: 30_000 });
await simplo.dtes.list({}, { maxRetries: 0 }); // override por request