# Artifacts and public links (/en/docs/emision/artefactos)

## The authenticated artifacts [#the-authenticated-artifacts]

```bash
# PDF — la copia original (tributaria), tamaño carta
curl -L https://api.facturia.cl/v1/documents/doc_01K2R7.../pdf \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA" -o factura.pdf

# Copia cedible (facturas: tipo 33 y 34)
curl -L "https://api.facturia.cl/v1/documents/doc_01K2R7.../pdf?copy=cedible" \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA" -o factura-cedible.pdf

# XML firmado, exactamente como se envió al SII (ISO-8859-1)
curl -L https://api.facturia.cl/v1/documents/doc_01K2R7.../xml \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA" -o dte.xml
```

`copy`: `original` (the default) or `cedible`. Any other value is `400 invalid_field`. Every PDF is
letter size — the printed-sample rules of the SII, Chile's tax authority, are built on letter, not A4.

PDFs are rendered when the SII accepts the document; the XML exists from the moment the document is
signed. A document in `queued` has no XML yet → `409 artifact_not_ready`.

**A document issued directly in the SII portal never has an XML here, and never will.** Facturia only
captures the signed XML of the documents it moved through the `emission_channel=sfg` issuing chain
itself; one your account issued in the Portal MIPYME before Facturia, or alongside it — imported into
`/v1/documents` by the history backfill — was never in Facturia's custody. `GET …/xml` on one of those
answers `409 artifact_not_available`, never `artifact_not_ready`: retrying does nothing, because nothing
is on its way. The error names the only way to get it: the manual "Respaldo de DTE y/o IECV" download in
the Portal MIPYME itself, which requires solving a reCAPTCHA (on the search and on the download) and
caps out at 20 documents per operation. `GET …/pdf` is unaffected: it proxies the SII's own render and
answers the same for either origin.

**The two copies are two different files**, generated together when the document is accepted. The
original carries no acknowledgement-of-receipt box and no `CEDIBLE` legend; the cedible carries both.
`expand=pdf_base64` and the §9.7 email always attach the original: the cedible is an instrument for
assigning the credit, not the factura you send a customer so they pay.

> **Trap — Two parameters that were here and are gone**
>
> **`format` (`carta` | `80mm`) was withdrawn.** No handler ever read it: `?format=80mm` returned the same
> letter-size PDF as everything else, so a POS integration wired to it printed a letter page on a thermal
> roll with nothing in the response saying so. There is no thermal layout. The same goes for
> `pdf: { "format": … }` at issue time.
>
> **`copy=both` was withdrawn.** The SII's Manual de Muestras Impresas allows exactly one page per file
> (p.12), so the two copies are two files and one response carries one of them.
>
> Both now answer `400 invalid_field` instead of being ignored in silence.

> **Trap — Documents rendered before 2026-08-15**
>
> They have a single stored file, and for a factura — Chile's electronic invoice — that file is the
> cedible: the old renderer drew the cedible layout on the only copy it made. For those documents
> `copy=cedible` is served correctly from it, and `copy=original` returns those same bytes, legend
> included, instead of breaking a read that works today. Asking for either copy queues a re-render, so the
> second call gets the true original.

The copia cedible belongs to **facturas**: tipo 33 and tipo 34. Asking for `copy=cedible` on any other
type returns `422 cedible_not_available`.

> **Trap — The copia cedible does not exist on emission_channel=sfg empresas, for any type**
>
> On that channel the SII signs and stamps the document itself — this API hands back the SII's own PDF
> (`mipeDisplayPDF.cgi`) instead of rendering one, and those bytes carry no acknowledgement-of-receipt box
> and no `CEDIBLE` legend to draw over. Asking for `copy=cedible` there returns `422
> cedible_not_available_for_channel`, never an `original` dressed up as a cedible. `copy=original` (the
> default) does not change: it still returns the SII's own printed representation.

**When you do not send `?copy=`, the default is not always `original`.** It resolves in this order, and
the first one that says anything wins:

1. **An explicit `?copy=`** on this read. Always wins.
2. **This document's own default** — `pdf.cedible` at issue time: `true` makes THIS document serve the
   copia cedible by default; `false` pins it explicitly to the original even if the empresa (next item)
   says otherwise.
3. **The empresa's `branding.cedible_by_default`.** It applies to every future document that does not
   pin its own.

No default can invent a copy the type does not have, or one the channel does not produce — a type
outside 33/34, or any type on an `emission_channel=sfg` empresa, always resolves to `original`, in
silence. Only an **explicit** `?copy=cedible` on the wrong type is `422 cedible_not_available`, and only
an **explicit** `?copy=cedible` on the SFG channel is `422 cedible_not_available_for_channel`.

That is a legal fact before it is a product decision. **Ley 19.983 art. 1** requires the copy in every
operation "en que el vendedor o prestador del servicio esté sujeto a la obligación de emitir factura":
the trigger is the factura, with no mention of IVA or afecta. **Res. Ex. SII N°14/2005** names it
explicitly for "facturas de ventas y servicios exentos o no afectos", with the legend
`FACTURA EXENTA CUADRUPLICADO COBRO EJECUTIVO`. And the SII's own **Manual de Muestras Impresas §1.4**
*requires* it for the Factura No Afecta o Exenta Electrónica.

> **Trap — Two honest limits on the copia cedible**
>
> **We are narrower than the law.** Tipos 46 and 52 are cedible too, and we do not render their cedible
> layout yet. We prefer a product limit said out loud to a claim about a file we do not produce.
>
> **Cedibility also depends on the payment terms.** A factura paid *al contado* — cash, on the spot — is
> not cedible. This parameter looks only at the document type, so it will hand you a copia cedible for a
> cash factura whose copy has no legal use. Getting it right needs an `FmaPago` mapping we have not
> verified, and guessing would be worse than saying this.

## Public links [#enlaces-publicos]

The URLs above need your key. To hand an artifact to someone who has none — the receptor, their
accountant, a WhatsApp thread — ask for a public link at issue time. &#x2A;*Both artifacts support it, on the
same terms:**

```json
"pdf": { "public": true },
"xml": { "public": true }
```

which add a `public_links` object **to the response of that issue call, and only there**:

```json
"public_links": {
  "object": "public_links",
  "pdf_public_url": "https://ver.facturia.cl/d/8f2c1a9e4b?k=SkQ2b1F4c0pYb2s5",
  "xml_public_url": "https://ver.facturia.cl/d/3d9f2b8a1c.xml?k=WmY3aE1uUjBkNVRi"
}
```

The key **does not appear** when the issue call asked for no link: absent, not present as `null`. And it
lives in the response to the issue call, not in the document object — no read, listing, expansion or
webhook carries a public URL.

Note the shape: **each artifact has its own slug and its own secret**, so rotating the PDF link does not
touch the XML one. A URL without its `?k=` is not a short version of the link: it is a different link
that does not exist, and answers 404.

> **Trap — These URLs are shown exactly once**
>
> The secret half is stored hashed, the same as an API key, so we cannot reconstruct a working URL
> afterwards — the same property that keeps a dump of the database from being a dump of documents.
>
> That is why **the document object has no public-link field at all**. Reading the document again tells
> you nothing about its links: we no longer hold the half that makes them work. The `pdf_public_url` and
> `xml_public_url` fields lived on the document object until ticket #18 and always returned a secretless
> URL that answered 404; we withdrew them instead of storing the secret recoverably, because a URL that
> opens an artifact without a key should not end up in every log, cache and proxy a read touches.
>
> Capture the URL when you receive it, or mint a new one with `POST …/public_links`.

The XML link is not a convenience twin of the PDF one: it is the artifact that matters to whoever has to
*do something* with the document. The PDF is for a person to read; the XML is what the receptor's
accounting system ingests and what their accountant will ask for when the PDF does not add up.

```bash
# Sin header Authorization — ese es el punto
curl -L https://ver.facturia.cl/d/8f2c1a9e4b.xml -o dte.xml

# Encender un enlace después de emitir, o rotarlo (invalida la URL anterior)
curl -X POST https://api.facturia.cl/v1/documents/doc_01K2R7.../public_links \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA" \
  -H "Content-Type: application/json" \
  -d '{"artifacts":["pdf","xml"],"expires_in_days":30}'

# Revocar todo lo compartido de este documento
curl -X DELETE https://api.facturia.cl/v1/documents/doc_01K2R7.../public_links \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA"
```

> **Trap — A third artifact: cedible**
>
> `{"artifacts":["cedible"]}` mints a link to the real cedible file —
> `https://ver.facturia.cl/d/{slug}.cedible?k=…`, `cedible_public_url` in the response — not the `pdf`
> link under another name. It is a real, separate artifact, so it inherits the same rules: it exists only
> for facturas (tipo 33/34; `422 cedible_not_available` on any other type), and it is not in the default
> set — omitting `artifacts` still mints exactly `pdf` and `xml`. Ask for it explicitly, every time.
> `pdf: { "public": true }` at issue time does not mint it either: that shortcut is still only for the
> original copy. It does not exist on `emission_channel=sfg` empresas either, for any type, for the same
> reason `?copy=cedible` does not exist there (above): asking for `artifacts: ["cedible"]` returns
> `422 cedible_not_available_for_channel` instead of a `.cedible` link that could only resolve to the
> non-cedible file.

### Expiry [#expiry]

Links are signed, unguessable, expiring (90 days by default), scoped to one artifact of one document,
and revocable at any time. `POST …/public_links` and the response to an issue call are the only two
places a working URL appears.

| Unit                      | Where                                              | Range                          |
| ------------------------- | -------------------------------------------------- | ------------------------------ |
| `link_expires_in_days`    | in the `pdf` / `xml` object of the issue call      | 1–365, or `null` for no expiry |
| `expires_in_days`         | in `POST /v1/documents/{document_id}/public_links` | 1–365                          |
| `link_expires_in_seconds` | in the `pdf` / `xml` object of the issue call      | 30 s – 365 days                |
| `expires_in_seconds`      | in `POST /v1/documents/{document_id}/public_links` | 30 s – 365 days                |

**Seconds win when you send both units.** Days are the right unit for a link you hand to a receptor and
the wrong one for a link you hand to a machine. Use seconds for anything that has to be dead within
minutes — an upload to an SII form, a one-time download in a chat.

The response of `POST …/public_links` returns both units, so a ten-minute link is never reported as
"0 days":

```json
{ "expires_in_seconds": 600, "expires_in_days": 0.00694 }
```

The `public_links` of an issue call returns no expiry: there `pdf` and `xml` carry their own
`link_expires_in_*`, so a single pair of fields would be false every time they differ.

### What it exposes and what it does not [#what-it-exposes-and-what-it-does-not]

A public link exposes exactly **one artifact of one document**. It never exposes the empresa's other
documents, or their metadata, or anything about your account, and it cannot be escalated into an API
credential.

`xml_public_url` serves the signed bytes exactly as they were sent to the SII, with
`Content-Type: application/xml; charset=ISO-8859-1` — byte for byte identical to the authenticated
`/xml` endpoint, because a document whose bytes change with the route is a document whose signature
somebody will eventually fail to verify.
