Create documents
The minimal issue call, the full issue call, what we fill in for you, and how to read the documents you have already issued.
The minimal issue call
The point of storing the emisor profile on the empresa is that a factura — Chile's electronic invoice — is three fields.
The receptor carries its giro and address because the SII will not file a factura without them: it accepts the ENVELOPE and then refuses the DOCUMENT, and the folio is spent either way. Once you have invoiced a RUT once, receptor: {"rut": "…"} on its own is enough — we fill the rest from your own earlier document to that RUT. When there is no earlier one, you get 400 receptor_incomplete naming the exact fields, before any folio is allocated. Boletas (39/41) and a foreign factura de compra (46) never need them.
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 }
]
}'What we fill in for you
| Field | Where it comes from |
|---|---|
| Emisor block — RUT (the tax id every Chilean business has), razón social, giro, address, comuna, acteco | the empresa profile |
folio | the next folio from the CAF active for this type — a folio is the pre-authorised sequential number a document must carry, and a CAF is the block of them signed by the SII, Chile's tax authority |
issue_date | today in America/Santiago |
| Razón social, giro, address and comuna of the receptor | the empresa's address book, otherwise the SII's taxpayer registry, resolved by RUT |
totals | calculated from items under the tax rules of the type |
payment_terms, currency, indicators | sensible defaults (contado, CLP) |
If the receptor cannot be resolved and the type requires those fields, you get 400 receptor_incomplete
naming exactly which ones to send — never a silent SII rejection three minutes later.
The full issue call
Everything the SII lets you say, said explicitly.
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" }
}'The four fields worth understanding
Trap
prices_include_tax is where the 19% bug comes from
The default depends on the type: true for boletas (39/41, the receipt a consumer gets), false for
everything else. Set it explicitly and we normalise in either direction.
It is the number-one source of 19% errors in Chilean invoicing. That it is a named field instead of an implicit convention is deliberate: if your system stores gross prices and you issue a factura, say so.
Normalisation is not symmetric. On a factura (33/34/56/61/46) the SII's <PrcItem>/<MontoItem> is
always NET (DTE_v10.xsd: net unless MntBruto is present, which we never send), so
prices_include_tax: true there normalises DOWNWARD at the door — the items we return, the PDF and
the <PrcItem> all end up net (11990 → 10075.6303), and the total can land one peso away from the
gross you quoted. On a boleta (39/41), whose MontoItem is gross by schema, the price stays gross
whatever prices_include_tax says.
totalsis optional, and when it is present it is an assertion. If our calculation differs by even one peso, you get422 totals_mismatchwith both figures and nothing is issued. A financial system should always send it.totals.other_taxesis 0 except on a tipo 46 withwithholding, where it carries the retención as a NEGATIVE number (for example-1891when the IVA — Chile's VAT — withheld is1891) — the only way fortotal = net + exempt + iva + other_taxesto stay true whether or not tax is withheld.global_discountstakes at most one entry. The SII's<DscRcgGlobal>is a singleNroLinDR>1, so a second entry is refused with400 invalid_fieldbefore any folio is reserved — merge them into one, or push the rest intoitems[].discount_percent.- Discounts belong to the
mercadochannel. On an empresa withemission_channel=sfgboth are refused at the door:global_discountswith422 global_discount_not_available_for_channel, and anyitems[].discount_percentgreater than 0 with422 line_discount_not_available_for_channel, naming the line. The SII portal's emission form has no discount field we have ever captured — in the captured form both the per-line "% Desc" column and itsEFXP_PCT_DESC/EFXP_MNT_DESCentries are commented out — so a discount accepted there would be a discount the portal never sees: we would quote youneto 80.000 / total 95.200and the SII would signneto 100.000 / total 119.000. It is refused rather than silently dropped, because you would be charging your customer the difference on a document that only a nota de crédito can undo. Fold the discount intounit_price, or issue throughemission_channel=mercado. Adiscount_percentof0is accepted on both channels: it is exactly what the portal would issue. referenceswithcode: 1|2|3are the SII's reference codes (1 = voids the referenced document, 2 = corrects text, 3 = corrects amounts). References to documents that are not DTEs (documentos tributarios electrónicos, Chile's electronic tax documents) — a purchase order, a contract — use the SII'stipo_dtecodes in the 800 range and omitcode.references[].datetakes the document'sissue_datewhen omitted;references[].folioaccepts a number or a string, and a number is converted to its decimal string (<FolioRef>isxs:stringin the SII schema).metadatais an arbitrary string map (≤ 20 keys, ≤ 500 characters each) that we store and hand back untouched. It never reaches the SII.email_document_tosends the PDF and the XML to the receptor when the document reachesaccepted.
The document object
{
"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"
}Reading documents
# 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=412Filters: status, tipo_dte, folio, receptor_rut, external_id, issue_date[gte|lte],
created_at[gte|lte]. The mode is implicit in the key.
An unrecognised status returns 400 invalid_field naming the seven valid values — an empty page would
read as "you have none of those", which is a different answer and a far more confusing one.
Every filter, status included, is applied in the query. A filtered page is a complete page, and
has_more / next_cursor mean what they say: page until has_more is false and you have the whole
set.
Expansion
Documents return pdf_url and xml_url by default. To avoid a second round trip:
curl "https://api.facturia.cl/v1/documents/doc_01K2R7Q4XW9M3B8ZC5YHTVJD6N?expand=xml,pdf_base64,events" \
-H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA"expand takes a comma-separated list: xml (an inline string), pdf_base64, events, references,
received_document (on documents issued in response to a received one). An unrecognised value is
400 invalid_field with the list this resource does accept — not a silent 200 in which you cannot tell
a typo from an empty result.
On a listing the vocabulary is narrower. GET /v1/documents?expand=… accepts events,
references and received_document; xml and pdf_base64 are a 400 there and exist only on
GET /v1/documents/{document_id}. Each of the excluded ones embeds a whole artifact per row, so a page
of a hundred documents would mean a hundred file reads in a single response.
received_document is resolved at issue time: the issued document must reference the tipo_dte and
folio of the received one, and the emisor RUT of the received document must be the receptor RUT of the
issued one, within the same empresa. If two match, the link is left null — the wrong antecedent is an
assertion, and an absence is not.
The document object carries no public links. Not on a read, not on a listing, not in a webhook: the
URL with its ?k= secret appears exactly once, inside the public_links object of the response that
mints it — the issue call that asked for pdf: { "public": true } / xml: { "public": true }, or
POST /v1/documents/{document_id}/public_links. We store the secret hashed, the same as an API key, so
no read can reconstruct it. See
Artifacts and public links.