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) | Tipo | Requerido | Descripción |
|---|---|---|---|
tipo | int | sí | 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. |
cantidad | int | no | Cuá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.
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:
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ódigo | HTTP | Qué 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) | 403 | Crea la key con ese scope en /app/api-keys |
caf.request_in_flight (cooldown; trae retry_in_seconds) | 409 | Reintenta después de ese plazo |
caf.type_not_prod_certified | 409 | El SII habilita el tipo en producción al aprobar la postulación |
cert.missing | 412 | Sube un certificado vigente primero |
org.required | 412 | — |
caf.cert_not_authorized (el certificado no está autorizado a timbrar) | 502 | Autoriza 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) | 502 | No 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_unavailable | 503 | — |
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) | Tipo | Requerido | Descripción |
|---|---|---|---|
file | archivo | sí | El XML CAF descargado del SII. |
tipo_esperado | int | no | Tipo de DTE que esperas (33, 34, 39, 41, 56, 61); si el XML trae otro, responde 422 caf.wrong_tipo antes de insertar. |
certificate_id | uuid | no | Certificado asociado; default: el primer certificado vigente de la org (sin ninguno → 422 caf.no_certificate). |
folio_inicial | int | no | Primer folio del rango que todavía no emitiste. Sólo hace falta si el CAF viene usándose en otro facturador. Ver abajo. |
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:
Reenvía el mismo archivo declarando desde dónde seguir:
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:
La deduplicación es por rango: re-subir el mismo (rut, tipo, folio_desde) responde 409 caf.duplicate.
Errores posibles:
| Código | HTTP | next_action |
|---|---|---|
caf.invalid_request (falta file) | 400 | — |
caf.duplicate | 409 | — |
org.required | 412 | — |
caf.parse_error (XML sin RE/TD/D/H) | 422 | — |
caf.wrong_tipo | 422 | — |
caf.no_certificate | 422 | — |
caf.insert_failed | 500 | — |
db.unavailable · caf.kms_unavailable | 503 | — |
GET /caf
Lista los CAF de tu organización con el consumo de folios de cada rango.
Respuesta 200 OK:
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.
Respuesta 200 OK:
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ódigo | HTTP | next_action |
|---|---|---|
caf.not_found | 404 | — |
caf.has_accepted_dtes | 409 | — |
caf.has_ceded_dtes | 409 | — |
org.required | 412 | — |
db.unavailable | 503 | — |
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) | Tipo | Requerido | Descripción |
|---|---|---|---|
enabled | bool | sí | Prende o apaga el automático. |
cantidad | int | no | Cuántos folios pedir en cada recarga (1–1000). Sin él, el default por tipo (100 para 33 y 34, 50 para el resto). |
umbral | int | no | Con 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.
Respuesta 200 OK (lo que quedó configurado; cantidad: null = default por tipo):
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ódigo | HTTP | next_action |
|---|---|---|
caf.invalid_request | 400 | — |
forbidden | 403 | — |
caf.auto_requires_production | 409 | — |
org.required | 412 | — |
db.unavailable | 503 | — |
Próximos pasos
- API: DTEs — con folios cargados, emite tu primera factura por API.
- Onboarding y certificación SII — cómo obtener CAFs en maullin (cert) y palena (prod).
- Conceptos — qué es un CAF y cómo se relaciona con folio, timbre y certificado.
Última actualización