# The sandbox (/en/docs/empezar/sandbox)

## What it is, and what it is not [#what-it-is-and-what-it-is-not]

|                                                                | **Test mode** (`sk_test_`) | **SII certification** (`sk_live_`, empresa on `ambiente: certificacion`)                      | **Production** (`sk_live_`, empresa on `ambiente: produccion`) |
| -------------------------------------------------------------- | -------------------------- | --------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| Traffic to the SII (Chile's tax authority)                     | none                       | maullin / apicert (the SII's test servers)                                                    | palena / api.sii.cl                                            |
| Certificate required                                           | no                         | yes (the empresa's real .pfx)                                                                 | yes                                                            |
| Real CAF required (the SII-signed block that folios come from) | no (virtual folios)        | yes (a maullin CAF)                                                                           | yes                                                            |
| Latency to a terminal state                                    | \~1 s, deterministic       | minutes to hours                                                                              | minutes to hours                                               |
| Outcome                                                        | forceable                  | whatever the SII says                                                                         | whatever the SII says                                          |
| Legal effect                                                   | none                       | none                                                                                          | full                                                           |
| Price                                                          | **free forever**           | the single plan: $19.990 CLP + IVA (Chile's VAT) a month **per empresa**, unlimited documents | the same plan, the same price                                  |

Test mode is a complete in-process simulation. It never opens a socket to the SII, so it is
instant, deterministic and immune to the SII's maintenance windows. SII certification is the real
SII on its test servers, used during go-live to certify one specific empresa — you will rarely drive
it directly.

## Determinism guarantees [#determinism-guarantees]

In test mode:

* Folios start at **1** for each `(empresa, tipo_dte)` and increment by one. No CAF is needed;
  `GET /v1/folios` reports a virtual range from 1 to 1.000.000.
* The `track_id` is a synthetic increasing 10-digit integer starting at `9000000001`.
* Documents settle `queued → sent → accepted` in roughly a second, firing every webhook a real
  document would fire.
* The XML is schema-valid, signed with Facturia's sandbox certificate, and carries a TED built from
  a sandbox CAF. **It will not validate against the SII** — that is the point.
* PDFs come with the real layout plus a `SIN VALOR TRIBUTARIO` watermark.
* Every object carries `"mode": "test"` stamped on it.

Test-mode rate limits are the same as live's, so a load test is representative.

## Forcing outcomes [#forcing-outcomes]

Add a `test` object to any issuance.

```bash
curl -sS https://api.facturia.cl/v1/documents \
  -H "Authorization: Bearer $FACTURIA_KEY" \
  -H "Facturia-Empresa: $EMPRESA" \
  -H "Content-Type: application/json" \
  -d '{
    "tipo_dte": 33,
    "receptor": {
      "rut": "76543212-K",
      "giro": "Venta al por mayor de alimentos",
      "address": { "street": "Los Militares 5001, of. 302", "comuna": "Las Condes", "city": "Santiago" }
    },
    "items": [{ "description": "Consultoría", "quantity": 1, "unit_price": 1200000 }],
    "test": { "outcome": "rejected", "reason_code": "TED-2-510", "settle_after_ms": 3000 }
  }'
```

| `test.outcome`           | Result                                                                              |
| ------------------------ | ----------------------------------------------------------------------------------- |
| `accepted` (the default) | `status: accepted`, SII status `DOK`                                                |
| `reparo`                 | `status: reparo` (accepted with objections), SII status `DNK`, a `rejection_reason` |
| `rejected`               | `status: rejected`, envelope status `RCT`, `reason_code` returned as sent           |
| `failed`                 | `status: failed` before transmitting (simulates a build or signing failure)         |
| `stuck`                  | stays at `sent` forever (to test your polling timeouts)                             |

> **Trap — The test field is an alarm, not an option**
>
> Sending the `test` object with a live key is rejected with `400 test_field_in_live_mode`. It is a
> deliberate trip wire: simulation code cannot quietly run against the SII.

If your client cannot add fields, the magic receptor RUTs do the same job:

| Receptor RUT | Forces                                                                                           |
| ------------ | ------------------------------------------------------------------------------------------------ |
| `44444440-1` | `accepted`                                                                                       |
| `44444441-K` | `reparo`                                                                                         |
| `44444442-8` | `rejected`                                                                                       |
| `44444443-6` | `failed`                                                                                         |
| `44444444-4` | `stuck`                                                                                          |
| `66666666-6` | anonymous final consumer (valid in live as well, and only on boletas, Chile's consumer receipts) |

## Test helpers [#test-helpers]

The endpoints under `/v1/test_helpers` exist only for test keys (`403 test_mode_only` otherwise).
They let you drive the halves of the system the outside world normally drives.

```bash
# Inyectar un DTE de proveedor como si hubiera llegado a la casilla de la empresa
# (una empresa de sandbox no tiene casilla real: `reception_email` va en null)
curl -sS https://api.facturia.cl/v1/test_helpers/received_documents \
  -H "Authorization: Bearer $FACTURIA_KEY" \
  -H "Facturia-Empresa: $EMPRESA" \
  -H "Content-Type: application/json" \
  -d '{
    "tipo_dte": 33,
    "folio": 8812,
    "emisor": { "rut": "96790240-3", "legal_name": "Proveedor Austral SpA" },
    "issue_date": "2026-08-12",
    "totals": { "net": 450000, "iva": 85500, "total": 535500 }
  }'
```

```bash
# Sembrar filas de RCV en un período para que la propuesta de F29 tenga sobre qué calcular
curl -sS https://api.facturia.cl/v1/test_helpers/rcv/seed \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA" \
  -H "Content-Type: application/json" \
  -d '{"period":"2026-07","purchases":12,"sales":40}'

# Adelantar el reloj simulado de esta empresa
# (prueba la ventana de acuse de 8 días, el vencimiento de folios, los plazos del F29)
curl -sS https://api.facturia.cl/v1/test_helpers/clock \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA" \
  -H "Content-Type: application/json" \
  -d '{"advance_days":9}'

# Llevar una máquina de estados de puesta en marcha a cualquier estado sin esperar al SII
curl -sS https://api.facturia.cl/v1/test_helpers/empresas/emp_01K2R.../onboarding \
  -H "Authorization: Bearer $FACTURIA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"action_required","step":"sii_postulacion"}'

# Forzar UN paso: lo que el estado completo no alcanza a expresar
# (una prueba de certificación que falla para el 61 mientras el 33 pasa)
curl -sS https://api.facturia.cl/v1/test_helpers/empresas/emp_01K2R.../onboarding/steps \
  -H "Authorization: Bearer $FACTURIA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"step":"set_de_pruebas","tipo_dte":61,"status":"failed"}'
```

> **The clock helper is how you test the 8-day window**
>
> Inject a received document, advance the clock nine days and watch the commercial response stop
> accepting actions with `422 commercial_response_window_closed`. It is the only way to exercise that
> deadline without waiting more than a week. The window is explained in
> [The 8-day window](/en/docs/recepcion/ventana-8-dias).

## Upstream realities worth designing for anyway [#upstream-realities-worth-designing-for-anyway]

Above our own limits there are two SII realities that exist even though the sandbox does not
simulate them: the SII rate-limits without publishing its numbers, and it has scheduled maintenance
windows. Issuance is queued on our side, so a burst over 10 req/s is a client-side `429`, never a
lost document — but a document we have already accepted will sit in `queued` longer during an SII
outage. Build on webhooks, not on tight polling.

## Next step [#next-step]

- [From sandbox to production](/en/docs/empezar/produccion): What the sandbox skips: the two lanes, the mandato (the power of attorney to act before the SII), the human steps and going live.
