Facturiadocs
Referencia API

Esquemas

Los objetos que las tablas «Cuerpo» y «Respuestas» nombran: la empresa, la puesta en marcha, el handoff a una persona, la re-lectura y el origen.

Las tablas «Cuerpo» y «Respuestas» de los capítulos anteriores nombran el tipo de cada campo. Cuando ese tipo es uno de los objetos de abajo, el nombre es un enlace a esta página. Acá está su forma completa, campo por campo, tal como la declara el contrato.

Es la misma información que vive en /openapi.yaml bajo components.schemas; esta página existe porque un archivo de 271 KB no es una respuesta para la persona que está armando el asistente de puesta en marcha.

EsquemaQué describe
EmpresaLa empresa, con capabilities, el canal de emisión y el ambiente.
OrigenCómo emite hoy la empresa, según lo declaras tú al crearla.
OnboardingLa puesta en marcha completa: estado, pasos, requisitos y el handoff vigente.
PendingActionEl handoff a una persona: quién, dónde, qué instrucciones y cómo nos enteramos.
OnboardingRecheckLa respuesta a «esto ya se hizo, anda a mirar».

Empresa

CampoTipoRequeridoNotas
objectstring—
idstring—
modelive · test—
rutRut—RUT canónico: sin puntos, un guion y K mayúscula. Ej.: "77928532-4".
legal_namestring—
girostring,null—NULEABLE (#496). POST /v1/empresas no lo exige y guarda null cuando se omite, que es la creación corriente — la misma clase de mentira que #473 encontró en contact_email, cazada validando un cuerpo real contra este esquema en vez de comparar las claves de un ejemplo escrito a mano.
actecointeger[]—
addressEmpresaAddress—La dirección TAL COMO SE LEE de vuelta en una empresa (#496). Estructuralmente Address, con todos sus miembros nuleables, porque POST /v1/empresas no exige ninguno de los tres y guarda null en lo que se omitió — que es la creación corriente. La forma de PETICIÓN sigue siendo Address con strings no nulos: lo que puedes mandar y lo que puedes leer de vuelta son afirmaciones distintas, y juntarlas debilitaría la petición o seguiría mintiendo sobre la respuesta. La misma familia de defecto que el contact_email de #473.
codigo_sucursalinteger,null—El código de sucursal del SII (CdgSIISucur) desde el que emite esta empresa. null es casa matriz, que es el caso de casi todas — y se REPORTA como null en vez de omitirse, porque «no tenemos sucursal tuya» es un hecho que un asistente del canal SFG tiene que poder ver: la propia previsualización del Portal MIPYME rechaza un documento cuyo origen emisor está incompleto.
contact_emailstring,null—NULEABLE, y null es lo que reporta una empresa creada sin él (#473). Se declaraba como type: string mientras el cable mandaba null para toda empresa que nunca lo entregó — el caso corriente —, así que un cliente generado estricto (pydantic, quicktype) rechazaba la respuesta de GET /v1/empresas/\{id\}, la lectura que lleva capabilities y el handoff de la puesta en marcha.
ambientecertificacion · produccion—A qué ambiente del SII apuntan las emisiones reales. DE SOLO LECTURA Y DERIVADO — no se acepta al crear ni al actualizar, y enviarlo se rechaza con 400 invalid_field. La regla: emission_channel: sfg siempre es produccion, porque el SII habilitó la empresa cuando se inscribió en el sistema gratuito. emission_channel: mercado es certificacion hasta que el SII emite su resolución, que es lo único que lo mueve — o desde el inicio cuando una resolución declarada con origen: mercado es verificada por nuestra propia sonda. Fue un campo de la solicitud con default: produccion hasta 2026-09, y el default aplicado era el contrario, así que un cliente generado que llenaba los defaults del esquema creaba empresas sin certificar en producción. Declara origen y emission_channel; lee este campo de vuelta.
emission_channelmercado · sfg—CÓMO EMITE HOY esta empresa. mercado es el motor certificado propio de Facturia; sfg opera el Sistema de Facturación Gratuito del SII en nombre de la empresa, con folios que administra el portal. Lo declara el titular — es una preferencia, no algo que podamos leer del SII — y decide qué checklist, qué tipos (sfg no tiene formulario de boleta ni tipo 46) y qué ambiente. Un NULL guardado se lee como mercado, así que este campo nunca es null. Por defecto: "mercado".
certification_trackmercado · sfg · null—QUÉ CHECKLIST DE CERTIFICACIÓN LA RIGE — una pregunta distinta de emission_channel, y las dos pueden legítimamente no coincidir: una empresa puede emitir por el portal gratuito mientras certifica para mercado, que es exactamente lo que hace el piloto. null es una respuesta real y el estado permanente y honesto de un cliente conforme con el portal gratuito para siempre: no está persiguiendo la certificación. Integra el checklist corto de SFG.
doc_types33 · 34 · 39 · 41 · 46 · 52 · 56 · 61[]—
origengratuito · mercado · ninguno · null—Cómo emite hoy la empresa, según lo declaras tú. gratuito = usa el Sistema de Facturación Gratuita del propio SII; mercado = ya usa un proveedor de mercado; ninguno = todavía no emite electrónicamente. null solo en empresas creadas antes de que el campo existiera — nunca declarado, y deliberadamente sin rellenar hacia atrás. Enruta la copia de la puesta en marcha que te mostramos, y nada más. Que una empresa pueda saltarse la certificación se decide 100% empíricamente — una resolución declarada solo gana el salto cuando se contrasta con una operación real efectivamente realizada en esa calidad, nunca con una lectura del portal — y el valor que declaras acá jamás es un insumo de esa decisión. Estar inscrito en el sistema gratuito convierte a un RUT en emisor de DTE reales sin resolución de mercado, así que un mercado declarado que creyéramos saltaría la certificación justo a los clientes que todavía la necesitan.
resolucionResolucionState—«¿YA EMITES CON OTRO PROVEEDOR?» — la resolución que esta empresa tiene, y si la PROBAMOS (fallo 2026-09-07). El SII certifica al contribuyente, no al software. Una empresa que ya factura con otro proveedor tiene una resolución que el SII le dio a su RUT, y esa resolución no deja de valer porque cambie de proveedor — así que puede declararla en POST /v1/empresas o en PATCH /v1/empresas/\{empresa_id\}, y una verificada se salta la certificación completa. verified NUNCA SALE DE LA DECLARACIÓN. Declarar una resolución no cambia nada por sí solo. La declaración espera como verification_status: pending_verification hasta que un operador de Facturia confirma, para esta empresa, el ÚNICO timbraje de producción que la verifica (POST /v1/empresas/\{empresa_id\}/resolucion/verify; verification_request es esa petición exacta). Palena rechaza el timbraje de un RUT que no autorizó, así que un rango otorgado es la prueba (verified) y un rechazo también es la respuesta (refused, con las palabras del SII en refusal). Sólo ese acto — o que el SII nos entregue la resolución al final de una certificación que corrimos — pone verified: true. Al SII no se le pregunta nada de forma implícita (sii-api#581 D1, 2026-09-23). Ramifica por verified, nunca por que venga numero. Mientras sea false, la empresa camina la lista normal y onboarding.blocked_on dice dónde va; si el sondeo fue rechazado, el blocked_reason.resolucion_claim de GET .../onboarding trae la frase textual del SII. null para una empresa que nunca declaró ninguna y a la que no le han emitido una.
resolucion_boletasResolucionBoletasState—La mitad de boletas de la misma pregunta (fallo 2026-09-07). El SII habilita la boleta electrónica aparte de la resolución, y su respuesta es una FECHA sin número: «habilitados los documentos de boleta electrónica a contar del …». verified es el mismo portón que lee POST /v1/documents antes de construir una 39 o una 41, así que es true tanto para una empresa que llegó habilitada (con su declaración probada por un timbraje de tipo 39/41) como para una cuya habilitación corrimos nosotros. fecha: null con verified: true es el estado normal de la segunda. null para una empresa que ni declaró una habilitación ni la tiene.
reception_emailstring,null—Casilla de recepción por empresa, <rut>@dte.facturia.cl — la dirección que la empresa registra ante el SII como Mail Contacto Empresas. null EN UNA EMPRESA DE SANDBOX (#450). Una empresa de sandbox no reserva nada: ni el RUT (la unicidad es por modo) ni esta casilla. Publicarla para una empresa de prueba sería publicar una dirección que enruta los documentos de los proveedores de una empresa real. La recepción entrante es un hecho del modo live.
statusonboarding · active · suspended · archived—
iva_shapeTaxpayerIvaShape—En qué forma de certificación puso el SII a este contribuyente, y cómo lo sabemos. Solo lectura — es la respuesta del SII sobre tu empresa, no una configuración: ningún cuerpo de petición la puede escribir. El SII decide por su cuenta si un contribuyente tiene actividades afectas a IVA, y esa decisión emite un set de pruebas DISTINTO. A un contribuyente exenta le emite guías de despacho, facturas exentas, un libro de guías y un libro de compras para exentos — y ningún set básico — con boletas tipo 41. A uno afecta le emite el set básico y los dos libros, con boletas tipo 39. null significa que todavía no lo hemos establecido. Nunca es un valor por defecto: suponer afecta en un contribuyente exento construiría once documentos que el SII jamás va a revisar.
onboardingobject—La misma derivación que usa GET .../onboarding (#414), así que los dos no pueden discrepar — antes sí lo hacían: status era la columna cruda del spine y next_action_url solo se llenaba cuando leía literalmente el string action_required, que la máquina nunca almacena.
capabilitiesEmissionCapabilities—«¿Puede esta empresa emitir ahora mismo, y si no, quién tiene que hacer qué?» (#416, addendum del 2026-09-02 al ADR-0011 §2) — calculado por la MISMA función con la que POST /v1/documents rechaza la emisión, así que este objeto y un 422 nunca pueden discrepar. blocked_by SÍ REPORTA subscription en GET /v1/empresas/\{empresa_id\} y no en el LISTADO (#475, ruling 3): la lectura de un solo objeto le pregunta a la puerta de facturación (una lectura indexada), el listado no, porque sería una consulta de facturación por cada fila de cada página. Ausente significa «no se preguntó», nunca «está bien». Cuando sí reporta subscription, next.url es la página de pago de ESA empresa — la misma URL que el 402 publica como details.checkout_url, así el objeto y el rechazo nombran una página, no dos. capabilities.tipos ES LA VERDAD DE EMISIÓN; doc_types ES LA DECLARACIÓN (#493). Responden preguntas distintas y se publicaban lado a lado sin que nada dijera cuál gobierna: doc_types es lo que el titular declaró que va a emitir — mueve el checklist de CERTIFICACIÓN y qué folios se piden — y tipos es lo que esta empresa puede emitir ahora mismo, que es doc_types acotado a lo que soporta el emission_channel vigente (sfg no tiene formulario de boleta ni tipo 46). Ramifica por tipos. Para emitir un tipo que no esté ahí, agrégalo a doc_types con PATCH /v1/empresas/\{id\} (y, para 39/41, estar en el canal mercado).
created_atstring—

Empresa.onboarding

La misma derivación que usa GET .../onboarding (#414), así que los dos no pueden discrepar — antes sí lo hacían: status era la columna cruda del spine y next_action_url solo se llenaba cuando leía literalmente el string action_required, que la máquina nunca almacena.

CampoTipoRequeridoNotas
statusdraft · mandato_pending · mandato_signed · certificate_pending · certificate_verified · sii_postulacion_pending · certification_in_progress · cumplimiento_pending · resolucion_pending · folios_pending · emitting · action_required · blocked · suspended—
progressnumber—
blocked_onstring,null—La clave del paso que tiene detenida a esta empresa, o null. El mismo campo que el blocked_on de GET .../onboarding.
next_action_urlstring,null—SIEMPRE null acá (contrato (d) — nunca una URL que abra «Falta el enlace»). Una página de onboarding con token de capacidad es un enlace acotado a UNA empresa y este objeto no tiene uno; url, más abajo, es dónde conseguirlo.
urlstring—GET .../onboarding de esta empresa — el handoff completo (pending_action, incluido pending_action.link.mint para acuñar una URL de acción viva) que este objeto resumen no lleva. ABSOLUTA (#473, #419): https://api.facturia.cl/v1/empresas/\{id\}/onboarding, el mismo origen que llevan pending_action.link.mint.url y verification.recheck.url. Era una ruta relativa bajo una declaración format: uri, que es una mentira que un cliente estricto rechaza y una URL que un agente no puede resolver.

Origen

Cómo emite hoy la empresa, según lo declaras tú. gratuito = usa el Sistema de Facturación Gratuita del propio SII; mercado = ya usa un proveedor de mercado; ninguno = todavía no emite electrónicamente. null solo en empresas creadas antes de que el campo existiera — nunca declarado, y deliberadamente sin rellenar hacia atrás.

Enruta la copia de la puesta en marcha que te mostramos, y nada más. Que una empresa pueda saltarse la certificación se decide 100% empíricamente — una resolución declarada solo gana el salto cuando se contrasta con una operación real efectivamente realizada en esa calidad, nunca con una lectura del portal — y el valor que declaras acá jamás es un insumo de esa decisión. Estar inscrito en el sistema gratuito convierte a un RUT en emisor de DTE reales sin resolución de mercado, así que un mercado declarado que creyéramos saltaría la certificación justo a los clientes que todavía la necesitan.

Valor
"gratuito"
"mercado"
"ninguno"
nullsin declarar

Onboarding

CampoTipoRequeridoNotas
objectstring—
empresa_idstring—
modelive · test | null—test o live — en qué mundo está esta empresa (#418). Una empresa de prueba nace emitting con un checklist de pasos skipped, y una real en el mismo emitting recorrió once de ellos; los dos cuerpos son por lo demás idénticos, así que una captura de pantalla, una línea de log o un webhook no podrían distinguir cuál los produjo. null cuando quien construyó este cuerpo no lo dijo — nunca una adivinanza, porque live es la respuesta sobre la que se actúa.
statusdraft · mandato_pending · mandato_signed · certificate_pending · certificate_verified · sii_postulacion_pending · certification_in_progress · cumplimiento_pending · resolucion_pending · folios_pending · emitting · action_required · blocked · suspended—
progressnumber—
blocked_onstring,null—El paso que una persona (o un requisito no cumplido) tiene detenido. Siempre nombra un paso presente en steps[]. Cuando status es action_required ese paso siempre trae un pending_action — ahí vive su link.mint, y ahí vive su action_url cuando hay uno vivo; cuando status es blocked trae un blocked_reason en su lugar, porque una empresa bloqueada no tiene nada que hacer clic — tiene que cambiar un hecho en el SII.
pending_actionPendingAction | null—El handoff del paso que nombra blocked_on, izado acá para que «qué me está bloqueando y qué le digo a mi humano» sea una sola lectura. null cuando el paso que bloquea es NUESTRO o del SII, que es la respuesta honesta y una cosa distinta de «no hay nada que hacer»: significa esperar, no actuar.
estimated_ready_atstring,null—Hoy más la mediana de días hábiles que toman los pasos que faltan (lunes a viernes, sin calendario de feriados), contando cada uno como lo publica steps[].eta_business_days — así que un paso que todavía espera a una persona nuestra (execution: operator) aporta el piso de una sesión de trabajo y no su estimación de cola (2026-09-07, #556). Es una estimación, no una promesa: las decisiones del representante legal no tienen tope de reloj, y esto cuenta sólo NUESTRO tiempo — no lo que el SII se demore en contestar la postulación ni en dictar la resolución. null una vez en emitting o suspended, mientras está blocked y mientras está draft.
requirementsobject[]—Lo que el SII le exige al contribuyente antes de autorizar a un emisor, tal como lo leímos la última vez. VACÍA hasta que la sonda corre una vez — una lista vacía significa «no hemos mirado», no «no hay nada que hacer». unknown significa que miramos y no pudimos leer la respuesta; nunca se trata como met.
stepsobject[]—La lista de pasos, en orden. habilitacion aparece solo en empresas cuyos doc_types incluyen un tipo de boleta (39 o 41) — dibuja lo que te entregan en vez de suponer un largo. Los cuatro pasos de certificación los evalúa el SII POR TIPO DE DOCUMENTO; cada uno aparece una vez acá con el peor estado entre los tipos declarados, y certifications[] desglosa los mismos hechos por tipo. QUÉ CHECKLIST RECIBES SIGUE A certification_track, NO A emission_channel (#474, corregido el 2026-09-04 — este párrafo nombraba el canal y el código siempre leyó el track). Una empresa cuyo certification_track es sfg o null devuelve una lista CORTA Y PARALELA — mandato, credential, clave_centralizado, usuario_autorizado, emisor_verificado, puesta_en_vivo, emitting — porque una empresa del Sistema de Facturación Gratuito ya es un emisor de producción y nunca corre postulación, certificación, cumplimiento, resolución ni timbraje. En ella certifications[] viene vacío. Los dos campos pueden legítimamente no coincidir (ADR-0010 §1): el piloto emite por el portal gratuito mientras certifica para mercado. El track de una empresa nueva se fija igual a su canal al crearla; PATCH /v1/empresas/\{empresa_id\} cambia cada uno por separado, y cambiar solo el canal deja el track donde está, a propósito. Dibuja los pasos que te entregan; no supongas cuál de las dos listas vas a recibir. Dos de esos siete son los hechos que REALMENTE gatillan la emisión y no aparecían en ningún checklist. clave_centralizado es la clave de la empresa en el Sistema de Facturación Gratuito: el Portal MIPYME la vuelve a pedir en el paso irreversible de cada emisión, así que sin ella no se emite nada aunque la delegación esté viva y el certificado en custodia. puesta_en_vivo es NUESTRO — la canaria con el dueño presente y la habilitación por empresa — y es actor: facturia, dibujado como una espera con estimación, nunca como algo que el cliente deba hacer.
certificationsobject[]—Los tipos de documento se certifican por separado; las boletas se certifican en paralelo con las facturas.
iva_shapeTaxpayerIvaShape—En qué forma de certificación puso el SII a este contribuyente, y cómo lo sabemos. Solo lectura — es la respuesta del SII sobre tu empresa, no una configuración: ningún cuerpo de petición la puede escribir. El SII decide por su cuenta si un contribuyente tiene actividades afectas a IVA, y esa decisión emite un set de pruebas DISTINTO. A un contribuyente exenta le emite guías de despacho, facturas exentas, un libro de guías y un libro de compras para exentos — y ningún set básico — con boletas tipo 41. A uno afecta le emite el set básico y los dos libros, con boletas tipo 39. null significa que todavía no lo hemos establecido. Nunca es un valor por defecto: suponer afecta en un contribuyente exento construiría once documentos que el SII jamás va a revisar.
updated_atstring—

Onboarding.requirements[]

CampoTipoRequeridoNotas
keyinicio_actividades · verificacion_actividades · representante_legal_certificate · usuario_administrador · sin_situaciones_pendientes—
labelstring—Castellano. El requisito tal como lo lee la persona que lo tiene que cumplir.
statusmet · unmet · unknown—

Onboarding.steps[]

CampoTipoRequeridoNotas
keymandato · certificate · sii_postulacion · set_de_pruebas · simulacion · intercambio · muestras_impresas · cumplimiento · resolucion · habilitacion · folios · credential · clave_centralizado · usuario_autorizado · emisor_verificado · puesta_en_vivo · emitting—
labelstring—Castellano. El nombre del paso tal como lo lee una persona en un checklist.
statuspending · in_progress · action_required · completed · failed · skipped—failed significa que algo que corremos nosotros se rindió después de sus reintentos. Es nuestro de arreglar y nunca mueve la empresa a blocked. skipped significa que el paso nunca fue exigible — casi siempre porque el RUT ya era emisor electrónico autorizado, así que no había nada que certificar.
actorfacturia · customer · sii—customer marca un paso que EJECUTA UNA PERSONA: el mandato, la subida del certificado y, en el catálogo SFG, la clave_centralizado y el registro del usuario autorizado. La subida del certificado y la clave ocurren en NUESTRA aplicación y no en el SII, y son los dos que un agente puede completar por su principal (ver pending_action.agent_actions). facturia NO significa «no hay nada que hacer». Tres pasos de mercado son nuestros y aun así no terminan sin una persona: sii_postulacion, cumplimiento y habilitacion. Cada uno lo hacemos ante el SII con el certificado en custodia —que es el del representante legal— después de que esa persona apruebe en una página alojada, y el campo que dice que hay una decisión pendiente es pending_action.approval, nunca actor. Ramifica por el handoff. puesta_en_vivo es el caso contrario y el que hay que mirar: todo lo que debía el cliente está hecho y el canario es nuestro, así que no lleva pending_action en absoluto.
helpstring—Castellano. Una o dos frases que explican el paso a la persona que lo tiene que hacer — escritas para ella, no para ti. Relátalas textuales en vez de parafrasear: varias llevan la razón legal por la que un acto no se puede delegar.
action_urlstring,null—Una página alojada que puedes abrir AHORA MISMO, o null. Solo es no-null en la respuesta que acuñó el enlace (POST .../onboarding/links, o POST .../mandato con cuerpo vacío): las páginas alojadas se alcanzan con un token de capacidad guardado solo como hash, así que ninguna lectura puede reconstruir la dirección. En GET .../onboarding es null. Antes era no-null en cada paso en action_required, construido desde la clave del paso — y esa URL es rechazada por la página a la que apunta. pending_action.link es el campo a usar: nombra el llamado que acuña una URL que sí funciona.
linkobject,null—QUÉ ESTÁ HACIENDO EL ENLACE ALOJADO DE ESTE PASO (contrato C3, 2026-09). Exactamente el objeto documentado en PendingAction.link — status, minted_at, expires_at, opened_at, requested_by, mint (nulo mientras el enlace está live u opened), ttl_seconds, reusable — publicado en TODOS los pasos a los que se puede mandar a una persona, no sólo en el que nombra blocked_on. Es null en los pasos que son nuestros, donde no hay a quién mandar. Antes sólo se publicaba dentro de pending_action, que existe en un paso a la vez, así que un agente que hubiera acuñado enlaces para dos pasos podía leer el estado de uno solo. Ésa es justamente la pregunta que este contrato existe para responder, porque volver a acuñar REVOCA la URL que alguien puede tener en la mano.
pending_actionPendingAction | null—El invariante del §7.4, en el campo que de verdad lo puede cumplir: onboarding nunca le pide algo a una persona sin decirle qué HACER y cómo llegar a la página. No-null en cada paso customer que debe una acción — el que está en action_required y el que nombra blocked_on — y null en cada paso que no debe nada, y en los pasos que son nuestros: darle instrucciones a una persona sobre un trabajo que corremos sin supervisión sería peor que el silencio.
completed_atstring,null—
evidence_sourcesii_probe · own_record · no_producer · attested—Cómo se conoce la finalización de este paso (#395), así quien llama nunca tiene que adivinar si algo lo está observando de verdad. sii_probe — volvemos a leer el SII y el paso avanza con lo que dice. own_record — el acto queda en nuestras propias tablas, así que lo vemos en el momento. no_producer — NADA EN ESTE BUILD LO OBSERVA (habilitacion hoy, la habilitación de boletas en certBolElectDteInternet, una SPA del SII que no capturamos). Usa POST .../onboarding/steps/\{step\}/attest — nunca un recheck simple, que no puede confirmar lo que nada observa. attested — un paso no_producer que llegó a completed justamente por esa atestación. Es la única forma en que un paso así puede llegar a completed en este build.
executionautomatic · operator—CÓMO CORRE DE VERDAD UN PASO NUESTRO (2026-09, #487; se deriva por despliegue desde el 2026-09-07, #556). actor: facturia dice de quién es el paso y nada sobre si hay alguien mirando. Siete de los nuestros llegan al SII sólo con su cerca abierta —la postulación, las subidas del intercambio, el envío de las muestras_impresas, la Declaración de Cumplimiento, la habilitacion de boletas y el «Avanzar Siguiente Paso» que cierra set_de_pruebas y simulacion (una cerca, un acto, los dos pasos)— y mientras la cerca está cerrada, ese paso es una persona nuestra frente a un teclado. Los cinco primeros publicaban in_progress sin distinguirse de un trabajo en cola; la pareja del avance de etapa era peor y publicaba automatic derechamente mientras una persona hacía clic en un navegador. En cualquiera de los dos casos es la diferencia entre «esto queda durante la noche» y «esto queda cuando alguien se siente a hacerlo». NO ES UNA CONSTANTE DEL PRODUCTO: SE LEE POR DESPLIEGUE, de la misma variable de entorno que lee el ejecutor, así que el MISMO paso puede contestar operator en un despliegue y automatic en otro, y puede cambiar de un día para otro sin una versión nueva de la API. Ramifica por el valor que te llegó; no lo guardes como un hecho de la llave del paso. automatic en todo paso sin cerca, incluidos los del cliente, donde quiere decir «no hay nada NUESTRO pendiente en él». La autoridad sobre QUÉ cerca le toca a cada paso es el registro de candados (worker/src/onboarding/locks.ts), cuyas filas nombran el paso que cada cerca detiene; un test lo recorre contra el catálogo en los dos sentidos, así que una cerca que nombra un paso que nadie publica como de operador rompe el build, y al revés también.
eta_business_daysinteger—El p50 de ESTE paso, en días hábiles (2026-09, #487). estimated_ready_at arriba del cuerpo es la suma de todo lo que falta; esto es la línea de uno solo, para que un checklist pueda poner un número al lado de la fila que la persona está mirando. Es un p50, no una promesa. UN PASO CON execution: operator NUNCA PUBLICA MENOS DE 2 (2026-09-07, #556). Los números base del catálogo se descompusieron pensando en una cola —0 quiere decir «el SII contesta este sobre en minutos»— y ninguno describe a una persona nuestra buscando una hora para una sesión de trabajo. Así que mientras un paso espera a una, su p50 tiene piso de dos días hábiles, y vuelve al número base en cuanto la cerca de ese paso se abre. Léelo del cable, paso por paso: se mueve junto con execution.
next_poll_atstring,null—CUÁNDO VOLVEMOS A CONSULTARLE AL SII POR ESTE PASO (2026-09-07, #563), o null cuando no hay ninguna consulta agendada. Va en todos los pasos, y es el campo que hace honesto a un paso in_progress. Un paso cuya última sonda no aprendió nada —el SII no contestó, o contestó «otorgado, todavía sin aplicar»— era indistinguible en el cable de uno que está por terminar, y un agente que leía la lista relataba «vamos bien» sobre una espera que nadie había cronometrado. Se muestra junto a blocked_reason: «el SII no contestó; volvemos a mirar a las 13:47». ES UN HORARIO, no una promesa: es cuando vence la cadencia de la sonda, y el reconciliador lo nota dentro de unos cinco minutos. POST .../onboarding/steps/\{step\}/recheck pregunta ahora en vez de esperarlo.
blocked_reasonobject—Por qué el paso está donde está, como dato estructurado con un code y un message en español. En los pasos que sólo puede hacer el representante legal trae además un packet —la página del SII que hay que abrir, el RUT de la empresa, una lista de chequeo y el recordatorio de que el paso necesita SU certificado, no el que nos dejaste en custodia. Pueden aparecer más llaves; ramifica por code. EN failed, action_required Y in_progress (2026-09-07, #563). Antes decía «en un paso failed o action_required», y el tercer caso es el que más le sirve a quien lee: un paso que está corriendo publica EL RESULTADO DE LA ÚLTIMA SONDA mientras esa sonda no lo resolvió — sii_unreachable (el SII no contestó), sii_propagation (el SII muestra el permiso otorgado y todavía no lo aplica), requirement_unmet (su propia tabla de usuarios todavía no le reconoce al emisor de la empresa lo que necesita; details.key nombra el permiso). Ninguno de los tres bloquea a la empresa, ninguno la nombra en blocked_on y ninguno pide una persona: llevan retriable: true y un hint en español, y next_poll_at al lado dice cuándo toca la próxima consulta. Ausente mientras un paso corriendo no tiene nada que reportar.

Onboarding.certifications[]

CampoTipoRequeridoNotas
tipo_dte33 · 34 · 39 · 41 · 46 · 52 · 56 · 61—33 factura afecta · 34 factura exenta · 39 boleta afecta · 41 boleta exenta · 46 factura de compra · 52 guía de despacho · 56 nota de débito · 61 nota de crédito. Las boletas (39/41) viajan por otro transporte del SII; eso es invisible desde esta API.
statusnot_started · in_progress · certified · failed—
notestring—

PendingAction

El traspaso de un acto que todavía necesita a una persona — el equivalente a un consentimiento OAuth en esta API.

POR QUÉ UNA PERSONA, Y NO ES LO QUE ESTE CAMPO DECÍA HASTA EL 2026-09-04. Decía que el SII «exige que el contribuyente o su representante legal se autentique personalmente, con SU PROPIO certificado», y que el que custodiamos «firma documentos; no autentica a nadie». Es falso, y era la frase que sostenía tres superficies. Una CA chilena emite certificados a PERSONAS NATURALES: el que está en la bóveda es el del representante legal (mandato 1.3), y una sesión TLS con certificado de cliente ante el SII autentica COMO SU TITULAR. Con ese certificado Facturia corrió una certificación de boletas completa, de punta a punta, desde el código, el 2026-09-04.

Lo que queda es un límite LEGAL, no técnico, y es más angosto. La cláusula 4.6 del mandato «Actos reservados al Cliente» reserva algunos actos al cliente; otros pocos son decisiones que sólo el cliente puede tomar por lo que el acto CUESTA — el mes en que la empresa deja de ser usuaria del sistema gratuito, el momento en que arranca un reloj de 24 horas del SII. Esos llegan como un bloque approval más abajo: el acto lo ejecutamos nosotros con el certificado en custodia y una persona lo autoriza en una página nuestra. El paso sii_postulacion tiene exactamente esa forma ahora — la postulación la presenta Facturia, después de que el titular aprueba.

Acá está todo lo que un consumidor necesita para trasladarle el encargo a una persona, así que no hace falta una segunda llamada ni improvisar una ruta de clics. Los textos que ve el cliente van en castellano porque quien ejecuta el acto es chileno y muchas veces no es el titular de la cuenta; los nombres de campo van en inglés como todo lo demás.

Aparece en steps[].pending_action de cualquier paso que le pida algo a una persona, se iza a onboarding.pending_action para el paso que nombra blocked_on, y viaja dentro de error.details.pending_action cuando una escritura de /v1 se rechaza porque hay uno pendiente. Es null cuando la espera es nuestra o del SII — que significa esperar, no actuar.

CampoTipoRequeridoNotas
objectstring—
actstring—EL ACTO, que no siempre es el paso. Un mismo paso puede quedarse en dos encargos distintos: certificate significa «compra un certificado en una CA» cuando no hay ninguno en ficha, y «otorga «Enviar Doctos.» en Mantención de usuarios» cuando el titular no tiene el permiso, que es otro portal y posiblemente otra persona. Ramifica por acá y no por step cuando te importe cuál de los dos es.
stepstring—El paso de la puesta en marcha del que cuelga este handoff.
empresa_idstring—
titlestring—Español, una línea: qué tiene que pasar.
summarystring—Español, para alguien que nunca ha oído hablar del SII.
action_urlstring,null—Una página alojada VIVA, o null. El mismo valor que el action_url del paso. Las páginas alojadas se alcanzan con un token de capacidad de un solo uso que se guarda solo como SHA-256, así que ninguna lectura puede reproducirlo: este campo es no-null solo en la respuesta que ACUÑÓ el enlace (POST /v1/empresas/\{id\}/onboarding/links, o POST /v1/empresas/\{id\}/mandato con cuerpo vacío). En cualquier otro lado es null y link, más abajo, dice cómo conseguir uno. Antes era una URL construida desde la clave del paso y nunca null - y esa URL es RECHAZADA por la página a la que apunta, que responde "Esta pagina se abre desde el enlace que te mandamos" sin un ?token=. Transmitirla mandaba a un representante legal a una página que le decía que le pidiera un enlace a alguien. No la reconstruyas; acúñala.
linkobject—Qué está haciendo el enlace alojado de este paso, y cómo conseguir uno. Siempre presente. El rechazo apunta al endpoint que lo arregla, igual que el resource_metadata de RFC 9728 apunta un 401 al flujo de consentimiento. Ejecuta mint tal cual viene; la respuesta trae la URL una sola vez. Primero status, después mint (2026-09, #459). action_url es null en toda lectura por construcción —el token sólo se guarda como SHA-256— así que un agente que releía este cuerpo veía una URL nula al lado de una receta de acuñe y volvía a acuñar. Para mandato eso REVOCA el enlace que el representante legal ya tiene en su correo; para cualquier otro paso retira una URL que alguien podría estar por abrir. Por eso mint es null exactamente mientras status sea live u opened: ya hay un enlace, y acuñar lo destruiría. Ramifica por status, y trata un mint nulo como «ya tienes uno», nunca como un error.
agent_actionsobject[]—Lo que TÚ puedes hacer acá, a diferencia de la persona. Siempre presente; normalmente vacío, porque estos actos ocurren en el SII bajo el certificado propio de una persona y una lista llena de llamados de aspecto prometedor sería peor que ninguna. El traspaso de cumplimiento es el que trae una entrada real: POST .../onboarding/go_live con cuerpo vacío, que acuña el enlace de aprobación para el titular. Ese llamado antes solo aparecía como prosa en español dentro de un prerequisites[].label - que este objeto te dice que transmitas TEXTUAL - así que los agentes se lo leían a un representante legal que no tiene llave de API.
sii_urlstring,null—La página del propio SII, cuando podemos nombrar una que no vaya a dar 404. null cuando no nos dijeron en qué ambiente corre la empresa — maullín es certificación y palena es producción, y un representante legal enviado al equivocado o rebota o ensaya la cosa real, así que preferimos no decir nada antes que adivinar.
performed_byobject—WHO, specifically. Never "the customer".
prerequisitesobject[]—Qué tener a mano ANTES de abrir la página.
instructionsobject[]—Ordenadas y concretas, contiguas desde 1. Trasládalas TEXTUALES: no las parafrasees ni les agregues pasos. Mira evidence para saber cuánto se pueden creer.
consequencesstring[]—Español. Qué pone en marcha ejecutar el acto, donde eso no es obvio y equivocarse sale caro: la declaración de cumplimiento inicia la obligación de Información Electrónica de Compras y Ventas de la empresa desde el mes en que se hace. Vacío en los actos que no tienen una consecuencia así.
verificationobject—Cómo nos enteramos de que ocurrió. Nunca «el cliente nos avisa».
estimated_minutesinteger—The human's time, at p50.
nextstring—Español. Qué pasa después, y si deben avisarnos.
evidencecaptured · documented · entry_point_only—Qué tan respaldada está la ruta de clics, publicado porque una instrucción segura y equivocada es peor que una honestamente vaga. captured — recorrimos esta cadena exacta y las páginas son fixtures en nuestro repositorio; las instrucciones nombran pantallas reales. documented — la documentación del propio SII nombra la ruta y nosotros no la hemos caminado; recibes el recorrido de menús, no un guion campo por campo. entry_point_only — podemos nombrar la puerta y nada de lo que hay detrás.
caveatstring,null—Español. No nulo cada vez que evidence no es captured, diciendo exactamente de qué no estamos seguros. TRASLÁDALO. Omitirlo convierte «la pantalla exacta puede diferir» en una promesa que no podemos cumplir.
copyobject,null—Copia por fase, para el único acto cuya página alojada y asistente del panel solían escribir su propia prosa y terminaban discrepando sobre quién declara (#393). null en todos los actos menos cumplimiento, que publica before_authorization, after_authorization y after_filed. ESTE ES EL AUTOR ÚNICO. Ambas superficies renderizan esto en vez de mantener su propia versión — que es exactamente cómo una llegó a decir «cuando el SII abra el trámite lo hacemos nosotros» mientras este mismo objeto, justo al lado, decía que el representante legal declara en persona (cláusula 4.6 b). Dice la verdad de este build: Facturia no declara (ALLOW_LIVE_CUMPLIMIENTO_WRITES es false, estructuralmente); una vez que el titular aprueba el mes, la copia dice que hay que declarar en el SII con el paquete — nunca que Facturia lo va a hacer.

Qué está haciendo el enlace alojado de este paso, y cómo conseguir uno. Siempre presente.

El rechazo apunta al endpoint que lo arregla, igual que el resource_metadata de RFC 9728 apunta un 401 al flujo de consentimiento. Ejecuta mint tal cual viene; la respuesta trae la URL una sola vez.

Primero status, después mint (2026-09, #459). action_url es null en toda lectura por construcción —el token sólo se guarda como SHA-256— así que un agente que releía este cuerpo veía una URL nula al lado de una receta de acuñe y volvía a acuñar. Para mandato eso REVOCA el enlace que el representante legal ya tiene en su correo; para cualquier otro paso retira una URL que alguien podría estar por abrir. Por eso mint es null exactamente mientras status sea live u opened: ya hay un enlace, y acuñar lo destruiría. Ramifica por status, y trata un mint nulo como «ya tienes uno», nunca como un error.

CampoTipoRequeridoNotas
statusnone · live · opened · consumed · expired · revoked—- none — nadie ha acuñado uno. Acuña. - live — acuñado, nadie lo ha abierto. Reenvía la URL que te dimos, o espera. - opened — alguien tiene la página abierta. Acuñar ahora le rompe la pestaña. - consumed — alguien actuó sobre él. Desde acá el paso avanza solo. - expired — se venció. Acuña uno nuevo. - revoked — se anuló o quedó reemplazado. Acuña uno nuevo.
minted_atstring,null—
expires_atstring,null—
opened_atstring,null—Cuándo alguien abrió la página por primera vez. No se mueve al recargar, y siempre es null para el mandato, cuya página no lo registra.
requested_byobject,null—Quién pidió el enlace, SEGÚN SE DESCRIBIÓ. Opcional en el acuñe (POST .../onboarding/links y POST .../mandato aceptan los dos requested_by: \{kind, name\}), y se le muestra a la persona que recibe el enlace: «Este enlace lo pidió Claude Desktop» en vez de «lo generó una integración». Es una AFIRMACIÓN, nunca un hecho autenticado: la credencial con la que realmente llegó el acuñe se registra aparte y no se publica acá.
mintobject,null—Cómo acuñar uno — null mientras status sea live u opened, porque acuñar ahí REEMPLAZA un enlace que alguien ya tiene en la mano.
ttl_secondsinteger—Cuánto dura el enlace acuñado. 7 días para el mandato, 30 para el resto.
reusableboolean—false para el mandato - su token es de un solo uso, porque produce un instrumento legal y un segundo clic debe decir «ya se firmó» en vez de ofrecer una segunda firma. true para cualquier otro paso: esos no producen ningún instrumento nuestro, así que el enlace es una dirección durable a un checklist que una persona puede releer antes de actuar.

PendingAction.agent_actions[]

CampoTipoRequeridoNotas
methodstring—
urlstring—Absolute; issue it as given.
bodyobject—
labelstring—Español, una línea: qué hace este llamado.
whenstring—Spanish, one line: when to make it, relative to the human's act.

PendingAction.performed_by

WHO, specifically. Never "the customer".

CampoTipoRequeridoNotas
rolerepresentante_legal · usuario_administrador · titular_certificado · titular_cuenta—
labelstring—Spanish. The person, not the party.
why_not_delegablestring—Español, una frase. Trasládala tal cual: «la ley te exige a ti en persona» convence a un firmante reticente mucho mejor que «el software no puede hacer esto».
legal_basisstring[]—Las citas en que se apoya esa frase. Textuales y verificables.

PendingAction.prerequisites[]

CampoTipoRequeridoNotas
keystring—
labelstring—

PendingAction.instructions[]

CampoTipoRequeridoNotas
orderinteger—
textstring—

PendingAction.verification

Cómo nos enteramos de que ocurrió. Nunca «el cliente nos avisa».

CampoTipoRequeridoNotas
methodsii_probe · own_record · no_producer—sii_probe — releemos el SII y el paso avanza con lo que diga. own_record — el acto cae en nuestras propias tablas, así que lo vemos ocurrir. no_producer — NADA EN ESTA VERSIÓN LO OBSERVA. Se nombra en vez de esconderse: un paso cuya conclusión nadie produce se quedaría en action_required para siempre mientras todos suponen que algo lo está mirando. No le digas a nadie que estamos vigilando uno de estos.
descriptionstring—
recheck_urlstring,null—El endpoint de re-lectura de este paso, como POST <ruta> — un método pegado a una ruta relativa que hay que separar a mano y anteponerle un host. Deprecado — usa recheck en su lugar, que nombra el mismo endpoint como \{method,url\}, absoluto (#419). Nulo cuando no hay nada que preguntar. Sigue viniendo, poblado, para quien ya lo lee.
recheckobject,null—El mismo endpoint que recheck_url, estructurado (#419, contrato (d)) — la forma que link.mint ya publica. null bajo la misma condición que recheck_url: no hay nada que preguntar (method de arriba es no_producer).
sii_latencystring—Español. Cuánto se demora el SII, más o menos, en mostrarlo.

OnboardingRecheck

La respuesta a una re-lectura. Deja constancia de que preguntaste y dice qué encolamos; por sí sola no verifica nada.

CampoTipoRequeridoNotas
objectstring—
empresa_idstring—
stepstring—
statuspending · in_progress · action_required · completed · failed · skipped—El estado del paso tal como estaba cuando llamaste, leído ANTES de encolar nada y devuelto SIN CAMBIOS. Leer de vuelta el mismo estado contra el que posteaste es el endpoint funcionando bien.
acceptedboolean—La declaración quedó registrada y los productores encolados.
verifiedboolean—Siempre false. Verificar es lo que hace el productor, después, contra el SII. Este campo existe para que nadie confunda aceptación con confirmación.
evidence_sourcesii_probe · own_record · no_producer · sandbox—Cómo se puede confirmar este paso. Ver PendingAction.verification.method.
contactobject,null—A DÓNDE IR cuando evidence_source: 'no_producer' significa que este mismo llamado no pudo confirmar nada (#395) — null para cualquier otro evidence_source. Se nombra como dato en vez de dejarlo solo en la prosa de message, así quien solo tiene esta respuesta igual puede actuar.
queuedstring[]—Tipos de job que esta llamada encoló.
already_queuedstring[]—Tipos de job ya agendados para esta ventana de cinco minutos. No es una falla: «ya estamos por mirar» es una respuesta verdadera a «por favor mira».
messagestring—Español, para la persona que hizo la afirmación.
pending_actionPendingAction | null—El handoff otra vez, para que quien se equivocó de encargo lo pueda releer.
onboarding_urlstring—Relativa. DEPRECADA (2026-09, #497) en favor de next, que es la misma lectura como \{method, url\} absoluta — sin un método que separar del principio ni un host que anteponer a mano. Sigue viniendo, igual que antes.
nextApiCall—UNA SOLA FORMA PARA «LA SIGUIENTE LLAMADA» (#419, #497). Un método más una url ABSOLUTA, y opcionalmente el cuerpo y las cabeceras que hay que mandar, de modo que un objeto que la lleva alcanza para hacer la petición: ningún método que separar del principio de un string, ningún host que anteponer a mano, ninguna ruta relativa que resolver contra una base que el llamador tiene que conocer. Cinco maneras distintas de decir esto llegaron a producción antes de que la idea tuviera nombre. Donde un campo viejo publicaba una de ellas (capabilities.next.api, contact.api, onboarding_url), ese campo SE MANTIENE y se sigue poblando, y al lado aparece un hermano ApiCall —aditivo, según la regla #419—; la prosa de cada uno dice por cuál ramificar.
requested_atstring—

En esta página