# From sandbox to production (/en/docs/empezar/produccion)

The quickstart ends with a boleta — Chile's consumer receipt — issued in the sandbox. This page is
what sits between that and a factura with real tax standing: **a state machine you can drive end to
end over the API**, with three or four moments where Chilean law demands that a person act and we hand
you the URL to give them.

Everything here lives behind a single read resource:

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

The exact shape of the body is in [`Onboarding`](/en/docs/referencia/esquemas#onboarding), and the
shape of the handoff to a person in [`PendingAction`](/en/docs/referencia/esquemas#pendingaction).

## The two lanes [#the-two-lanes]

An empresa emits through **one** of two channels, and the channel decides which list of steps it
walks. They are not two products: it is the same endpoint, the same fields and the same response.

|                                                          | **mercado**                                                                                | **SFG** (Sistema de Facturación Gratuito)                          |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------ |
| Who emits                                                | Our own engine, certified before the SII (Chile's tax authority)                           | The SII's free portal, operated by us                              |
| Certification                                            | Yes: set de pruebas, simulación, intercambio, muestras impresas                            | **None** — the SII already enabled the empresa when it enrolled it |
| Ambiente                                                 | `certificacion` until the resolución (the SII's authorizing ruling), `produccion` after it | Always `produccion`                                                |
| Folios                                                   | Our own timbraje — a CAF, the SII-signed block of folios                                   | The SII assigns them on each emission                              |
| Steps                                                    | 11 (10 without boletas)                                                                    | 7                                                                  |
| Copia cedible (the transferable copy used for factoring) | Yes (types 33 and 34)                                                                      | No                                                                 |

`emission_channel` is **how the empresa emits today**. `certification_track` is **what it is
certifying for**, and it can be something else, or `null`: an empresa can emit on SFG while it
certifies for mercado, and an empresa happy with the free portal forever holds the track at `null`.
Both fields come on the [`Empresa`](/en/docs/referencia/esquemas#empresa) object and are read-only
except the channel, which you declare yourself.

> **Render the steps you are handed**
>
> `onboarding.steps[]` carries one list or the other according to the active lane. Do not assume the
> length or the keys: walk whatever arrives, read each step's `actor` and show its `help`. A step with a
> key your code does not know is rendered all the same; one that disappeared was not required.

### The steps, with their `actor` [#the-steps-with-their-actor]

`actor` says whose step it is: `customer` is yours or your client's, `facturia` we run, `sii` means
waiting. Three steps of the mercado lane are ours and **still do not move without a person** —
`sii_postulacion`, `cumplimiento` and `habilitacion`: we file them with the custodied certificate,
after an approval — and what says so is `pending_action.approval`, not `actor`.

`execution` is the wire's other field ([`Onboarding`](/en/docs/referencia/esquemas#onboarding)):
`automatic` is a queued job and `operator` is a person of ours at a keyboard, which is the difference
between «it lands overnight» and «it lands when somebody sits down to do it».

It is not a fixed fact about the product: **`execution` goes back to `operator` in the API if that
step's fence is closed in production** (`execution` vuelve a `operator` en el API si la cerca de ese
paso se cierra en producción), and the step's `eta_business_days` and the body's
`estimated_ready_at` go up with it. The table below shows the production state — every fence open,
`automatic` on all eleven rows — so read it off the wire rather than from here. While a step says
`operator`, its `eta_business_days` never drops below 2 business days: a sitting is scheduled, not
queued.

**mercado lane** — eleven steps, or ten if you did not declare boletas:

| #  | `key`               | `actor`    | `execution` | What it is                                                                                                                                                               |
| -- | ------------------- | ---------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 1  | `mandato`           | `customer` | `automatic` | The representante legal (the company's legal representative) authorizes us to act before the SII. Revocable                                                              |
| 2  | `certificate`       | `customer` | `automatic` | The representante legal's `.pfx`, into the vault                                                                                                                         |
| 3  | `sii_postulacion`   | `facturia` | `automatic` | We file it with the custodied certificate; the account holder authorizes **the moment**                                                                                  |
| 4  | `set_de_pruebas`    | `facturia` | `automatic` | We emit the set the SII requires for each type                                                                                                                           |
| 5  | `simulacion`        | `facturia` | `automatic` | The simulation that precedes authorization                                                                                                                               |
| 6  | `intercambio`       | `facturia` | `automatic` | We prove the empresa receives and answers third-party documents                                                                                                          |
| 7  | `muestras_impresas` | `facturia` | `automatic` | We send the printed samples of each type                                                                                                                                 |
| 8  | `cumplimiento`      | `facturia` | `automatic` | We file it with the custodied certificate; the representante legal approves the act and, if it was not set before, the month                                             |
| 9  | `resolucion`        | `sii`      | `automatic` | The SII issues the resolución. Here you only wait                                                                                                                        |
| 10 | `habilitacion`      | `facturia` | `automatic` | Only if you declared boletas: we enable them end to end with the custodied certificate; the representante legal decides when to start and approves the final declaración |
| 11 | `folios`            | `facturia` | `automatic` | We request the first timbraje (CAF) of each type                                                                                                                         |

**SFG lane** — seven steps, no certification, plus two if you declare boletas:

| # | `key`                | `actor`    | `execution` | What it is                                                                                                                                                                                                      |
| - | -------------------- | ---------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | `mandato`            | `customer` | `automatic` | The same mandato, the same record, the same revocation                                                                                                                                                          |
| 2 | `credential`         | `customer` | `automatic` | A custodied certificate **or** delegation: our RUT (the Chilean tax id) as a usuario autorizado                                                                                                                 |
| 3 | `clave_centralizado` | `customer` | `automatic` | The Sistema de Facturación Centralizado password, through `POST /v1/empresas/{empresa_id}/sfg_credential`                                                                                                       |
| 4 | `usuario_autorizado` | `customer` | `automatic` | We check that our certificate can act for the empresa in Portal MIPYME                                                                                                                                          |
| 5 | `emisor_verificado`  | `facturia` | `automatic` | We confirm the empresa is enrolled and authorized to emit                                                                                                                                                       |
| 6 | `puesta_en_vivo`     | `facturia` | `automatic` | **Ours**: the canary and per-empresa enabling of real emission                                                                                                                                                  |
| 7 | `emitting`           | `facturia` | `automatic` | Done: we emit and receive through its free-system account                                                                                                                                                       |
| 8 | `habilitacion`       | `facturia` | `automatic` | Only if you declared boletas: the free portal has no boleta form, so our own engine issues them and we get them habilitated at the SII first. Two decisions are yours: when to start, and the final declaración |
| 9 | `folios`             | `facturia` | `automatic` | Only if you declared boletas: the first CAF for types 39/41. The free lane's facturas carry no CAF                                                                                                              |

> **Trap — A free-lane empresa's boletas go through our own engine**
>
> Portal MIPYME never had a boleta form, so a 39 or a 41 does **not** travel the free system: our own
> engine builds, signs and sends it, exactly as it does for a mercado empresa. The empresa's
> `emission_channel` does not decide this — the type does. What decides whether you can issue one is
> the SII's boleta habilitación (step 8) and the first CAF (step 9); until both are in,
> `POST /v1/documents` answers `422 empresa_not_emitting` naming the `habilitacion` step, and
> `capabilities.tipos` lists neither 39 nor 41. **Your facturas wait for none of it**: they go live as
> soon as step 7 is done.

> **Trap — `puesta_en_vivo` is never yours**
>
> It is an `actor: facturia` step with `stall_actor: facturia`: a wait with an estimate, not an
> `action_required`. If your assistant renders it as something the client can press, it will send a
> person to stare at a page where there is nothing to do. `habilitación` is reserved for acts of the
> SII; **puesta en vivo** is ours, and it happens in both lanes.

## Already emitting with another provider? [#already-emitting-with-another-provider]

The SII certifies the **taxpayer**, not the software. The resolución your empresa already holds stays
valid when it changes vendors, and certifying again would mean re-earning something it already has.
So you state it when you create the empresa — or later, with `PATCH /v1/empresas/{empresa_id}` — and
we check it.

```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` and `fecha` are the `NroResol` and `FchResol` from the carátula: they are printed on every
document your empresa has already issued with its current vendor. Nobody has to go to the SII for
them.

> **Warning**
>
> **Stating it skips nothing.** The field writes two columns and moves no step. Read it back:
> `resolucion.verified` is `false` until we have checked it, and until then the empresa walks the whole
> list.

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

### How we check it [#how-we-check-it]

By asking the SII for folios. As soon as your certificate is in custody, the machine asks the portal
to timbrar **one** of the document types you declared: the SII refuses a timbraje for a RUT it has
not authorized for that type, so a granted range is the proof and a refusal is the answer.

* **It grants them** → `resolucion.verified` turns `true`, the certification steps report `skipped`
  with the note that says why, and `estimated_ready_at` is today: all that is left is the rest of
  your folios.
* **It refuses** → `verified` stays `false` and the `sii_postulacion` step is yours again, with
  `blocked_reason.resolucion_claim` carrying the SII's own sentence. We never leave you without an
  explanation.

It is an act, not a reading, and there is no other way: no SII consulta separates a mercado
resolución from an enrolment in the free system, which is why we check it by using it.

### Boletas [#boletas]

The boleta electrónica habilitación is a separate authorization, with its own date and no number —
the SII's mail says «habilitados los documentos de boleta electrónica a contar del …» and that is
all of it. State it separately, alongside `doc_types` containing 39 or 41:

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

It is checked the same way, with a 39 or 41 timbraje. If it works, your boletas are enabled without
going through our habilitación chain; if not, the ordinary habilitación steps appear.

### The casilla de intercambio stays yours [#the-casilla-de-intercambio-stays-yours]

Changing the exchange address in Mi SII is an act of administration on your own account, and the
mandato expressly forbids us from touching it. Until you change it, **your suppliers keep sending
documents to your previous provider**: nothing is lost, but nothing arrives here either. Your address
with us is published per empresa as `reception_email`, and the `casilla_intercambio` act on
`GET /v1/empresas/{empresa_id}/onboarding` carries the instructions for the person who can do it.

## The path, step by step [#the-path-step-by-step]

### Account and first key [#account-and-first-key]

The same as the [quickstart](/en/docs/empezar/quickstart), with one change: `Facturia-Mode: live`
instead of `test`, and the full cold-start scope set.

```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":["*"]}'
```

Minting an `sk_live_` key from an unverified account answers `403 email_verification_required`.

### Build the whole thing against the sandbox first [#build-the-whole-thing-against-the-sandbox-first]

An `sk_test_` key walks the full cycle — emission, statuses, webhooks, PDF — without touching the SII
and at no cost. A sandbox empresa is born `emitting`: the machine on this page **is skipped entirely**
in test mode, and that is deliberate. Finish the integration there and change the prefix afterwards.

### The real empresa: `origen` and emission channel [#the-real-empresa-origen-and-emission-channel]

```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` is **required in live mode** ([`Origen`](/en/docs/referencia/esquemas#origen)): `gratuito` if
the empresa already emits through the SII's portal, `mercado` if it already uses a provider, `ninguno`
if it does not emit electronically yet. Omitting it with an `sk_live_` key is `400 invalid_field`.

`origen` **does not choose the channel** and does not decide what can be skipped. It is a statement
about the present that routes the wizard's copy; the certification skip is settled against the SII,
empirically. The emission channel is a different preference — stay on the free portal or move to the
certified engine — and that is why it is declared separately.

`ambiente` is **derived and read-only**: SFG is always producción; mercado is certificación until the
resolución arrives. Do not send it.

### Mandato: an approval link, not a checkbox [#mandato-an-approval-link-not-a-checkbox]

The mandato is signed by the **representante legal**, who very often is not the person holding the
account. So the default path is to mint a link and hand it over:

```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 '{}'
```

The response carries the `signing_url`. **It is returned once only** — from there on we keep nothing
but its hash. To find out how it is going, you read it:

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

which answers `status`, `terms_current` and `acceptance_method` (available from v1.1).

Scopes: minting and reading the mandato wants `mandato:write`. Posting an **acceptance with a body**
—the case of the integrator who collected the signature in their own flow— wants `mandato:attest`, is
recorded visibly as `acceptance_method: api`, never enters the default set of a new key, and is never
granted by implication: `empresas:write` implies `mandato:write` (so a key that already exists can
still mint and read the mandato) but not `mandato:attest`, which has to be asked for explicitly
(ADR-0012 §4). See [Scopes](/en/docs/autenticacion/scopes).

`GET /v1/mandato_terms` is public (no authentication) and today publishes the current version:

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

An acceptance against a version that is not current — yours went stale because the text changed —
is refused with `422 mandato_terms_changed`, not `mandato_terms_draft`: there is no draft state
blocking live-mode acceptance. `GET …/mandato` publishes `terms_current` with the current version and
its `content_hash`; if it does not match what you signed, you need to re-sign with a new
`signing_url` (see [«Revoke and re-sign»](#revoke-and-re-sign) below).

### Revoke and re-sign [#revoke-and-re-sign]

The mandato is revocable "at any time, without stating a reason, without penalty or charge"
(cláusula 7.1), through two doors:

* **With your key or your session:** `DELETE /v1/empresas/{empresa_id}/mandato` — `204` even when
  there was no live mandato, because you asked for a state ("I no longer have your authorisation")
  and that state is the outcome either way.
* **The representante legal, without a key:** the signing receipt e-mail carries a perpetual link to
  `.../onboarding/{empresa_id}/mandato_revocation?token=…`. The page asks for the representante
  legal's RUT **exactly as recorded on the mandato they signed** — the link never expires, so the RUT
  is what stops a forwarded e-mail from being a stop button — and calls
  `POST https://api.facturia.cl/onboarding/mandato/revoke` with
  `{"token": "…", "rep_legal_rut": "…", "revoke": true}`. If the link was lost, it is re-minted with
  `POST .../onboarding/mandato/revocation_link` (same two factors).

**What revoking does:** the empresa becomes `suspended` immediately — the same tick, not up to five
minutes later — and stops being able to emit; documents already emitted with tax value keep existing,
revocation does not delete them. Certificate custody is a separate decision (cláusula 7.1.1):
revoking the mandato does not destroy the `.pfx` — that is
`DELETE /v1/empresas/{empresa_id}/certificate`.

**Re-signing** restores the position the empresa held before the revocation (cláusula 7.1.3): if it
had already certified, it does not certify again just because it re-signed. The same signing-link
flow above is what re-signs.

**A terms version change**, the other reason to re-sign: if `GET /v1/mandato_terms` publishes a
version different from the one the current mandato accepted, any act that depends on the mandato
answers `422 mandato_terms_changed` instead of proceeding. The way out is the same: mint a fresh
`signing_url` with `POST …/mandato {}` and have the representante legal sign it again; the rest of
the empresa's position does not move.

### Certificate (mercado) or centralized password (SFG) [#certificate-mercado-or-centralized-password-sfg]

In the **mercado** lane, step 2 is the representante legal's digital certificate:

```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=…"
```

It is encrypted into a per-empresa vault and never comes back out; it is not an argument of the
emission call. It can be purged with `DELETE /v1/empresas/{empresa_id}/certificate`, which revokes the
custody and suspends emission.

In the **SFG** lane the equivalent step is the portal credential: the password of the SII's
**centralizado** certificate (not the clave tributaria), which Portal MIPYME asks for again at the
final step of every emission.

```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` on the same resource returns the same shape — only whether we hold it and since when, never
the password itself — and `DELETE /v1/empresas/{empresa_id}/sfg_credential` destroys it (`204`,
even when there was nothing to destroy: you asked for a state, "you hold nothing of mine", and that
is the outcome either way).

The `clave_centralizado` step (available from v1.1) is the one that names that handoff. It is
`actor: customer`, its `performed_by.role` is `usuario_administrador`, and its evidence is
`own_record`: it lands in our own tables in the same act as the `POST`, so we watch it happen
without asking the 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": "…" }
      }
    ]
  }
}
```

> **Trap — We never ask for the clave tributaria**
>
> The whole tax suite runs on a certificate-authenticated session against the SII's own data area. The
> Sistema de Facturación Centralizado password is not the clave tributaria — the taxpayer's own SII
> password — and does not replace it.

### The human steps: the handoff pattern [#the-human-steps-the-handoff-pattern]

When a step needs a person, the response does not say "blocked". It says who, where, what to do and
how we are going to find out — the same shape with which OAuth hands over authentication.

```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` is deprecated**
>
> `recheck_url` still ships, unchanged, so nothing reading it today breaks. It is a relative `"POST <path>"` string — you have to parse the method apart and prefix the host yourself. Use `recheck`
> above instead: the same endpoint, as `{method, url}`, absolute. The same applies to
> `error.details.onboarding_url` below: `error.details.onboarding` is the structured version.

Three fields decide what your agent does with this:

* **`performed_by.role`** — who you hand it to. `representante_legal`, `usuario_administrador`,
  `titular_certificado` or `titular_cuenta`. A handoff addresses a **role**, never a person: we do not
  know which of them your principal is.
* **`verification.method`** — how we find out. `sii_probe` (we re-read the SII), `own_record` (it
  lands in our tables) or `no_producer`, which means that **nothing in this version observes it** and
  that here a person has to confirm it. Do not tell anyone you are watching a `no_producer`.
* **`evidence`*&#x2A; and &#x2A;*`caveat`** — how well backed the click path is. If `evidence` is not
  `captured`, `caveat` is not null and says what we are unsure of. &#x2A;*Pass it on.**

`instructions[]` comes in Spanish and is passed on **verbatim**: do not paraphrase it and do not add
steps to it.

#### The link: `action_url` or `link.mint` [#the-link-action_url-or-linkmint]

`pending_action.action_url` (and `steps[].action_url`) is the URL of a **live** link —not expired, not
revoked— if one exists for that step. If none exists, it is `null`. It is never a URL that opens on
"the link is missing".

To mint one, the handoff itself says how (available from 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` accepts **any** `actor: customer` step in the
empresa's active catalog, whether from the mercado lane or from SFG. The one exception is `mandato`,
which has its own endpoint (`POST …/mandato` with an empty body), which is why its `link.mint` points
there with `body: {}`. Minting wants `onboarding:write`.

The `url` is returned **once only**; minting again revokes the previous one. You revoke it with
`DELETE /v1/empresas/{empresa_id}/onboarding/links/{step}`, which answers `204` whether or not one
existed.

#### The way back: `recheck` [#the-way-back-recheck]

When the person is done, your agent tells us and we go and look:

```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"
```

This **accepts a claim and produces no fact**. It carries no body, it writes no step's status and it
does not move the empresa. It enqueues the producer that reads the SII and returns the state exactly
as it stood at the moment of the call. If you post about a step in `action_required` and read
`action_required` back, that is the endpoint working: `verified` comes in the response and it is
&#x2A;*always `false`**. The full shape is in
[`OnboardingRecheck`](/en/docs/referencia/esquemas#onboardingrecheck).

It is not mandatory: `sii_probe` and `own_record` reach us on their own. `recheck` is there so you do
not have to wait for the next sweep.

#### When the step is OURS and all that is missing is a yes: `approve` [#when-the-step-is-ours-and-all-that-is-missing-is-a-yes-approve]

Three steps are ours to run and still do not start on their own, because each has a consequence
someone has to decide first: the **postulación** (an empresa on the Sistema de Facturación Gratuito
loses its active-user status the instant the SII authorizes it), the **Declaración de Cumplimiento**
(it closes certification and, from the month it is filed for, opens the Información Electrónica de
Compras y Ventas obligation) and the **boleta habilitación** (it opens a 24-hour SII clock). Those
steps carry an `approval` block in their `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"
}
```

The canonical way to take that decision is the hosted page (ADR-0012 §1): mint the step's link and
hand it to the account holder. If your integration collected the decision in its own flow, the door
is:

```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"}'
```

It wants `onboarding:attest` — never implied by `empresas:write` — the `act` must be the one the step
is currently asking for (otherwise `409 onboarding_approval_not_offered` or `400 invalid_field`), and
`decision: "reject"` is recorded exactly like an approval: «not yet» is an answer, and executors read
the MOST RECENT one.

### The Declaración de Cumplimiento: we file it [#the-declaración-de-cumplimiento-we-file-it]

The Declaración de Cumplimiento closes mercado certification. Once it is filed, the empresa is
operating in the electronic invoicing system and owes the Información Electrónica de Compras y
Ventas **from the month it is filed for**, whether or not it emits that month. The SII allows
postponing it, so the month stays a decision rather than an automatic effect of the rest of
certification.

**We file it ourselves, with the representante legal's certificate we hold in custody** — the same
one we sign your documents with. You do not have to log in to the SII site or fill out anything
there.

All that is left is for the representante legal to approve, on their own page. The role that can
approve this act is `rep_legal`, not just any `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"
}
```

That one click approves two things at once: the act before the SII, and — if the account holder had
not already set the month through the next section — the month itself. Before filing we check,
against the SII itself, that the holder of the custodied certificate is the same representante legal
the empresa has authorized there: that is what Res. Ex. SII N° 81 de 2005, resolutivo CUARTO d)
requires — that the representante legal be the one to declare "con su certificado digital" — and the
same check we already run for the boleta habilitación.

#### Setting the month ahead of time, separately: `go_live` [#setting-the-month-ahead-of-time-separately-go_live]

If the account holder wants to decide the month before there is anything to approve — or is a
different person from the representante legal — they can set it in advance:

```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 '{}'
```

The response carries `{ "approval_url": "…" }` (available from v1.1): the page where the account
holder sees exactly what they are approving — the month's consequences above, the authorize button
below. Minting wants `onboarding:write`.

**There has to be something to decide.** If the empresa has not reached the declaración yet we do
not mint a link that would open onto an empty page:

* `409 go_live_not_ready` — the mercado lane has not reached the decision.
  `error.details.blocked_on` names the step holding it up and `error.details.next` is the
  `{method, url}` that reports when it moves. Retry: it will be ready.
* `409 go_live_not_applicable` — this empresa is not certifying for mercado, so it has no
  declaración de cumplimiento at all. What gates its emission is `puesta_en_vivo`, which is ours.

Neither applies to `{"authorized": false}`: a decision that exists can always be withdrawn.

The form with a body —`{"authorized": true}` posted by an integrator who collected the approval in
their own flow— sits behind `onboarding:attest`, the same treatment as `mandato:attest`. Sending
`{"authorized": false}` withdraws the authorization as long as nothing has been filed; once it is
filed there is nothing left to withdraw and the answer is `409 cumplimiento_already_filed`.

**This authorization is free** (#530). It was one of the acts that required an active subscription
until 2026-09-04, and only on this door: the hosted `cumplimiento` page for the representante legal
answered `200` for the same unsubscribed empresa. It is a decision, not an emission. The
subscription is required where the value is: emitting, the credit note, requesting folios and
sending. An authorized empresa that has not paid emits nothing.

> **Facturia files it; the representante legal approves it**
>
> Since ADR-0013 (2026-09-06) we file the act before the SII with the custodied certificate — never
> the representante legal in person. What they do is approve, on their own page, an act that already
> identifies them: Res. Ex. SII N° 81 de 2005 requires that the representante legal be the one to
> declare "con su certificado digital", and that is exactly the certificate we hold in custody under
> the mandato.

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

Once the lane has finished its part, one step of ours is left: enabling real emission for that
empresa. In SFG it is the `puesta_en_vivo` step, after the canary; in mercado it is what follows the
folios. It is shown as a **wait with an estimate** and never as something the client has to do.

In the meantime, `Empresa.capabilities` tells the truth in a single read (available from v1.1):

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

It is the **same function** the emission gate uses to answer `422 empresa_not_emitting`, so the block
and the refusal cannot contradict each other. `blocked_by` takes `onboarding`, `subscription`,
`status`, `puesta_en_vivo` or `null`, and `next.who` is `agent`, `human` or `facturia`.

### Folios [#folios]

In the **mercado** lane the folios — the pre-authorized numbers each document has to carry — are a CAF
we request from the SII and that lives on the empresa:

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

`auto_request_folios` ships **on** by default — it is the only automation that is — and requests a
fresh timbraje when availability drops below the threshold. You can also ask for one yourself with
`POST /v1/folios/requests`, or upload your own CAF with `POST /v1/cafs`.

In the **SFG** lane there is no timbraje: the SII assigns the folio on each emission. `GET /v1/folios`
answers with the `sfg_managed` shape and `folio_preview` is `null` on drafts. That is not missing
data; it is how that channel works.

Details in [Folios and CAF](/en/docs/emision/folios).

### Webhooks [#webhooks]

The last thing worth having ready before the first real emission, because it is what saves you from
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"]
  }'
```

The `secret` (`whsec_…`) is returned once only. How to verify the signature is in
[Webhooks · Signature](/en/docs/webhooks/firma).

## When the gate closes anyway [#when-the-gate-closes-anyway]

If you emit ahead of time, the refusal carries the handoff inside it:

```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` is the same address as always, but relative — you have to prefix the host
yourself. `onboarding` (#419) carries the identical endpoint absolute, as `{method, url}`, the same
shape as `pending_action.link.mint`. Both still ship together; `onboarding_url` is marked
`deprecated` in the contract and is the one to stop reading.

It is the same [`PendingAction`](/en/docs/referencia/esquemas#pendingaction) object the onboarding
returns, so your error handling and your wizard can share the code. The codes you will see: `422
empresa_not_emitting`, `422 mandato_required`, `422 postulacion_required`, `422 onboarding_requirement_unmet`
and, if the empresa is otherwise able to emit and has no subscription, `402 subscription_required`. Eligibility is evaluated first (#475): while the checklist is unfinished the refusal is `422 empresa_not_emitting` with its handoff, never «pay». Each has its own entry in
[Errors](/en/docs/errores).

## Next step [#next-step]

- [Empresas (reference)](/en/docs/referencia/empresas): Every endpoint of the empresa, generated from the contract.

- [Schemas](/en/docs/referencia/esquemas): `Onboarding`, `PendingAction`, `OnboardingRecheck`, `Origen` and `Empresa`, field by field.
