Notta Docs

API: Folios (CAF)

Carga, inventario y eliminación de los archivos CAF que autorizan tus rangos de folios por tipo de DTE, con alerta de folios bajos

El recurso /caf administra los CAF (Código de Autorización de Folios) de tu organización: el XML que el SII te entrega con un rango de folios autorizado por tipo de DTE. Notta consume folios secuencialmente de cada CAF al emitir y avisa cuando quedan pocos (low_folios).

Base URL: https://app.notta.cl/api/v1. El CAF se guarda cifrado con la misma envelope encryption de los certificados.

Autenticación: API key Bearer o sesión del dashboard

Los cinco endpoints de /caf aceptan las dos credenciales: Authorization: Bearer ntt_cert_… para consumidores externos (API, CLI, MCP) o la cookie de sesión del dashboard. Las cuatro mutaciones (POST /caf/request, POST /caf, DELETE /caf/:id y PUT /caf/auto) exigen además el scope caf:write en la key y un rol escritor en la organización: una key con solo dte:write, o un viewer, reciben 403 forbidden. Sin organización responden 412 org.required.

Endpoints

POST /caf/request

Le pide al SII un rango nuevo de folios para un tipo, sin que tengas que pasar por el timbraje electrónico del portal. Es el camino programático cuando todavía no tienes ningún CAF: Notta abre la sesión mutual-TLS con tu certificado, completa el wizard de timbraje y guarda el XML cifrado, listo para emitir.

El grant es irreversible

El SII otorga folios reales que no se devuelven ni se anulan, en cert y en producción. Pide la cantidad que vayas a usar. Hay un cooldown anti doble-grant: una segunda llamada mientras la primera sigue en vuelo responde 409 caf.request_in_flight con retry_in_seconds y no vuelve a pegarle al SII.

Campo (JSON)TipoRequeridoDescripción
tipointsíTipo de DTE a timbrar. Uno de 33, 34, 46, 52, 56, 61, 110, 112. Otro tipo responde 400, salvo que tu cuenta en Notta tenga una habilitación específica para ese tipo en ese ambiente.
cantidadintnoCuántos folios pedir (1–1000). Sin él, Notta usa el default por tipo.

Los folios de exportación (110 y 112) se timbran por esta misma vía, y desde el 19 de agosto de 2026 también se emiten por la API: POST /dtes con tipo_dte: 110 acepta el documento. Lleva su propio cuerpo —moneda, ind_servicio y bloque aduana—; está descrito en Emitir un DTE. Tu organización necesita el tipo autorizado por el SII: sin eso la respuesta es 403.

curl -X POST https://app.notta.cl/api/v1/caf/request \
  -H "Authorization: Bearer $NOTTA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tipo": 33, "cantidad": 20}'

El ambiente sale de la API key: una ntt_cert_ pide folios en maullin (cert) y una ntt_prod_ en palena. Con sesión del dashboard sale del ambiente activo de tu sesión.

Respuesta 201 Created — se solicitó un rango nuevo:

{
  "data": {
    "requested": true,
    "cafs": [{ "tipo": 33, "estado": "requested", "cafId": "01977f6e-5f90-7000-8000-000000000005" }]
  }
}

Si ya tienes folios vigentes para ese tipo, la respuesta es 200 OK con requested: false y reason: "no_cafs_missing": no se pidió nada.

Errores posibles:

CódigoHTTPQué hacer
caf.invalid_request (tipo no soportado, cantidad fuera de 1–1000)400—
forbidden (la key no tiene caf:write, o el rol es viewer)403Crea la key con ese scope en /app/api-keys
caf.request_in_flight (cooldown; trae retry_in_seconds)409Reintenta después de ese plazo
caf.type_not_prod_certified409El SII habilita el tipo en producción al aprobar la postulación
cert.missing412Sube un certificado vigente primero
org.required412—
caf.cert_not_authorized (el certificado no está autorizado a timbrar)502Autoriza el certificado en el portal del SII
caf.grant_rejected (el SII rechazó el grant, con su mensaje)502—
caf.persist_failed (el SII otorgó el rango y Notta no pudo guardarlo)502No vuelvas a solicitar: recupera el rango con "Reobtención de folios" en el portal del SII y súbelo con POST /caf
caf.request_failed (falló la sesión con el SII)502—
db.unavailable · caf.kms_unavailable503—

POST /caf

Sube un archivo CAF (multipart/form-data): parsea el XML (RE, TD, D, H), valida el tipo esperado y registra el rango de folios.

Campo (form)TipoRequeridoDescripción
filearchivosíEl XML CAF descargado del SII.
tipo_esperadointnoTipo de DTE que esperas (33, 34, 39, 41, 56, 61); si el XML trae otro, responde 422 caf.wrong_tipo antes de insertar.
certificate_iduuidnoCertificado asociado; default: el primer certificado vigente de la org (sin ninguno → 422 caf.no_certificate).
folio_inicialintnoPrimer folio del rango que todavía no emitiste. Sólo hace falta si el CAF viene usándose en otro facturador. Ver abajo.
curl -X POST https://app.notta.cl/api/v1/caf \
  -H "Authorization: Bearer $NOTTA_API_KEY" \
  -F "file=@CAF-33-1000-1500.xml" \
  -F "tipo_esperado=33"

El CAF subido queda etiquetado con el ambiente de la credencial que lo sube, igual que en /caf/request: una ntt_cert_ lo archiva en certificación y una ntt_prod_ en producción (con sesión del dashboard manda el ambiente activo). La etiqueta decide en qué ambiente lo encuentra la emisión, así que sube el CAF de palena con una key de producción.

Si el CAF viene de otro facturador

Un CAF recién descargado del SII tiene el rango virgen y el contador arranca solo. Pero si el rango ya se venía usando en otro sistema, arrancar en folio_desde repetiría folios que el SII tiene registrados — y un folio repetido no se deshace.

Por eso, si detectamos folios de ese rango ya emitidos y no declaraste desde dónde seguir, la respuesta es 422:

{
  "code": "caf.rango_parcialmente_consumido",
  "message": "Tu numeración del tipo 33 ya llega hasta el folio 480. Si cargamos este CAF desde el 401 repetiríamos folios que el SII ya registró.",
  "hint": "Reenvía el mismo CAF con `folio_inicial` = el primer folio que NO hayas emitido.",
  "detail": {
    "tipo_dte": 33,
    "folio_desde": 401,
    "folio_hasta": 500,
    "ultimo_folio_visto": 480,
    "sugerido": 481
  }
}

Reenvía el mismo archivo declarando desde dónde seguir:

curl -X POST https://app.notta.cl/api/v1/caf \
  -H "Authorization: Bearer $NOTTA_API_KEY" \
  -F "file=@CAF-33-401-500.xml" \
  -F "tipo_esperado=33" \
  -F "folio_inicial=481"

sugerido sale del último folio que el SII tiene registrado, así que es una ayuda y no un veredicto: quien sabe dónde quedó la numeración eres tú. Tiene que caer dentro del rango del CAF; folio_hasta + 1 es válido y marca el rango como agotado.

Respuesta 201 Created:

{
  "data": {
    "id": "01977f6e-5f90-7000-8000-000000000005",
    "tipo_dte": 33,
    "folio_desde": 1000,
    "folio_hasta": 1500,
    "remaining": 501,
    "low_folios": false
  }
}

La deduplicación es por rango: re-subir el mismo (rut, tipo, folio_desde) responde 409 caf.duplicate.

Errores posibles:

CódigoHTTPnext_action
caf.invalid_request (falta file)400—
caf.duplicate409—
org.required412—
caf.parse_error (XML sin RE/TD/D/H)422—
caf.wrong_tipo422—
caf.no_certificate422—
caf.insert_failed500—
db.unavailable · caf.kms_unavailable503—

GET /caf

Lista los CAF de tu organización con el consumo de folios de cada rango.

curl https://app.notta.cl/api/v1/caf \
  -H "Authorization: Bearer $NOTTA_API_KEY"

Respuesta 200 OK:

{
  "data": [
    {
      "id": "01977f6e-5f90-7000-8000-000000000005",
      "rut_emisor": "76123456-0",
      "tipo_dte": 33,
      "sii_env": "prod",
      "folio_desde": 1000,
      "folio_hasta": 1500,
      "next_folio": 1235,
      "remaining": 266,
      "low_folios": false,
      "not_after": "2026-12-09T00:00:00.000Z",
      "exhausted": false,
      "created_at": "2026-06-09T18:20:00.000Z"
    }
  ]
}

remaining = folio_hasta - next_folio + 1; low_folios se prende con 10 o menos. Errores posibles: 412 org.required, 503 db.unavailable.

La lista viene recortada a un solo ambiente, el de la credencial con la que llamas, y cada fila lo declara en sii_env. Como certificación y producción son secuencias de folios independientes, tu organización puede tener un CAF del mismo tipo en cada una con rangos distintos: para ver los del otro ambiente, llama con una API key de ese ambiente.

DELETE /caf/:id

Elimina un CAF (hard delete, permite re-subir el mismo rango) junto con los DTE del rango que el SII no aceptó; está bloqueado si algún DTE del CAF fue aceptado.

curl -X DELETE https://app.notta.cl/api/v1/caf/01977f6e-5f90-7000-8000-000000000005 \
  -H "Authorization: Bearer $NOTTA_API_KEY"

Respuesta 200 OK:

{ "data": { "id": "01977f6e-5f90-7000-8000-000000000005", "deleted": true, "dtes_deleted": 2 } }

Retención legal de 6 años

Si algún DTE del CAF fue aceptado por el SII (EPR, RPR, DOK, aceptado con reparos), el borrado responde 409 caf.has_accepted_dtes: esos folios quedaron registrados en el SII y los documentos se conservan por obligación legal de 6 años. Lo mismo si un DTE del rango fue cedido vía RPETC (409 caf.has_ceded_dtes).

Errores posibles:

CódigoHTTPnext_action
caf.not_found404—
caf.has_accepted_dtes409—
caf.has_ceded_dtes409—
org.required412—
db.unavailable503—

PUT /caf/auto

Enciende o apaga el timbraje automático: con él prendido, Notta le pide al SII un rango nuevo de folios cuando a un tipo de documento le quedan pocos, sin que tengas que pedirlo. Es el mismo grant irreversible de POST /caf/request, disparado por un barrido diario, por la mañana, en vez de por tu llamada.

Campo (JSON)TipoRequeridoDescripción
enabledboolsíPrende o apaga el automático.
cantidadintnoCuántos folios pedir en cada recarga (1–1000). Sin él, el default por tipo (100 para 33 y 34, 50 para el resto).
umbralintnoCon cuántos folios restantes en total para el tipo se dispara la recarga (0–10). Sin él, 10. Con 0 se recarga sólo cuando no queda ninguno.

cantidad y umbral se guardan por organización y quedan hasta que los cambies: un PUT que sólo trae enabled (el interruptor de la pantalla) no los toca. Van juntos a propósito: si pides de a 1 con el umbral en 10, cada rango nuevo de 1 vuelve a estar bajo el umbral y el barrido pediría otro cada día. Con umbral: 1 y cantidad: 1 tienes siempre 1 o 2 folios y ninguno de sobra.

curl -X PUT https://app.notta.cl/api/v1/caf/auto \
  -H "Authorization: Bearer $NOTTA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true, "cantidad": 1, "umbral": 1 }'

Respuesta 200 OK (lo que quedó configurado; cantidad: null = default por tipo):

{ "data": { "enabled": true, "cantidad": 1, "umbral": 1 } }

Solo en producción, y cada recarga es irreversible. Mientras tu empresa está en certificación el toggle responde 409 caf.auto_requires_production: ahí el proceso lo conduce el asistente de Puesta en Marcha, que además autoriza gestiones que no se deshacen. En producción, cada recarga automática le pide folios reales al SII y el SII no los devuelve: prenderlo es autorizar por adelantado esas solicitudes en tu nombre.

Errores posibles:

CódigoHTTPnext_action
caf.invalid_request400—
forbidden403—
caf.auto_requires_production409—
org.required412—
db.unavailable503—

Próximos pasos

Última actualización

En esta página