Factuarea APIDevelopers

Documento de control (DeCA)

Emite desde la API el documento electrónico de control administrativo del transporte de mercancías por carretera — ámbito y exenciones, el flujo emitir → copia al conductor → modificar → cerrar, la URL pública permanente para la inspección, albaranes y peso bruto.

Desde el 5 de octubre de 2026 el documento de control administrativo del transporte público de mercancías por carretera tiene que ser digital. El DeCA (documento de control) identifica las partes, los envíos, la mercancía, el vehículo y las fechas de un transporte, y viaja con el conductor como un PDF cuyo código QR abre una URL pública permanente. Tanto el cargador contractual como el transportista efectivo están obligados a formalizarlo (Orden FOM/2861/2012, art. 4).

El recurso transport_documents cubre toda la vida de ese documento: borradores (también prerrellenados desde tus albaranes), emisión, la copia para el conductor, modificaciones durante el servicio, cierre, anulación y conservación de los ficheros para la inspección. Un DeCA no es una factura ni un registro VeriFactu, y nunca cambia la numeración, el stock ni la facturación de tus albaranes.

Todos los endpoints siguientes cuelgan de https://api.factuarea.com/v1 y usan el mismo envoltorio de error, la misma paginación por cursor, la misma idempotencia y los mismos scopes que el resto de la API.

Cuándo lo necesitas

Se exige en el transporte público de mercancías por carretera, nacional o de cabotaje, con origen y destino en España. El transporte internacional se documenta con la carta de porte CMR: un envío con origen o destino fuera de España no se puede emitir (transport_outside_spain).

No se exige, con la redacción de la Orden:

  • en el transporte privado complementario, con vehículos y conductores propios de la empresa; la documentación a bordo debe acreditar igualmente que la mercancía, el vehículo y el conductor son de la empresa;
  • en la paquetería de pocos bultos;
  • en las mudanzas;
  • en el transporte de vehículos accidentados o averiados en vehículos especiales;
  • en los transportes que no necesitan una autorización de transporte previa.

Fuentes oficiales: Orden FOM/2861/2012, Resolución de 5 de junio de 2026, página del Ministerio sobre el DeCA y sus preguntas frecuentes.

El módulo transport_documents está incluido allí donde lo están los albaranes (planes Profesional, Avanzado y Enterprise) y requiere el módulo delivery_notes. Scopes: transport_documents:read, transport_documents:write (borradores y envío por email), transport_documents:delete (solo borradores) y transport_documents:transition (emitir, modificar, cerrar, reabrir, anular y sustituir). El PDF de cada versión exige pdfs:read; el justificante de emisión exige transport_documents:read, como el registro de emisión que contiene.

Tu papel y las partes

Cada documento declara si tu empresa actúa como cargador contractual (company_role: "shipper") o como transportista efectivo ("carrier"), incluido el transportista que subcontrata el transporte. Tu propia parte se rellena con los datos de tu empresa; la otra sale de contact_id o de los campos que envíes. Un transportista extranjero (cabotaje) se identifica con su propio tax_id y su tax_id_country.

Un NIF o un domicilio que no supera la validación en los datos de tu empresa o en el contacto se deja vacío: el borrador lo admite y la emisión lo pide. Lo que envías en la petición se valida al guardarlo. Un domicilio español copiado de tu empresa o de un contacto sin provincia toma la de su código postal (sus dos primeras cifras: 28001 es Madrid); un domicilio que envías se guarda tal como lo envías.

Mientras el documento es un borrador, cada parte copiada sigue a su origen, sin volver a guardar el borrador:

  • Al actualizar un contacto, su nombre, su NIF (o identificador extranjero con su país) y su domicilio se vuelven a copiar en los borradores cuya otra parte está enlazada a él.
  • Al cambiar el nombre fiscal, la razón social, el NIF o el domicilio fiscal de tu empresa, se vuelven a copiar en tu propia parte de todos los borradores. Los demás cambios en los datos de tu empresa (email, teléfono, web, cuenta bancaria, idioma) no tocan ningún borrador.
  • Lo que el origen tiene válido manda. Un NIF vacío o no válido o un domicilio incompleto en el origen nunca borra lo que el borrador ya tenía, por ejemplo lo que enviaste en la petición. Una parte escrita a mano o enlazada a otro contacto no cambia.
  • El refresco se hace unos segundos después del cambio y, si algo cambia, publica transport_document.updated, así que puedes recibirlo de un borrador que no has tocado por la API. Archivar o borrar el contacto deja sus borradores como estaban.

Un documento emitido nunca sigue a sus orígenes: conserva los datos con los que se emitió.

Una vez emitido, las partes no pueden cambiar: una modificación que las toque devuelve 422 transport_document_parties_immutable. Anula el documento y emite otro.

El flujo: emitir, entregar la copia, modificar, cerrar

Crea un borrador. Solo company_role es obligatorio: un borrador admite datos incompletos y cualquier fecha. Pasa delivery_note_ids (de 1 a 50 de tus albaranes) para obtener un envío por albarán, con su destino, su fecha y su mercancía, sin servicios y con los pesos congelados. Un envío que omite origin toma tu lugar de carga por defecto o, si no tienes ninguno, tu domicilio fiscal (como transportista, la dirección del contacto cargador); un "origin": null explícito lo deja sin origen. Un albarán de otra empresa devuelve 422 en delivery_note_ids.N.

curl -X POST https://api.factuarea.com/v1/transport_documents \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "company_role": "shipper",
    "carrier": { "contact_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a10" },
    "vehicle": { "tractor_plate": "1234BCD", "trailer_plate": "R1234BCD" },
    "transport_date": "2026-10-06",
    "delivery_note_ids": [
      "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a11",
      "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a12"
    ]
  }'

Edita el borrador con PUT /v1/transport_documents/{id} (un campo ausente se conserva, un null explícito lo borra y shipments sustituye la lista completa) o bórralo con DELETE /v1/transport_documents/{id}.

El id del documento lo genera el servidor. shipments[].id es opcional: al crear, un identificador que ya está en uso, en tu empresa o en cualquier otra, devuelve 422 en shipments.N.id; en PUT y en modify solo identifica un envío de ese mismo documento (cualquier otro devuelve 422 parameter_invalid_value en shipments.N.id), y un envío nuevo va sin id. Todo UUID que envíes se normaliza a minúsculas. Los envíos, y las líneas de mercancía de cada envío, se guardan en el orden en que los envías al crear, editar, modificar y en la vista previa. Una línea de mercancía cuyo quantity × gross_weight_kg redondea a 0 kg o supera 9.999.999.999,9999 kg devuelve 422 invalid_gross_weight en shipments.N.goods.M.gross_weight_kg al guardarla, no al emitir. Si una petición simultánea ocupa el mismo identificador entre la comprobación y el guardado, la API devuelve 409 resource_conflict: no se guardó nada y la petición se puede reintentar tal cual.

Emítelo antes de que empiece el transporte. POST /v1/transport_documents/{id}/issue comprueba los datos mínimos: nombre, NIF y domicilio completo del cargador; nombre y NIF del transportista; matrícula de la tractora; y en cada envío, origen, destino, mercancía, magnitud declarada y una fecha de transporte de hoy o posterior (Europe/Madrid). En una sola operación atómica asigna el siguiente número de la serie DC (creada en la primera emisión), la URL pública permanente y la versión 1 del PDF. Es irreversible: envía un Idempotency-Key.

Cuando falta algo, el error lo enumera en error.details.missing:

{
  "error": {
    "type": "invalid_request_error",
    "code": "transport_document_not_issuable",
    "message": "No se puede emitir el DeCA. Faltan 3 datos: el NIF del transportista efectivo; la matrícula de la tractora o del vehículo rígido; la magnitud declarada del envío 1 (peso u otra).",
    "details": {
      "missing": ["carrier.tax_id", "vehicle.tractor_plate", "shipments.1.measure"]
    }
  }
}

missing usa un vocabulario cerrado: shipper.name, shipper.tax_id, shipper.address, carrier.name, carrier.tax_id, vehicle.tractor_plate, transport_date, shipments (ningún envío) y shipments.N.origin, shipments.N.destination, shipments.N.goods o shipments.N.measure, donde N cuenta desde 1 (a diferencia del param de un error de validación, que cuenta desde 0). Si el PDF superara 5 MB (5.000.000 bytes), la petición devuelve 422 transport_document_pdf_too_large y no se emite nada: acorta el contenido o reparte los envíos en varios documentos.

Una fecha de transporte pasada devuelve transport_date_in_past: el documento se crea antes del servicio y no hay forma de regularizarlo después.

Entrega la copia al conductor. Vale cualquiera de estas: el enlace public_url, el código QR (GET /v1/transport_documents/{id}/qr?format=svg|png, con la zona de silencio estándar de 4 módulos de la ISO/IEC 18004, para que se lea tal como se descarga al imprimirlo o pegarlo en otro documento), el PDF (GET /v1/transport_documents/{id}/pdf) o un email al transportista con POST /v1/transport_documents/{id}/send (hasta 5 direcciones distintas entre to, cc y bcc; una dirección repetida en cualquier lista, sin distinguir mayúsculas, cuenta y se envía una sola vez; sin to, el email del contacto transportista). El envío es irreversible y queda en el registro de emisión. Si el proveedor de correo rechaza el mensaje o no puede aceptarlo, la API devuelve 500 send_failed y no se anota nada en el registro: reintenta con una Idempotency-Key nueva.

Modifícalo durante el servicio. POST /v1/transport_documents/{id}/modify conserva el mismo número, la misma URL y el mismo QR, y guarda una versión nueva y completa del PDF con los datos nuevos, el motivo y los datos anteriores marcados como ya no válidos. Envía solo lo que cambia, más reason (de 3 a 500 caracteres) y data_origin: company, o carrier_reported cuando el cambio lo comunicó el transportista. Una petición que no cambia nada devuelve transport_document_no_changes. Un documento tiene como máximo 50 versiones y la última se reserva para anularlo o sustituirlo: con 49 versiones, una modificación devuelve 422 transport_document_version_limit_reached, así que emite un documento nuevo y marca este como sustituido. Una modificación que deja sin algún dato mínimo devuelve 422 transport_document_cannot_be_modified con el mismo error.details.missing. Vuelve a enviar el PDF modificado al conductor.

{
  "reason": "Avería de la tractora",
  "data_origin": "carrier_reported",
  "vehicle": { "tractor_plate": "5678FGH" }
}

Ciérralo. POST /v1/transport_documents/{id}/close lo cierra a mano. Si no, un proceso diario lo cierra automáticamente 7 días después del final de su última fecha de transporte (Europe/Madrid). Un documento cerrado a mano se puede reabrir con un reason hasta reopen_deadline; un cierre automático no se puede reabrir.

Un documento emitido tiene dos salidas terminales, ambas irreversibles y ambas conservando el original: POST /v1/transport_documents/{id}/cancel (con reason) guarda una versión final marcada «ANULADO», y POST /v1/transport_documents/{id}/supersede (con reference y reason) deja constancia de que el documento propio del transportista lo sustituye. Un documento emitido nunca se borra. Igual que emitir y modificar, anular y sustituir devuelven 422 transport_document_pdf_too_large sin cambiar nada cuando el PDF superaría 5 MB.

La URL pública y la inspección

La public_url tiene la forma https://app.factuarea.com/api/public/deca/{token}, con un token opaco de 43 caracteres. Es la URL que codifica el código QR (el PDF y el justificante de emisión no la imprimen como texto) y no cambia entre versiones.

  • GET y HEAD responden sin login, sin credenciales y sin página intermedia: 200 con el PDF de la versión vigente y Content-Type: application/pdf.
  • El fichero se sirve exactamente como se guardó, nunca se renderiza en la petición. El ETag es su sha256, que coincide con versions[].sha256.
  • Las peticiones tienen dos límites, y ninguno depende solo de la IP, porque la inspección puede llegar a la URL a través de una pasarela con una dirección compartida: uno por token y prefijo de red (/24 en IPv4, /48 en IPv6; 60 por minuto por defecto), que se comprueba primero, y otro por token (600 por minuto por defecto, diez veces el de una red, para que dos redes que agotan el suyo no dejen sin el documento a una tercera). Una dirección IPv4 mapeada en IPv6 (::ffff:a.b.c.d) cuenta como su /24. Superar cualquiera de los dos devuelve 429 con Retry-After.
  • Las peticiones con un token que no corresponde a ningún documento, también uno mal formado (de otra longitud o con caracteres fuera de base64url), cuentan en un límite por prefijo de red (30 por minuto por defecto), nunca por token: desde una red, el intento 31 del minuto recibe 429.
  • Una descarga se anota en el registro solo después de comprobar que el fichero existe, con la versión que se sirvió de verdad. HEAD no se anota.
  • Un token desconocido devuelve 404, y un documento cuyos ficheros se purgaron, 410.

Cada versión es un PDF de hasta 5 MB (5.000.000 bytes) cuya CreationDate es la primera emisión y cuya ModDate es la fecha de la versión. Su historial muestra la fecha, el motivo, el origen del dato y los datos anteriores de cada modificación, pero no quién la hizo: el autor de cada versión solo está en el recurso autenticado, el registro y el justificante de emisión. La URL sigue sirviendo el documento como mínimo un año después de cerrarse, anularse o sustituirse (retention_until), aunque la empresa cambie de plan. Pasado ese plazo, los ficheros solo se purgan si la cuenta está cerrada: la suscripción está cancelada, o la empresa gestionada fue archivada por su gestoría. Una cuenta bloqueada (impago, prueba caducada) no está cerrada. Tras la purga, el documento y sus datos se conservan, y los endpoints del PDF devuelven 410 transport_document_file_unavailable. Pedir GET /v1/transport_documents/{id}/pdf?version=N con una versión que el documento no tiene devuelve 404 transport_document_version_not_found con error.param = version.

Prueba de emisión

GET /v1/transport_documents/{id}/evidence enumera, en orden cronológico, las versiones emitidas o modificadas, los cierres manuales (closed) y automáticos (auto_closed), las reaperturas, la anulación o sustitución, los envíos por email con sus destinatarios y las descargas públicas con la IP truncada y el agente de usuario. La respuesta es { data, meta: { downloads_total, downloads_listed } }:

  • Cada entrada nombra a su actor en actor_name, congelado con el nombre que tenía en ese momento: el usuario en la app, la clave de API en la API REST y en MCP con clave de API, y el cliente OAuth en MCP por OAuth. Una clave nunca toma el nombre del usuario que la creó. actor_name es null solo en auto_closed y en las descargas públicas; user_name es el nombre actual de la persona, cuando la hay. occurred_at es ISO 8601 con su desplazamiento UTC.
  • Una descarga pública se anota una vez por hora para cada combinación de versión y prefijo de red. El agente de usuario se guarda pero no cuenta, así que cambiarlo no añade entradas y escanear varias veces el QR no las multiplica.
  • data trae siempre todas las entradas que no son descargas y, como máximo, las 100 primeras descargas en orden cronológico. meta.downloads_total es el número de descargas registradas y meta.downloads_listed, cuántas de ellas hay en data.

La tool MCP get_transport_document_evidence devuelve la misma forma. GET /v1/transport_documents/{id}/issuance_proof exporta el registro como un PDF generado bajo demanda, con el autor de cada versión. Exige transport_documents:read: una clave con solo pdfs:read recibe 403 insufficient_scope. Es la prueba que exime al cargador que demuestra haberlo emitido (art. 7.1 de la Orden). Cada versión se verifica recalculando el sha256 de su PDF.

Albaranes y peso bruto

  • Envíos desde albaranes. Filtra los documentos de un albarán con GET /v1/transport_documents?delivery_note_id=…. Cuando el albarán cambia después de la versión vigente en algo que declara el envío (líneas, cantidades, unidad, peso, dirección de entrega o fecha), su envío informa source_changed: true con las diferencias en source_changes; entregarlo o facturarlo no cuenta, ni tampoco un peso añadido después al catálogo. Un albarán anulado o borrado marca source_missing: true. Un albarán anulado no puede ser el origen de un borrador nuevo (delivery_note_cancelled).
  • Peso bruto en el catálogo. Los productos, las variantes y las presentaciones fijas aceptan gross_weight_kg, lo que pesa una unidad tal como se carga, embalaje incluido. Es opcional y solo ayuda a rellenar la magnitud declarada de cada envío.
  • Peso en los albaranes. Cada línea de albarán congela su peso al crearse o editarse en borrador (gross_weight_kg de la línea); el albarán publica total_gross_weight_kg y lines_without_weight_count, e imprime el total en su PDF. Duplicar un albarán copia tal cual el peso congelado de cada línea.

Eventos

transport_document.created, .updated, .issued, .modified, .closed, .reopened, .cancelled y .superseded llevan el recurso completo en data.object. transport_document.closed distingue un cierre manual de uno automático con close_mode. transport_document.updated llega también cuando un borrador sigue un cambio de su contacto o de los datos de tu empresa (partes). transport_document.email_sent y .email_failed informan de cada email del documento con sus destinatarios y la versión del PDF adjunta. Consulta Eventos.

Lo que la API pública no cubre

Esto se queda en la app a propósito:

  • Los lugares de carga. Son ajustes de la empresa. La API pública y MCP identifican el origen de un envío por su dirección.
  • La vista previa en vivo de un documento sin guardar, una ayuda del editor de la app.
  • WebMCP, las acciones de automatización sobre el DeCA, los asistentes de IA y la importación masiva.

En esta página

¿Te echamos una mano?Contactar con soporte