The sandbox
Test mode — what it guarantees, how to force a rejection or a reparo, and the test helpers that move the outside world.
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
In test mode:
- Folios start at 1 for each
(empresa, tipo_dte)and increment by one. No CAF is needed;GET /v1/foliosreports a virtual range from 1 to 1.000.000. - The
track_idis a synthetic increasing 10-digit integer starting at9000000001. - Documents settle
queued → sent → acceptedin 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 TRIBUTARIOwatermark. - 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
Add a test object to any issuance.
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
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.
# 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 }
}'# 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.
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
Concepts
Empresa, document, test/live mode and the Facturia-Empresa header — the four ideas that make the rest of the API legible.
From sandbox to production
The whole path of a real empresa — origen and channel, mandato, certificate or centralized password, the steps a person performs at the SII, the month approval, the puesta en vivo and the folios.