# De sandbox a producción (/docs/empezar/produccion)

El quickstart termina con una boleta emitida en el sandbox. Esta página es lo que falta entre eso y
una factura con valor tributario: **una máquina de estados que puedes recorrer entera por API**, con
tres o cuatro momentos en que la ley chilena exige que actúe una persona y nosotros te damos la URL
para dársela.

Todo lo de acá vive detrás de un solo recurso de lectura:

```bash
curl -sS https://api.facturia.cl/v1/empresas/emp_01K2R7Q4XW9M3B8ZC5YHTVJD6N/onboarding \
  -H "Authorization: Bearer $FACTURIA_KEY"
```

La forma exacta del cuerpo está en [`Onboarding`](/docs/referencia/esquemas#onboarding), y la del
handoff a una persona en [`PendingAction`](/docs/referencia/esquemas#pendingaction).

## Los dos carriles [#los-dos-carriles]

Una empresa emite por **uno** de dos canales, y el canal decide qué lista de pasos recorre. No son
dos productos: son el mismo endpoint, los mismos campos y la misma respuesta.

|               | **mercado**                                                    | **SFG** (Sistema de Facturación Gratuito)                    |
| ------------- | -------------------------------------------------------------- | ------------------------------------------------------------ |
| Quién emite   | Nuestro motor propio, certificado ante el SII                  | El portal gratuito del SII, operado por nosotros             |
| Certificación | Sí: set de pruebas, simulación, intercambio, muestras impresas | **Ninguna** — el SII ya habilitó a la empresa al inscribirla |
| Ambiente      | `certificacion` hasta la resolución, después `produccion`      | Siempre `produccion`                                         |
| Folios        | Timbraje propio (CAF)                                          | Los asigna el SII en cada emisión                            |
| Pasos         | 11 (10 sin boletas)                                            | 7                                                            |
| Copia cedible | Sí (tipos 33 y 34)                                             | No                                                           |

`emission_channel` es **cómo emite hoy** la empresa. `certification_track` es **para qué se está
certificando**, y puede ser otra cosa o `null`: una empresa puede emitir en SFG mientras certifica
para mercado, y una empresa contenta con el portal gratuito para siempre tiene la pista en `null`.
Los dos campos vienen en el objeto [`Empresa`](/docs/referencia/esquemas#empresa) y son de solo
lectura salvo el canal, que declaras tú.

> **Dibuja los pasos que te entregan**
>
> `onboarding.steps[]` trae una lista o la otra según el carril activo. No supongas el largo ni las
> llaves: recorre lo que venga, lee `actor` de cada paso y muestra `help`. Un paso con una llave que
> tu código no conoce se dibuja igual; uno que desapareció no era exigible.

### Los pasos, con su `actor` [#los-pasos-con-su-actor]

`actor` dice de quién es el paso: `customer` es tuyo o de tu cliente, `facturia` lo corremos
nosotros, `sii` es esperar. Tres pasos del carril de mercado son nuestros y **aun así no avanzan sin
una persona** —`sii_postulacion`, `cumplimiento` y `habilitacion`: los presentamos con el certificado
en custodia, después de una aprobación—, y lo que lo dice es `pending_action.approval`, no `actor`.

`execution` es el otro campo del cable ([`Onboarding`](/docs/referencia/esquemas#onboarding)):
`automatic` es un trabajo en cola y `operator` es una persona nuestra frente a un terminal, que es la
diferencia entre «queda durante la noche» y «queda cuando alguien se siente a hacerlo».

No es un dato fijo del producto: **`execution` vuelve a `operator` en el API si la cerca de ese paso
se cierra en producción**, y con ella sube el `eta_business_days` del paso y la fecha de
`estimated_ready_at`. La tabla de abajo muestra el estado de producción —todas las cercas abiertas,
`automatic` en las once filas—, así que léelo del cable y no de acá. Mientras un paso diga
`operator`, su `eta_business_days` no baja de 2 días hábiles: una sesión de trabajo se agenda, no se
encola.

**Carril mercado** — once pasos, o diez si no declaraste boletas:

| #  | `key`               | `actor`    | `execution` | Qué es                                                                                                                                                                              |
| -- | ------------------- | ---------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1  | `mandato`           | `customer` | `automatic` | El representante legal nos autoriza a actuar ante el SII. Revocable                                                                                                                 |
| 2  | `certificate`       | `customer` | `automatic` | El `.pfx` del representante legal, a la bóveda                                                                                                                                      |
| 3  | `sii_postulacion`   | `facturia` | `automatic` | La presentamos nosotros con el certificado en custodia; el titular autoriza **el momento**                                                                                          |
| 4  | `set_de_pruebas`    | `facturia` | `automatic` | Emitimos el set que el SII exige por cada tipo                                                                                                                                      |
| 5  | `simulacion`        | `facturia` | `automatic` | La simulación previa a la autorización                                                                                                                                              |
| 6  | `intercambio`       | `facturia` | `automatic` | Probamos que la empresa recibe y responde documentos de terceros                                                                                                                    |
| 7  | `muestras_impresas` | `facturia` | `automatic` | Enviamos las muestras impresas de cada tipo                                                                                                                                         |
| 8  | `cumplimiento`      | `facturia` | `automatic` | La presentamos nosotros con el certificado en custodia; el representante legal aprueba el acto y, si no estaba fijado antes, el mes                                                 |
| 9  | `resolucion`        | `sii`      | `automatic` | El SII dicta la resolución. Acá solo se espera                                                                                                                                      |
| 10 | `habilitacion`      | `facturia` | `automatic` | Solo si declaraste boletas: las habilitamos nosotros de principio a fin con el certificado en custodia; el representante legal decide cuándo empezar y aprueba la declaración final |
| 11 | `folios`            | `facturia` | `automatic` | Pedimos el primer timbraje (CAF) de cada tipo                                                                                                                                       |

**Carril SFG** — siete pasos, sin certificación, más dos si declaras boletas:

| # | `key`                | `actor`    | `execution` | Qué es                                                                                                                                                                                                         |
| - | -------------------- | ---------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | `mandato`            | `customer` | `automatic` | El mismo mandato, el mismo registro, la misma revocación                                                                                                                                                       |
| 2 | `credential`         | `customer` | `automatic` | Certificado en custodia **o** delegación: nuestro RUT como usuario autorizado                                                                                                                                  |
| 3 | `clave_centralizado` | `customer` | `automatic` | La clave del Sistema de Facturación Centralizado, por `POST /v1/empresas/{empresa_id}/sfg_credential`                                                                                                          |
| 4 | `usuario_autorizado` | `customer` | `automatic` | Comprobamos que nuestro certificado puede actuar por la empresa en el Portal MIPYME                                                                                                                            |
| 5 | `emisor_verificado`  | `facturia` | `automatic` | Confirmamos que la empresa está inscrita y autorizada a emitir                                                                                                                                                 |
| 6 | `puesta_en_vivo`     | `facturia` | `automatic` | **Nuestro**: el canario y la habilitación de emisión real por empresa                                                                                                                                          |
| 7 | `emitting`           | `facturia` | `automatic` | Listo: emitimos y recibimos por su cuenta del sistema gratuito                                                                                                                                                 |
| 8 | `habilitacion`       | `facturia` | `automatic` | Solo si declaraste boletas: el portal gratuito no tiene formulario de boleta, así que las emite nuestro motor y antes las habilitamos ante el SII. Dos decisiones tuyas: cuándo empezar y la declaración final |
| 9 | `folios`             | `facturia` | `automatic` | Solo si declaraste boletas: el primer timbraje (CAF) de la 39/41. Las facturas del carril gratuito no llevan CAF                                                                                               |

> **Trampa — Las boletas del carril gratuito salen por nuestro motor**
>
> El Portal MIPYME nunca tuvo formulario de boleta, así que una 39 o una 41 **no** viaja por el
> sistema gratuito: la construye, la firma y la envía el motor propio de Facturia, exactamente igual
> que la de una empresa del carril mercado. El `emission_channel` de la empresa no decide esto — lo
> decide el tipo. Lo que sí decide si puedes emitirla es la habilitación de boletas del SII (paso 8) y
> el primer CAF (paso 9); hasta que estén, `POST /v1/documents` responde `422 empresa_not_emitting`
> nombrando el paso `habilitacion`, y `capabilities.tipos` no lista la 39 ni la 41. **Tus facturas no
> esperan a nada de esto**: salen en cuanto termina el paso 7.

> **Trampa — `puesta_en_vivo` nunca es tuyo**
>
> Es un paso `actor: facturia` con `stall_actor: facturia`: una espera con estimación, no un
> `action_required`. Si tu asistente lo dibuja como algo que el cliente puede apretar, va a mandar a
> una persona a mirar una página donde no hay nada que hacer. `habilitación` está reservado para actos
> del SII; **puesta en vivo** es el nuestro, y ocurre en los dos carriles.

## ¿Ya emites con otro proveedor? [#ya-emites-con-otro-proveedor]

El SII certifica al **contribuyente**, no al software. La resolución que tu empresa ya tiene sigue
valiendo aunque cambies de proveedor, y volver a certificarse sería ganarse de nuevo algo que ya se
tiene. Así que se declara al crear la empresa — o después, con `PATCH /v1/empresas/{empresa_id}` —
y nosotros la comprobamos.

```bash
curl -X POST https://api.facturia.cl/v1/empresas \
  -H "Authorization: Bearer $FACTURIA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "rut": "77928532-4",
    "legal_name": "Connect SpA",
    "origen": "mercado",
    "doc_types": [33, 34, 61],
    "resolucion": { "numero": 80, "fecha": "2026-06-21" }
  }'
```

`numero` y `fecha` son el `NroResol` y el `FchResol` de la carátula: están impresos en cualquier
documento que tu empresa ya haya emitido con su proveedor actual. No hay que ir al SII a buscarlos.

> **Advertencia**
>
> **Declarar no salta nada.** El campo escribe dos columnas y no mueve ni un paso. Léelo de vuelta:
> `resolucion.verified` es `false` hasta que lo comprobemos, y mientras tanto la empresa camina la
> lista completa.

```json
{
  "resolucion": {
    "numero": 80,
    "fecha": "2026-06-21",
    "verified": false,
    "verified_at": null
  }
}
```

### Cómo lo comprobamos [#cómo-lo-comprobamos]

Pidiéndole folios al SII. En cuanto tenemos tu certificado en custodia, la máquina le pide al portal
que timbre **un** tipo de documento de los que declaraste: el SII rechaza el timbraje de un RUT que
no autorizó para ese tipo, así que un rango otorgado es la prueba y un rechazo es la respuesta.

* **Te los da** → `resolucion.verified` pasa a `true`, los pasos de certificación quedan `skipped`
  con la nota que dice por qué, y `estimated_ready_at` es hoy: lo único que falta es el resto de tus
  folios.
* **Te los niega** → `verified` se queda en `false` y el paso `sii_postulacion` vuelve a ser tuyo,
  con `blocked_reason.resolucion_claim` llevando la frase textual del SII. Nunca te dejamos sin
  explicación.

Es un acto, no una lectura, y no hay otra forma: ninguna consulta del SII distingue una resolución
de mercado de una inscripción en el sistema gratuito, y por eso lo comprobamos usándola.

### Boletas [#boletas]

La habilitación de boleta electrónica es otra autorización, con su propia fecha y sin número — el
correo del SII dice «habilitados los documentos de boleta electrónica a contar del …» y eso es todo.
Se declara aparte, junto a `doc_types` con 39 o 41:

```json
{ "resolucion_boletas": { "fecha": "2026-09-01" } }
```

Se comprueba igual, con un timbraje de 39 o 41. Si resulta, tus boletas quedan habilitadas sin pasar
por nuestra cadena de habilitación; si no, aparecen los pasos de habilitación normales.

### La casilla de intercambio sigue siendo tuya [#la-casilla-de-intercambio-sigue-siendo-tuya]

Cambiar el correo de intercambio en Mi SII es un acto de administración de tu cuenta y el mandato nos
prohíbe expresamente tocarlo. Mientras no lo cambies, **tus proveedores le siguen enviando los
documentos a tu proveedor anterior**: nada se pierde, pero no llega acá. Tu dirección con nosotros la
publica cada empresa en `reception_email`, y el acto `casilla_intercambio` de
`GET /v1/empresas/{empresa_id}/onboarding` trae las instrucciones para la persona que puede hacerlo.

## El camino, paso por paso [#el-camino-paso-por-paso]

### Cuenta y primera llave [#cuenta-y-primera-llave]

Lo mismo que el [quickstart](/docs/empezar/quickstart), con un cambio: `Facturia-Mode: live` en vez
de `test`, y el set de scopes completo del arranque en frío.

```bash
curl -sS https://api.facturia.cl/dashboard/api_keys \
  -H "Authorization: Bearer $TOKEN" \
  -H "Facturia-Mode: live" \
  -H "Content-Type: application/json" \
  -d '{"name":"produccion","scopes":["documents:read","documents:write","empresas:read","empresas:write","webhooks:read","webhooks:write"],"empresas":["*"]}'
```

Mintear una llave `sk_live_` desde una cuenta sin verificar responde `403 email_verification_required`.

### Construye entero contra el sandbox primero [#construye-entero-contra-el-sandbox-primero]

Una llave `sk_test_` recorre el ciclo completo — emisión, estados, webhooks, PDF — sin tocar el SII
y sin costo. La empresa de sandbox nace `emitting`: la máquina de esta página **se salta entera** en
modo test, y eso es a propósito. Termina la integración ahí y cambia el prefijo después.

### La empresa real: `origen` y canal de emisión [#la-empresa-real-origen-y-canal-de-emisión]

```bash
curl -sS https://api.facturia.cl/v1/empresas \
  -H "Authorization: Bearer $FACTURIA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "rut": "76000002-7",
    "legal_name": "Ejemplo SpA",
    "origen": "ninguno",
    "doc_types": [33, 34, 61]
  }'
```

`origen` es **obligatorio en modo real** ([`Origen`](/docs/referencia/esquemas#origen)): `gratuito`
si la empresa ya emite por el portal del SII, `mercado` si ya usa un proveedor, `ninguno` si todavía
no emite electrónicamente. Omitirlo con una llave `sk_live_` es `400 invalid_field`.

`origen` **no elige el canal** y no decide qué se puede saltar. Es una declaración sobre el presente
que enruta la copia del asistente; el salto de certificación se decide contra el SII, empíricamente.
El canal de emisión es una preferencia distinta —seguir en el portal gratuito o pasar al motor
certificado— y por eso se declara aparte.

`ambiente` es **derivado y de solo lectura**: SFG es siempre producción; mercado es certificación
hasta que llega la resolución. No lo mandes.

### Mandato: un enlace de aprobación, no una casilla [#mandato-un-enlace-de-aprobación-no-una-casilla]

El mandato lo firma el **representante legal**, que muy seguido no es quien tiene la cuenta. Así que
el camino por defecto es acuñar un enlace y dárselo:

```bash
# Cuerpo vacío = acuñar el enlace de firma.
curl -sS -X POST https://api.facturia.cl/v1/empresas/$EMPRESA_ID/mandato \
  -H "Authorization: Bearer $FACTURIA_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

La respuesta trae el `signing_url`. **Se devuelve una sola vez** — de ahí en adelante solo guardamos
su hash. Para saber cómo va, se lee:

```bash
curl -sS https://api.facturia.cl/v1/empresas/$EMPRESA_ID/mandato \
  -H "Authorization: Bearer $FACTURIA_KEY"
```

que responde `status`, `terms_current` y `acceptance_method` (disponible desde v1.1).

Scopes: acuñar y leer el mandato pide `mandato:write`. Postear una **aceptación con cuerpo** —el caso
del integrador que recogió la firma en su propio flujo— pide `mandato:attest`, se registra visible
como `acceptance_method: api` y nunca entra en el set por defecto de una llave nueva ni se concede
por implicación: `empresas:write` implica `mandato:write` (así una llave que ya existe sigue
pudiendo acuñar y leer el mandato) pero no `mandato:attest`, que hay que pedirlo explícitamente
(ADR-0012 §4). Ver [Scopes](/docs/autenticacion/scopes).

`GET /v1/mandato_terms` es público (sin autenticación) y hoy publica la versión vigente:

```json
{ "object": "mandato_terms", "version": "1.0", "status": "active", "effective_date": "2026-08-24" }
```

Una aceptación contra una versión distinta a la vigente —la tuya quedó vieja porque el texto
cambió— se rechaza con `422 mandato_terms_changed`, no con `mandato_terms_draft`: no hay ningún
estado de borrador que bloquee la aceptación en modo real. `GET …/mandato` publica `terms_current`
con la versión vigente y su `content_hash`; si no coincide con lo que firmaste, toca re-firmar con
un `signing_url` nuevo (ver [«Revocar y re-firmar»](#revocar-y-re-firmar) más abajo).

### Revocar y re-firmar [#revocar-y-re-firmar]

El mandato es revocable «en cualquier tiempo, sin expresión de causa, sin sanción ni cobro alguno»
(cláusula 7.1), y hay dos puertas:

* **Con tu llave o tu sesión:** `DELETE /v1/empresas/{empresa_id}/mandato` — `204` incluso si ya no
  había un mandato vivo, porque pediste un estado («ya no tengo autorización tuya») y ese es el
  resultado en los dos casos.
* **El representante legal, sin llave:** el correo-recibo de la firma lleva un enlace perpetuo a
  `.../onboarding/{empresa_id}/mandato_revocation?token=…`. La página pide el RUT del representante
  legal **tal como quedó registrado en el mandato que firmó** —el enlace no vence, así que el RUT es
  lo que evita que un correo reenviado sea un botón de parada— y llama
  `POST https://api.facturia.cl/onboarding/mandato/revoke` con
  `{"token": "…", "rep_legal_rut": "…", "revoke": true}`. Si el enlace se perdió, se remite con
  `POST .../onboarding/mandato/revocation_link` (mismos dos factores).

**Qué pasa al revocar:** la empresa queda `suspended` de inmediato —el mismo tick, no en cinco
minutos— y deja de poder emitir; los documentos ya emitidos con valor tributario siguen existiendo,
la revocación no los borra. La custodia del certificado es una decisión aparte (cláusula 7.1.1):
revocar el mandato no destruye el `.pfx`, eso es `DELETE /v1/empresas/{empresa_id}/certificate`.

**Volver a firmar** restablece la posición que la empresa tenía antes de revocar (cláusula 7.1.3): si
ya había certificado, no vuelve a certificar solo porque re-firmó. El mismo flujo del signing link de
arriba sirve para volver a firmar.

**Cambio de versión del texto**, el otro motivo para re-firmar: si `GET /v1/mandato_terms` publica
una versión distinta a la que el mandato vigente aceptó, cualquier acto que dependa del mandato
responde `422 mandato_terms_changed` en vez de proceder. La salida es la misma: acuña un `signing_url`
nuevo con `POST …/mandato {}` y que el representante legal lo firme de nuevo; el resto de la posición
de la empresa no se mueve.

### Certificado (mercado) o clave centralizada (SFG) [#certificado-mercado-o-clave-centralizada-sfg]

En el carril **mercado** el paso 2 es el certificado digital del representante legal:

```bash
curl -sS -X POST https://api.facturia.cl/v1/empresas/$EMPRESA_ID/certificate \
  -H "Authorization: Bearer $FACTURIA_KEY" \
  -F "file=@certificado.pfx" -F "password=…"
```

Queda cifrado en una bóveda por empresa y no vuelve a salir; no es un argumento de la llamada de
emisión. Se puede purgar con `DELETE /v1/empresas/{empresa_id}/certificate`, lo que revoca la
custodia y suspende la emisión.

En el carril **SFG** el paso equivalente es la credencial del portal: la clave del certificado
**centralizado** del SII (no la clave tributaria) que el Portal MIPYME vuelve a pedir en el paso
final de cada emisión.

```bash
curl -sS -X POST https://api.facturia.cl/v1/empresas/$EMPRESA_ID/sfg_credential \
  -H "Authorization: Bearer $FACTURIA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"clave_centralizado": "…"}'
```

```json
{
  "object": "sfg_credential",
  "empresa_id": "emp_01K2R7Q4XW9M3B8ZC5YHTVJD6N",
  "clave_centralizado": { "stored": true, "stored_at": "2026-08-24T12:03:11Z" },
  "purpose": "sfg_emission_centralizado_signing"
}
```

`GET` del mismo recurso responde la misma forma —solo si la tenemos guardada y desde cuándo, nunca
la clave misma— y `DELETE /v1/empresas/{empresa_id}/sfg_credential` la destruye (`204`, también
cuando no había nada que borrar: pediste un estado, «no tengo nada tuyo», y ese es el resultado en
los dos casos).

El paso `clave_centralizado` (disponible desde v1.1) es el que nombra ese handoff. Es `actor:
customer`, su `performed_by.role` es `usuario_administrador` y su evidencia es `own_record`: cae en
nuestras propias tablas en el mismo acto del `POST`, así que lo vemos ocurrir sin preguntarle al SII:

```json
{
  "blocked_on": "clave_centralizado",
  "pending_action": {
    "object": "pending_action",
    "act": "sfg_clave_centralizado",
    "step": "clave_centralizado",
    "title": "Entregarnos la clave de tu Sistema de Facturación Gratuito",
    "performed_by": {
      "role": "usuario_administrador",
      "label": "El usuario administrador de la empresa ante el SII, o quien tenga la clave del Sistema de Facturación Gratuito."
    },
    "verification": {
      "method": "own_record",
      "recheck_url": "POST /v1/empresas/{empresa_id}/onboarding/steps/clave_centralizado/recheck"
    },
    "agent_actions": [
      {
        "method": "POST",
        "url": "https://api.facturia.cl/v1/empresas/{empresa_id}/sfg_credential",
        "body": { "clave_centralizado": "…" }
      }
    ]
  }
}
```

> **Trampa — Nunca pedimos la clave tributaria**
>
> Toda la suite tributaria funciona con una sesión autenticada por certificado contra la zona de datos
> del propio SII. La clave del Sistema de Facturación Centralizado no es la clave tributaria y no la
> sustituye.

### Los pasos humanos: el patrón de handoff [#los-pasos-humanos-el-patrón-de-handoff]

Cuando un paso necesita a una persona, la respuesta no dice «bloqueado». Dice quién, dónde, qué
hacer y cómo nos vamos a enterar — la misma forma con que OAuth entrega la autenticación.

```json
{
  "object": "onboarding",
  "status": "action_required",
  "blocked_on": "sii_postulacion",
  "pending_action": {
    "object": "pending_action",
    "act": "sii_postulacion",
    "step": "sii_postulacion",
    "title": "Autorizar la postulación al sistema de facturación de mercado",
    "action_url": "https://app.facturia.cl/onboarding/…",
    "sii_url": "https://www.sii.cl/…",
    "performed_by": {
      "role": "titular_cuenta",
      "label": "El titular de la cuenta de Facturia",
      "why_not_delegable": "…",
      "legal_basis": ["Mandato Facturia, cláusula 4.2 a) «Certificación como emisor electrónico»", "…"]
    },
    "approval": {
      "act": "postulacion_autorizacion",
      "role": "titular_cuenta",
      "headline": "¿Presentamos la postulación ahora?",
      "consequences": ["Si hoy emites en el Sistema de Facturación Gratuito, tu empresa pierde su calidad de usuario activo…"],
      "button_label": "Sí, preséntenla",
      "reject_label": "No todavía"
    },
    "instructions": [{ "order": 1, "text": "…" }],
    "verification": {
      "method": "sii_probe",
      "recheck_url": "POST /v1/empresas/{empresa_id}/onboarding/steps/sii_postulacion/recheck",
      "recheck": {
        "method": "POST",
        "url": "https://api.facturia.cl/v1/empresas/{empresa_id}/onboarding/steps/sii_postulacion/recheck"
      },
      "sii_latency": "…"
    },
    "evidence": "captured",
    "caveat": null
  }
}
```

> **`recheck_url` está deprecado**
>
> `recheck_url` sigue viniendo, tal cual, para no romper a nadie que ya lo lee. Es un string `"POST <path>"` relativo — hay que separar el método a mano y anteponer el host. Úsalo como el `recheck`
> de arriba: el mismo endpoint, como `{method, url}`, absoluto. Lo mismo pasa con
> `error.details.onboarding_url` más abajo: `error.details.onboarding` es la versión estructurada.

Tres campos deciden qué hace tu agente con esto:

* **`performed_by.role`** — a quién se lo entregas. `representante_legal`, `usuario_administrador`,
  `titular_certificado` o `titular_cuenta`. Un handoff se dirige a un **rol**, nunca a una persona:
  nosotros no sabemos cuál de ellos es tu principal.
* **`verification.method`** — cómo nos enteramos. `sii_probe` (releemos el SII), `own_record` (cae en
  nuestras tablas) o `no_producer`, que significa que **nada en esta versión lo observa** y que acá
  una persona tiene que confirmarlo. No le digas a nadie que estás vigilando un `no_producer`.
* **`evidence`*&#x2A; y &#x2A;*`caveat`** — qué tan respaldada está la ruta de clics. Si `evidence` no es
  `captured`, `caveat` no es nulo y dice de qué no estamos seguros. &#x2A;*Trasládalo.**

`instructions[]` va en español y se traslada **textual**: no la parafrasees ni le agregues pasos.

#### El enlace: `action_url` o `link.mint` [#el-enlace-action_url-o-linkmint]

`pending_action.action_url` (y `steps[].action_url`) es la URL de un enlace **vivo** —no vencido, no
revocado— si existe uno para ese paso. Si no existe, es `null`. Nunca es una URL que abra «falta el
enlace».

Para acuñar uno, el propio handoff dice cómo (disponible desde v1.1):

```json
"link": {
  "mint": {
    "method": "POST",
    "url": "https://api.facturia.cl/v1/empresas/{empresa_id}/onboarding/links",
    "body": { "step": "sii_postulacion" }
  },
  "ttl_seconds": 2592000,
  "reusable": true
}
```

`POST /v1/empresas/{empresa_id}/onboarding/links` acepta **cualquier** paso `actor: customer` del
catálogo activo de la empresa, sea del carril mercado o del SFG. La única excepción es `mandato`, que
tiene su propio endpoint (`POST …/mandato` con cuerpo vacío) y por eso su `link.mint` apunta ahí con
`body: {}`. Acuñar pide `onboarding:write`.

La `url` se devuelve **una sola vez**; re-acuñar revoca la anterior. Se revoca con
`DELETE /v1/empresas/{empresa_id}/onboarding/links/{step}`, que responde `204` haya existido o no.

#### La vuelta: `recheck` [#la-vuelta-recheck]

Cuando la persona terminó, tu agente nos lo dice y nosotros vamos a mirar:

```bash
STEP=sii_postulacion

curl -sS -X POST \
  https://api.facturia.cl/v1/empresas/$EMPRESA_ID/onboarding/steps/$STEP/recheck \
  -H "Authorization: Bearer $FACTURIA_KEY"
```

Esto **acepta una declaración y no produce ningún hecho**. No lleva cuerpo, no escribe el estado de
ningún paso y no mueve la empresa. Encola al productor que lee el SII y devuelve el estado tal como
estaba al momento de llamar. Si posteas sobre un paso en `action_required` y lees `action_required`
de vuelta, eso es el endpoint funcionando: `verified` viene en la respuesta y &#x2A;*siempre es `false`**.
La forma completa está en [`OnboardingRecheck`](/docs/referencia/esquemas#onboardingrecheck).

No es obligatorio: `sii_probe` y `own_record` nos llegan solos. `recheck` sirve para no esperar el
próximo barrido.

#### Cuando el paso es NUESTRO y sólo falta que alguien diga que sí: `approve` [#cuando-el-paso-es-nuestro-y-sólo-falta-que-alguien-diga-que-sí-approve]

Tres pasos los ejecutamos nosotros y aun así no arrancan solos, porque cada uno tiene una consecuencia
que alguien tiene que decidir primero: la **postulación** (una empresa del Sistema de Facturación
Gratuito pierde su calidad de usuario activo en cuanto el SII la autorice), la **Declaración de
Cumplimiento** (cierra la certificación y abre, desde el mes que se declare, la obligación de
Información Electrónica de Compras y Ventas) y la **habilitación de boletas** (abre un reloj de 24
horas del SII). Esos pasos traen un bloque `approval` en su `pending_action`:

```json
"approval": {
  "act": "postulacion_autorizacion",
  "role": "titular_cuenta",
  "headline": "¿Presentamos la postulación ahora?",
  "consequences": ["…lo que hay que leer ANTES de apretar…"],
  "button_label": "Sí, preséntenla",
  "reject_label": "No todavía"
}
```

La forma canónica de tomar esa decisión es la página alojada (ADR-0012 §1): acuña el enlace del paso
y entrégaselo al titular. Si tu integración recogió la decisión en su propio flujo, la puerta es:

```bash
curl -sS -X POST \
  https://api.facturia.cl/v1/empresas/$EMPRESA_ID/onboarding/steps/$STEP/approve \
  -H "Authorization: Bearer $FACTURIA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"act": "postulacion_autorizacion", "decision": "approve"}'
```

Pide `onboarding:attest` —nunca implicado por `empresas:write`—, el `act` tiene que ser el que el
paso está pidiendo ahora (si no, `409 onboarding_approval_not_offered` o `400 invalid_field`), y
`decision: "reject"` se registra igual que una aprobación: «no todavía» es una respuesta, y los
ejecutores leen la MÁS RECIENTE.

### La Declaración de Cumplimiento: la presentamos nosotros [#la-declaración-de-cumplimiento-la-presentamos-nosotros]

La Declaración de Cumplimiento cierra la certificación de mercado. Al presentarla, la empresa queda
operando en el sistema de facturación electrónica y debe la Información Electrónica de Compras y
Ventas **desde el mes que se declare**, emita o no emita ese mes. El SII permite postergarla, así que
el mes sigue siendo una decisión y no un efecto automático del resto de la certificación.

**La presentamos nosotros, con el certificado del representante legal que tenemos en custodia** — el
mismo con el que firmamos tus documentos. No tienes que entrar al sitio del SII ni completar ningún
formulario allá.

Lo único que falta es que el representante legal apruebe, en su propia página. El rol que puede
aprobar este acto es `rep_legal`, no cualquier `titular_cuenta`:

```json
"approval": {
  "act": "cumplimiento_declaracion",
  "role": "rep_legal",
  "headline": "¿Presentamos la Declaración de Cumplimiento?",
  "consequences": [
    "Tu empresa queda operando en el sistema de facturación electrónica desde el mes indicado abajo.",
    "Desde ese mes debes la Información Electrónica de Compras y Ventas, emitas o no."
  ],
  "button_label": "Sí, preséntenla",
  "reject_label": "No todavía"
}
```

Ese único clic aprueba dos cosas a la vez: el acto ante el SII, y —si el titular no había fijado el
mes antes por la vía de la sección siguiente— el mes mismo. Antes de presentar comprobamos, contra el
propio SII, que el titular del certificado en custodia es el mismo representante legal que la empresa
tiene autorizado allá: es lo que exige la Res. Ex. SII N° 81 de 2005, resolutivo CUARTO d) — que sea
el representante legal quien declare «con su certificado digital» —, y la misma comprobación que ya
corrimos para la habilitación de boletas.

#### Fijar el mes antes, por separado: `go_live` [#fijar-el-mes-antes-por-separado-go_live]

Si el titular quiere decidir el mes antes de que exista algo que aprobar —o es una persona distinta
del representante legal—, puede fijarlo por adelantado:

```bash
# Cuerpo vacío = acuñar el enlace de aprobación para el titular.
curl -sS -X POST https://api.facturia.cl/v1/empresas/$EMPRESA_ID/onboarding/go_live \
  -H "Authorization: Bearer $FACTURIA_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

La respuesta trae `{ "approval_url": "…" }` (disponible desde v1.1): la página donde el titular ve
exactamente qué está aprobando —las consecuencias del mes arriba, y el botón de autorizar debajo—.
Acuñar pide `onboarding:write`.

**Tiene que haber algo que decidir.** Si la empresa todavía no llegó a la declaración, no acuñamos
un enlace que abriría una página vacía:

* `409 go_live_not_ready` — el carril de mercado aún no llega a la decisión. `error.details.blocked_on`
  dice qué paso lo está deteniendo y `error.details.next` es el `{method, url}` que avisa cuando se
  mueve. Vuelve a intentarlo: va a estar listo.
* `409 go_live_not_applicable` — la empresa no se está certificando para mercado, así que no tiene
  declaración de cumplimiento. Lo que abre su emisión es la `puesta_en_vivo`, que es nuestra.

Ninguno de los dos aplica a `{"authorized": false}`: una decisión que existe siempre se puede retirar.

La forma con cuerpo —`{"authorized": true}` posteado por un integrador que recogió la aprobación en
su propio flujo— queda detrás de `onboarding:attest`, el mismo trato que `mandato:attest`. Mandar
`{"authorized": false}` retira la autorización mientras no se haya declarado nada; después de
declarada no queda nada que retirar y se responde `409 cumplimiento_already_filed`.

**Esta autorización es gratis** (#530). Estuvo entre los actos que exigían suscripción activa hasta
el 2026-09-04, y solo por esta puerta: la página alojada de `cumplimiento` para el representante
legal respondía `200` para la misma empresa sin suscripción. Es una decisión, no una emisión. La
suscripción se exige donde está el valor: emitir, la nota de crédito, pedir folios y enviar. Una
empresa autorizada que no ha pagado no emite nada.

> **La presenta Facturia; el representante legal la aprueba**
>
> Desde ADR-0013 (2026-09-06) el acto ante el SII lo presentamos nosotros con el certificado en
> custodia — nunca el representante legal en persona. Lo que él hace es aprobar, en su propia página,
> un acto que ya lo identifica: la Res. Ex. SII N° 81 de 2005 exige que sea el representante legal quien
> declare «con su certificado digital», y ese es exactamente el certificado que tenemos en custodia bajo
> el mandato.

### Puesta en vivo [#puesta-en-vivo]

Después de que el carril terminó lo suyo, queda un paso nuestro: habilitar la emisión real de esa
empresa. En SFG es el paso `puesta_en_vivo`, después del canario; en mercado es lo que sigue a los
folios. Se muestra como una **espera con estimación** y nunca como algo que el cliente deba hacer.

Mientras tanto, `Empresa.capabilities` dice la verdad en una lectura (disponible desde v1.1):

```json
"capabilities": {
  "can_emit": false,
  "tipos": [],
  "blocked_by": "puesta_en_vivo",
  "next": { "who": "facturia" }
}
```

Es la **misma función** que usa la puerta de emisión para responder `422 empresa_not_emitting`, así
que el bloque y el rechazo no se pueden contradecir. `blocked_by` toma `onboarding`, `subscription`,
`status`, `puesta_en_vivo` o `null`, y `next.who` es `agent`, `human` o `facturia`.

### Folios [#folios]

En el carril **mercado** los folios son un CAF que le pedimos al SII y que vive en la empresa:

```bash
curl -sS https://api.facturia.cl/v1/folios \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA"
```

`auto_request_folios` viene **encendido** por defecto — es la única automatización que sí lo está — y
pide un timbraje nuevo cuando la disponibilidad baja del umbral. También puedes pedirlo tú con
`POST /v1/folios/requests`, o cargar un CAF propio con `POST /v1/cafs`.

En el carril **SFG** no hay timbraje: el SII asigna el folio en cada emisión. `GET /v1/folios`
responde con la forma `sfg_managed` y `folio_preview` es `null` en los borradores. Eso no es una
carencia de datos; es cómo funciona ese canal.

Detalles en [Folios y CAF](/docs/emision/folios).

### Webhooks [#webhooks]

Lo último que conviene tener listo antes de la primera emisión real, porque es lo que evita el
polling:

```bash
curl -sS https://api.facturia.cl/v1/webhook_endpoints \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.miempresa.cl/hooks/facturia",
    "enabled_events": ["document.accepted","document.rejected","document.reparo"]
  }'
```

El `secret` (`whsec_…`) se devuelve una sola vez. Cómo verificar la firma está en
[Webhooks · Firma](/docs/webhooks/firma).

## Cuando la puerta se cierra igual [#cuando-la-puerta-se-cierra-igual]

Si emites antes de tiempo, el rechazo trae el handoff adentro:

```json
{
  "error": {
    "type": "unprocessable_error",
    "code": "empresa_not_emitting",
    "message": "…",
    "hint": "…",
    "retriable": false,
    "details": {
      "onboarding_status": "sii_postulacion_pending",
      "onboarding_url": "/v1/empresas/{empresa_id}/onboarding",
      "onboarding": { "method": "GET", "url": "https://api.facturia.cl/v1/empresas/{empresa_id}/onboarding" },
      "pending_action": { "…": "…" }
    }
  }
}
```

`onboarding_url` es la misma dirección de siempre, pero relativa — hay que anteponerle el host.
`onboarding` (#419) trae el mismo endpoint absoluto, como `{method, url}`, igual que
`pending_action.link.mint`. Los dos siguen viniendo juntos; `onboarding_url` queda marcado
`deprecated` en el contrato y es el que hay que dejar de leer.

Es el mismo objeto [`PendingAction`](/docs/referencia/esquemas#pendingaction) del onboarding, así que
tu manejo de errores y tu asistente pueden compartir el código. Los códigos que vas a ver: `422
empresa_not_emitting`, `422 mandato_required`, `422 postulacion_required`, `422 onboarding_requirement_unmet`
y, si la empresa ya puede emitir por lo demás y no tiene suscripción, `402 subscription_required`.
La elegibilidad se evalúa primero (#475): mientras el checklist esté incompleto el rechazo es
`422 empresa_not_emitting` con su handoff, nunca «paga». Cada uno tiene su ficha en
[Errores](/docs/errores).

## Siguiente paso [#siguiente-paso]

- [Empresas (referencia)](/docs/referencia/empresas): Todos los endpoints de la empresa, generados desde el contrato.

- [Esquemas](/docs/referencia/esquemas): `Onboarding`, `PendingAction`, `OnboardingRecheck`, `Origen` y `Empresa`, campo por campo.
