Test helpers
Endpoints que solo existen para llaves de prueba y mueven las piezas que normalmente mueve el mundo.
Base: https://api.facturia.cl/v1. Todos los endpoints de este capítulo se autentican con Authorization: Bearer salvo que se diga lo contrario, y el sobre de error es el de Errores.
| Método | Ruta | Qué hace |
|---|---|---|
POST | /test_helpers/received_documents | Inyecta un DTE de proveedor como si hubiera llegado a la casilla de recepción |
POST | /test_helpers/rcv/seed | Siembra filas de RCV en un período para que la propuesta de F29 tenga datos |
POST | /test_helpers/clock | Adelanta el reloj simulado de esta empresa |
POST | /test_helpers/empresas/{empresa_id}/onboarding | Lleva la puesta en marcha de una empresa a cualquier estado |
POST | /test_helpers/empresas/{empresa_id}/onboarding/steps | Fuerza el estado de un paso concreto de la puesta en marcha |
POST | /test_helpers/documents/{document_id}/estado | Fuerza el estado crudo del SII de un documento de sandbox ya emitido |
POST | /test_helpers/f29/proposals | Inject a computed F29 propuesta |
POST | /test_helpers/f29/validations | Fuerza el veredicto de la validación cruzada de un período |
POST /test_helpers/received_documents
Inyecta un DTE de proveedor como si hubiera llegado a la casilla de recepción
Solo en sandbox: inyecta un DTE de proveedor como si acabara de llegar a la casilla de recepción, sin esperar que una contraparte real lo envíe.
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"}}'Parámetros
| Parámetro | En | Requerido | Tipo | Notas |
|---|---|---|---|---|
Facturia-Empresa | header | — | string | La empresa destino: RUT canónico (77928532-4) o id de empresa (emp_…). Obligatorio en toda llamada con alcance de empresa, salvo que la llave sea un token de empresa. Este header es el ÚNICO mecanismo: no hay parámetro ?empresa= ni campo empresa en el cuerpo. Omitido donde se requiere → 400 empresa_required; fuera de la lista blanca → 403 empresa_not_allowed. |
Cuerpo
| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
tipo_dte | 33 · 34 · 39 · 41 · 46 · 52 · 56 · 61 | sí | 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. Las boletas (39/41) viajan por otro transporte del SII; eso es invisible desde esta API. |
folio | integer | — | |
emisor | Party | sí | Una contraparte — una empresa O una persona natural. No hay bandera de empresa, y no hace falta ninguna: una persona natural (rut + legal_name, sin giro, sin address) es un receptor válido para una boleta (39/41). Su nombre se escribe en <RznSocRecep> y en el <RSR> del timbre, su RUT en <RUTRecep>/<RR> — la boleta que un paciente presenta a una Isapre o a un seguro complementario. Sin legal_name la boleta nombra el RUT junto a «Sin RUT»; sin receptor se emite al consumidor anónimo 66666666-6. Una nota de crédito (61) a una persona natural también es válida, pero es un documento de la familia factura: como toda 61 necesita giro, address.street y address.comuna, incluso cuando anula una boleta. POST /v1/documents/\{id\}/credit_note copia el receptor de la boleta original, así que guarda esos tres datos una vez con PATCH /v1/empresas/\{id\}/counterparties/\{rut\} antes de anular, o la nota se rechaza con 400 receptor_incomplete antes de gastar ningún folio. |
issue_date | string | — | |
totals | Totals | — | |
items | DocumentItem[] | — | |
| Respuestas |
| Código | Significado | Cuerpo |
|---|---|---|
201 | El documento recibido inyectado; se dispara received_document.created. | ReceivedDocument |
401 | Llave ausente, mal formada o revocada. | ErrorResponse |
403 | Llave válida pero sin permiso: scope, lista blanca de empresas o modo. | ErrorResponse |
429 | Demasiadas solicitudes. | ErrorResponse |
default | Un error no enumerado aparte para esta operación, en el mismo sobre §5.5 que cualquier otro rechazo. Ramifica por error.code, nunca solo por el estado HTTP ni por error.message — un código que esta operación aún no se ha visto lanzar sigue siendo más seguro para decidir que un texto pensado para una persona. | ErrorResponse |
POST /test_helpers/rcv/seed
Siembra filas de RCV en un período para que la propuesta de F29 tenga datos
Solo en sandbox: siembra los registros de compras/ventas con entradas sintéticas, para poder probar flujos que dependen del RCV (caracterización, F29) sin una sincronización real con el SII.
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"}'Parámetros
| Parámetro | En | Requerido | Tipo | Notas |
|---|---|---|---|---|
Facturia-Empresa | header | — | string | La empresa destino: RUT canónico (77928532-4) o id de empresa (emp_…). Obligatorio en toda llamada con alcance de empresa, salvo que la llave sea un token de empresa. Este header es el ÚNICO mecanismo: no hay parámetro ?empresa= ni campo empresa en el cuerpo. Omitido donde se requiere → 400 empresa_required; fuera de la lista blanca → 403 empresa_not_allowed. |
Cuerpo
| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
period | Period | sí | Período tributario YYYY-MM (America/Santiago). Ej.: "2026-07". |
purchases | integer | — | Por defecto: 10. |
sales | integer | — | Por defecto: 10. |
| Respuestas |
| Código | Significado | Cuerpo |
|---|---|---|
201 | Las filas se escribieron, en esta misma petición. No hay job y no hay nada que consultar: el sandbox no tiene worker que esperar (§4.1), así que una siembra que respondiera 202 con un job sería la promesa de una asincronía que acá no existe. | RcvSeed |
401 | Llave ausente, mal formada o revocada. | ErrorResponse |
403 | Llave válida pero sin permiso: scope, lista blanca de empresas o modo. | ErrorResponse |
429 | Demasiadas solicitudes. | ErrorResponse |
default | Un error no enumerado aparte para esta operación, en el mismo sobre §5.5 que cualquier otro rechazo. Ramifica por error.code, nunca solo por el estado HTTP ni por error.message — un código que esta operación aún no se ha visto lanzar sigue siendo más seguro para decidir que un texto pensado para una persona. | ErrorResponse |
POST /test_helpers/clock
Adelanta el reloj simulado de esta empresa
Ejercita la ventana de acuse de 8 días, el vencimiento de folios y los plazos del F29 sin esperar.
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":"…"}'Parámetros
| Parámetro | En | Requerido | Tipo | Notas |
|---|---|---|---|---|
Facturia-Empresa | header | — | string | La empresa destino: RUT canónico (77928532-4) o id de empresa (emp_…). Obligatorio en toda llamada con alcance de empresa, salvo que la llave sea un token de empresa. Este header es el ÚNICO mecanismo: no hay parámetro ?empresa= ni campo empresa en el cuerpo. Omitido donde se requiere → 400 empresa_required; fuera de la lista blanca → 403 empresa_not_allowed. |
Cuerpo
| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
advance_days | integer | — | |
set_to | string | — | |
| Respuestas |
| Código | Significado | Cuerpo |
|---|---|---|
200 | La nueva fecha simulada de la empresa. | object |
401 | Llave ausente, mal formada o revocada. | ErrorResponse |
403 | Llave válida pero sin permiso: scope, lista blanca de empresas o modo. | ErrorResponse |
429 | Demasiadas solicitudes. | ErrorResponse |
default | Un error no enumerado aparte para esta operación, en el mismo sobre §5.5 que cualquier otro rechazo. Ramifica por error.code, nunca solo por el estado HTTP ni por error.message — un código que esta operación aún no se ha visto lanzar sigue siendo más seguro para decidir que un texto pensado para una persona. | ErrorResponse |
POST /test_helpers/empresas/{empresa_id}/onboarding
Lleva la puesta en marcha de una empresa a cualquier estado
Solo en sandbox: fuerza el estado de puesta en marcha de la empresa a un onboarding_status dado, saltándose todos los pasos intermedios.
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"}'Parámetros
| Parámetro | En | Requerido | Tipo | Notas |
|---|---|---|---|---|
empresa_id | path | sí | string |
Cuerpo
| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
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 | sí | |
step | string | — | |
| Respuestas |
| Código | Significado | Cuerpo |
|---|---|---|
200 | El estado de puesta en marcha forzado. | Onboarding |
401 | Llave ausente, mal formada o revocada. | ErrorResponse |
403 | Llave válida pero sin permiso: scope, lista blanca de empresas o modo. | ErrorResponse |
429 | Demasiadas solicitudes. | ErrorResponse |
default | Un error no enumerado aparte para esta operación, en el mismo sobre §5.5 que cualquier otro rechazo. Ramifica por error.code, nunca solo por el estado HTTP ni por error.message — un código que esta operación aún no se ha visto lanzar sigue siendo más seguro para decidir que un texto pensado para una persona. | ErrorResponse |
POST /test_helpers/empresas/{empresa_id}/onboarding/steps
Fuerza el estado de un paso concreto de la puesta en marcha
La más fina de las dos palancas de puesta en marcha. POST .../onboarding mueve el ESTADO
de la empresa; esta mueve UN PASO, que es como se producen las partes de §7.4 que un estado
no alcanza a expresar: un paso en action_required con su action_url, una prueba de
certificación que failed para un tipo_dte mientras los demás pasaron, o un paso skipped
porque el RUT ya era emisor autorizado.
Los cuatro pasos de certificación (set_de_pruebas, simulacion, intercambio,
muestras_impresas) existen por tipo de documento. Omite tipo_dte para forzar todos los
tipos declarados de una vez; inclúyelo para forzar uno.
Devuelve el objeto de puesta en marcha completo y dispara el mismo webhook
empresa.onboarding.* que dispararía la máquina real.
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"}'Parámetros
| Parámetro | En | Requerido | Tipo | Notas |
|---|---|---|---|---|
empresa_id | path | sí | string |
Cuerpo
| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
step | mandato · certificate · sii_postulacion · set_de_pruebas · simulacion · intercambio · muestras_impresas · cumplimiento · resolucion · habilitacion · folios | sí | |
status | pending · in_progress · action_required · completed · failed · skipped | sí | |
tipo_dte | 33 · 34 · 39 · 41 · 46 · 52 · 56 · 61 | — | Solo tiene sentido en un paso de certificación. Tiene que ser uno de los doc_types declarados de la empresa. |
blocked_reason | object | — | Motivo estructurado que se adjunta a un paso failed o action_required. |
| Respuestas |
| Código | Significado | Cuerpo |
|---|---|---|
200 | El objeto de puesta en marcha después de forzar el paso. | Onboarding |
400 | Entrada mal formada o incompleta. | ErrorResponse |
401 | Llave ausente, mal formada o revocada. | ErrorResponse |
403 | Llave válida pero sin permiso: scope, lista blanca de empresas o modo. | ErrorResponse |
429 | Demasiadas solicitudes. | ErrorResponse |
default | Un error no enumerado aparte para esta operación, en el mismo sobre §5.5 que cualquier otro rechazo. Ramifica por error.code, nunca solo por el estado HTTP ni por error.message — un código que esta operación aún no se ha visto lanzar sigue siendo más seguro para decidir que un texto pensado para una persona. | ErrorResponse |
POST /test_helpers/documents/{document_id}/estado
Fuerza el estado crudo del SII de un documento de sandbox ya emitido
test.outcome en POST /documents fuerza un resultado AL EMITIR. Esto fuerza uno DESPUÉS, y es la única manera determinista de ejercitar la transición que de verdad le importa a quien consume webhooks: sin esto, sent → accepted es correrle al asentamiento de ~1 s del sandbox, y stuck → accepted es inalcanzable.
ESCRIBE EL ESTADO, NO EL STATUS, a propósito. El status público se deriva después con el mapeo de siempre, así que una prueba que lleva un documento a DNK demuestra que el mapeo a reparo funciona en vez de afirmarlo. Se publica el mismo evento document.* del ciclo de vida que dispararía la transición real, así que quien esté bajo prueba recibe exactamente lo que manda producción.
Solo sandbox, revisado tres veces: el modo de la llave, la bandera de la empresa y el is_sandbox del PROPIO documento — que se estampa al insertar y es inmutable, así que un documento real sigue siendo real aunque después se cambie la bandera de la empresa.
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"}'Parámetros
| Parámetro | En | Requerido | Tipo | Notas |
|---|---|---|---|---|
document_id | path | sí | string | |
Facturia-Empresa | header | — | string | La empresa destino: RUT canónico (77928532-4) o id de empresa (emp_…). Obligatorio en toda llamada con alcance de empresa, salvo que la llave sea un token de empresa. Este header es el ÚNICO mecanismo: no hay parámetro ?empresa= ni campo empresa en el cuerpo. Omitido donde se requiere → 400 empresa_required; fuera de la lista blanca → 403 empresa_not_allowed. |
Cuerpo
| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
estado | DOK · DNK · FAU · FNA · FAN · EMP · RCT · EPR · SOK · RPR · RLV | sí | El estado del SII que se va a escribir, exactamente como lo haría el ejecutor del poll. |
| Respuestas |
| Código | Significado | Cuerpo |
|---|---|---|
200 | El documento, con su status derivado. | Document |
400 | Entrada mal formada o incompleta. | ErrorResponse |
401 | Llave ausente, mal formada o revocada. | ErrorResponse |
403 | Llave válida pero sin permiso: scope, lista blanca de empresas o modo. | ErrorResponse |
404 | No existe ese objeto bajo esta llave. | ErrorResponse |
429 | Demasiadas solicitudes. | ErrorResponse |
default | Un error no enumerado aparte para esta operación, en el mismo sobre §5.5 que cualquier otro rechazo. Ramifica por error.code, nunca solo por el estado HTTP ni por error.message — un código que esta operación aún no se ha visto lanzar sigue siendo más seguro para decidir que un texto pensado para una persona. | ErrorResponse |
POST /test_helpers/f29/proposals
Inject a computed F29 propuesta
Calcular una propuesta es un job del WORKER, y el worker no es parte de la promesa del sandbox (§4.1: el modo de prueba es «una simulación completa en proceso»). Sin este helper, el sandbox puede sembrar un RCV y quedarse esperando para siempre una propuesta que solo produce un worker corriendo, lo que deja intestable toda la superficie de §12.3/§12.4 en modo de prueba.
Escribe la misma fila que escribe el cálculo real, así que todo lo que viene después —el serializador, el enlace al archivo de carga y sobre todo la PUERTA DE DECLARACIÓN— lee una propuesta ordinaria. La puerta no se salta: sigue leyendo total_payable y observations de esta fila, y sigue exigiendo una validación (ver /test_helpers/f29/validations).
Volver a sembrar el mismo período lo actualiza en vez de crear un segundo.
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"}'Parámetros
| Parámetro | En | Requerido | Tipo | Notas |
|---|---|---|---|---|
Facturia-Empresa | header | — | string | La empresa destino: RUT canónico (77928532-4) o id de empresa (emp_…). Obligatorio en toda llamada con alcance de empresa, salvo que la llave sea un token de empresa. Este header es el ÚNICO mecanismo: no hay parámetro ?empresa= ni campo empresa en el cuerpo. Omitido donde se requiere → 400 empresa_required; fuera de la lista blanca → 403 empresa_not_allowed. |
Cuerpo
| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
period | Period | sí | Período tributario YYYY-MM (America/Santiago). Ej.: "2026-07". |
summary | object | — | Cualquier campo omitido vale 0, y total_payable vale max(0, iva_debito - iva_credito) + iva_retenido + ppm. |
codes | object[] | — | Las filas de códigos del F29. Omítelas para un conjunto por defecto plausible. |
observations | object[] | — | Una observación con severity: error deja la propuesta en review_required, que es lo que la puerta de declaración rechaza. |
| Respuestas |
| Código | Significado | Cuerpo |
|---|---|---|
201 | La propuesta sembrada. | object |
400 | Entrada mal formada o incompleta. | ErrorResponse |
401 | Llave ausente, mal formada o revocada. | ErrorResponse |
403 | Llave válida pero sin permiso: scope, lista blanca de empresas o modo. | ErrorResponse |
429 | Demasiadas solicitudes. | ErrorResponse |
default | Un error no enumerado aparte para esta operación, en el mismo sobre §5.5 que cualquier otro rechazo. Ramifica por error.code, nunca solo por el estado HTTP ni por error.message — un código que esta operación aún no se ha visto lanzar sigue siendo más seguro para decidir que un texto pensado para una persona. | ErrorResponse |
POST /test_helpers/f29/validations
Fuerza el veredicto de la validación cruzada de un período
La validación cruzada real SE SALTA la consulta al SII en una empresa de sandbox y siempre registra unavailable — correctamente, porque una empresa de sandbox no tiene un SII contra el cual contrastarse. Pero unavailable no es validated, así que sin este helper la puerta de declaración rechaza TODA declaración de sandbox con not_validated y al resto de la §12.4 no se llega nunca.
Forzar el VEREDICTO en vez de la declaración es lo que lo mantiene honesto: una prueba que lleva una propuesta a validated y después declara demuestra que la puerta deja pasar una propuesta validada, y una que la deja en unavailable demuestra que la puerta detiene a la que no lo está. La puerta misma no se toca en ninguno de los dos casos.
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"}'Parámetros
| Parámetro | En | Requerido | Tipo | Notas |
|---|---|---|---|---|
Facturia-Empresa | header | — | string | La empresa destino: RUT canónico (77928532-4) o id de empresa (emp_…). Obligatorio en toda llamada con alcance de empresa, salvo que la llave sea un token de empresa. Este header es el ÚNICO mecanismo: no hay parámetro ?empresa= ni campo empresa en el cuerpo. Omitido donde se requiere → 400 empresa_required; fuera de la lista blanca → 403 empresa_not_allowed. |
Cuerpo
| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
period | Period | sí | Período tributario YYYY-MM (America/Santiago). Ej.: "2026-07". |
verdict | validated · discrepant · unavailable | — | Por defecto: "validated". |
material_count | integer | — | Solo en discrepant; cuántas diferencias materiales registrar. |
| Respuestas |
| Código | Significado | Cuerpo |
|---|---|---|
201 | La validación registrada. | object |
400 | Entrada mal formada o incompleta. | ErrorResponse |
401 | Llave ausente, mal formada o revocada. | ErrorResponse |
403 | Llave válida pero sin permiso: scope, lista blanca de empresas o modo. | ErrorResponse |
404 | No existe ese objeto bajo esta llave. | ErrorResponse |
429 | Demasiadas solicitudes. | ErrorResponse |
default | Un error no enumerado aparte para esta operación, en el mismo sobre §5.5 que cualquier otro rechazo. Ramifica por error.code, nunca solo por el estado HTTP ni por error.message — un código que esta operación aún no se ha visto lanzar sigue siendo más seguro para decidir que un texto pensado para una persona. | ErrorResponse |