Facturiadocs
Empieza aquí

Conceptos

Empresa, documento, modo test/live y el header Facturia-Empresa — las cuatro ideas que hacen entendible el resto de la API.

Cuatro ideas. Si las tienes claras, el resto de esta documentación es detalle.

1. Empresa

Una empresa es una compañía chilena — un RUT — para la que Facturia emite. Es el límite de tenencia de todo: folios, documentos, RCV, payloads de webhook y consumo. Nada cruza de una empresa a otra.

Una cuenta es un contenedor de facturación y control de acceso sobre muchas empresas. Eso significa que un ERP, un SaaS vertical o un estudio contable pueden operar N clientes bajo una sola cuenta, con una llave por cliente si quieren.

curl -sS https://api.facturia.cl/v1/empresas \
  -H "Authorization: Bearer $FACTURIA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "rut": "77928532-4",
    "legal_name": "Connect SpA",
    "giro": "Servicios de desarrollo de software",
    "acteco": [620200],
    "address": { "street": "Av. Apoquindo 4700, of. 1201", "comuna": "Las Condes", "city": "Santiago" },
    "contact_email": "[email protected]",
    "ambiente": "produccion",
    "doc_types": [33, 34, 39, 61]
  }'

legal_name, giro, address, acteco y contact_email se vuelven el bloque emisor de cada documento. No los repites nunca en un payload de emisión — ese es el punto de guardar el perfil en la empresa.

DELETE no existe: los documentos emitidos son registros tributarios. Un PATCH {"status":"archived"} detiene la emisión y la facturación conservando la historia.

Puesta en marcha

Llevar una empresa hasta emitting es una máquina de estados que puedes dibujar como asistente con GET /v1/empresas/{empresa_id}/onboarding. En modo test la máquina se salta entera.

Tres pasos son irreduciblemente humanos: el representante legal debe autenticarse en persona con su certificado en la postulación, en la declaración de cumplimiento y —para boletas— en la habilitación. La ley chilena no permite delegarlos. Todo el resto — sets de pruebas, libros, simulación, intercambio, muestras impresas, timbraje — es guionizado y desatendido.

onboarding.status nunca se queda en action_required sin un action_url que puedas poner delante de una persona.

2. Documento

Un documento es un DTE emitido. Cada recurso de la API es un objeto JSON con id, object, mode y created_at. Los ids son opacos, con prefijo, seguros para URL y ordenables por fecha de creación (ULID por debajo). Nunca parsees un id.

PrefijoRecurso
emp_empresa
doc_documento emitido
rdoc_documento recibido
caf_CAF
whe_ / whd_endpoint de webhook / entrega
evt_evento
job_job asíncrono
rcv_entrada del RCV
f29_propuesta o declaración de F29
sug_sugerencia tributaria
exp_gasto ingresado
con_conexión de conector (Fintoc)
key_llave de API (su id, no su secreto)
usg_registro de consumo

Plata, cantidades y fechas

  • Los montos son enteros en CLP. Sin decimales, sin strings, sin centavos. 29990 son veintinueve mil novecientos noventa pesos.
  • Los precios unitarios pueden llevar hasta 4 decimales (unit_price: 1234.5678), porque el SII lo permite. Los montos de línea y los totales del documento siempre son enteros, redondeados medio-arriba como especifica el SII.
  • issue_date, period y demás fechas civiles son YYYY-MM-DD / YYYY-MM en America/Santiago. La fecha de un DTE es una fecha del calendario chileno, no un instante.
  • Los timestamps (created_at, accepted_at, …) son RFC 3339 en UTC: 2026-08-14T13:04:11Z.
  • Los RUT son canónicos ########-D: sin puntos, un guion, K mayúscula. Aceptamos 77.928.532-4 y 779285324 en la entrada y siempre devolvemos la forma canónica.

3. Modo: test y live

El modo lo decide el prefijo de la llave, y es inmutable.

PrefijoModo¿Habla con el SII?Costo
sk_test_test — sandbox completamente simuladoNuncaGratis para siempre
sk_live_live — emisión realSí (certificación o producción del SII, según la empresa)Medido

Trampa

El modo y el ambiente son dos ejes distintos

El modo viene de la llave. El ambiente (certificacion / produccion) viene de la empresa. Una llave de test contra una empresa de producción sigue estando completamente simulada: no toca el SII. Y al revés, una llave live contra una empresa en ambiente: certificacion sí habla con el SII, pero con sus servidores de prueba (maullin / apicert).

Todo objeto lleva estampado su "mode". Los objetos de test son invisibles para las llaves live y viceversa: no comparten ids, ni contadores de folios, ni libro de consumo.

El modo se hereda y no se puede escalar: una llave sk_test_root_ solo mintea llaves de test. No hay camino de una credencial de test a una live.

4. El header Facturia-Empresa

Cada llamada con alcance de empresa nombra su empresa destino en un header:

Facturia-Empresa: 77928532-4

El valor es el RUT en forma canónica (sin puntos, guion, K mayúscula — 77928532-4, 76543212-K) o el id de Facturia (emp_…). Recomendamos el RUT: es la llave natural que tus propios sistemas ya tienen.

Trampa

Este header es el único mecanismo

No hay parámetro ?empresa= ni campo empresa en el cuerpo, y no lo habrá dentro de la v1. Una segunda forma de decir lo mismo es una segunda forma de decirlo distinto, y sobre un recurso cuya identidad determina en qué registros tributarios estás escribiendo, esa ambigüedad no vale la conveniencia. Si tu cliente HTTP no puede poner un header, no puede llamar a esta API.

Las reglas:

SituaciónComportamiento
Requerido en/documents, /received_documents, /tax/*, /folios, /cafs, /connections, /test_helpers/*
Ignorado en/empresas/{empresa_id}/... — la empresa sale de la ruta
Prohibido en/empresas (listar/crear), /webhook_endpoints, /events, /usage, /account, /api_keys
Omitido donde se requiere400 empresa_required, salvo que la llave sea un token de empresa
Presente pero fuera de la lista blanca403 empresa_not_allowed. Nunca un 404: te decimos que la empresa está fuera de tu alcance en vez de fingir que no existe, porque ya conoces su RUT

Token de empresa: cuando el header sobra

Una llave cuya lista blanca tiene exactamente una empresa es un token de empresa. Para un token de empresa el header es opcional; si lo mandas, debe coincidir, o recibes 400 empresa_mismatch.

# token de empresa — sin header Facturia-Empresa
curl https://api.facturia.cl/v1/documents \
  -H "Authorization: Bearer sk_live_qT8vN2wLmR5xKpJ7hYdCbA3s" \
  -H "Content-Type: application/json" \
  -d '{"tipo_dte":39,"items":[{"description":"Café","quantity":2,"unit_price":2500}]}'

Es la forma recomendada cuando integras una sola compañía (el caso común) o cuando le entregas una llave al sistema del propio cliente: la llave no puede direccionar nada más que ese RUT, y tu código nunca tiene que pensar en empresas.

Convenciones transversales

Listas y paginación

Todos los endpoints de lista devuelven el mismo sobre y usan paginación por cursor.

{
  "object": "list",
  "url": "/v1/documents",
  "data": [ { "object": "document", "...": "..." } ],
  "has_more": true,
  "next_cursor": "cur_01K2R7Q4XW9M3B8ZC5YHTVJD6N"
}

Parámetros: limit (1–100, por defecto 25) y cursor (opaco, del next_cursor). El orden por defecto es del más nuevo al más antiguo por created_at. No hay offsets ni números de página: los cursores son estables mientras se están emitiendo documentos nuevos.

Idempotencia

Todo POST acepta un header Idempotency-Key. Usa un UUID nuevo por operación lógica y reúsalo en los reintentos.

-H "Idempotency-Key: 0f9f3f4e-6a3d-4f0a-9d70-9b5f19d4b0e6"
  • Las llaves tienen alcance (cuenta, empresa, endpoint) y se retienen 24 horas.
  • Un replay devuelve el código y el cuerpo originales, más Facturia-Idempotent-Replay: true.
  • Misma llave con cuerpo distinto → 409 idempotency_key_reused.
  • Un request todavía en vuelo bajo la misma llave → 409 idempotency_in_progress; reintenta en un momento.
  • Los requests que fallan con 4xx no queman la llave (arregla y reintenta con la misma). Los fallos 5xx son seguros de reintentar con la misma llave.

Debajo de eso vive una garantía más fuerte: el motor de emisión sostiene una restricción única sobre (RUT de empresa, tipo_dte, folio). Un folio nunca se emite dos veces, y una vez asignado a un documento, cualquier reintento de esa misma emisión lógica reutiliza el folio asignado en vez de quemar uno nuevo. Eso es lo que de verdad te protege del modo de falla caro — una factura duplicada con un folio botado — y se sostiene aunque olvides el header.

Tu propio identificador

curl "https://api.facturia.cl/v1/documents?external_id=order-8842" \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA"

external_id es único por (empresa, external_id); una segunda emisión con el mismo valor devuelve 409 external_id_exists con el id del documento existente en error.existing_id.

Versionado

  • El contrato es el prefijo /v1. Los cambios incompatibles van a /v2; /v1 no se reforma en silencio.
  • Dentro de /v1 evolucionamos de forma aditiva: endpoints nuevos, campos de request opcionales nuevos, campos de respuesta nuevos, valores de enum nuevos, tipos de evento nuevos.
  • Por lo tanto tu cliente debe: ignorar campos de respuesta desconocidos, tolerar valores de enum desconocidos (mapéalos a una rama por defecto en vez de lanzar) e ignorar tipos de webhook desconocidos. Un cliente que se cae con un campo desconocido tiene un bug, y esa es la única regla de compatibilidad que te pedimos.
  • Las eliminaciones y los cambios de comportamiento tienen 12 meses de aviso, un header Sunset en los endpoints afectados y una entrada en el changelog.
  • No hay header de versión fechada en la v1. El prefijo de ruta es todo el contrato, lo que mantiene el modelo mental en una sola pieza móvil.

En esta página