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": "facturacion@connect.cl",
"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.
| Prefijo | Recurso |
|---|---|
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.
29990son 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,periody demás fechas civiles sonYYYY-MM-DD/YYYY-MMen 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. Aceptamos77.928.532-4y779285324en 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.
| Prefijo | Modo | ¿Habla con el SII? | Costo |
|---|---|---|---|
sk_test_ | test — sandbox completamente simulado | Nunca | Gratis para siempre |
sk_live_ | live — emisión real | Sí (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-4El 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ón | Comportamiento |
|---|---|
| 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 requiere | 400 empresa_required, salvo que la llave sea un token de empresa |
| Presente pero fuera de la lista blanca | 403 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;/v1no se reforma en silencio. - Dentro de
/v1evolucionamos 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
Sunseten 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.