# Scopes and empresas (/en/docs/autenticacion/scopes)

## The scope table [#the-scope-table]

| Scope                              | Grants                                                                                                     |
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `documents:read`                   | Read emitted documents, PDF, XML and events                                                                |
| `documents:write`                  | Emit documents, retry, send by email                                                                       |
| `received:read`                    | Read the electronic tax documents (DTE) received from suppliers                                            |
| `received:write`                   | Accept / claim / acknowledge receipt                                                                       |
| `empresas:read`                    | Read empresas, onboarding, configuration, folios (the SII-authorised document numbers)                     |
| `empresas:write`                   | Create empresas, upload certificates, change configuration                                                 |
| `folios:write`                     | Ask the SII (Chile's tax authority) for a CAF, the signed block of folios; upload a CAF                    |
| `tax:read`                         | RCV (the SII's ledger of purchases and sales), F29 (the monthly return) proposals and filings, suggestions |
| `tax:write`                        | Characterise, file F29, confirm suggestions, enter expenses                                                |
| `webhooks:read` / `webhooks:write` | Webhook endpoints and event replay                                                                         |
| `usage:read`                       | Usage ledger                                                                                               |
| `keys:write`                       | Mint, rotate and revoke API keys. Root keys only; never combined with the scopes above                     |

A missing scope gets you `403 insufficient_scope`, with the required scope named in
`error.message`. You do not have to guess which one it was.

### The starter set [#the-starter-set]

An account's first key is minted with these six, which are exactly what the
[quickstart](/en/docs/empezar/quickstart) walks through end to end:

```
documents:read, documents:write, empresas:read, empresas:write, webhooks:read, webhooks:write
```

Not one more. `keys:write` is left out because it is a root key's power, and the four onboarding
scopes are left out because they grant acts with legal effect.

### The onboarding scopes [#the-onboarding-scopes]

Four narrow scopes govern the acts that authorise Facturia before the SII (available from v1.1).
**None of them belongs to the starter set, not by default and not for convenience**, and they are
requested one at a time:

| Scope               | Grants                                                                                                                                                                                                                                                                                                    |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mandato:write`     | Mint the signing link for the mandato — the power of attorney an empresa grants Facturia — and read it (`GET …/mandato`)                                                                                                                                                                                  |
| `mandato:attest`    | Post an acceptance **with a body** (the integrator collected the signature in its own flow; recorded as `acceptance_method: api`) and **revoke** the mandato with `DELETE …/mandato` — revocation is the act with the largest effect in the contract, and since v1.1 `empresas:write` no longer grants it |
| `onboarding:write`  | Mint links for human steps, ask for a re-read, mint the `go_live` approval link, upload the certificate                                                                                                                                                                                                   |
| `onboarding:attest` | Post the `go_live` authorisation **with a body**                                                                                                                                                                                                                                                          |

`empresas:write` implies the two `:write` scopes (`mandato:write`, `onboarding:write`) — so a key
that already exists can still mint the signing link and the human-step links. **It implies neither
`:attest` scope**: those are granted explicitly or not at all (ADR-0012 §4). A key holding only
`empresas:write` that tries `POST …/mandato` with a body gets `403 insufficient_scope:
mandato:attest`, exactly as if it held no onboarding scope at all. The difference between `:write`
and `:attest` is the difference between **asking a person to approve** and **asserting that they
already approved somewhere else**: the second is an attestation, it is recorded visibly as one, and
that is why it lives behind its own scope that is never granted by implication or by default. The
full path is in [From sandbox to production](/en/docs/empezar/produccion).

> **Trap — A root key cannot read documents**
>
> `keys:write` does not combine with any other scope. A root key (`sk_live_root_…` /
> `sk_test_root_…`) **cannot emit, cannot read documents and cannot touch tax data**: its only power
> is minting and revoking other keys.
>
> It is the most common confusion when starting out: you mint the root key, try it against
> `/v1/documents` and get a `403`. It is not broken — it is doing exactly what it should. Use it to
> mint an ordinary key, and use that one.

## The empresa allowlist [#the-empresa-allowlist]

On top of its scopes, every key carries a list of empresas:

* `["*"]` — every empresa in the account, including ones created later.
* An explicit list of RUT, Chile's taxpayer id numbers — `["77928532-4", "78281439-7"]`.

A key with exactly one RUT is an [empresa token](/en/docs/empezar/conceptos#token-de-empresa), and
the `Facturia-Empresa` header becomes optional.

The two axes apply together: a key with `documents:write` over `["77928532-4"]` can emit for that
empresa and for no other. Naming a different empresa returns `403 empresa_not_allowed` — not a
`404`, because you already know its RUT and pretending it does not exist protects nobody.

## Combinations worth keeping in your head [#combinations-worth-keeping-in-your-head]

| Shape                                     | Scopes                                     | Empresas                         | What for                                                |
| ----------------------------------------- | ------------------------------------------ | -------------------------------- | ------------------------------------------------------- |
| Backend of your own product               | `documents:*`, `received:*`, `tax:*`       | `["*"]`                          | A single key that operates every empresa in the account |
| One key per customer (ERP, vertical SaaS) | the minimum that customer needs            | a single RUT                     | The customer's system cannot address anything else      |
| Read-only for BI or reconciliation        | `documents:read`, `tax:read`, `usage:read` | `["*"]`                          | A key that cannot emit even if it leaks                 |
| Root                                      | `keys:write`                               | whatever it is going to delegate | Mint and revoke keys. Nothing else                      |
| Exploration and demos                     | anything, but `sk_test_`                   | anything                         | A test key is a capability boundary, not a policy       |

## Session mode: `Facturia-Mode` [#session-mode-facturia-mode]

The `/dashboard/*` surface authenticates with an account JWT instead of an `sk_` key, because a
browser must never hold an `sk_` key. A session has no prefix to carry its mode, so the mode travels
in a header:

```
Facturia-Mode: test
```

The default is `live`. The backend enforces it exactly as it would enforce a key's mode: in live
mode a sandbox empresa does not exist, and vice versa. An unrecognised value is a `400`, never a
silent fall back to `live`.

Two rules apply to a session that do not apply to a key:

* **A session can mint keys AND read data.** A root key cannot do the second. The difference is
  deliberate: a root key is a delegated credential, and a session is the account owner in person.
* **An account session is the only credential that mints a root key:**
  `POST /dashboard/api_keys` with `{"root": true}` (no `scopes`, no `empresas`) returns a
  `sk_live_root_…` / `sk_test_root_…` without opening any dashboard. A key — root or ordinary —
  that asks for `root: true` gets `403 privilege_escalation`: delegation goes one level deep, and a
  root key cannot mint another root key. See [Root keys and rotation](/en/docs/autenticacion/llaves-root).
