Factuarea APIDevelopers

Vistas previas y artefactos PDF

Previsualiza una plantilla antes de usarla, prepara y renderiza el PDF exacto de un documento, descárgalo con ETag y 304, y lee pdf_output_state con un bucle de reintento correcto.

Todo PDF que la API genera a partir de la biblioteca de plantillas es un artefacto: un PDF exacto e inmutable con su propio identificador, su estado y su suma de comprobación. Existen dos tipos.

purposeQué esVida útil
documentEl PDF original de un documento: se genera a partir de la presentación con la que se capturó el documento y sus bytes nunca cambian.Se conserva con el documento.
previewUn borrador para comprobar cómo quedará una plantilla, una marca o un documento. Nunca es el PDF oficial de nada.Caduca 24 horas después de crearse (expires_at).

Cuatro grupos de operaciones cubren el flujo. La gestión de la biblioteca, los valores por defecto y la precedencia están en la guía de la biblioteca de plantillas PDF.

OperaciónScopesIdempotency-Key
POST /v1/companies/{company}/pdf-template-previewspdfs:read y pdf_templates:readObligatoria
POST /v1/companies/{company}/pdf-artifacts (preparar)pdfs:readObligatoria
POST /v1/companies/{company}/pdf-artifacts/{artifact}/renderpdfs:readObligatoria
GET /v1/companies/{company}/pdf-artifacts/{artifact} (metadatos)pdfs:readNo aplica
GET /v1/companies/{company}/pdf-artifacts/{artifact}/downloadpdfs:readNo aplica
GET /v1/companies/{company}/invoices/{invoice}/pdf y sus equivalentes de presupuesto, proforma y albaránpdfs:readNo aplica

Todas las rutas de esta guía cuelgan de /v1/companies/{company}/…: {company} es el id (UUID v7) del NIF con el que operas, y en los ejemplos curl es $COMPANY_ID. Alcanzar un NIF explica cómo resolverlo.

Los artefactos necesitan el rol admin o user y la sección de módulo de la familia del documento (invoices, quotes, proformas o delivery_notes). Los artefactos de vista previa siguen además el módulo pdf_templates y el scope pdf_templates:read, así que leer uno también exige ese scope. Generar un PDF consume el mismo presupuesto de renderizado que los demás endpoints de PDF: el trabajo nuevo puede devolver 429 pdf_generation_budget_exceeded con Retry-After, mientras que las repeticiones y los artefactos ya renderizados no cuestan nada más (véanse los límites de peticiones).

Previsualizar una plantilla

POST /v1/companies/{company}/pdf-template-previews genera un PDF sin guardar nada: no publica una plantilla, no cambia un valor por defecto ni toca un documento. El cuerpo tiene cinco campos obligatorios y un source cerrado:

CampoValor
document_typeinvoice, quote, proforma o delivery-note.
purposeFacturas: complete, simplified, corrective, simplified_corrective. Otros tipos: complete.
formata4, ticket_80 o ticket_58 (tickets solo para facturas).
localees, en o ca.
sourceDe dónde salen los datos, véase abajo.

Según el origen, otros tres campos describen el diseño que se genera: base_preset, layout_version (siempre "1") y configuration (la configuración normalizada completa, tal como la devuelve una revisión de plantilla).

source.kindDatos que generaBloque visual
exampleDatos de ejemplo: scenario es standard, long, simplified, corrective, simplified_corrective, f3 o unvalued_delivery_note. commercial_identity es opcional: { "mode": "inherit" } (por defecto), { "mode": "exact_revision", "identity_id", "revision_id" } o { "mode": "draft", "configuration": { ... } } con las seis claves de marca y, opcionalmente, logo_dark_asset_id, brand_image_dark_asset_id, dark_background_fallback y brand_size.Los tres, obligatorios.
unsaved_formEl objeto form que envías (serie, contacto, documento, destinatario, líneas, condiciones de pago...), sin guardar nada.Los tres, obligatorios.
persisted_draftUn borrador de documento existente: { "kind": "persisted_draft", "document_id" }.Los tres o ninguno.
presentation_revisionUn documento emitido y su presentación retenida: document_id, parent_id, template_id (null para heredar), source_policy (captured o current_legal) y expected_version. Admite un format distinto del conservado para pedir otro papel del mismo emitido (ticket o A4), sin crear revisión.Los tres o ninguno.
invoice_substitutionUna vista previa independiente de la factura que sustituiría a facturas simplificadas: document_id (null si es nueva), source_invoice_uuids y line_uuids (misma longitud y orden, hasta 100), client_uuid, notes, prepared_date y template_id (null para heredar). Solo con document_type: invoice, purpose: complete y format: a4; no se guarda ni se sustituye nada.Ninguno: omite los tres.

Cuando un origen persistido omite el bloque visual, el servidor previsualiza la plantilla que se aplicaría hoy. Enviar solo uno o dos de los tres campos es un 422 pdf_presentation_invalid, con el que falta en param. El selector de marca draft exige el rol admin y activos que ya hayas subido.

Con document_type: invoice, source.form.document.exemption_reason es una causa de exención de cabecera opcional (catálogo en GET /v1/tax-catalog): su mención legal abre las menciones automáticas de la vista previa, igual que al guardar la factura. Omítelo o envía null para no usar causa. Una causa desconocida, o enviada en otro tipo de documento, se rechaza.

Una buena forma de previsualizar un diseño que estás editando es reutilizar la revisión que leíste:

curl -s https://api.factuarea.com/v1/companies/$COMPANY_ID/pdf-templates/0199f3a2-7c4e-7b1a-9d3e-5a2c8f1b6e40 \
  -H "Authorization: Bearer fact_test_3pXnR2VbY7TcA9eFmN5z8KqW" \
  | jq '{
      document_type: "invoice", purpose: "complete", format: "a4", locale: "en",
      base_preset: .current_revision.base_preset,
      layout_version: .current_revision.layout_version,
      configuration: .current_revision.configuration,
      source: { kind: "example", scenario: "standard" }
    }' > preview.json

curl -X POST https://api.factuarea.com/v1/companies/$COMPANY_ID/pdf-template-previews \
  -H "Authorization: Bearer fact_test_3pXnR2VbY7TcA9eFmN5z8KqW" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  --data-binary @preview.json

La respuesta es el artefacto, envuelto en data. Es 200 cuando el PDF está listo y 202 mientras está prepared, queued o processing; en este segundo caso consulta metadata_url como se describe en el bucle de reintento (abreviado):

{
  "data": {
    "id": "0199f405-91ab-7c42-8d6e-2a9f3b5c7e10",
    "purpose": "preview",
    "state": "ready",
    "source_kind": "example",
    "context_authority": "illustrative",
    "sha256": "7a265f5f003866f84e23c7e237301879b7d4a4ecc0ab80d2041a3c980b12310a",
    "size_bytes": 48210,
    "expires_at": "2026-10-15T09:20:00.000000Z",
    "geometry": { "format": "a4", "orientation": "portrait", "width_mm": 210, "height_mm": 297 },
    "metadata_url": "https://api.factuarea.com/v1/companies/0193f1a0-5c2e-7b41-8d06-9a1e2f3c4d5e/pdf-artifacts/0199f405-91ab-7c42-8d6e-2a9f3b5c7e10",
    "download_url": "https://api.factuarea.com/v1/companies/0193f1a0-5c2e-7b41-8d06-9a1e2f3c4d5e/pdf-artifacts/0199f405-91ab-7c42-8d6e-2a9f3b5c7e10/download",
    "retry_after": null,
    "error": null,
    "unavailable_reason": null
  }
}

El artefacto también informa de effective_font y warnings del renderizado, de context_fingerprint, de artifact_fingerprint y de los tokens que se describen a continuación.

Autoridad: qué puede autorizar una vista previa

context_authority indica hasta dónde puedes fiarte de la vista previa.

context_authorityOrigenUso
illustrativeexample, unsaved_form, o un origen persistido con otro bloque visual.Solo para mirar. No tiene document_id, source_token ni selection_version.
delivery_preconditionpersisted_draft previsualizando la plantilla que se aplica.Devuelve source_token, selection_version, template_revision_id, commercial_identity_revision_id y expected_preview_context_fingerprint: los valores de la precondición de presentación de las operaciones de emitir, enviar y aceptar. Véase envío de documentos.
revision_preconditionpresentation_revision.Los mismos valores, válidos solo para una revisión explícita de ese documento. Nunca autorizan emitir, enviar, aceptar ni firmar.
substitution_preconditioninvoice_substitution.Devuelve los mismos valores que las otras precondiciones, propios de la sustitución de esa factura. Ninguna operación v1 los consume (POST /v1/companies/{company}/invoices/substitute-simplified no recibe precondición) y nunca autorizan emitir, enviar, aceptar ni firmar.
capturedLos artefactos document originales.El PDF retenido de un documento capturado.

Una vista previa nunca se convierte en el PDF oficial. Si después de previsualizar cambia el origen, la selección, la marca, la configuración o el entorno de renderizado, la precondición deja de coincidir y la acción responde 409; vuelve a previsualizar.

Vistas previas y el interruptor de la empresa

Las vistas previas nuevas y los artefactos de documento nuevos necesitan que la salida revisionada esté activa para tu empresa: el interruptor de la plataforma encendido y la inicialización de tu empresa terminada (el render_activation de solo lectura de los valores por defecto lo indica). Si no, la llamada devuelve 422 pdf_presentation_not_ready y no crea nada. No se pierde nada más: reintentar más tarde funciona, y las lecturas y el renderizado clásico siguen funcionando.

Preparar, renderizar y leer el PDF de un documento

El artefacto official de un documento se crea a partir de su presentación retenida: el id del bloque presentation que lleva el documento (véase Presentaciones de documento), junto con su tipo de documento, propósito y formato de papel.

Preparar. POST /v1/companies/{company}/pdf-artifacts con document_type, document_id, presentation_id, purpose, format y variant. Crea o encuentra el artefacto de exactamente esa presentación, así que repetir la llamada devuelve el mismo.

curl -X POST https://api.factuarea.com/v1/companies/$COMPANY_ID/pdf-artifacts \
  -H "Authorization: Bearer fact_test_3pXnR2VbY7TcA9eFmN5z8KqW" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "document_type": "invoice",
    "document_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a42",
    "presentation_id": "0199f401-33de-7a8c-9e15-b7c4d2a60f38",
    "purpose": "complete",
    "format": "a4",
    "variant": "official"
  }'

Renderizar. POST /v1/companies/{company}/pdf-artifacts/{artifact}/render con { "mode": "sync" } para esperar el PDF dentro de la petición, o { "mode": "async" } para encolarlo y consultarlo después. Renderizar un artefacto ready no vuelve a renderizar ni a cobrar.

Leer el estado. GET /v1/companies/{company}/pdf-artifacts/{artifact} responde siempre 200 con los metadatos, sea cual sea el estado. Estas lecturas no admiten query string, cuerpo ni If-Match.

Descargar. GET /v1/companies/{company}/pdf-artifacts/{artifact}/download devuelve los bytes cuando el estado es ready.

prepare y render responden 200 con el artefacto cuando ya está ready, y 202 mientras está prepared, queued o processing. state es uno de prepared, queued, processing, ready, failed, unavailable y corrupt. Un artefacto failed lleva error con el code (pdf_generation_failed, pdf_engine_profile_unavailable, pdf_image_resource_unavailable o pdf_payload_resource_unavailable), un indicador retryable y un correlation_id para dárselo a soporte. retry_after es el número de segundos que hay que esperar mientras está pendiente.

PDF originales y derivados

  • La variante official es el original: el PDF de la presentación capturada con el documento, con una suma de comprobación inmutable. Es el que informa pdf_output_state. Cambiar después una plantilla, un valor por defecto o una marca nunca lo altera.
  • Un aspecto distinto para un documento ya emitido es una presentación nueva: revisas el documento de forma explícita y eso crea una presentación derivada con su propio artefacto. El original sigue siendo legible.
  • La variante alternative es otro artefacto de la misma presentación en un formato de papel autorizado. Nunca sustituye al original, nunca cambia lo que publica el documento y no es el que informa pdf_output_state. Un estilo distinto siempre necesita una presentación nueva.
  • Si los bytes de un original se pierden o no superan su suma de comprobación, recibes 500 pdf_original_unavailable o 500 pdf_original_corrupt. Nada se regenera en silencio en su lugar.
  • Una presentación derivada (una firma, el QR legal o los datos legales actualizados) conserva el diseño de su original. Si la versión actual ya no sirve ese diseño, prepararla responde 500 pdf_original_layout_unavailable: el original está íntegro y no se prepara nada. Contacta con soporte; una revisión explícita y autorizada crea otro artefacto sin sustituir el original.

Cómo responden las descargas

EstadoCuándoQué hacer
200El PDF está listo. application/pdf, Content-Disposition: attachment, Cache-Control: private, no-store y un ETag fuerte: el SHA-256 de los bytes entre comillas.Guárdalo; conserva el ETag.
304Enviaste If-None-Match con el ETag actual. Se decide después de la autorización, la privacidad y una comprobación de los bytes guardados.Usa tu copia.
202Solo GET /v1/companies/{company}/{documents}/{id}/pdf: el PDF aún se está generando, también cuando un renderizado falló pero se puede reintentar y esta petición lo ha vuelto a reclamar (un fallo reintentable nunca se responde con el error anterior). JSON data con pdf_url y pdf_output_state, y una cabecera Retry-After.Espera y repite la misma petición.
503GET .../download de un artefacto que no está listo: pdf_artifact_not_ready con Retry-After. Los metadatos siguen respondiendo 200. El mismo 503 llega de una vista previa, de prepare, de render, de GET /v1/companies/{company}/{documents}/{id}/pdf o de GET /v1/companies/{company}/invoices/{invoice}/pdf-link mientras otra petición sigue preparando ese mismo PDF; esa petición no ha creado nada.Espera Retry-After segundos y repite la misma petición.
503pdf_generation_failed: los endpoints directos del documento no pudieron producir el PDF; el documento y sus datos siguen intactos.Espera y repite la misma petición.
410pdf_signature_personal_data_forgotten: se borraron los datos de la firma del documento. Terminal.No reintentes; el artefacto ya no tiene descarga.
404pdf_artifact_not_found: desconocido, de otra empresa o una vista previa caducada.Crea una vista previa nueva.
500pdf_original_unavailable, pdf_original_corrupt, pdf_original_layout_unavailable o un renderizado que falló de forma definitiva: pdf_engine_profile_unavailable, pdf_payload_resource_unavailable, pdf_image_resource_unavailable. No se encola nada.Contacta con soporte con el request_id.

GET /v1/companies/{company}/invoices/{invoice}/pdf, GET /v1/companies/{company}/quotes/{quote}/pdf, GET /v1/companies/{company}/proformas/{proforma}/pdf y GET /v1/companies/{company}/delivery-notes/{delivery_note}/pdf, a nivel de documento, devuelven el mismo 200 con los bytes, el 304 con If-None-Match y el 202 anterior. Aceptan download=1 para una disposición de adjunto, y las facturas también format. Consulta la referencia de facturas, presupuestos, facturas proforma y albaranes. Cuando el original no se puede servir responden con un error en lugar de un 202: se aplican los estados anteriores, más 409 cuando la presentación conservada no se puede entregar como se pide: pdf_presentation_conflict (cambió desde que la leíste), pdf_presentation_context_changed (por ejemplo, el format solicitado no coincide con el conservado) y, en facturas, pdf_legal_source_changed (revísala con source_policy: "current_legal"). Vuelve a leer la presentación del documento antes de pedir el PDF. Mientras un documento no tiene presentación retenida, o tu empresa sigue renderizando con el flujo clásico, el PDF se sirve como antes.

El bloque pdf_output_state

Las facturas, los presupuestos, las facturas proforma y los albaranes llevan un objeto pdf_output_state de solo lectura que resume su PDF original. Nunca prepara ni renderiza nada.

{
  "state": "processing",
  "presentation_id": "0199f401-33de-7a8c-9e15-b7c4d2a60f38",
  "artifact_id": "0199f405-91ab-7c42-8d6e-2a9f3b5c7e10",
  "format": "a4",
  "variant": "official",
  "unavailable_reason": null,
  "error": null,
  "retry_after": 1,
  "download_url": null
}
CampoSignificado
stateprepared, queued y processing (pendiente), ready, failed, unavailable, corrupt, o null cuando el documento aún no tiene un original capturado.
presentation_id, artifact_idLa presentación retenida y su artefacto original; artifact_id es lo que pasas a /v1/companies/{company}/pdf-artifacts/{artifact}.
format, variantFormato de papel y official.
unavailable_reasonprivacy_erased (estado unavailable, el original se ha perdido para siempre) u original_corrupt (estado corrupt).
error{ code, retryable, correlation_id } para failed, corrupt y originales borrados.
retry_afterSegundos que hay que esperar mientras está pendiente; en otro caso null.
download_urlSolo cuando está ready: la URL v1 relativa /v1/companies/{company}/pdf-artifacts/{id}/download, nunca una ruta de almacenamiento. Resuélvela contra el host de la API o construye la llamada a partir de artifact_id.

Todas las claves están siempre presentes; un documento sin original capturado las tiene todas a null. Un borrado por privacidad se muestra como state: unavailable con error.code: pdf_signature_personal_data_forgotten, sin retry_after y sin download_url.

Un bucle de reintento correcto

Consulta la lectura barata, espera lo que pide el servidor, limita los intentos y para ante las respuestas terminales. No regeneres para "ir más rápido".

async function downloadOriginal(companyId, artifactId, key) {
  const base = 'https://api.factuarea.com/v1/companies/' + companyId + '/pdf-artifacts/' + artifactId;
  const headers = { Authorization: 'Bearer ' + key };

  for (let attempt = 1; attempt <= 12; attempt++) {
    const res = await fetch(base + '/download', { headers });

    if (res.status === 200) return Buffer.from(await res.arrayBuffer());
    if (res.status === 503) {
      const wait = Number(res.headers.get('Retry-After')) || 2 * attempt;
      await new Promise((r) => setTimeout(r, wait * 1000));
      continue;
    }
    const { error } = await res.json();
    // 410 is terminal, 500 needs support, 404 means it expired or never existed.
    throw new Error(error.code + ' (' + res.status + ') ' + error.request_id);
  }
  throw new Error('The PDF is still not ready; check the artifact metadata later.');
}
  • Respeta Retry-After y el campo retry_after. Son segundos. Añade un pequeño incremento entre intentos y un tope en el número de intentos.
  • Consulta, no vuelvas a crear. Repite el mismo GET. Llamar a prepare o render en cada consulta solo añade ruido: ya devuelven el mismo artefacto.
  • Reintenta un fallo a propósito. Cuando state es failed y error.retryable es true, llama de nuevo a render con una Idempotency-Key nueva (la misma clave repetiría el fallo guardado). Si retryable es false, cambia antes la causa: el activo, la cantidad de datos o la configuración.
  • Una clave nueva es una intención nueva. Reutiliza la misma clave solo para reintentar la misma petición tras un timeout.
  • Para ante un 410. No va a cambiar.
  • Usa caché con el ETag. Envía If-None-Match para obtener un 304 gratis.

Idempotencia

preview, prepare y render necesitan Idempotency-Key. En estas tres la huella se calcula sobre el cuerpo crudo, así que la misma clave con un cuerpo que difiere en un solo byte (también el orden de las claves o los espacios) devuelve 422 idempotency_key_reused. Una repetición devuelve la respuesta guardada, con Idempotent-Replayed: true, antes de hacer ningún trabajo y aunque los datos vivos hayan cambiado desde entonces; la autorización de quien llama se comprueba antes. Dos claves con cuerpos equivalentes llevan al mismo artefacto. Consulta Idempotencia.

Errores

EstadoCódigoSignificado
403pdf_template_access_deniedEl rol no está permitido, o la sección de módulo del tipo de documento no está disponible para ti.
404pdf_artifact_not_found, pdf_presentation_not_foundNo existe, es de otra empresa o caducó.
409pdf_presentation_conflict, pdf_presentation_context_changedCambió la selección, el origen, la marca o el entorno después de la vista previa. Vuelve a previsualizar.
409pdf_legal_source_changedLos datos legales de una factura emitida cambiaron desde que se capturó; el original sigue siendo legible y el envío nuevo queda bloqueado hasta que la revises de forma explícita.
412 / 428pdf_presentation_etag_mismatch, pdf_presentation_precondition_requiredPrecondiciones de presentación en las operaciones que las usan.
410pdf_signature_personal_data_forgottenBorrado terminal.
422pdf_presentation_invalidUn campo no es válido. param trae la ruta, como source.kind.
422pdf_presentation_not_readyLa salida revisionada aún no está activa para tu empresa.
422idempotency_key_requiredFalta la clave.
429pdf_generation_budget_exceededPresupuesto de renderizado; espera Retry-After.
500pdf_original_unavailable, pdf_original_corrupt, pdf_original_layout_unavailable, pdf_engine_profile_unavailable, pdf_image_resource_unavailable, pdf_payload_resource_unavailableEl original, su diseño o los recursos de renderizado no están disponibles. Los dos últimos no son reintentables.
503pdf_artifact_not_readyAún no está listo; espera Retry-After.
503pdf_generation_failedLos endpoints directos del PDF del documento no pudieron producirlo. El documento está intacto; repite más tarde.

Ramifica según code, no según message. Consulta el modelo de errores.

MCP

Las cinco operaciones de artefacto son las tools preview_pdf_template, prepare_pdf_artifact, render_pdf_artifact, get_pdf_artifact y read_pdf_artifact, que devuelve un enlace de descarga autorizado en lugar de los bytes. Usan los mismos scopes, aceptan idempotency_key y siguen los mismos estados. Consulta las tools MCP.

En esta página

¿Te echamos una mano?Contactar con soporte