Crear documentos
La emisión mínima, la emisión completa, qué completamos por ti y cómo leer los documentos que ya emitiste.
La emisión mínima
El punto de guardar el perfil emisor en la empresa es que una factura son tres campos.
El receptor lleva giro y address porque el SII no acepta una factura sin ellos: acepta el SOBRE y después rechaza el DOCUMENTO, y el folio se gasta igual. Cuando ya le emitiste una vez a ese RUT, basta con receptor: {"rut": "…"} — el resto lo completamos desde tu documento anterior a ese mismo RUT. Si no hay ninguno, recibes 400 receptor_incomplete nombrando los campos exactos, antes de asignar folio. Las boletas (39/41) y una factura de compra extranjera (46) nunca los necesitan.
curl -sS https://api.facturia.cl/v1/documents \
-H "Authorization: Bearer $FACTURIA_KEY" \
-H "Facturia-Empresa: $EMPRESA" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"tipo_dte": 33,
"receptor": {
"rut": "76543212-K",
"giro": "Venta al por mayor de alimentos",
"address": { "street": "Los Militares 5001, of. 302", "comuna": "Las Condes", "city": "Santiago" }
},
"items": [
{ "description": "Desarrollo a medida — agosto", "quantity": 1, "unit_price": 2400000 }
]
}'Qué completamos por ti
| Campo | De dónde sale |
|---|---|
| Bloque emisor (RUT, razón social, giro, dirección, comuna, acteco) | el perfil de la empresa |
folio | el siguiente folio del CAF activo para este tipo |
issue_date | hoy en America/Santiago |
| Razón social, giro, dirección y comuna del receptor | la libreta de direcciones de la empresa, si no el registro de contribuyentes del SII, resuelto por RUT |
totals | calculados desde items bajo las reglas tributarias del tipo |
payment_terms, currency, indicators | valores por defecto sensatos (contado, CLP) |
Si el receptor no se puede resolver y el tipo requiere esos campos, recibes 400 receptor_incomplete
nombrando exactamente cuáles enviar — nunca un rechazo silencioso del SII tres minutos después.
La emisión completa
Todo lo que el SII te deja decir, dicho explícitamente.
curl -sS https://api.facturia.cl/v1/documents \
-H "Authorization: Bearer $FACTURIA_KEY" \
-H "Facturia-Empresa: $EMPRESA" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"tipo_dte": 33,
"external_id": "order-8842",
"issue_date": "2026-08-14",
"prices_include_tax": false,
"receptor": {
"rut": "76543212-K",
"legal_name": "Comercial Andina Ltda.",
"giro": "Venta al por mayor de alimentos",
"address": { "street": "Los Militares 5001, of. 302", "comuna": "Las Condes", "city": "Santiago" },
"contact": "[email protected]",
"email_document_to": ["[email protected]", "[email protected]"]
},
"items": [
{
"description": "Licencia plataforma — plan Enterprise",
"detail": "Periodo 2026-08-01 a 2026-08-31",
"quantity": 12, "unit": "UN", "unit_price": 180000,
"discount_percent": 5,
"item_code": { "type": "INT1", "value": "PLAT-ENT" }
},
{ "description": "Servicio exento de capacitación", "quantity": 1, "unit_price": 300000, "exempt": true }
],
"global_discounts": [ { "type": "percent", "value": 2, "applies_to": "afecto", "reason": "Acuerdo comercial" } ],
"references": [
{ "tipo_dte": 801, "folio": "OC-99213", "date": "2026-08-01", "code": null, "reason": "Orden de compra" }
],
"payment": { "terms": "credito", "due_date": "2026-09-13", "method": "transferencia" },
"totals": { "net": 2352000, "iva": 446880, "exempt": 300000, "total": 3098880 },
"pdf": { "format": "carta", "cedible": true, "public": true },
"xml": { "public": true },
"notes": "Gracias por su compra.",
"metadata": { "crm_deal": "D-1182", "cost_center": "LATAM" }
}'Los cuatro campos que conviene entender
Trampa
prices_include_tax es el origen del bug del 19%
Por defecto depende del tipo: true para boletas (39/41), false para todo lo demás. Ponlo
explícito y normalizamos en cualquier dirección.
Es la fuente número uno de errores de 19% en la facturación chilena. Que sea un campo con nombre en vez de una convención implícita es deliberado: si tu sistema guarda precios brutos y emites una factura, dilo.
La normalización no es simétrica. En una factura (33/34/56/61/46) el <PrcItem>/<MontoItem> del
SII siempre es NETO (DTE_v10.xsd: neto salvo que venga MntBruto, que nunca mandamos), así que
prices_include_tax: true ahí se normaliza HACIA ABAJO en la puerta — los items que devolvemos, el
PDF y el <PrcItem> quedan en neto (11990 → 10075.6303), y el total puede quedar un peso distinto
del bruto cotizado. En una boleta (39/41), cuyo MontoItem es bruto por esquema, el precio se queda
bruto sin importar prices_include_tax.
totalses opcional y, cuando viene, es una aserción. Si nuestro cálculo difiere aunque sea en un peso, recibes422 totals_mismatchcon ambas cifras y no se emite nada. Un sistema financiero debería mandarlo siempre.totals.other_taxeses 0 salvo en un tipo 46 conwithholding, donde lleva la retención en NEGATIVO (p. ej.-1891si el IVA retenido es1891) — es la única forma de quetotal = net + exempt + iva + other_taxessiga siendo cierto se retenga o no.global_discountsacepta como máximo una entrada. El<DscRcgGlobal>del SII es un soloNroLinDR>1, así que una segunda entrada se rechaza con400 invalid_fieldantes de reservar folio — júntalas en una, o pasa el resto aitems[].discount_percent.- Los descuentos son del canal
mercado. En una empresa conemission_channel=sfgse rechazan en la puerta los dos:global_discountscon422 global_discount_not_available_for_channel, y cualquieritems[].discount_percentmayor que 0 con422 line_discount_not_available_for_channel, nombrando la línea. El formulario de emisión del portal del SII no tiene ningún campo de descuento que hayamos capturado — el formulario capturado trae comentadas tanto la columna «% Desc» por línea como sus entradasEFXP_PCT_DESC/EFXP_MNT_DESC—, así que un descuento aceptado ahí sería un descuento que el portal nunca ve: te cotizaríamosneto 80.000 / total 95.200y el SII firmaríaneto 100.000 / total 119.000. Se rechaza en vez de descartarse en silencio, porque la diferencia se la cobrarías a tu cliente en un documento que solo una nota de crédito puede deshacer. Métele el descuento alunit_price, o emite poremission_channel=mercado. Undiscount_percenten0se acepta en los dos canales: es exactamente lo que el portal emitiría. referencesconcode: 1|2|3son los códigos de referencia del SII (1 = anula el documento referenciado, 2 = corrige texto, 3 = corrige montos). Las referencias a documentos que no son DTE (orden de compra, contrato) usan los códigos detipo_dtedel SII en el rango 800 y omitencode.references[].datetoma laissue_datedel documento si se omite;references[].folioacepta número o string, y un número se convierte a su string decimal (<FolioRef>esxs:stringen el esquema del SII).metadataes un mapa de strings arbitrario (≤ 20 llaves, ≤ 500 caracteres cada una) que guardamos y devolvemos intacto. Nunca llega al SII.email_document_toenvía el PDF y el XML al receptor cuando el documento llega aaccepted.
El objeto documento
{
"id": "doc_01K2R7Q4XW9M3B8ZC5YHTVJD6N",
"object": "document",
"mode": "live",
"empresa": { "id": "emp_01K2R6ZC4P8QW1VN7T3MHDY9EK", "rut": "77928532-4" },
"external_id": "order-8842",
"tipo_dte": 33,
"folio": 412,
"issue_date": "2026-08-14",
"status": "accepted",
"receptor": { "rut": "76543212-K", "legal_name": "Comercial Andina Ltda." },
"items": [ { "description": "Licencia plataforma — plan Enterprise", "quantity": 12, "unit_price": 180000, "amount": 2052000 } ],
"totals": { "net": 2352000, "iva": 446880, "exempt": 300000, "other_taxes": 0, "total": 3098880 },
"references": [],
"pdf_url": "https://api.facturia.cl/v1/documents/doc_01K2R7Q4XW9M3B8ZC5YHTVJD6N/pdf",
"xml_url": "https://api.facturia.cl/v1/documents/doc_01K2R7Q4XW9M3B8ZC5YHTVJD6N/xml",
"sii": {
"environment": "produccion",
"track_id": "0252083112",
"estado": "DOK",
"glosa": "Documento Recibido por el SII, datos coinciden",
"err_code": "0",
"num_atencion": "8812394",
"received_at": "2026-08-14T13:06:55Z",
"last_checked_at": "2026-08-14T13:07:02Z"
},
"rejection_reasons": [],
"metadata": { "crm_deal": "D-1182" },
"created_at": "2026-08-14T13:04:11Z",
"accepted_at": "2026-08-14T13:06:55Z"
}Leer documentos
# Listar, del más nuevo al más antiguo, con filtros
curl -G https://api.facturia.cl/v1/documents \
-H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA" \
-d status=accepted -d tipo_dte=33 \
-d "issue_date[gte]=2026-08-01" -d "issue_date[lte]=2026-08-31" \
-d limit=50
# Un documento
curl https://api.facturia.cl/v1/documents/doc_01K2R7Q4XW9M3B8ZC5YHTVJD6N \
-H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA"
# Por folio (los folios son únicos por empresa+tipo, así que es una búsqueda legítima)
curl -G https://api.facturia.cl/v1/documents \
-H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA" \
-d tipo_dte=33 -d folio=412Filtros: status, tipo_dte, folio, receptor_rut, external_id, issue_date[gte|lte],
created_at[gte|lte]. El mode es implícito de la llave.
Un status no reconocido devuelve 400 invalid_field nombrando los siete valores válidos — una
página vacía se leería como «no tienes ninguno de esos», que es una respuesta distinta y mucho más
confusa.
Cada filtro, status incluido, se aplica en la consulta. Una página filtrada es una página
completa, y has_more / next_cursor significan lo que dicen: pagina hasta que has_more sea false
y tienes el conjunto entero.
Expansión
Los documentos devuelven pdf_url y xml_url por defecto. Para evitar una segunda vuelta:
curl "https://api.facturia.cl/v1/documents/doc_01K2R7Q4XW9M3B8ZC5YHTVJD6N?expand=xml,pdf_base64,events" \
-H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA"expand acepta una lista separada por comas: xml (string en línea), pdf_base64, events,
references, received_document (en documentos emitidos en respuesta a uno recibido). Un valor no
reconocido es 400 invalid_field con la lista de lo que sí acepta este recurso — no un 200 en
silencio en el que no puedes distinguir un typo de un resultado vacío.
En un listado el vocabulario es más angosto. GET /v1/documents?expand=… acepta events,
references y received_document; xml y pdf_base64 son 400 ahí y solo existen en
GET /v1/documents/{document_id}. Cada uno de los excluidos incorpora un artefacto completo por
fila, así que en una página de cien documentos serían cien lecturas de archivo en una sola respuesta.
received_document se resuelve al emitir: el documento emitido debe referenciar el tipo_dte y
folio del recibido, y el RUT emisor del recibido debe ser el RUT receptor del emitido, dentro de la
misma empresa. Si calzan dos, el vínculo queda en null — un antecedente equivocado es una
afirmación, y una ausencia no lo es.
El objeto documento no trae enlaces públicos. Ni en una lectura, ni en un listado, ni en un
webhook: la URL con su secreto ?k= aparece una sola vez, dentro del objeto public_links de la
respuesta que la mintea — la emisión que pidió pdf: { "public": true } / xml: { "public": true },
o POST /v1/documents/{document_id}/public_links. Guardamos el secreto hasheado, igual que una llave
de API, así que no hay lectura capaz de reconstruirlo. Ver
Artefactos y enlaces públicos.