# Test helpers (/en/docs/referencia/test-helpers)

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](/en/docs/errores).

| Method | Path                                                                                                              | What it does                                                        |
| ------ | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| `POST` | [`/test_helpers/received_documents`](#post-test-helpers-received-documents)                                       | Inject a supplier DTE as if it had arrived at the reception address |
| `POST` | [`/test_helpers/rcv/seed`](#post-test-helpers-rcv-seed)                                                           | Seed RCV rows for a period so F29 propuestas have data              |
| `POST` | [`/test_helpers/clock`](#post-test-helpers-clock)                                                                 | Advance this empresa's simulated clock                              |
| `POST` | [`/test_helpers/empresas/{empresa_id}/onboarding`](#post-test-helpers-empresas-empresa-id-onboarding)             | Drive an empresa's onboarding to any state                          |
| `POST` | [`/test_helpers/empresas/{empresa_id}/onboarding/steps`](#post-test-helpers-empresas-empresa-id-onboarding-steps) | Force one onboarding step to a status                               |
| `POST` | [`/test_helpers/documents/{document_id}/estado`](#post-test-helpers-documents-document-id-estado)                 | Force the raw SII estado of an already-emitted sandbox document     |
| `POST` | [`/test_helpers/f29/proposals`](#post-test-helpers-f29-proposals)                                                 | Inject a computed F29 propuesta                                     |
| `POST` | [`/test_helpers/f29/validations`](#post-test-helpers-f29-validations)                                             | Force the cross-validation verdict for a period                     |

## `POST /test_helpers/received_documents` [#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.

```bash
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**

| Parameter          | In     | Required | Type   | Notes                                                                                                                                                                                                                                                                                                                                                       |
| ------------------ | ------ | -------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Facturia-Empresa` | header | —        | string | Target 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**

| Field         | Type                                                  | Required | Notes                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| ------------- | ----------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tipo_dte`    | `33` · `34` · `39` · `41` · `46` · `52` · `56` · `61` | yes      | 33 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.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `folio`       | integer                                               | —        |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `emisor`      | Party                                                 | yes      | A 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 `&lt;RznSocRecep&gt;` and to the timbre's `&lt;RSR&gt;`, its RUT to `&lt;RUTRecep&gt;`/`&lt;RR&gt;` — 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_date`  | string                                                | —        |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `totals`      | Totals                                                | —        |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `items`       | DocumentItem\[]                                       | —        |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| **Responses** |                                                       |          |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |

| Code      | Meaning                                                                                                                                                                                                                                                                                                 | Body             |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
| `201`     | The injected received document; `received_document.created` fires.                                                                                                                                                                                                                                      | ReceivedDocument |
| `401`     | Missing, malformed or revoked key.                                                                                                                                                                                                                                                                      | ErrorResponse    |
| `403`     | Valid key, not permitted — scope, empresa allowlist, or mode.                                                                                                                                                                                                                                           | ErrorResponse    |
| `429`     | Too many requests.                                                                                                                                                                                                                                                                                      | ErrorResponse    |
| `default` | An 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` [#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.

```bash
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**

| Parameter          | In     | Required | Type   | Notes                                                                                                                                                                                                                                                                                                                                                       |
| ------------------ | ------ | -------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Facturia-Empresa` | header | —        | string | Target 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**

| Field         | Type    | Required | Notes                                                       |
| ------------- | ------- | -------- | ----------------------------------------------------------- |
| `period`      | Period  | yes      | Tax period `YYYY-MM` (America/Santiago). E.g.: `"2026-07"`. |
| `purchases`   | integer | —        | Default: `10`.                                              |
| `sales`       | integer | —        | Default: `10`.                                              |
| **Responses** |         |          |                                                             |

| Code      | Meaning                                                                                                                                                                                                                                                                                                 | Body          |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
| `201`     | The 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       |
| `401`     | Missing, malformed or revoked key.                                                                                                                                                                                                                                                                      | ErrorResponse |
| `403`     | Valid key, not permitted — scope, empresa allowlist, or mode.                                                                                                                                                                                                                                           | ErrorResponse |
| `429`     | Too many requests.                                                                                                                                                                                                                                                                                      | ErrorResponse |
| `default` | An 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` [#post-test-helpers-clock]

**Advance this empresa's simulated clock**

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

```bash
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**

| Parameter          | In     | Required | Type   | Notes                                                                                                                                                                                                                                                                                                                                                       |
| ------------------ | ------ | -------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Facturia-Empresa` | header | —        | string | Target 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**

| Field          | Type    | Required | Notes |
| -------------- | ------- | -------- | ----- |
| `advance_days` | integer | —        |       |
| `set_to`       | string  | —        |       |
| **Responses**  |         |          |       |

| Code      | Meaning                                                                                                                                                                                                                                                                                                 | Body          |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
| `200`     | The empresa's new simulated date.                                                                                                                                                                                                                                                                       | object        |
| `401`     | Missing, malformed or revoked key.                                                                                                                                                                                                                                                                      | ErrorResponse |
| `403`     | Valid key, not permitted — scope, empresa allowlist, or mode.                                                                                                                                                                                                                                           | ErrorResponse |
| `429`     | Too many requests.                                                                                                                                                                                                                                                                                      | ErrorResponse |
| `default` | An 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` [#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.

```bash
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**

| Parameter    | In   | Required | Type   | Notes |
| ------------ | ---- | -------- | ------ | ----- |
| `empresa_id` | path | yes      | string |       |

**Body**

| Field         | Type                                                                                                                                                                                                                                                                                    | Required | Notes |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ----- |
| `status`      | `draft` · `mandato_pending` · `mandato_signed` · `certificate_pending` · `certificate_verified` · `sii_postulacion_pending` · `certification_in_progress` · `cumplimiento_pending` · `resolucion_pending` · `folios_pending` · `emitting` · `action_required` · `blocked` · `suspended` | yes      |       |
| `step`        | string                                                                                                                                                                                                                                                                                  | —        |       |
| **Responses** |                                                                                                                                                                                                                                                                                         |          |       |

| Code      | Meaning                                                                                                                                                                                                                                                                                                 | Body                                                  |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
| `200`     | The forced onboarding state.                                                                                                                                                                                                                                                                            | [Onboarding](/en/docs/referencia/esquemas#onboarding) |
| `401`     | Missing, malformed or revoked key.                                                                                                                                                                                                                                                                      | ErrorResponse                                         |
| `403`     | Valid key, not permitted — scope, empresa allowlist, or mode.                                                                                                                                                                                                                                           | ErrorResponse                                         |
| `429`     | Too many requests.                                                                                                                                                                                                                                                                                      | ErrorResponse                                         |
| `default` | An 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` [#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.

```bash
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**

| Parameter    | In   | Required | Type   | Notes |
| ------------ | ---- | -------- | ------ | ----- |
| `empresa_id` | path | yes      | string |       |

**Body**

| Field            | Type                                                                                                                                                                              | Required | Notes                                                                                       |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------- |
| `step`           | `mandato` · `certificate` · `sii_postulacion` · `set_de_pruebas` · `simulacion` · `intercambio` · `muestras_impresas` · `cumplimiento` · `resolucion` · `habilitacion` · `folios` | yes      |                                                                                             |
| `status`         | `pending` · `in_progress` · `action_required` · `completed` · `failed` · `skipped`                                                                                                | yes      |                                                                                             |
| `tipo_dte`       | `33` · `34` · `39` · `41` · `46` · `52` · `56` · `61`                                                                                                                             | —        | Only meaningful on a certification step. Must be one of the empresa's declared `doc_types`. |
| `blocked_reason` | object                                                                                                                                                                            | —        | Structured reason to attach to a `failed` or `action_required` step.                        |
| **Responses**    |                                                                                                                                                                                   |          |                                                                                             |

| Code      | Meaning                                                                                                                                                                                                                                                                                                 | Body                                                  |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
| `200`     | The onboarding object after the forced step.                                                                                                                                                                                                                                                            | [Onboarding](/en/docs/referencia/esquemas#onboarding) |
| `400`     | Malformed or missing input.                                                                                                                                                                                                                                                                             | ErrorResponse                                         |
| `401`     | Missing, malformed or revoked key.                                                                                                                                                                                                                                                                      | ErrorResponse                                         |
| `403`     | Valid key, not permitted — scope, empresa allowlist, or mode.                                                                                                                                                                                                                                           | ErrorResponse                                         |
| `429`     | Too many requests.                                                                                                                                                                                                                                                                                      | ErrorResponse                                         |
| `default` | An 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` [#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.

```bash
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**

| Parameter          | In     | Required | Type   | Notes                                                                                                                                                                                                                                                                                                                                                       |
| ------------------ | ------ | -------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `document_id`      | path   | yes      | string |                                                                                                                                                                                                                                                                                                                                                             |
| `Facturia-Empresa` | header | —        | string | Target 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**

| Field         | Type                                                                                  | Required | Notes                                                        |
| ------------- | ------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------ |
| `estado`      | `DOK` · `DNK` · `FAU` · `FNA` · `FAN` · `EMP` · `RCT` · `EPR` · `SOK` · `RPR` · `RLV` | yes      | The SII estado to write, exactly as the poll executor would. |
| **Responses** |                                                                                       |          |                                                              |

| Code      | Meaning                                                                                                                                                                                                                                                                                                 | Body          |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
| `200`     | The document, with its derived status.                                                                                                                                                                                                                                                                  | Document      |
| `400`     | Malformed or missing input.                                                                                                                                                                                                                                                                             | ErrorResponse |
| `401`     | Missing, malformed or revoked key.                                                                                                                                                                                                                                                                      | ErrorResponse |
| `403`     | Valid key, not permitted — scope, empresa allowlist, or mode.                                                                                                                                                                                                                                           | ErrorResponse |
| `404`     | No such object under this key.                                                                                                                                                                                                                                                                          | ErrorResponse |
| `429`     | Too many requests.                                                                                                                                                                                                                                                                                      | ErrorResponse |
| `default` | An 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` [#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.

```bash
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**

| Parameter          | In     | Required | Type   | Notes                                                                                                                                                                                                                                                                                                                                                       |
| ------------------ | ------ | -------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Facturia-Empresa` | header | —        | string | Target 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**

| Field          | Type      | Required | Notes                                                                                                               |
| -------------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
| `period`       | Period    | yes      | Tax period `YYYY-MM` (America/Santiago). E.g.: `"2026-07"`.                                                         |
| `summary`      | object    | —        | Any omitted field defaults to 0, and `total_payable` to `max(0, iva_debito - iva_credito) + iva_retenido + ppm`.    |
| `codes`        | object\[] | —        | The F29 code rows. Omit for a plausible default set.                                                                |
| `observations` | object\[] | —        | An observation with `severity: error` lands the propuesta `review_required`, which is what the filing gate refuses. |
| **Responses**  |           |          |                                                                                                                     |

| Code      | Meaning                                                                                                                                                                                                                                                                                                 | Body          |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
| `201`     | The seeded propuesta.                                                                                                                                                                                                                                                                                   | object        |
| `400`     | Malformed or missing input.                                                                                                                                                                                                                                                                             | ErrorResponse |
| `401`     | Missing, malformed or revoked key.                                                                                                                                                                                                                                                                      | ErrorResponse |
| `403`     | Valid key, not permitted — scope, empresa allowlist, or mode.                                                                                                                                                                                                                                           | ErrorResponse |
| `429`     | Too many requests.                                                                                                                                                                                                                                                                                      | ErrorResponse |
| `default` | An 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` [#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.

```bash
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**

| Parameter          | In     | Required | Type   | Notes                                                                                                                                                                                                                                                                                                                                                       |
| ------------------ | ------ | -------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Facturia-Empresa` | header | —        | string | Target 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**

| Field            | Type                                       | Required | Notes                                                          |
| ---------------- | ------------------------------------------ | -------- | -------------------------------------------------------------- |
| `period`         | Period                                     | yes      | Tax period `YYYY-MM` (America/Santiago). E.g.: `"2026-07"`.    |
| `verdict`        | `validated` · `discrepant` · `unavailable` | —        | Default: `"validated"`.                                        |
| `material_count` | integer                                    | —        | Only on `discrepant`; how many material differences to record. |
| **Responses**    |                                            |          |                                                                |

| Code      | Meaning                                                                                                                                                                                                                                                                                                 | Body          |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
| `201`     | The recorded validation.                                                                                                                                                                                                                                                                                | object        |
| `400`     | Malformed or missing input.                                                                                                                                                                                                                                                                             | ErrorResponse |
| `401`     | Missing, malformed or revoked key.                                                                                                                                                                                                                                                                      | ErrorResponse |
| `403`     | Valid key, not permitted — scope, empresa allowlist, or mode.                                                                                                                                                                                                                                           | ErrorResponse |
| `404`     | No such object under this key.                                                                                                                                                                                                                                                                          | ErrorResponse |
| `429`     | Too many requests.                                                                                                                                                                                                                                                                                      | ErrorResponse |
| `default` | An 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 |
