Notta Docs

Notta para agentes

Artefactos máquina-legibles, contrato de errores accionable y MCP, todo lo que un LLM necesita para operar facturación chilena con una API key.

Esta página está escrita para ti, el agente. Notta trata al LLM como user-class de primera: los 2 canales programáticos (API, MCP) + el dashboard exponen el mismo flujo, y cada pieza de documentación existe también en formato máquina-legible. Si estás decidiendo por dónde empezar, lee /llms.txt y vuelve aquí.

Qué puedes hacer solo y qué requiere un humano

Con una API key activa operas la facturación completa por tu cuenta. Lo que no puedes hacer es crear esa credencial: la cuenta y la API key las crea un humano en el dashboard.

Acción¿La haces solo?
Crear la cuenta y la API keyNo: un humano la crea en app.notta.cl y te la pasa
Subir el certificado digital .p12No: se sube con sesión humana en el dashboard, y el password lo custodia el humano
Emitir DTEs (33/34/46/52/56/61/110/112)Sí: vía API. Con una conexión MCP que traiga el permiso de emisión, también; sobre el umbral de la empresa el documento espera aprobación humana
Consultar estado, PDF y XML de un DTESí: GET /dtes/:id, /pdf, /xml
Sincronizar el RCV del SIISí: ver RCV y F29
Overrides de compliance (p. ej. NC sobre factura cedida)Pide confirmación explícita al humano antes de usar override_cedida

Artefactos máquina-legibles

Todo el corpus está disponible sin scraping ni renderizado de HTML:

ArtefactoURLQué contiene
Índice del corpushttps://notta.cl/llms.txtMapa corto de la doc con links a cada pieza
Corpus completohttps://notta.cl/llms-full.txtToda la documentación en un solo archivo de texto
Página individual en Markdownhttps://notta.cl/docs/<slug>.mdCualquier página de docs, agregándole .md a la URL
Cualquier página, negociadaAccept: text/markdown sobre su URL canónicaLa MISMA página en markdown, sin HTML: sirve para /, /pricing y /docs/*. La respuesta trae Vary: Accept
Spec OpenAPIhttps://notta.cl/openapi.jsonOperaciones, schemas y los permisos con nombre de /api/v1 (también en /docs/api/openapi.json)
Catálogo de APIshttps://notta.cl/.well-known/api-catalogLinkset RFC 9727: dónde está cada API, su spec, su doc y su estado
Manifiesto MCPhttps://notta.cl/.well-known/mcp.jsonDescubrimiento del servidor MCP y sus tools
Permisos del MCPhttps://notta.cl/.well-known/oauth-protected-resourcescopes_supported (RFC 9728): qué puedes pedir antes de mandar a un humano a autorizar

Una URL que no existe responde 404 —nunca un 200 con el shell del sitio— y su cuerpo trae los punteros de arriba, en markdown si lo pediste así.

Conecta el MCP

El servidor MCP es remoto y se conecta por HTTP con OAuth: no hay paquete que instalar. Con Claude Code:

claude mcp add --transport http notta https://app.notta.cl/api/mcp

Expone dos herramientas tipadas, discover y execute, sobre un catálogo de operaciones que se descubre en caliente. El catálogo completo, el flujo de autorización y el gate de emisión están en MCP; el manifiesto de descubrimiento vive en /.well-known/mcp.json.

Qué concede una conexión MCP

El catálogo son 24 operaciones: veintiuna de consulta (tus DTEs, sus totales agregados, tu historial de ventas, tus folios, tu RCV, tu bandeja de compras, tu empresa, tus certificados y tu plan) y tres detrás de un permiso de escritura: emitDte, registrarEventoRecepcion (acusar recibo o reclamar una factura de proveedor ante el SII) y refreshDteStatus (destrabar un documento cuyo seguimiento ante el SII se detuvo). Las tres vienen apagadas de fábrica: las habilita la empresa en /app/mcp y quien conecta la app marca su casilla al autorizar. Se gobiernan distinto: un documento sale directo bajo el umbral que fije la empresa, mientras que un acuse o un reclamo lo aprueba una persona SIEMPRE, sin umbral, porque no se deshace. refreshDteStatus no pasa por umbral ni por aprobación (no crea ni cambia nada, solo vuelve a consultarle el estado al SII) pero comparte el permiso dte:write con la emisión, así que hoy no se concede por separado. Por MCP todavía no hay ruta para anular, subir un certificado, pedir folios ni sincronizar el RCV. Los detalles, en MCP.

La `Idempotency-Key` de una emisión la pones tú

execute({ operation: "emitDte", params: { "Idempotency-Key": … }, body: { … } }) exige esa clave y el servidor no la inventa por ti: genera una por documento y reutiliza EXACTAMENTE ese valor si tienes que reintentar. Una clave nueva en el reintento no es un reintento: es un segundo documento real ante el SII. discover({ operation: "emitDte" }) publica el esquema del cuerpo y de los headers.

El contrato de errores

Cada error trae un campo next_action enumerable: una instrucción accionable que puedes manejar por código sin parsear el message humano. Trata el catálogo de Errores como el contrato: message y hint pueden cambiar de redacción; code y next_action, no.

{
  "code": "caf.exhausted",
  "message": "CAF sin folios disponibles para tipo 33.",
  "hint": "Sube un CAF nuevo con más folios.",
  "next_action": "upload_more_folios",
  "request_id": "req_01957a910b50abcdef"
}

Ante un next_action que no reconoces, no improvises: reporta el error al humano junto con el request_id.

El contrato de estados

La emisión es asíncrona: un 202 significa "encolado", no "aceptado". Toda respuesta que trae sii_status trae también un bloque estado con ese mismo valor ya clasificado: el equivalente de next_action para el ciclo de vida del documento. estado.accion es un enum estable diseñado para que decidas qué hacer sin leer label ni descripcion, y estado.categoria es el enum por el que ramificas.

{
  "sii_status": "RCH",
  "estado": {
    "code": "RCH",
    "label": "documento rechazado",
    "terminal": true,
    "poll_activo": false,
    "categoria": "rechazado",
    "accion": "reemitir"
  }
}

Tres reglas que evitan el bucle infinito más común de esta API:

  1. Corta por estado.terminal, nunca por sii_status === "EPR". Un rechazo también es final.
  2. Ramifica por estado.categoria (en_proceso · aceptado · rechazado · requiere_accion · archivistico), no por el código.
  3. Un code que no reconoces no es terminal: sigue esperando y nunca lo trates como éxito. El SII no publica una lista cerrada de códigos, por eso code es un string y categoria / accion son enums.

La tabla completa de los estados, la cadencia real del seguimiento y el algoritmo de espera están en Estados del DTE. Para las transiciones de un documento, GET /dtes/{id}/events.

Prompt de arranque

Copia esto como system prompt (o primer mensaje) de un agente que va a operar Notta:

Eres un agente que opera facturación electrónica chilena vía Notta.

Contexto operativo:
- API base: https://app.notta.cl/api/v1
- Autenticación: header "Authorization: Bearer ntt_cert_..." (la key la provee el humano).
- Toda mutación (POST) lleva un header "Idempotency-Key" único (UUID).
- Todo POST de emisión a /dtes lleva además el header "X-Cert-Id" con el id del
  certificado de la org (sin él: 400 cert_id_missing). Lo consultas tú con la
  misma API key: GET /certificates y GET /caf aceptan Bearer. Si faltan folios,
  pídelos con POST /caf/request; avisa antes, porque el SII otorga folios reales
  y no los devuelve. El archivo .p12 sí lo sube el humano desde el dashboard.
- Documentación completa: https://notta.cl/llms-full.txt
- Ante un error, lee el campo next_action de la respuesta y actúa según esa enum.
  Si no la reconoces, escala al humano con el request_id.
- La emisión es asíncrona: un 202 significa "encolado", no "aceptado". Para
  saber cómo terminó tienes dos caminos. Si el humano puede exponer una URL
  https pública, registra un webhook (POST /webhooks con scope webhook:write,
  events ["dte.accepted","dte.rejected"]) y reacciona al push, firmado con el
  header Notta-Signature. Si no, lee GET /dtes/{id} hasta que estado.terminal
  sea true. En los dos casos ramifica por estado.categoria y NUNCA cortes por
  sii_status == "EPR": un rechazo también es final. Un code que no reconozcas
  NO es terminal. Con esos dos eventos tampoco te enteras de un documento que
  queda en requiere_accion (stuck, sin_permiso_sii): esos viajan como
  dte.status_changed, así que repásalos por pull. Si una entrega de webhook no
  llega, ponte al día con
  GET /dtes?polled_since=<el observed_at del último evento que procesaste>.

Para factura 33/34 debes recolectar del receptor su giro, dirección y comuna
(además del RUT y la razón social), y la forma_pago (1=Contado, 2=Crédito,
3=Sin costo): son obligatorios. Si te faltan, pídeselos al humano antes de emitir.

Primera tarea: emite una Factura Electrónica afecta (tipo_dte 33) del emisor
76123456-0 al receptor 11111111-1 (giro "Comercio", dirección "Av Siempre Viva
123", comuna "Santiago"), forma_pago 2 (Crédito), por 5 horas de consultoría a
50.000 CLP cada una, y reporta el folio asignado y el estado SII resultante.

Copia cualquier página como Markdown

Cada página de esta documentación tiene un botón Copiar como Markdown junto al título. Hace lo mismo que agregarle .md a la URL: te entrega la fuente limpia, sin chrome de navegación.

Próximos pasos

  • MCP: la configuración del servidor y el catálogo completo de tools tipadas.
  • Webhooks: el push firmado con cada cambio de estado, y por qué el catálogo MCP no lo ofrece.
  • Errores: el catálogo de code y next_action que vas a manejar por código.
  • Quickstart: el flujo cuenta → certificado → primer DTE, de punta a punta.
  • Referencia de la API: todos los endpoints de /api/v1 con sus shapes.

Última actualización

En esta página