Facturiadocs
Issuing

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

FieldWhere it comes from
Emisor block — RUT (the tax id every Chilean business has), razón social, giro, address, comuna, actecothe empresa profile
foliothe 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_datetoday in America/Santiago
Razón social, giro, address and comuna of the receptorthe empresa's address book, otherwise the SII's taxpayer registry, resolved by RUT
totalscalculated from items under the tax rules of the type
payment_terms, currency, indicatorssensible 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.

  • totals is optional, and when it is present it is an assertion. If our calculation differs by even one peso, you get 422 totals_mismatch with both figures and nothing is issued. A financial system should always send it.
  • totals.other_taxes is 0 except on a tipo 46 with withholding, where it carries the retención as a NEGATIVE number (for example -1891 when the IVA — Chile's VAT — withheld is 1891) — the only way for total = net + exempt + iva + other_taxes to stay true whether or not tax is withheld.
  • global_discounts takes at most one entry. The SII's <DscRcgGlobal> is a single NroLinDR>1, so a second entry is refused with 400 invalid_field before any folio is reserved — merge them into one, or push the rest into items[].discount_percent.
  • Discounts belong to the mercado channel. On an empresa with emission_channel=sfg both are refused at the door: global_discounts with 422 global_discount_not_available_for_channel, and any items[].discount_percent greater than 0 with 422 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 its EFXP_PCT_DESC/EFXP_MNT_DESC entries are commented out — so a discount accepted there would be a discount the portal never sees: we would quote you neto 80.000 / total 95.200 and the SII would sign neto 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 into unit_price, or issue through emission_channel=mercado. A discount_percent of 0 is accepted on both channels: it is exactly what the portal would issue.
  • references with code: 1|2|3 are 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's tipo_dte codes in the 800 range and omit code. references[].date takes the document's issue_date when omitted; references[].folio accepts a number or a string, and a number is converted to its decimal string (<FolioRef> is xs:string in the SII schema).
  • metadata is an arbitrary string map (≤ 20 keys, ≤ 500 characters each) that we store and hand back untouched. It never reaches the SII.
  • email_document_to sends the PDF and the XML to the receptor when the document reaches accepted.

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=412

Filters: 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.

On this page