Facturiadocs
API reference

Test helpers

Endpoints that only exist for test keys, and move the pieces the real world normally moves.

Base: https://api.facturia.cl/v1. Every endpoint in this chapter authenticates with Authorization: Bearer unless stated otherwise, and the error envelope is the one in Errors.

MethodPathWhat it does
POST/test_helpers/received_documentsInject a supplier DTE as if it had arrived at the reception address
POST/test_helpers/rcv/seedSeed RCV rows for a period so F29 propuestas have data
POST/test_helpers/clockAdvance this empresa's simulated clock
POST/test_helpers/empresas/{empresa_id}/onboardingDrive an empresa's onboarding to any state
POST/test_helpers/empresas/{empresa_id}/onboarding/stepsForce one onboarding step to a status
POST/test_helpers/documents/{document_id}/estadoForce the raw SII estado of an already-emitted sandbox document
POST/test_helpers/f29/proposalsInject a computed F29 propuesta
POST/test_helpers/f29/validationsForce the cross-validation verdict for a period

POST /test_helpers/received_documents

Inject a supplier DTE as if it had arrived at the reception address

Sandbox only: inject a supplier DTE as if it had just arrived at the reception address, without waiting for a real counterparty to send one.

curl -sS -X POST 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,"emisor":{"rut":"96790240-3","legal_name":"Proveedor Austral SpA"}}'

Parameters

ParameterInRequiredTypeNotes
Facturia-Empresaheader—stringTarget empresa: canonical RUT (77928532-4) or empresa id (emp_…). Required on every empresa-scoped call unless the key is an empresa token. This header is the ONLY mechanism: there is no ?empresa= query parameter and no empresa body field. Omitted where required → 400 empresa_required; outside the allowlist → 403 empresa_not_allowed.

Body

FieldTypeRequiredNotes
tipo_dte33 · 34 · 39 · 41 · 46 · 52 · 56 · 61yes33 factura afecta · 34 factura exenta · 39 boleta afecta · 41 boleta exenta · 46 factura de compra · 52 guía de despacho · 56 nota de débito · 61 nota de crédito. Boletas (39/41) travel a different SII transport; that is invisible through this API.
foliointeger—
emisorPartyyesA counterparty — a company OR a natural person. There is no company flag, and none is needed: a natural person (rut + legal_name, no giro, no address) is a valid receptor for a boleta (39/41). Its name is written to <RznSocRecep> and to the timbre's <RSR>, its RUT to <RUTRecep>/<RR> — the boleta a patient presents to an Isapre or a seguro complementario. Without legal_name the boleta names the RUT beside «Sin RUT»; without a receptor it is issued to the anonymous consumer 66666666-6. A nota de crédito (61) to a natural person is valid too, but it is a factura-family document: like every 61 it needs giro, address.street and address.comuna, even when it annuls a boleta. POST /v1/documents/\{id\}/credit_note copies the receptor from the original boleta, so store those three once with PATCH /v1/empresas/\{id\}/counterparties/\{rut\} before annulling, or the note is refused with 400 receptor_incomplete before any folio is spent.
issue_datestring—
totalsTotals—
itemsDocumentItem[]—
Responses
CodeMeaningBody
201The injected received document; received_document.created fires.ReceivedDocument
401Missing, malformed or revoked key.ErrorResponse
403Valid key, not permitted — scope, empresa allowlist, or mode.ErrorResponse
429Too many requests.ErrorResponse
defaultAn error not otherwise enumerated for this operation, in the same §5.5 envelope as every other refusal. Branch on error.code, never on the HTTP status alone or on error.message — a code this operation has not been seen to throw yet is still safer to route on than a string meant for a human.ErrorResponse

POST /test_helpers/rcv/seed

Seed RCV rows for a period so F29 propuestas have data

Sandbox only: seed the purchases/sales ledgers with synthetic entries, so RCV-dependent flows (characterization, F29) can be tested without a real SII sync.

curl -sS -X POST 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"}'

Parameters

ParameterInRequiredTypeNotes
Facturia-Empresaheader—stringTarget empresa: canonical RUT (77928532-4) or empresa id (emp_…). Required on every empresa-scoped call unless the key is an empresa token. This header is the ONLY mechanism: there is no ?empresa= query parameter and no empresa body field. Omitted where required → 400 empresa_required; outside the allowlist → 403 empresa_not_allowed.

Body

FieldTypeRequiredNotes
periodPeriodyesTax period YYYY-MM (America/Santiago). E.g.: "2026-07".
purchasesinteger—Default: 10.
salesinteger—Default: 10.
Responses
CodeMeaningBody
201The rows were written, in this request. No job, and nothing to poll: the sandbox has no worker to wait for (§4.1), so a seed that answered 202 with a job would be a promise of an asynchrony that does not exist here.RcvSeed
401Missing, malformed or revoked key.ErrorResponse
403Valid key, not permitted — scope, empresa allowlist, or mode.ErrorResponse
429Too many requests.ErrorResponse
defaultAn error not otherwise enumerated for this operation, in the same §5.5 envelope as every other refusal. Branch on error.code, never on the HTTP status alone or on error.message — a code this operation has not been seen to throw yet is still safer to route on than a string meant for a human.ErrorResponse

POST /test_helpers/clock

Advance this empresa's simulated clock

Exercises the 8-day acuse window, folio expiry and F29 deadlines without waiting.

curl -sS -X POST 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,"set_to":"…"}'

Parameters

ParameterInRequiredTypeNotes
Facturia-Empresaheader—stringTarget empresa: canonical RUT (77928532-4) or empresa id (emp_…). Required on every empresa-scoped call unless the key is an empresa token. This header is the ONLY mechanism: there is no ?empresa= query parameter and no empresa body field. Omitted where required → 400 empresa_required; outside the allowlist → 403 empresa_not_allowed.

Body

FieldTypeRequiredNotes
advance_daysinteger—
set_tostring—
Responses
CodeMeaningBody
200The empresa's new simulated date.object
401Missing, malformed or revoked key.ErrorResponse
403Valid key, not permitted — scope, empresa allowlist, or mode.ErrorResponse
429Too many requests.ErrorResponse
defaultAn error not otherwise enumerated for this operation, in the same §5.5 envelope as every other refusal. Branch on error.code, never on the HTTP status alone or on error.message — a code this operation has not been seen to throw yet is still safer to route on than a string meant for a human.ErrorResponse

POST /test_helpers/empresas/{empresa_id}/onboarding

Drive an empresa's onboarding to any state

Sandbox only: force the empresa's onboarding spine to a named onboarding_status, skipping every step between here and there.

curl -sS -X POST https://api.facturia.cl/v1/test_helpers/empresas/emp_01K2R6ZC4P8QW1VN7T3MHDY9EK/onboarding \
  -H "Authorization: Bearer $FACTURIA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"draft"}'

Parameters

ParameterInRequiredTypeNotes
empresa_idpathyesstring

Body

FieldTypeRequiredNotes
statusdraft · mandato_pending · mandato_signed · certificate_pending · certificate_verified · sii_postulacion_pending · certification_in_progress · cumplimiento_pending · resolucion_pending · folios_pending · emitting · action_required · blocked · suspendedyes
stepstring—
Responses
CodeMeaningBody
200The forced onboarding state.Onboarding
401Missing, malformed or revoked key.ErrorResponse
403Valid key, not permitted — scope, empresa allowlist, or mode.ErrorResponse
429Too many requests.ErrorResponse
defaultAn error not otherwise enumerated for this operation, in the same §5.5 envelope as every other refusal. Branch on error.code, never on the HTTP status alone or on error.message — a code this operation has not been seen to throw yet is still safer to route on than a string meant for a human.ErrorResponse

POST /test_helpers/empresas/{empresa_id}/onboarding/steps

Force one onboarding step to a status

The finer of the two onboarding levers. POST .../onboarding moves the empresa's STATE; this moves ONE STEP, which is how you produce the parts of §7.4 a state cannot express: a step at action_required with its action_url, a certification gate that failed for one tipo_dte while the others passed, a step skipped because the RUT was already an authorized emisor.

The four certification steps (set_de_pruebas, simulacion, intercambio, muestras_impresas) exist per document type. Omit tipo_dte to force every declared type at once; supply it to force one.

Returns the full onboarding object and fires the same empresa.onboarding.* webhook the real machine would.

curl -sS -X POST https://api.facturia.cl/v1/test_helpers/empresas/emp_01K2R6ZC4P8QW1VN7T3MHDY9EK/onboarding/steps \
  -H "Authorization: Bearer $FACTURIA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"step":"mandato","status":"pending"}'

Parameters

ParameterInRequiredTypeNotes
empresa_idpathyesstring

Body

FieldTypeRequiredNotes
stepmandato · certificate · sii_postulacion · set_de_pruebas · simulacion · intercambio · muestras_impresas · cumplimiento · resolucion · habilitacion · foliosyes
statuspending · in_progress · action_required · completed · failed · skippedyes
tipo_dte33 · 34 · 39 · 41 · 46 · 52 · 56 · 61—Only meaningful on a certification step. Must be one of the empresa's declared doc_types.
blocked_reasonobject—Structured reason to attach to a failed or action_required step.
Responses
CodeMeaningBody
200The onboarding object after the forced step.Onboarding
400Malformed or missing input.ErrorResponse
401Missing, malformed or revoked key.ErrorResponse
403Valid key, not permitted — scope, empresa allowlist, or mode.ErrorResponse
429Too many requests.ErrorResponse
defaultAn error not otherwise enumerated for this operation, in the same §5.5 envelope as every other refusal. Branch on error.code, never on the HTTP status alone or on error.message — a code this operation has not been seen to throw yet is still safer to route on than a string meant for a human.ErrorResponse

POST /test_helpers/documents/{document_id}/estado

Force the raw SII estado of an already-emitted sandbox document

test.outcome on POST /documents forces an outcome AT EMISSION. This forces one AFTERWARDS, and it is the only deterministic way to exercise the transition a webhook consumer actually cares about: without it, sent → accepted means racing the sandbox's own ~1 s settle, and stuck → accepted is unreachable.

IT WRITES THE ESTADO, NOT THE STATUS, on purpose. The public status is then derived by the ordinary mapping, so a test that drives a document to DNK proves the reparo mapping works rather than asserting it. The same document.* lifecycle event the real transition would fire is published, so a consumer under test receives exactly what production sends.

Sandbox only, checked three times: the key's mode, the empresa's flag, and the DOCUMENT's own is_sandbox — which is stamped at insert and immutable, so a real document stays real even if the empresa flag were flipped afterwards.

curl -sS -X POST https://api.facturia.cl/v1/test_helpers/documents/doc_01K2R7Q4XW9M3B8ZC5YHTVJD6N/estado \
  -H "Authorization: Bearer $FACTURIA_KEY" \
  -H "Facturia-Empresa: $EMPRESA" \
  -H "Content-Type: application/json" \
  -d '{"estado":"DOK"}'

Parameters

ParameterInRequiredTypeNotes
document_idpathyesstring
Facturia-Empresaheader—stringTarget empresa: canonical RUT (77928532-4) or empresa id (emp_…). Required on every empresa-scoped call unless the key is an empresa token. This header is the ONLY mechanism: there is no ?empresa= query parameter and no empresa body field. Omitted where required → 400 empresa_required; outside the allowlist → 403 empresa_not_allowed.

Body

FieldTypeRequiredNotes
estadoDOK · DNK · FAU · FNA · FAN · EMP · RCT · EPR · SOK · RPR · RLVyesThe SII estado to write, exactly as the poll executor would.
Responses
CodeMeaningBody
200The document, with its derived status.Document
400Malformed or missing input.ErrorResponse
401Missing, malformed or revoked key.ErrorResponse
403Valid key, not permitted — scope, empresa allowlist, or mode.ErrorResponse
404No such object under this key.ErrorResponse
429Too many requests.ErrorResponse
defaultAn error not otherwise enumerated for this operation, in the same §5.5 envelope as every other refusal. Branch on error.code, never on the HTTP status alone or on error.message — a code this operation has not been seen to throw yet is still safer to route on than a string meant for a human.ErrorResponse

POST /test_helpers/f29/proposals

Inject a computed F29 propuesta

Computing a propuesta is a WORKER job, and the worker is not part of the sandbox's promise (§4.1: test mode is "a complete in-process simulation"). Without this helper the sandbox can seed an RCV and then wait for ever for a propuesta only a running worker produces, which makes the whole §12.3/§12.4 surface untestable in test mode.

It writes the same row the real computation writes, so everything downstream — the serializer, the upload-file link, and above all the FILING GATE — reads an ordinary propuesta. The gate is not bypassed: it still reads total_payable and observations from this row, and it still demands a validation (see /test_helpers/f29/validations).

Re-seeding the same period updates it rather than creating a second one.

curl -sS -X POST https://api.facturia.cl/v1/test_helpers/f29/proposals \
  -H "Authorization: Bearer $FACTURIA_KEY" \
  -H "Facturia-Empresa: $EMPRESA" \
  -H "Content-Type: application/json" \
  -d '{"period":"2026-07"}'

Parameters

ParameterInRequiredTypeNotes
Facturia-Empresaheader—stringTarget empresa: canonical RUT (77928532-4) or empresa id (emp_…). Required on every empresa-scoped call unless the key is an empresa token. This header is the ONLY mechanism: there is no ?empresa= query parameter and no empresa body field. Omitted where required → 400 empresa_required; outside the allowlist → 403 empresa_not_allowed.

Body

FieldTypeRequiredNotes
periodPeriodyesTax period YYYY-MM (America/Santiago). E.g.: "2026-07".
summaryobject—Any omitted field defaults to 0, and total_payable to max(0, iva_debito - iva_credito) + iva_retenido + ppm.
codesobject[]—The F29 code rows. Omit for a plausible default set.
observationsobject[]—An observation with severity: error lands the propuesta review_required, which is what the filing gate refuses.
Responses
CodeMeaningBody
201The seeded propuesta.object
400Malformed or missing input.ErrorResponse
401Missing, malformed or revoked key.ErrorResponse
403Valid key, not permitted — scope, empresa allowlist, or mode.ErrorResponse
429Too many requests.ErrorResponse
defaultAn error not otherwise enumerated for this operation, in the same §5.5 envelope as every other refusal. Branch on error.code, never on the HTTP status alone or on error.message — a code this operation has not been seen to throw yet is still safer to route on than a string meant for a human.ErrorResponse

POST /test_helpers/f29/validations

Force the cross-validation verdict for a period

The real cross-validation SKIPS the SII fetch for a sandbox empresa and always records unavailable — correctly, because a sandbox empresa has no SII to contrast against. But unavailable is not validated, so without this helper the filing gate refuses EVERY sandbox filing with not_validated and the rest of §12.4 can never be reached.

Forcing the VERDICT rather than the filing is what keeps it honest: a test that drives a proposal to validated and then files proves the gate lets a validated proposal through, and one that leaves it unavailable proves the gate stops one that is not. The gate itself is untouched either way.

curl -sS -X POST https://api.facturia.cl/v1/test_helpers/f29/validations \
  -H "Authorization: Bearer $FACTURIA_KEY" \
  -H "Facturia-Empresa: $EMPRESA" \
  -H "Content-Type: application/json" \
  -d '{"period":"2026-07"}'

Parameters

ParameterInRequiredTypeNotes
Facturia-Empresaheader—stringTarget empresa: canonical RUT (77928532-4) or empresa id (emp_…). Required on every empresa-scoped call unless the key is an empresa token. This header is the ONLY mechanism: there is no ?empresa= query parameter and no empresa body field. Omitted where required → 400 empresa_required; outside the allowlist → 403 empresa_not_allowed.

Body

FieldTypeRequiredNotes
periodPeriodyesTax period YYYY-MM (America/Santiago). E.g.: "2026-07".
verdictvalidated · discrepant · unavailable—Default: "validated".
material_countinteger—Only on discrepant; how many material differences to record.
Responses
CodeMeaningBody
201The recorded validation.object
400Malformed or missing input.ErrorResponse
401Missing, malformed or revoked key.ErrorResponse
403Valid key, not permitted — scope, empresa allowlist, or mode.ErrorResponse
404No such object under this key.ErrorResponse
429Too many requests.ErrorResponse
defaultAn error not otherwise enumerated for this operation, in the same §5.5 envelope as every other refusal. Branch on error.code, never on the HTTP status alone or on error.message — a code this operation has not been seen to throw yet is still safer to route on than a string meant for a human.ErrorResponse

On this page