Facturiadocs
Emisión

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.xml

copy: 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:

  1. Un ?copy= explícito en esta lectura. Siempre gana.
  2. El valor por defecto de este documento — pdf.cedible en la emisión: true hace que ESTE documento por defecto entregue la copia cedible; false lo fija explícitamente en la original aunque la empresa (siguiente punto) diga lo contrario.
  3. branding.cedible_by_default de 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.

UnidadDóndeRango
link_expires_in_daysen el objeto pdf / xml de la emisión1–365, o null para no vencer
expires_in_daysen POST /v1/documents/{document_id}/public_links1–365
link_expires_in_secondsen el objeto pdf / xml de la emisión30 s – 365 días
expires_in_secondsen POST /v1/documents/{document_id}/public_links30 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.

En esta página