Artefactos y enlaces públicos
PDF, XML, la copia cedible, y cómo entregarle un documento a alguien que no tiene una llave.
Los artefactos autenticados
# 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.xmlcopy: original (por defecto) o cedible. Cualquier otro valor es 400 invalid_field. Todos los
PDF son tamaño carta — las reglas de muestras del SII están basadas en carta, no en A4.
Los PDF se renderean cuando el SII acepta el documento; el XML existe desde el momento en que el
documento se firma. Un documento en queued todavía no tiene XML → 409 artifact_not_ready.
Un documento emitido directamente en el portal del SII nunca tiene XML acá, y nunca lo va a
tener. Facturia solo captura el XML firmado de los documentos que él mismo hizo avanzar por la
cadena de emisión emission_channel=sfg; uno que tu cuenta emitió en el Portal MIPYME antes o al
margen de Facturia — importado a /v1/documents por el backfill de historial — nunca estuvo bajo
custodia de Facturia. GET …/xml sobre uno de esos responde 409 artifact_not_available, nunca
artifact_not_ready: reintentar no hace nada, porque no viene nada en camino. El error nombra la
única forma de conseguirlo: la descarga manual «Respaldo de DTE y/o IECV» del propio Portal MIPYME,
que exige resolver un reCAPTCHA (en la búsqueda y en la descarga) y tope de 20 documentos por
operación. GET …/pdf no se ve afectado: proxea el render del propio SII y responde igual para
cualquiera de los dos orígenes.
Las dos copias son dos archivos distintos, generados juntos al aceptarse el documento. La original
no lleva recuadro de acuse de recibo ni leyenda CEDIBLE; la cedible lleva ambos. expand=pdf_base64
y el correo de §9.7 adjuntan siempre la original: la cedible es un instrumento para ceder el crédito,
no la factura que le mandas a un cliente para que pague.
Trampa
Dos parámetros que estaban aquí y ya no están
format (carta | 80mm) fue retirado. Ningún handler lo leyó nunca: ?format=80mm devolvía el
mismo PDF carta que todo lo demás, así que una integración de POS conectada a él imprimía una hoja
carta en un rollo térmico sin nada en la respuesta que lo dijera. No existe layout térmico. Lo mismo
aplica a pdf: { "format": … } en la emisión.
copy=both fue retirado. El Manual de Muestras Impresas del SII permite exactamente una página por
archivo (p.12), así que las dos copias son dos archivos y una respuesta lleva uno de ellos.
Ambos ahora responden 400 invalid_field en vez de ignorarse en silencio.
Trampa
Documentos rendereados antes del 2026-08-15
Tienen un solo archivo guardado, y para una factura ese archivo es el cedible — el renderer antiguo
dibujaba el layout cedible sobre la única copia que hacía. Para esos documentos copy=cedible se
sirve correctamente desde él, y copy=original devuelve esos mismos bytes, leyenda incluida, en vez
de romper una lectura que hoy funciona. Pedir cualquiera de las dos copias encola un re-render, así
que la segunda llamada obtiene la original verdadera.
La copia cedible es de las facturas: tipo 33 y tipo 34. Pedir copy=cedible en cualquier otro
tipo devuelve 422 cedible_not_available.
Trampa
La copia cedible no existe en empresas emission_channel=sfg, en ningún tipo
Para ese canal el SII firma y timbra el documento él mismo — esta API entrega el PDF propio del SII
(mipeDisplayPDF.cgi) en vez de renderear uno, y esos bytes no llevan recuadro de acuse de recibo ni
leyenda CEDIBLE para dibujar encima. Pedir copy=cedible ahí devuelve 422 cedible_not_available_for_channel, nunca un original disfrazado de cedible. copy=original (el
valor por defecto) no cambia: sigue devolviendo la representación impresa del propio SII.
Cuando no mandas ?copy=, el valor por defecto no siempre es original. Se resuelve en este
orden, y el primero que dice algo gana:
- Un
?copy=explícito en esta lectura. Siempre gana. - El valor por defecto de este documento —
pdf.cedibleen la emisión:truehace que ESTE documento por defecto entregue la copia cedible;falselo fija explícitamente en la original aunque la empresa (siguiente punto) diga lo contrario. branding.cedible_by_defaultde la empresa. Aplica a todo documento futuro que no fije el suyo propio.
Ningún valor por defecto puede inventar una copia que el tipo no tiene, ni una que el canal no
produce — un tipo fuera de 33/34, o cualquier tipo en una empresa emission_channel=sfg, siempre
resuelve a original, en silencio. Solo un ?copy=cedible explícito en el tipo equivocado es
422 cedible_not_available, y solo un ?copy=cedible explícito en el canal SFG es 422 cedible_not_available_for_channel.
Eso es un hecho legal antes que una decisión de producto. La Ley 19.983 art. 1 exige la copia en
toda operación "en que el vendedor o prestador del servicio esté sujeto a la obligación de emitir
factura": el gatillo es la factura, sin mencionar IVA ni afecta. La Res. Ex. SII N°14/2005 la
nombra explícitamente para "facturas de ventas y servicios exentos o no afectos", con la leyenda
FACTURA EXENTA CUADRUPLICADO COBRO EJECUTIVO. Y el Manual de Muestras Impresas §1.4 del propio
SII la exige para la Factura No Afecta o Exenta Electrónica.
Trampa
Dos límites honestos sobre la copia cedible
Somos más angostos que la ley. Los tipos 46 y 52 también son cedibles y todavía no rendereamos su layout cedible. Preferimos una restricción de producto dicha en voz alta antes que una afirmación sobre un archivo que no producimos.
La cedibilidad también depende de la forma de pago. Una factura al contado no es cedible. Este
parámetro mira solo el tipo de documento, así que te va a entregar una copia cedible para una factura
al contado cuya copia no tiene uso legal. Afinarlo exige un mapeo de FmaPago que no hemos
verificado, y adivinarlo sería peor que decir esto.
Enlaces públicos
Las URL de arriba necesitan tu llave. Para entregarle un artefacto a alguien que no tiene una — el receptor, su contador, un hilo de WhatsApp — pide un enlace público al emitir. Los dos artefactos lo soportan, en los mismos términos:
"pdf": { "public": true },
"xml": { "public": true }que agregan un objeto public_links a la respuesta de esa emisión, y solo ahí:
"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"
}La llave no aparece cuando la emisión no pidió ningún enlace: ausente, no presente en null. Y
vive en la respuesta de la emisión, no en el objeto documento — ninguna lectura, listado, expansión
ni webhook trae una URL pública.
Fíjate en la forma: cada artefacto tiene su propio slug y su propio secreto, así que rotar el
enlace del PDF no toca el del XML. Una URL sin su ?k= no es una versión corta del enlace: es un
enlace distinto que no existe, y responde 404.
Trampa
Estas URL se muestran una sola vez
La mitad secreta se guarda hasheada, igual que una llave de API, así que no podemos reconstruir una URL funcional después — la misma propiedad que hace que un volcado de la base de datos no sea un volcado de documentos.
Por eso el objeto documento no tiene ningún campo de enlace público. Volver a leer el documento
no te dice nada sobre sus enlaces: ya no tenemos la mitad que los hace funcionar. Los campos
pdf_public_url y xml_public_url existieron en el objeto documento hasta el ticket #18 y siempre
devolvieron una URL sin secreto que respondía 404; los retiramos en vez de guardar el secreto de
forma recuperable, porque una URL que abre un artefacto sin llave no debería quedar en cada log,
caché y proxy que toca una lectura.
Captura la URL cuando la recibas, o mintea una nueva con POST …/public_links.
El enlace del XML no es un gemelo de conveniencia del PDF: es el artefacto que le importa a quien tiene que hacer algo con el documento. El PDF es para que una persona lea; el XML es lo que ingiere el sistema contable del receptor y lo que su contador va a pedir cuando el PDF no cuadre.
# 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"Trampa
Un tercer artefacto: cedible
{"artifacts":["cedible"]} mintea un enlace al archivo cedible de verdad —
https://ver.facturia.cl/d/{slug}.cedible?k=…, cedible_public_url en la respuesta — no el enlace
del pdf con otro nombre. Es un artefacto real y separado, así que hereda sus mismas reglas: solo
existe para facturas (tipo 33/34; 422 cedible_not_available en cualquier otro tipo), y no está en
el conjunto por defecto — omitir artifacts sigue minteando exactamente pdf y xml. Pídelo
explícitamente, cada vez. pdf: { "public": true } en la emisión tampoco lo mintea: ese atajo sigue
siendo solo para la copia original. Tampoco existe en empresas emission_channel=sfg, en ningún
tipo, por la misma razón que ?copy=cedible no existe ahí (arriba): pedir artifacts: ["cedible"]
devuelve 422 cedible_not_available_for_channel en vez de un enlace .cedible que solo podría
resolver al archivo no cedible.
Vencimiento
Los enlaces son firmados, inadivinables, con vencimiento (90 días por defecto), con alcance de un
artefacto de un documento, y revocables en cualquier momento. POST …/public_links y la respuesta de
la emisión son los dos únicos lugares donde aparece una URL funcional.
| Unidad | Dónde | Rango |
|---|---|---|
link_expires_in_days | en el objeto pdf / xml de la emisión | 1–365, o null para no vencer |
expires_in_days | en POST /v1/documents/{document_id}/public_links | 1–365 |
link_expires_in_seconds | en el objeto pdf / xml de la emisión | 30 s – 365 días |
expires_in_seconds | en POST /v1/documents/{document_id}/public_links | 30 s – 365 días |
Los segundos ganan cuando mandas ambas unidades. Los días son la unidad correcta para un enlace que le entregas a un receptor y la incorrecta para uno que le entregas a una máquina. Usa segundos para cualquier cosa que deba estar muerta en minutos — una subida a un formulario del SII, una descarga de una sola vez en un chat.
La respuesta de POST …/public_links devuelve ambas unidades, así que un enlace de diez minutos
nunca se reporta como «0 días»:
{ "expires_in_seconds": 600, "expires_in_days": 0.00694 }El public_links de una emisión no devuelve vencimiento: ahí pdf y xml llevan sus propios
link_expires_in_*, así que un solo par de campos sería falso cada vez que difieren.
Qué expone y qué no
Un enlace público expone exactamente un artefacto de un documento. Nunca expone los demás documentos de la empresa, ni su metadata, ni nada de tu cuenta, y no puede escalarse a una credencial de API.
xml_public_url sirve los bytes firmados exactamente como se enviaron al SII, con
Content-Type: application/xml; charset=ISO-8859-1 — idénticos byte a byte al endpoint autenticado
/xml, porque un documento cuyos bytes cambian según la ruta es un documento cuya firma alguien va a
terminar fallando en verificar.