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]",
"origen": "ninguno",
"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.
origen y canal de emisión
origen es obligatorio con una llave sk_live_ y opcional en modo test, donde vale ninguno.
Omitirlo en modo real es 400 invalid_field — el hint te rescata, pero es exactamente el
round-trip que este ejemplo existe para evitarte.
Es tu declaración de cómo emite hoy la empresa: gratuito si ya usa el Sistema de Facturación
Gratuita del SII, mercado si ya usa un proveedor, ninguno si todavía no emite electrónicamente.
Enruta la copia del asistente de puesta en marcha y nada más — no elige el carril y no decide qué
se puede saltar. Que una empresa pueda saltarse la certificación se resuelve contra el SII,
empíricamente: estar inscrito en el sistema gratuito ya convierte a un RUT en emisor de DTE reales,
así que un mercado declarado que creyéramos saltaría la certificación justo a quien la necesita.
El canal de emisión es otra cosa y se declara aparte: es la preferencia entre seguir emitiendo
por el portal gratuito del SII, operado por nosotros, o pasar al motor certificado. Y ambiente no
se declara en absoluto: es derivado y de solo lectura. Los dos carriles, con sus pasos lado a lado,
están en De sandbox a producción.
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.
Los pasos que necesitan a una persona son cinco, no tres. Un paso necesita a una persona cuando
actor: "customer" (lo ejecuta ella) o cuando lo ejecutamos nosotros y no puede avanzar sin una
decisión suya. En orden:
- Mandato — el representante legal nos autoriza a actuar ante el SII. Revocable cuando quiera.
- Certificado digital (carril mercado) o la clave del Sistema de Facturación Centralizado (carril SFG) — la credencial con la que operamos.
- La autorización para postular — la postulación la presentamos nosotros, con el certificado del representante legal que tenemos en custodia y bajo la cláusula 4.2 a) del mandato. Lo que decide el titular es el momento: si la empresa hoy emite en el Sistema de Facturación Gratuito, deja de ser usuaria activa de ese sistema en cuanto el SII la autorice.
- La Declaración de Cumplimiento — la presentamos nosotros, con el mismo certificado, después de
que el representante legal la aprueba en una página alojada (acto
cumplimiento_declaracion, rolrep_legal). Esa misma aprobación fija también el mes desde el cual la empresa queda operando, si el titular no lo fijó antes por separado. - Habilitación de boletas — solo si declaraste boletas: la hacemos nosotros de punta a punta con el mismo certificado, con dos aprobaciones del representante legal en el camino — cuándo empezar y la declaración final.
Los actos 3, 4 y 5 los ejecutamos nosotros con el certificado en custodia y solo esperamos que la
persona correspondiente apruebe en su página; ninguno de los tres lo presenta una persona ante el SII
en persona. Los dos primeros son tuyos porque nadie más puede autorizarnos ni entregarnos una
credencial. Todo el resto — sets de pruebas, libros, simulación, intercambio, muestras impresas,
timbraje y la puesta en vivo — es guionizado y desatendido, y viene marcado actor: "facturia" o
actor: "sii".
El certificado que nos entregas sí identifica a su titular
Un certificado digital chileno se emite a una persona natural —el representante legal— y una sesión con certificado ante el SII autentica a esa persona. Por eso la postulación y la declaración de cumplimiento las presenta Facturia con ese certificado, después de que quien corresponde las aprueba en una página. Cuando aun así te pedimos que hagas algo tú —agregar un usuario autorizado, dar el permiso «Enviar Doctos.», cambiar la casilla de intercambio—, la razón es de autoridad, no legal: esos actos se hacen en la sesión del usuario administrador de tu cuenta en el SII, y el certificado que custodiamos no tiene esa autoridad justo en los casos en que el acto falta. Nunca «el certificado no sirve».
onboarding.status nunca se queda en action_required sin un action_url que puedas poner delante
de una persona, ni sin un pending_action que diga a quién dárselo y qué hacer con él. El patrón
completo está en De sandbox a producción.
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) | El plan único: $19.990 CLP + IVA al mes por empresa, documentos ilimitados |
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. - «En vuelo» es un arriendo, no una bandera: la toma se respeta 120 segundos y después se
considera abandonada. Si el proceso que atendía tu request se muere — un deploy, una máquina
reemplazada — el siguiente reintento con la misma llave retoma la operación en vez de chocar
con un 409 por el resto de las 24 horas. El reintento tiene que traer el mismo cuerpo: uno
distinto, dentro de la ventana de retención, sigue siendo
409 idempotency_key_reused. - 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.