API de Enrolamiento

Errores y reintentos

Cómo leer un error de la API y cuándo es seguro reintentar.

Esta guía cubre las familias de errores de la API y cómo interpretar cada una, el catálogo de códigos y qué hacer con cada uno, y cuándo una operación se puede reintentar sin riesgo.

Las cuatro familias

Un error de la API pertenece a una de cuatro familias. Cada una tiene su forma de cuerpo, y no todas se reconocen por el status.

FamiliaStatusCuerpo
NegocioCasi siempre 500; también 403, 404 y 429Texto plano: CODIGO:mensaje
Framework400, 404, 405 o 500JSON con Message
Autenticación401JSON con Message, o vacío
Rate limit409Texto plano, sin código

Negocio

Son los errores que produce la propia API: un dato que falta, un rechazo del SII, una falla de comunicación con el SII. El cuerpo es texto plano con una de estas formas:

  • CODIGO:mensaje
  • CODIGO:SUBCODIGO:mensaje -- excepción
  • el mensaje solo, sin código. Así llegan el 403 y el 404 de negocio, por ejemplo «No existe una certificación…».

Los rechazos de negocio llegan como HTTP 500, aunque no sean fallas. Un cliente sin saldo prepago que llama a certificaciones_v1_crear recibe un 500 con este cuerpo:

INSUFICIENTE_SALDO:No es posible crear la certificación en estos momentos, por favor verifique su saldo e intente nuevamente.

Por eso el status no sirve para decidir qué hacer; el código sí. INSUFICIENTE_SALDO y CERT_REQUERIDO vuelven con el mismo 500 que una falla de comunicación con el SII (SII_ERROR_CXN), y piden cosas opuestas: los dos primeros no se arreglan reintentando, el tercero sí.

Decida por el código, nunca por el texto del mensaje. El mismo código puede llegar con más de un mensaje, y códigos distintos pueden traer el mismo mensaje. En caf_v1_descargar, por ejemplo, AUT_DOCTYPE_NOT_AUTHORIZED y UKN pueden traer exactamente el mismo texto («EL SII no autoriza timbraje…»): el primero no se reintenta nunca y el segundo se reintenta con cuidado. El mensaje sirve para mostrárselo a una persona o para guardarlo en su registro, no para que su sistema decida.

Un caso aparte es CAF_REQUEST_IN_PROGRESS: también es de negocio y trae el código, pero llega con status 429 y la cabecera Retry-After. Está más abajo, en 409 y 429.

Las cabeceras de un error con código

Desde la versión 1.1 del contrato, cada error de negocio con código trae además tres cabeceras:

CabeceraQué dice
X-Codigo-ErrorEl código del error, el mismo que abre el cuerpo (CODIGO:…). Viene esté o no el código en el catálogo.
X-Reintentablesi, no o desconocido: la clasificación del código en el catálogo, y desconocido para un código que no figura ahí. no quiere decir no reintentar, cualquiera sea el status.
Retry-AfterSegundos a esperar antes de reintentar. Viene solo cuando X-Reintentable es si (hoy, 30).

Solo las traen los errores con código, que llegan con status 500. El 403 y el 404 de negocio, que llegan sin código, no las traen; tampoco los errores del framework, el 401 ni el 409. El 429 de CAF_REQUEST_IN_PROGRESS trae Retry-After, pero todavía no X-Codigo-Error: lea el código del cuerpo.

X-Reintentable es la clasificación del catálogo, no la decisión de la operación. No sabe si la operación se puede repetir sin efectos duplicados. En caf_v1_descargar, por ejemplo, CNX_HTTP_ERROR_500 llega con X-Reintentable: si y Retry-After: 30, pero la operación no es idempotente y lo tiene en manual: un reintento a ciegas podría timbrar folios dos veces. Decida siempre con x-reintentable de la operación, como se explica en Cuándo reintentar, y use la cabecera para lo que la operación no lista.

Framework

Los responde la plataforma web antes de la operación o en su lugar: una ruta que no existe (404), un verbo incorrecto (405), un parámetro de query obligatorio que falta (404), un parámetro numérico de la ruta que no convierte (400) o una excepción no controlada (500). Desde la versión 1.1, una excepción de un tipo que la API reconoce, como las de negocio y las del certificado, ya no llega como error del framework: llega como error de negocio, en texto plano y con su código. El cuerpo de un error del framework es JSON:

{ "Message": "The request is invalid." }

Message siempre viene. Según la configuración del servidor pueden venir además MessageDetail y, en un 500, ExceptionMessage, ExceptionType y StackTrace. No dependa de esos campos: pueden no estar.

El 404 por un parámetro de query ausente sale antes de validar la credencial, así que llega aunque el request no traiga credenciales o traiga unas inválidas. El 400 por un parámetro que no convierte, en cambio, sale después de autenticar.

Autenticación

Es el 401. Si el request no trae el header Authorization, el cuerpo es JSON: {"Message": "Authorization has been denied for this request."}. Si lo trae con otro esquema que no sea Basic, o con credenciales inválidas, el 401 llega sin cuerpo. En los dos casos viene la cabecera WWW-Authenticate: Basic. Más detalle en Autenticación.

Rate limit

Es el 409, con el cuerpo de texto The allowed number of requests has been exceeded.. No es JSON ni trae código. Se explica completo en Rate limits.

Cómo leer un error

Lea siempre el cuerpo como texto, sin depender del Content-Type: en el 409, que ninguna capa intermedia cambie ese header no está verificado. Después decida por el status y por la forma del cuerpo, en este orden:

  1. 409: rate limit.
  2. 401: autenticación. El cuerpo puede estar vacío.
  3. El cuerpo es un objeto JSON con Message: framework.
  4. El cuerpo empieza con un código seguido de dos puntos: negocio. El código es lo que va antes del primer :.
  5. Cualquier otro texto: negocio sin código, como el 403 o el 404 de negocio.

Un ejemplo en JavaScript:

// Clasifica la respuesta de error de la API en su familia y, si lo hay, extrae el código.
async function leerError(respuesta) {
  const texto = await respuesta.text();

  if (respuesta.status === 409) return { familia: 'rate-limit', texto };
  if (respuesta.status === 401) return { familia: 'autenticacion', texto };

  // Desde la 1.1, un error de negocio con código lo trae también en la cabecera.
  const codigoEnCabecera = respuesta.headers.get('X-Codigo-Error');
  if (codigoEnCabecera) {
    const mensaje = texto.startsWith(codigoEnCabecera + ':') ? texto.slice(codigoEnCabecera.length + 1) : texto;
    return { familia: 'negocio', codigo: codigoEnCabecera, mensaje, reintentable: respuesta.headers.get('X-Reintentable') };
  }

  if (texto.trimStart().startsWith('{')) {
    try {
      const json = JSON.parse(texto);
      if (typeof json.Message === 'string') {
        return { familia: 'framework', mensaje: json.Message };
      }
    } catch {
      // No era JSON: se sigue como texto plano.
    }
  }

  // CODIGO:mensaje o CODIGO:SUBCODIGO:mensaje -- excepción
  const conCodigo = /^([A-Z][A-Z0-9_]*):(.*)$/s.exec(texto);
  if (conCodigo) {
    return { familia: 'negocio', codigo: conCodigo[1], mensaje: conCodigo[2] };
  }
  return { familia: 'negocio', codigo: null, mensaje: texto };
}

Guarde siempre el texto completo junto con la operación, el RUT y la hora: si tiene que contactar a soporte, es lo que se le va a pedir.

Un 200 no siempre es éxito

Algunas consultas responden 200 aunque el resultado sea un error. En caf_v1_anulados y caf_v1_consulta, el resultado viaja en campos de estado de la respuesta (Estado, UltimoEnviadoEstado, UltimoAnuladoEstado). Revise esos campos antes de dar el resultado por bueno: la referencia de cada operación lista sus valores posibles.

Catálogo de códigos

Cada código de negocio está clasificado con uno de tres valores:

  • Sí: reintentable. Es una falla pasajera.
  • No: reintentar no cambia el resultado. Hay que corregir algo antes: un dato, el certificado, el saldo, una situación de la empresa en el SII.
  • Desconocido: puede ser pasajero o no. Antes de repetir, consulte el estado de la operación.

Cada fila tiene un ancla propia: para enlazar un código, use #error- seguido del código, por ejemplo CAF_REQUEST_IN_PROGRESS.

Código¿Reintentable?Qué hacer
AUTH_ERRORSíNo se obtuvo la sesión del SII con el certificado. El propio mensaje pide intentar nuevamente: reintentar con espera y un tope de 3 intentos.
AUT_COMP_ACTIVITIES_VERIFICATIONNoEl SII no verifica actividades económicas positivas para la empresa. Resolverlo en el SII antes de volver a pedir folios.
AUT_COMP_OR_REP_SITUATIONNoEl SII no autoriza el timbraje por una situación de la empresa o de su representante legal. Resolverla en el SII antes de volver a pedir folios.
AUT_DOCTYPE_NOT_AUTHORIZEDNoEl SII no autoriza timbrar este tipo de documento; lo más común es que la empresa ya tenga folios suficientes. Usar los vigentes o anular los sobrantes y volver a pedir.
CAF_DENIED_SII_EXCEEDNoEl SII no autoriza timbrar más folios de este tipo, ni siquiera uno: la API ya bajó la cantidad sola. Pedir menos no cambia la respuesta; usar los folios vigentes o anular los sobrantes.
CAF_DENIED_SII_NOTENOUGHNoEl SII no autoriza los folios pedidos para este tipo de documento. Usar los vigentes, anular los sobrantes o gestionar con el SII una autorización mayor.
CAF_REQUEST_IN_PROGRESSSíOtra solicitud de folios para el mismo RUT y tipo de documento sigue en curso; este request no se ejecutó. Esperar los segundos del header Retry-After (hoy 30) y repetir el mismo request. Si el 429 llega al reintentar un caf_v1_descargar propio cuya respuesta no se recibió, la solicitud en curso puede ser esa: antes de repetir, consultar con caf_v1_consulta o recuperar con caf_v1_reobtener si el CAF quedó timbrado.
CAF_UNKNOWNDesconocidoEl SII no entregó el CAF por un motivo que no se pudo identificar. Antes de pedir folios de nuevo, consultar con caf_v1_consulta o caf_v1_reobtener si el CAF quedó timbrado.
CERTIFICACION_EXISTE_EN_OTRO_CLIENTENoOtro cliente está certificando ese RUT. Contactar a soporte; reintentar no lo cambia.
CERTIFICADO_DIGITAL_REQUERIDONoCargar el certificado digital con actualizar (campo Certificado) antes de iniciar.
CERT_AUTH_NOT_CONFIGUREDNoEl titular tiene que habilitar el certificado para autenticarse en el sitio del SII.
CERT_ERRORDesconocidoNo se pudo cargar o validar el certificado. Casi siempre son los datos o la contraseña del PFX: revisarlos antes de repetir. Si el certificado está bien, reintentar una vez.
CERT_EXPIRADONoEl certificado está vencido. Renovarlo y volver a mandarlo.
CERT_NO_CONFIGURADO_AUTHNoEl titular tiene que habilitar el certificado para autenticarse en el sitio del SII.
CERT_NO_VALIDONoEl RUT del certificado termina en «k» minúscula y no se puede usar. Revocarlo y generarlo de nuevo, o usar otro certificado.
CERT_REQUERIDONoMandar el cuerpo con Data (PFX en base64) y Contrasenna.
CERT_REQUIREDNoMandar el cuerpo con Data (PFX en base64) y Contrasenna.
CERT_REVOCADONoEl certificado fue revocado por su titular o por la entidad emisora. Emitir uno nuevo y volver a mandarlo.
CERT_SIN_PERMISONoEl certificado no tiene permisos en el SII para esa empresa. Usar el certificado de un usuario habilitado, o darle permisos (empresas_v1_permisos con un certificado administrador).
CNX_HTTP_ERROR_500SíEl SII respondió 500 al pedir el CAF. Reintentar con espera exponencial y un tope de 3 intentos; antes de pedir folios de nuevo, consultar con caf_v1_consulta si el CAF salió igual.
CNX_HTTP_ERROR_CLOSEDSíEl SII cerró la conexión. Reintentar con espera exponencial y un tope de 3 intentos; antes de pedir folios de nuevo, consultar con caf_v1_consulta si el CAF salió igual.
CNX_HTTP_ERROR_REMOTESíNo se pudo conectar con el SII. Reintentar con espera exponencial y un tope de 3 intentos; si persiste, contactar a soporte.
CNX_HTTP_ERROR_RETRYDesconocidoEl SII devolvió su página genérica «No ha sido posible completar su solicitud», que a veces es una falla pasajera y a veces envuelve un rechazo. Leer el mensaje; reintentar una sola vez y, si se repite, no insistir y contactar a soporte.
CNX_HTTP_ERROR_SSLTLSSíNo se pudo abrir el canal TLS con el SII; suele ser intermitente. Reintentar con espera exponencial y un tope de 3 intentos; si persiste, contactar a soporte.
CNX_HTTP_ERROR_TIMEOUTSíVenció el tiempo de espera con el SII. Reintentar con espera exponencial y un tope de 3 intentos; antes de pedir folios de nuevo, consultar con caf_v1_consulta si el CAF salió igual.
CONTRIBUYENTE_SIN_PERMISONoEl SII no habilita a la empresa para la anulación masiva de folios; reintentar no lo cambia.
DATOS_REQUERIDOSNoMandar el cuerpo de crear con los datos del certificado y los sets.
EMPRESA_VERIFICACION_ACTIVIDADES_SIINoEl SII no verifica actividades económicas positivas para la empresa. Resolverlo en el SII y recién entonces volver a crear.
ERROR_ACTUALIZANDO_CASILLASDesconocidoEl SII rechazó o no completó la actualización de las casillas de intercambio; el detalle viaja en el mensaje. Leerlo antes de repetir.
ERROR_DECLARANDO_CUMPLIMENTODesconocidoEl SII rechazó o no completó la declaración de cumplimiento, y la certificación queda marcada con este error. Consultar la certificación y leer el mensaje antes de repetir.
ERROR_DESCARGANDO_CERTIFICADODesconocidoFalló la descarga desde E-Cert. Si el mensaje habla del usuario o la contraseña, corregirlos; si no, reintentar una vez.
ERROR_GENERALDesconocidoCódigo genérico. Si el mensaje dice que el RUT es nulo o no es válido, corregirlo; si no, leer el mensaje y consultar el estado de la certificación antes de repetir.
ERROR_MODALIDAD_CLIENTENoEl SII dice que el contribuyente no está autorizado para operar en esta modalidad (facturación MiPyme del SII). Lo tiene que resolver la empresa con el SII.
ERROR_SOLICITANDO_CERTIFICADODesconocidoFalló la solicitud a E-Cert. Si el mensaje empieza con «Errores en lo datos», corregir los campos que nombra; si no, reintentar una vez.
INSUFICIENTE_SALDONoEl cliente no tiene saldo prepago. Cargar saldo en el portal y volver a crear.
NOT_ENOUGH_FOLIO_FROM_SIINoEl SII no autoriza los folios pedidos para este tipo de documento. Usar los vigentes, anular los sobrantes o gestionar con el SII una autorización mayor.
NO_CERTIFICATENoMandar el certificado en el cuerpo: la plataforma no tiene uno guardado para usar.
PRODUCTO_REQUERIDONoCargar al menos un producto con nombre y precio (actualizar) antes de iniciar.
RANGE_INVALIDNoAl menos un folio del rango ya fue recibido por el SII y no se puede anular. Anular un rango que no incluya folios usados.
RESP_HTTP_ERRORDesconocidoError interno sin código reconocible. Desde la versión 1.1, un rechazo en la validación del certificado llega con su propio código (por ejemplo CERT_REQUERIDO o CERT_EXPIRADO), así que este código ya no corresponde a ese caso. Si aparece, reintentar una vez; si se repite, no insistir y contactar a soporte indicando la hora del request.
RESP_INVALIDDesconocidoEl SII respondió a la anulación algo que no se reconoce; el texto del SII viaja en el mensaje. Leerlo antes de repetir.
SEC_CERT_NOT_AUTHORIZEDNoEl SII rechaza a este certificado para descargar CAF. Usar un certificado con ese permiso: repetir con el mismo no cambia la respuesta.
SEC_EMP_NOT_AUTHORIZEDNoLa empresa no está autorizada para operar en esta modalidad (usa la facturación MiPyme del SII). Lo tiene que resolver la empresa con el SII.
SEC_USER_BOLSIINoEl contribuyente usa el Sistema de Emisión de Boletas Electrónicas del SII. Lo tiene que resolver la empresa con el SII.
SEC_USER_NOT_AUTHENTICATEDDesconocidoEl certificado no quedó autenticado en el sitio del SII. Puede ser una sesión vencida que la llamada siguiente renueva: reintentar una vez y, si se repite, revisar que el certificado esté habilitado para autenticarse en el SII.
SERVICIO_REQUERIDONoCargar al menos un servicio con nombre y precio (actualizar) antes de iniciar.
SET_AMBAS_BOLETAS_JUNTASNoCertificar boleta afecta (39) y boleta exenta (41) en certificaciones separadas.
SET_BASICO_REQUERIDO_SIINoReenviar el pedido incluyendo el set básico (33).
SET_CERTIFICADONoQuitar del pedido el set que el mensaje dice que ya está certificado.
SET_DTE_BOLETA_JUNTOSNoCertificar los sets de DTE y los de boletas en certificaciones separadas.
SET_REQUERIDONoIndicar al menos un set a certificar en Sets.
SII_CALL_ERRORDesconocidoFalló una llamada de negocio al facade de anulación masiva del SII. Volver a listar los folios antes de repetir la anulación, para no actuar sobre un estado viejo.
SII_ERROR_CXNSíFalla de comunicación con el SII. Reintentar con espera exponencial y un tope de 3 intentos; si persiste, contactar a soporte con el RUT.
SII_RESPUESTA_INESPERADANoEl SII devolvió algo que no se pudo interpretar. No reintentar; contactar a soporte con el RUT.
SOLICITUD_FOLIOS_MAYOR_AUTORIZADOS_SIINoEl SII no autoriza la cantidad pedida, ni siquiera un folio. Usar los folios vigentes o anular los sobrantes antes de volver a pedir.
UKNDesconocidoError no clasificado: puede ser pasajero (una falla TLS, por ejemplo) o no. Consultar el estado de la operación antes de repetirla y, si se repite, contactar a soporte con el RUT y la hora.
UNKNOWN_ERROR_CAFDesconocidoError no clasificado al reobtener o descargar el CAF; el motivo viaja en el mensaje. Leerlo y, antes de pedir folios nuevos, consultar con caf_v1_consulta si el CAF quedó timbrado.
UNK_ERRORDesconocidoEl SII no respondió a la anulación. Repetirla con el mismo rango es seguro: si ya estaba anulado, el SII lo acepta como hecho.
VALOR_REQUERIDONoMandar en el cuerpo al menos un campo a actualizar.

Cuándo reintentar

Saber si un código es reintentable no alcanza: también importa si la operación se puede repetir sin efectos duplicados. El contrato OpenAPI declara las dos cosas en cada operación, con dos extensiones. Las dos se ven en el bloque «Reintentos e idempotencia» de cada página de la referencia, y el contrato completo se descarga en /openapi.json.

x-idempotente

Dice si repetir la operación con los mismos datos deja todo igual que una sola llamada:

  • true: repetirla no duplica nada. Las consultas, por ejemplo.
  • false: cada llamada tiene efecto propio. caf_v1_descargar timbra folios nuevos en cada llamada; certificaciones_v1_crear crea una certificación.
  • desconocido: no está establecido. Trátela como false.

x-reintentable

Lista qué hacer con cada código del catálogo de esa operación, y con algunos status:

  • automatico: su sistema puede reintentar solo, con espera.
  • manual: antes de repetir hay que consultar el estado, o que decida una persona.
  • nunca: no se repite; hay que corregir la causa.

Por ejemplo, en caf_v1_descargar la lista automatico tiene 409 y 429, y CNX_HTTP_ERROR_500 está en manual. En el catálogo, CNX_HTTP_ERROR_500 es reintentable, pero caf_v1_descargar no es idempotente: un reintento a ciegas podría timbrar folios dos veces. Por eso la operación lo baja a manual. Antes de volver a pedir, consulte con caf_v1_consulta, o recupere con caf_v1_reobtener, si el CAF quedó timbrado.

La regla de respaldo

x-reintentable no es completa: los códigos que la API arma en el momento no figuran en ninguna operación. Si recibe un código que no está en x-reintentable de la operación, búsquelo en el catálogo y combine su clasificación con x-idempotente:

Catálogox-idempotenteQué hacer
SítrueAutomático
Sífalse o desconocidoManual
DesconocidocualquieraManual
NocualquieraNunca

Hay dos casos que el contrato no clasifica, y que conviene tratar como manuales: un código que no está en el catálogo, y un 500 del framework, que no trae código.

Cómo reintentar

Para lo automático, espere entre intentos, con espera exponencial y un tope. Es lo que pide SII_ERROR_CXN, una falla de comunicación con el SII, en las operaciones que lo tienen en automatico, como caf_v1_consulta: espera exponencial y un tope de 3 intentos. Si sigue fallando, pase el caso a manual y contacte a soporte con el RUT y la hora. En otra operación, el mismo código puede estar en manual: siempre manda la lista de la operación.

409 y 429: el request no se ejecutó

Los dos status dicen que la API rechazó este request sin ejecutarlo, así que repetirlo no duplica nada por sí mismo. Por eso los dos aparecen en automatico. Con el 429 hay que mirar además qué otra solicitud está en curso: ver abajo.

  • 409, rate limit. Se superó el límite de llamadas. No trae Retry-After. Espere al menos 60 segundos antes de reintentar. El detalle está en Rate limits.

  • 429, CAF_REQUEST_IN_PROGRESS. Ya hay otra solicitud de folios en curso para el mismo RUT y tipo de documento. Puede pasar en caf_v1_descargar, caf_v1_reobtener y caf_v1_anular, y solo si su cuenta tiene habilitado el control de solicitudes concurrentes de CAF. La API espera hasta 60 segundos (por omisión) a que la otra solicitud termine; si no termina en ese plazo, responde 429. Este request no se ejecutó. El cuerpo es de negocio:

    CAF_REQUEST_IN_PROGRESS:Ya existe una solicitud de folios en curso para este RUT y tipo de documento. Reintente en 30 segundos.

    La cabecera Retry-After dice cuántos segundos esperar (hoy, 30). Espere ese tiempo y repita el mismo request.

    Cuidado con caf_v1_descargar. Si el 429 llega al reintentar una descarga propia cuya respuesta no recibió, la solicitud en curso puede ser esa misma: su intento original, que sigue timbrando. Repetir a ciegas puede terminar en folios timbrados dos veces. Antes de repetir, consulte con caf_v1_consulta o recupere con caf_v1_reobtener si el CAF quedó timbrado, igual que después de cualquier error dudoso (ver Qué no reintentar nunca).

// Cuántos segundos esperar antes de repetir un 409 o un 429.
function segundosDeEspera(respuesta) {
  if (respuesta.status === 429) {
    const pedido = Number(respuesta.headers.get('Retry-After'));
    return Number.isFinite(pedido) && pedido > 0 ? pedido : 30;
  }
  if (respuesta.status === 409) return 60; // sin Retry-After: la ventana del rate limit es de 60 s
  return null; // no es un rechazo sin ejecutar
}

Qué no reintentar nunca

  • Un código que la operación lista en nunca, o que el catálogo marca con «No»: repetir da la misma respuesta. Corrija la causa que indica la columna «Qué hacer».
  • Un 401: las credenciales no van a cambiar solas. Revíselas antes de volver a llamar.
  • Un 400 o un 404 del framework: el request está mal armado (una ruta, un verbo, un parámetro). Repetirlo igual da el mismo error.
  • Una operación con x-idempotente en false o desconocido, después de un error dudoso o de una respuesta que no llegó: puede haberse ejecutado. Consulte el estado primero. Con caf_v1_descargar, eso es caf_v1_consulta o caf_v1_reobtener antes de pedir folios otra vez.

En esta página