Para emitir documentos electrónicos de control administrativo desde tu propio sistema de gestión, sin teclear nada dos veces.
Mandas los datos del transporte. Nosotros componemos el DeCA en PDF, le estampamos el código QR, le incrustamos los datos estructurados eFTI, lo firmamos digitalmente si tu suscripción tiene firma, lo archivamos en tu Digitaldocu y avisamos al conductor.
Te devolvemos su número de expedición y su identificador. A partir de ahí el documento vive en tu Digitaldocu como cualquier otro.
Solo si tu contrato lleva tope. Por defecto no hay tope: se
mide el consumo, se avisa y el exceso se factura, pero se emite siempre. Si tu
contrato sí lo lleva, agotar la bolsa del periodo devuelve 402
volumen_agotado —en el plan mensual, también si la cuota está
cancelada— y no se emite hasta el periodo siguiente (el día 1 en el mensual, el
aniversario del alta en el anual), hasta comprar un pack o hasta ampliar el
contrato. La respuesta trae periodo, usados y
bolsa.
La bolsa es el cupo del periodo más los DeCAs de packs que te
queden, en el mensual y en el anual. Los packs no caducan y se gastan
después del cupo del periodo. Cada plan tiene sus packs: los
que puedes comprar aparecen en Ajustes → Saldo. Si ahí no aparece
ninguno, tu plan no tiene pack a la venta y el 402 se resuelve
escribiéndonos para ampliar el contrato.
Conviene que tu programa lo contemple: es el único error que se resuelve con
una gestión comercial o de pago. Ningún otro 4xx se arregla
repitiendo la misma petición; reintenta solo ante fallos de red y
5xx (ver el punto 9).
La API manda los datos del DeCA. Tu suscripción decide cómo se emite. La firma digital, el aviso al conductor —por SMS o por correo— y la generación automática son ajustes de tu suscripción, no parámetros de la llamada. Se cambian desde el panel.
Uno de esos ajustes afecta a lo que se imprime: si tu suscripción tiene
marcado No imprimir el nombre, el móvil ni el correo del conductor en el
DeCA (en Los datos de tu empresa), conductor,
movil y email_chofer se guardan —y el móvil y el
correo sirven para el aviso— pero no salen en el PDF, y el nombre y el móvil
tampoco en los datos eFTI (el correo no va nunca en ellos). Si los mandas y no
los ves impresos, es por eso. Si no podemos leer ese ajuste al generar el DeCA,
no lo generamos: la llamada devuelve un error y hay que repetirla. Si no lo
podemos leer al rehacerlo en una versión nueva, salen impresos igualmente. Y sus
datos eFTI no se generan mientras no lo podamos leer: se vuelve a intentar más
tarde, así que, con el ajuste marcado, nunca llevan el nombre ni el móvil. Tampoco
se generan sin él, desde la aplicación, el justificante de una entrega, el acta de
recogida, el cierre de una entrega sin firma, la inspección final de un vehículo
ni el expediente: hay que repetirlo en un momento. Pero unos
datos eFTI nuevos no quitan los que el PDF ya llevaba: cuando se añaden a un PDF
que ya los tenía —al estampar sobre él una entrega firmada o el acta de una
recogida sin inspección de vehículo, o en una corrección que no lo rehace (la que
pone una banda roja en la primera página)—, los anteriores siguen dentro tal como
se generaron, con el nombre y el móvil si entonces los llevaban. Ese ajuste no
cambia el acta de recogida firmada ni lo que la inspección de vehículo añade al
PDF, si tu suscripción las usa: los dos llevan el nombre del conductor.
Un DeCA nombra siempre a las dos partes, así que la API pide los datos de las dos vengas de donde vengas. Lo que cambia es cuáles son tuyos:
| Tus datos (los ponemos nosotros) | Los de la otra parte (por envío) | |
|---|---|---|
| Eres el cargador | cargador, cargador_nif, cargador_domicilio |
transportista, transportista_nif |
| Eres el transportista | transportista, transportista_nif |
cargador, cargador_nif, cargador_domicilio |
Tus datos no hace falta que los mandes. Guárdalos una vez en el panel, en Los datos de tu empresa: dices de qué lado estás y los ponemos nosotros en cada DeCA. Si aun así los mandas en la petición, mandan los tuyos — lo guardado es solo el valor por defecto.
Merece la pena aunque tu programa pueda repetirlos: son los datos que no cambian nunca, y repetirlos mil veces son mil oportunidades de colar una errata. Un NIF mal escrito acaba impreso y firmado en un documento legal.
Se crea desde el panel, en Claves para tu programa. Se enseña una sola vez: guárdala en cuanto la veas, porque ni nosotros podemos volver a mostrarla. Si se pierde, se revoca y se crea otra.
Authorization: Bearer deca_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
La clave identifica tu suscripción por sí sola: no hay que mandar ningún otro dato para decir quién eres. Solo sirve para lo que hay en este documento — no da acceso al resto de tus documentos de Digitaldocu.
Hay otras dos, cada una para una sola cosa: la de aviso de buzón
(deca_aviso_, punto 11) y la de auditoría para tu
SIEM (deca_audit_, punto 12). Ninguna de las
dos sirve para emitir.
Son los que exige la norma (Orden FOM/2861/2012, art. 6). Si falta alguno no se emite nada y te decimos cuáles. Los de tu propia empresa puedes omitirlos si los has guardado en el panel (ver el punto 2).
| Campo | Ejemplo | |
|---|---|---|
cargador | INDUSTRIAS EJEMPLO, S.L. | Cargador contractual |
cargador_nif | B00000000 | |
cargador_domicilio | Calle Ejemplo 1, Valencia | |
transportista | TRANSPORTES EJEMPLO, S.L. | Transportista efectivo |
transportista_nif | B11111119 | |
origen | Valencia | Lugar de carga |
destino | Madrid | Lugar de entrega |
fecha | 2026-08-25 | aaaa-mm-dd |
mercancia | Palés de mercancía general | |
peso | 17000 · 17,00 Tn · 1.500 kg | Con unidad o sin ella |
matricula | 1234BCD · CR-1234-AB | Vehículo tractor. La actual o la provincial anterior a 2000 |
Todos los valores van como texto, también los que son
números: "peso": "17000", no "peso": 17000. Un número
sin comillas se trata como si el campo no viniera, y si es obligatorio te
devolvemos campos_incompletos.
El peso admite la unidad que uses. Si mandas "17,00 Tn"
lo convertimos a 17.000 kg; si mandas solo la cifra, la tomamos por kilos. Y
comprobamos que sea un peso razonable para un camión: por encima de 60 t
avisamos, porque suele ser un error de unidad.
| Campo | |
|---|---|
conductor | Nombre del conductor |
movil | Su móvil, para el aviso |
email_chofer | Su correo, para el aviso por correo. Si no es una dirección válida, el DeCA se emite igual y te lo decimos en avisos |
remolque | Matrícula del remolque: R-1234-BCD, o la provincial antigua, M-12345-R |
bultos | Texto libre: 15, 26 big-bags |
destinatario | Quien recibe la mercancía. También se acepta con el nombre cliente. Si no lo mandas, el documento lo deja en blanco |
observaciones | Texto libre que se imprime en el documento |
autorizacion | Autorización especial de circulación, si el transporte la lleva |
albaran | Tu nº de albarán de este envío. Se imprime en el documento como referencia, para cruzarlo con tu albarán |
email_chofer solo se usa si tu suscripción avisa por
correo, o si tiene encendida la copia por correo. Es un ajuste de la
suscripción, como el SMS, y no de la llamada. Si no, se guarda en el DeCA y no se
envía nada.
Usa exactamente los nombres de estas tablas. Al crear,
un campo que no está en ellas se ignora: matricula_remolque en vez
de remolque deja el DeCA sin remolque. Cada valor admite hasta 400
caracteres.
Si tu suscripción tiene el perfil de residuos, el DeCA lleva
además el bloque del Documento de Identificación (DI) del traslado,
y por la API es obligatorio: si falta algo, no se emite nada y te
devolvemos campos_incompletos con lo que falta en faltan.
El nº de DI lo ponemos nosotros, con el contador de tu NIMA, y te
lo devolvemos en numDi.
| Campo | |
|---|---|
res_ler | Obligatorio. Código LER, seis cifras: 17 04 05 |
res_operacion | Obligatorio. Operación de tratamiento: R12, o su desagregado, R0401 |
res_operador | Obligatorio. Operador del traslado |
res_operador_nif | Obligatorio. Su NIF |
res_operador_tipo | Obligatorio. Tipo de operador del modelo de DI: P01-P05, G01-G06, E01, E02, N02, A02, SIR, SCR |
res_productor | Obligatorio. Productor o poseedor del residuo (origen) |
res_productor_nif | Obligatorio. Su NIF |
res_destino | Obligatorio. Instalación de destino |
res_destino_nif | Obligatorio. Su NIF |
res_operador_nima | NIMA del operador (10 cifras) |
res_productor_nima | NIMA del productor |
res_destino_nima | NIMA de la instalación de destino |
res_transportista_nima | NIMA del transportista |
res_contrato | Nº del contrato de tratamiento, si lo hay |
res_scrap | SCRAP, si aplica |
res_num_di | Solo si numeras tú los DI: tu nº, 21 cifras (NIMA + año + contador). Si no lo mandas, lo asignamos nosotros |
Los NIMA vacíos no impiden emitir. Solo los tiene quien está
inscrito en el Registro de producción y gestión de residuos (nota 3 del modelo de
DI): si no llegan, el DeCA se emite y te lo decimos en avisos.
Lo que necesita notificación previa no sale como DI: un LER
peligroso (con *), el 20 03 01 de residuos domésticos
mezclados o una operación de eliminación (D). Te devolvemos
campos_incompletos con el motivo, o 409
res_requiere_notificacion, y no se emite nada.
Si tu suscripción no es de residuos y mandas campos
res_*, te devolvemos 409 sin_perfil_residuos y no se emite
nada: un DeCA sin el bloque no sirve como DI.
Con Idempotency-Key, un reintento del mismo envío recibe el
mismo nº de DI. Los campos del bloque no se corrigen por
/versiones.
201 Created
{ "ok": true,
"documentId": 123456,
"expedicion": "DECA-20261001-A1B2C3",
"numDi": "280006692420260000123",
"avisos": [] }
Manda tu propia referencia en la cabecera Idempotency-Key —tu número
de expedición sirve—. Si repites la llamada porque se te cayó la red y no viste
la respuesta, te devolvemos el mismo documento en vez de emitir otro.
curl -X POST https://deca.digitaldocu.es/api/deca/v1/expediciones \
-H "Authorization: Bearer $DECA_KEY" \
-H "Idempotency-Key: ALB-2026-000123" \
-H "Content-Type: application/json" \
-d '{
"cargador": "INDUSTRIAS EJEMPLO, S.L.",
"cargador_nif": "B00000000",
"cargador_domicilio": "Calle Ejemplo 1, Valencia",
"transportista": "TRANSPORTES EJEMPLO, S.L.",
"transportista_nif": "B11111119",
"origen": "Valencia",
"destino": "Madrid",
"fecha": "2026-08-25",
"mercancia": "Palés de mercancía general",
"peso": "17,00 Tn",
"matricula": "1234BCD",
"conductor": "Juan Ejemplo",
"movil": "600000000",
"email_chofer": "juan.ejemplo@example.com",
"destinatario": "DISTRIBUCIONES EJEMPLO, S.A."
}'
201 Created
{ "ok": true,
"documentId": 123456,
"expedicion": "DECA-20260825-A1B2C3",
"avisos": [] }
En avisos llegan las dudas que no impiden emitir:
un NIF que no pasa la letra de control, una matrícula sin forma de matrícula
española, un peso de más de 60 t, una fecha que no llegó como
aaaa-mm-dd o un email_chofer que no es una dirección
válida. El DeCA ya está emitido; conviene enseñárselas a quien lo mandó.
Si repites con la misma Idempotency-Key y los mismos
datos:
{ "ok": true, "repetida": true,
"documentId": 123456,
"expedicion": "DECA-20260825-A1B2C3" }
Y si repites la referencia con datos DISTINTOS, te decimos que no.
Eso no es un reintento: es una corrección, y devolverte el documento viejo con un
200 te la perdería en silencio —tu sistema la daría por aplicada y el
DeCA seguiría diciendo lo de antes—.
409 Conflict
{ "code": "datos_distintos",
"documentId": 123456,
"expedicion": "DECA-20260825-A1B2C3",
"error": "Esta Idempotency-Key ya emitió un DeCA con otros datos…" }
Para corregir un documento ya emitido, versiónalo: conserva la versión anterior, que es lo que exige el apartado Quinto de la Resolución. Para emitir uno nuevo, manda otra referencia.
Si borras el DeCA desde el panel de DeCA, su referencia queda
libre: puedes volver a emitir con la misma Idempotency-Key y los datos
corregidos, y se emite un DeCA nuevo.
La comparación se hace sobre los datos del transporte;
observaciones, autorizacion, email_chofer y
albaran no cuentan para ella.
Ante un 5xx al crear, o si no llegas a ver la respuesta,
repite con la misma Idempotency-Key: si el DeCA llegó a
emitirse, te devolvemos ese mismo documento.
Si repites antes de que el DeCA exista, recibes 409
en_curso. Espera unos segundos y vuelve a repetir con la misma
referencia. No cambies de Idempotency-Key: con otra,
emitiríamos otro DeCA.
Durante unos segundos después de crearse, un DeCA puede no aparecer todavía en
la consulta ni admitir adjuntos: si una llamada inmediata
devuelve 404, repítela.
Para colgar del DeCA el documento de origen: tu albarán, tu CMR, el tique de báscula. Queda enlazado al DeCA y no se lo enseñamos al destinatario salvo que alguien lo comparta a mano desde el panel — suele llevar datos del cargador que el receptor no tiene por qué ver.
curl -X POST \
"https://deca.digitaldocu.es/api/deca/v1/expediciones/DECA-20260825-A1B2C3/adjuntos" \
-H "Authorization: Bearer $DECA_KEY" \
-F "tipo=albaran" \
-F "file=@albaran-000123.pdf"
Valores de tipo: albaran, cmr,
foto_carga, ticket_bascula, packing_list,
otros.
Manda el fichero como multipart/form-data, no en base64
dentro de un JSON. Base64 infla el fichero un tercio: por multipart
entra hasta 4 MB, y por JSON el fichero no puede pasar de unos 3 MB. Un cuerpo
de más de unos 4,5 MB lo rechaza la plataforma con un 413 que no
trae JSON ni code.
Si aun así lo tienes más a mano en JSON, manda tipo,
nombre y fileBase64. El nombre con su
extensión: es lo que decide el formato.
Formatos: PDF, JPG o PNG, reconocidos por la extensión del
nombre del fichero. Otro formato devuelve 400, igual que un
tipo que no esté en la lista.
{ "ok": true,
"expedicion": "DECA-20260825-A1B2C3",
"documentId": 123456,
"estado": "En tránsito",
"entregado": false,
"fechaFirma": null,
"matricula": "1234BCD",
"conductor": "Juan Ejemplo",
"origen": "Valencia",
"destino": "Madrid",
"enlace": "https://backend.digitaldocu.com/api/Documents/Share/…" }
fechaFirma es la hora de la firma de la entrega, en formato
dd/mm/aaaa hh:mm y hora española; null mientras no se ha
entregado.
El enlace es el mismo al que apunta el QR del documento, y es
fijo: pedirlo otra vez no lo renueva. Está abierto hasta 30 días
desde la emisión o desde la fecha del transporte, si es posterior; si tu empresa ha
pactado con nosotros un plazo más corto (de 8 días como mínimo), ese en lugar de los 30,
salvo que el documento no tenga fecha del transporte. Una vez firmada la entrega, el
plazo pasa a ser de siete días naturales desde la firma, que es el del apartado
Tercero.5 de la Resolución de 5 de junio de 2026 (en un DeCA con varias entregas, los
siete días cuentan desde que se cierra la última). Pasado eso deja de servir el PDF.
Qué expediciones encuentra. Las de la carpeta compartida de tu suscripción, que es donde se archivan las emitidas por la API, por el buzón y por una hoja de ruta.
En un reparto con varias entregas, cada entrega es su propio DeCA con su propio número de expedición. Usa el que te devolvimos al crear cada una.
Un DeCA con varias entregas (lo emite tu empresa desde el panel o
desde una hoja de ruta en CSV; por la API no se emiten) solo sale aquí si está en esa
carpeta compartida; los de una hoja de ruta están siempre en ella. Entonces
destino es "Varios (N envíos)", origen el común
o "Varios", estado pasa por "En tránsito",
"En tránsito (k/N)" y, al cerrarse la última entrega,
"Entregado (…)", y entregado y fechaFirma no
cambian hasta ese cierre. Entonces fechaFirma es el fin del servicio, calculado
como firmado_iso en el aviso entrega_firmada (apartado 8): la hora
más tardía entre la del cierre que completa el documento y las de las entregas cerradas
antes, sin contar las anuladas.
La norma permite modificar el DeCA una vez iniciada la marcha, pero obliga a dejar constancia. Por eso esto no reescribe nada: crea una versión nueva, con su motivo y su fecha, y la anterior se conserva. Es el mismo botón «Nueva versión» del panel, puesto al alcance de tu programa.
El QR no cambia nunca. El enlace público de la expedición es el mismo antes y después, así que el conductor que ya lo tiene sigue enseñando el mismo código: si se estropea el camión o se desvía el destino, mandas la corrección y no hay que reenviarle nada.
Si cambias movil, avisamos al conductor nuevo por el canal
configurado y el enlace del anterior deja de permitir firmar.
curl -X POST \
"https://deca.digitaldocu.es/api/deca/v1/expediciones/DECA-20260825-A1B2C3/versiones" \
-H "Authorization: Bearer $DECA_KEY" \
-H "Content-Type: application/json" \
-d '{
"changes": { "matricula": "5678JKL", "conductor": "Ana Ejemplo", "bultos": "14" },
"motivo": "Cambio de vehiculo por averia"
}'
Campos admitidos en changes:
cargador, cargador_nif, cargador_domicilio,
transportista, transportista_nif, conductor,
movil, origen, destino, fecha,
destinatario (o cliente), mercancia, peso,
bultos, matricula, remolque, albaran.
observaciones y autorizacion no se corrigen por
aquí.
Manda siempre motivo (hasta 200 caracteres).
Queda en el historial de versiones y se imprime en el PDF, junto a los
datos que dejan de ser válidos. Si no lo mandas, el documento dirá
«Motivo: no consignado».
{ "ok": true,
"commentary": "Cambio de vehiculo por averia",
"fecha": "25/08/2026 10:15",
"applied": ["Matricula: 5678JKL", "Conductor: Ana Ejemplo", "Bultos: 14"],
"signed": true,
"sealed": false }
fecha va en formato dd/mm/aaaa hh:mm y en hora
UTC, no en hora española. applied dice qué ha quedado
cambiado: compáralo con lo que mandaste. Si la respuesta trae
kwErrors, esos cambios no han quedado guardados. signed
y sealed dicen si la versión nueva salió firmada o sellada, y
signError por qué no, si no.
Un nombre de campo mal escrito se rechaza, no se ignora. Si
mandas uno que no está en la lista te devolvemos 400
campos_desconocidos con los que admitimos, para que no te quedes creyendo
que cambiaste algo que sigue igual. Y los valores pasan por las mismas
comprobaciones que al emitir: una matrícula se guarda normalizada y, si algo no
cuadra, viaja en avisos sin bloquear el cambio.
Los valores, en cambio, tienen que ser texto no vacío. Un número sin comillas o una cadena vacía se ignoran sin error, y por esta vía no se puede borrar un campo.
Una expedición ya firmada por el destinatario devuelve 409
ya_entregado: está cerrada y no admite más versiones. Un DeCA con varias
entregas no se corrige por la API: devuelve 409 multientrega_sin_version
(se corrige desde el panel, con «Nueva versión»).
En vez de preguntar cada poco por cada expedición, dinos una dirección y te llamamos nosotros cuando pasa algo. Se configura en el panel, en Avisar a tu sistema.
| Evento | Cuándo |
|---|---|
deca_emitido | Cuando el DeCA está terminado: con su QR, sus datos eFTI y, si la tienes, su firma. No al crearlo — te mandaríamos a descargar un PDF a medias |
deca_modificado | Cada vez que se versiona, con el antes y el después de cada campo |
entrega_firmada | Cuando el destinatario firma la entrega: desde el enlace del SMS o del QR, en la app del conductor o al cerrar la inspección final del vehículo. En un DeCA con varias entregas, uno solo, cuando se cierra la última (ver la nota de abajo) |
parada_registrada_N | Solo en hojas de ruta de viajeros: cada vez que el conductor registra una parada intermedia. N es el orden de la parada. En un reparto de mercancías con varias entregas no se usa: si cada entrega es su propio DeCA, cada uno trae su propio entrega_firmada; si van todas en un único DeCA, llega uno solo, con el detalle de cada entrega |
POST https://erp.tuempresa.com/webhooks/deca
X-DeCA-Evento: deca_modificado
{ "evento": "deca_modificado",
"documentId": 123456,
"expedicion": "DECA-20260825-A1B2C3",
"version": 2,
"motivo": "Cambio de vehículo por avería en origen",
"cambios": [
{ "campo": "dca_matricula", "nombre": "Matricula",
"antes": "1234ABC", "ahora": "4916MDH" }
],
"enlace": "https://…" }
Los cambios vienen con el valor anterior y el nuevo a
propósito: es lo que te deja conciliar sin volver a preguntarnos nada. Solo
aparecen los campos que ya tenían valor: uno que estaba vacío y se rellena al
corregir no sale en cambios.
deca_emitido y deca_modificado vienen
apagados y se encienden con sus dos casillas en Avisar a tu
sistema. No es burocracia: son eventos nuevos, y encenderlos para todo el
mundo llenaría de mensajes desconocidos ERPs que no los esperan.
Si emites por la API, deca_emitido puede llegarte antes
que el 201 de la propia llamada: el aviso sale en cuanto el
documento está terminado, y la respuesta después. Tu receptor tiene que aceptar
un documentId que todavía no conoce.
POST https://erp.tuempresa.com/webhooks/deca
X-DeCA-Evento: deca_emitido
{ "evento": "deca_emitido",
"documentId": 123456,
"tenant": "…",
"expedicion": "DECA-20260825-A1B2C3",
"via": "api",
"enlace": "https://…",
"enviado_at": "2026-08-25T10:15:03.412Z" }
POST https://erp.tuempresa.com/webhooks/deca
X-DeCA-Evento: entrega_firmada
X-DeCA-Firma: sha256=a1b2c3…
{ "evento": "entrega_firmada",
"documentId": 123456,
"expedicion": "DECA-20260825-A1B2C3",
"matricula": "1234BCD",
"firmado_iso": "2026-08-25T14:32:00+02:00",
"firmado_at": "25/08/2026 12:32",
"firmante": "Ana Ejemplo",
"firmante_nif": "00000000T",
"incidencias": "Sin Incidencias",
"observaciones": null,
"via": "enlace" }
Usa firmado_iso: la hora de la firma en ISO 8601, en
hora española y con su desfase, así que tu sistema la lee sin ambigüedad.
firmado_at es el mismo instante en el formato
dd/mm/aaaa hh:mm y en hora UTC; se mantiene para las integraciones que
ya lo leen.
via dice por dónde se firmó: "enlace" (SMS o QR),
"app" o "inspeccion". incidencias es siempre
"Sin Incidencias" o "Con Incidencias", salvo en un DeCA con
varias entregas (la nota siguiente).
Un DeCA con varias entregas. Lo emite tu empresa desde el panel, si
tiene activada la opción «Varias entregas en un mismo viaje», o desde una hoja de ruta en
CSV, si en esa opción ha elegido «Un único DeCA con todas las entregas» (con «Preguntar al
generar», cada entrega de la hoja sale en su propio DeCA); por la API no se emiten.
Trae un solo entrega_firmada, cuando se cierra la última de
sus entregas, ya sea firmada, cerrada sin firma o anulada, y con dos campos más:
n_envios, el número de entregas, y envios, una por entrega, con
orden, destino, estado ("entregada",
"entregada_incidencias", "cerrada_sin_firma" o
"anulada"), via ("puerta", que es el enlace del
conductor, "app" u "oficina") y cerrado_iso, la hora
de su cierre. En la cabecera, firmante, firmante_nif,
incidencias y observaciones llegan a null: serían los
de una sola entrega. via puede ser también "oficina" (la última
entrega la cerró la oficina, sin firma o anulándola), y firmado_iso es la hora
más tardía entre la del cierre que completa el documento y las de las entregas cerradas
antes, sin contar las anuladas.
Todos los avisos llevan además tenant —el identificador de tu
suscripción— y enviado_at, en ISO 8601 y UTC. Ojo:
enviado_at es la hora del primer intento, también en los
reenvíos.
La cabecera X-DeCA-Firma es el HMAC-SHA256 del cuerpo, con el
secreto que verás en el panel al configurar la dirección. Compáralo antes
de fiarte del mensaje:
# Python
import hmac, hashlib
esperado = "sha256=" + hmac.new(
SECRETO.encode(), cuerpo_en_bruto, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(esperado, cabecera_firma):
return 401
2xx en menos de 8 segundos. Cualquier otra cosa
—o no contestar a tiempo— la tomamos por fallo.evento + documentId (y version en
deca_modificado).https y con nombre de dominio. No
admitimos direcciones IP ni nombres de red interna.| Código | Qué pasa | Qué hacer |
|---|---|---|
401 | Clave ausente, mal escrita o revocada | Revisa la cabecera |
403 clave_solo_aviso | Esa clave es de aviso de buzón (empieza por deca_aviso_): no sirve para la API | Usa una clave de la API (deca_live_) |
403 clave_solo_auditoria | Esa clave es de auditoría (empieza por deca_audit_): solo lee el registro de actividad | Usa una clave de la API (deca_live_) |
400 campos_incompletos | Faltan obligatorios | Mira faltan |
400 json_invalido | El cuerpo no es JSON válido. error dice la linea, la columna y el motivo: casi siempre unas comillas ", una barra \ o un tabulador sin escapar dentro de un texto | Escapa los textos (\", \\, \t). Mándalo en UTF-8; si llega en Windows-1252 (lo normal en VB6) también lo leemos |
400 json_no_objeto | El cuerpo es JSON, pero no un objeto {…} | Manda un objeto |
404 no_encontrada | Esa expedición no existe | Revisa el número |
402 volumen_agotado | Se acabó la bolsa del periodo (solo con tope pactado) | Compra un pack en Ajustes → Saldo si tu plan lo ofrece; si no, amplía el contrato o espera al periodo siguiente |
400 campos_desconocidos | Un campo de changes no existe | Mira admitidos |
400 sin_cambios | No viene ningún valor que aplicar | Revisa el cuerpo |
409 ya_entregado | El DeCA ya está firmado: no admite versiones | Emite otro |
409 multientrega_sin_version | Es un DeCA con varias entregas: no se corrige por la API | Corrígelo desde el panel, con «Nueva versión» |
409 entregada | Ya está firmada y cerrada | No admite cambios |
409 en_curso | Otra llamada con esa Idempotency-Key está emitiendo ahora mismo | Espera unos segundos y repite con la misma referencia |
409 datos_distintos | Esa Idempotency-Key ya emitió un DeCA con otros datos | Versiónalo, o manda otra referencia |
409 | Tu suscripción no tiene la estructura DeCA montada (sin code) | Escríbenos |
409 sin_perfil_residuos | Mandas campos res_* y tu suscripción no es de residuos | Pide que se active el perfil, o quita los res_* |
409 res_requiere_notificacion | El traslado necesita notificación previa: no sale como DI | Tramítalo por la vía de la notificación previa |
409 di_sin_nima · di_sin_contador | Tu suscripción de residuos no tiene configurado el NIMA o el contador con que numerar los DI | Escríbenos |
409 di_repetido · di_salto | El res_num_di que mandas ya está dado, o salta demasiado | Revisa tu numeración, o no lo mandes y lo asignamos nosotros |
409 di_ya_emitido | Ese envío ya emitió su DeCA con ese nº de DI. Trae expedicion | Nada: ya está emitido |
400 | Adjunto: tipo no válido (con admitidos) o formato no admitido | Revisa el tipo y la extensión del nombre |
413 demasiado_grande | El adjunto pasa de 4 MB | Comprímelo |
413 | El cuerpo entero pasa de unos 4,5 MB (sin JSON ni code) | Manda el adjunto por multipart |
500 error_interno | Fallo nuestro. Trae incidencia | Espera y reintenta; si se repite, mándanos el número de incidencia |
502 · 503 | Servicio no disponible o sin respuesta | Reintenta. Al crear, con la misma Idempotency-Key |
Cuando faltan datos te decimos exactamente cuáles, para que puedas programar contra ello:
400 Bad Request
{ "error": "faltan datos obligatorios del DeCA",
"code": "campos_incompletos",
"faltan": ["cargador_nif", "origen", "peso"],
"avisos": [
{ "campo": "cargador_nif",
"nivel": "falta",
"motivo": "obligatorio: no viene en la petición" }
] }
Las respuestas en JSON van en UTF-8 (Content-Type: application/json; charset=utf-8),
con las tildes y la ñ tal cual: petición, no petici\u00f3n.
| Adjunto | 4 MB por fichero en multipart; unos 3 MB si va en base64 dentro de un JSON |
| Formatos | PDF, JPG o PNG, reconocidos por la extensión del nombre del fichero |
| Claves activas | 10 por suscripción, más 2 de aviso de buzón y 2 de auditoría |
| Avisos de buzón | 12 por minuto y suscripción |
| Peso admitido | Hasta 60 t (por encima, avisamos) |
| Cada campo | 400 caracteres |
motivo | 200 caracteres |
Idempotency-Key | 120 caracteres |
| Número de expedición en la ruta | 60 caracteres |
Lo que pase de esos topes se recorta sin avisar.
Un DeCA entregado es registro legal y no se toca. Una vez que el destinatario ha firmado la recepción, esa expedición no admite documentos nuevos ni modificaciones. Si te equivocaste, emite otra.
Para programas que no llaman a la API, sino que dejan el PDF del albarán o del ticket en una carpeta que Digitaldocu sube a tu buzón. El piloto automático revisa ese buzón cada cinco minutos. Con el aviso, tu programa nos dice que acaba de dejar un PDF y revisamos tu buzón en ese momento.
No es otra forma de emitir: el PDF entra por el buzón como siempre y sale DeCA solo si el piloto automático lo lee sin dudas. Lo que no, se queda en el buzón para que lo revise una persona, igual que sin aviso. Tu suscripción tiene que tener encendido el piloto automático (Ajustes → Generar DeCAs solo).
El piloto automático no lee los buzones que en Digitaldocu están marcados como
de facturas o con proceso automático, salvo que sea el buzón que tu suscripción
tiene configurado para DeCA: esos documentos ya los trata Digitaldocu. Si tu
carpeta sube a uno de ellos, el aviso responde 202 igual, pero el PDF
no se procesa: haz que suba a otro buzón.
Una clave de la API (punto 3), que crea el administrador de tu
empresa en Ajustes. Va en la cabecera Authorization, nunca en la
dirección. Guárdala como cualquier clave de la API: además de avisar, sirve para
emitir. Las claves que empiezan por deca_aviso_, que dábamos antes y
solo sirven para avisar, siguen valiendo mientras no se revoquen.
Si tu Digitaldocu nos avisa ya de cada documento nuevo (el webhook «Documentos · NewDocument», que damos de alta nosotros), no hace falta llamar: el aviso llega solo.
Deja el PDF en la carpeta, espera unos 10 segundos para que Digitaldocu lo suba al buzón y llama. El cuerpo es opcional. Si mandas el nombre del fichero, ese PDF va el primero de la revisión y, si todavía no está en el buzón, volvemos a buscarlo a los 10, 30, 60 y 120 segundos. Sin nombre, revisamos el buzón una vez.
curl -X POST https://deca.digitaldocu.es/api/deca/v1/aviso \
-H "Authorization: Bearer deca_live_xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"fichero": "T-000123.pdf"}'
Respondemos al momento, antes de procesar nada. El trabajo sigue por nuestra parte:
202 Accepted
{ "ok": true, "aviso": "recibido", "fichero": "T-000123.pdf" }
Manda solo el nombre del fichero. Si mandas la ruta completa, nos quedamos con
lo que va detrás de la última barra, pero en el JSON las barras van dobladas:
{"fichero": "C:\\Tickets\\T-000123.pdf"}. Con las barras sin doblar
el JSON no es válido (400), o peor: \t y \n
se leen como un tabulador y un salto de línea, y el nombre ya no es el del
fichero.
Con MSXML2.ServerXMLHTTP.6.0 (viene con Windows) y tiempos de
espera cortos, para que el programa no se quede esperando si no hay red. Lo que
no sea un 202 se apunta en un fichero junto al programa: en un
.exe compilado, Debug.Print no escribe nada, y sin ese
fichero un 409 o una clave revocada pasarían semanas sin que nadie
lo viera.
Private Const URL_AVISO As String = "https://deca.digitaldocu.es/api/deca/v1/aviso"
Private Const CLAVE_AVISO As String = "deca_live_xxxxxxxxxxxxxxxx"
' Llamar DESPUÉS de dejar el PDF en la carpeta y de esperar unos 10 segundos.
Public Sub AvisarBuzonDeCA(ByVal rutaPdf As String)
Dim http As Object
Dim nombre As String
On Error GoTo Fallo
nombre = Mid$(rutaPdf, InStrRev(rutaPdf, "\") + 1)
nombre = Replace(Replace(nombre, "\", "\\"), """", "\""")
Set http = CreateObject("MSXML2.ServerXMLHTTP.6.0")
' Resolver, conectar, enviar y recibir, en milisegundos.
http.setTimeouts 3000, 3000, 5000, 8000
http.Open "POST", URL_AVISO, False
http.setRequestHeader "Authorization", "Bearer " & CLAVE_AVISO
http.setRequestHeader "Content-Type", "application/json"
http.send "{""fichero"": """ & nombre & """}"
If http.Status <> 202 Then
' Se apunta y se sigue: no se reintenta.
ApuntarAviso http.Status & " " & http.responseText
End If
Set http = Nothing
Exit Sub
Fallo:
' Sin red, sin respuesta a tiempo o sin TLS 1.2: el programa sigue.
ApuntarAviso "no enviado (" & Hex$(Err.Number) & "): " & Err.Description
Set http = Nothing
End Sub
' Una línea por fallo en aviso_deca.log, junto al programa. Nunca corta nada.
Private Sub ApuntarAviso(ByVal texto As String)
Dim f As Integer
On Error Resume Next
f = FreeFile
Open App.Path & "\aviso_deca.log" For Append As #f
Print #f, Format$(Now, "yyyy-mm-dd hh:nn:ss") & " " & texto
Close #f
End Sub
Si prefieres WinHttp.WinHttpRequest.5.1, la llamada es la misma:
SetTimeouts, Open, SetRequestHeader y
Send.
TLS 1.2. El servidor solo acepta TLS 1.2 o superior.
Windows 8, 10 y 11 lo traen puesto. En Windows 7, las dos librerías de arriba no lo
usan hasta instalar la actualización KB3140245 y crear el valor
DefaultSecureProtocols = 0xA00 (DWORD) en
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\WinHttp
y, como VB6 es de 32 bits, también en
HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Internet Settings\WinHttp.
Sin eso, cada aviso falla con el error 80072F7D (se ve en
aviso_deca.log). En Windows XP no funciona.
Para comprobar que llega: en nuestro panel se ve la fecha del último aviso de
cada clave. Si pasan los tickets y sigue en «todavía sin usar», mira
aviso_deca.log.
Si la llamada falla, tu programa sigue sin más. No reintentes ni esperes: la revisión de cada cinco minutos recoge el PDF igual. Lo que no cambia es lo de siempre: el camión no sale hasta que el conductor tiene el documento.
| Código | Qué pasa | Qué hacer |
|---|---|---|
202 | Aviso recibido. Revisamos tu buzón ahora | Nada |
400 fichero_no_valido · cuerpo_no_valido | El cuerpo no es JSON, o fichero no es texto | Revisa el cuerpo |
401 clave_no_valida · sin_clave | Clave ausente, mal escrita o revocada | Revisa la cabecera |
403 clave_sin_permiso | Esa clave no sirve para avisar | Usa la clave de aviso |
405 | La llamada no es un POST | Usa POST |
409 piloto_apagado | El piloto automático de tu suscripción está apagado | Enciéndelo en Ajustes → Generar DeCAs solo |
409 suscripcion_inactiva | Tu suscripción no está activa para DeCA | Escríbenos |
413 | El cuerpo pasa de 4 KB | Manda solo el nombre del fichero |
429 demasiados_avisos | Más de 12 avisos en un minuto | Nada: lo que haya en el buzón se procesa en la revisión de cada cinco minutos |
503 no_disponible | No hemos podido recibir el aviso (fallo nuestro) | Nada: lo mismo |
La API (puntos 4 a 7) rechaza la clave de aviso con 403
clave_solo_aviso. El aviso rechaza la de auditoría con 403
clave_solo_auditoria.
No es instantáneo. Una vez el PDF está en el buzón, leerlo, generar el DeCA, sellarlo y avisar al conductor lleva del orden de medio minuto con un papel que ya sabemos leer, y algo más la primera vez que llega un modelo de papel nuevo. A eso se suma lo que tarde Digitaldocu en subir el PDF de la carpeta al buzón. Cada revisión coge como mucho 5 papeles; el que nos avisas va el primero.
Si llegan varios avisos seguidos, no hace falta esperar entre uno y otro: los atendemos en la revisión que esté en marcha o en la siguiente.
Para que tu sistema de seguridad (Splunk, Sentinel, Elastic, Wazuh…) se lleve solo el registro de actividad de tu suscripción: quién entra, desde qué IP y qué hace. Es el mismo registro que ves en la pestaña Actividad y que exportas en CSV, en líneas JSON con los campos del Elastic Common Schema (ECS 8.11) y las huellas que encadenan cada evento con el anterior.
La crea y la revoca el administrador de tu suscripción, en la pestaña
Actividad → Clave para tu SIEM. Solo se puede crear con la
pestaña activada, y como mucho hay dos activas: la que usas y la que la sustituye
cuando la cambias. Se enseña una sola vez. Empieza por deca_audit_.
Solo sirve para leer este registro. Con ella no se puede
emitir, consultar ni corregir nada, ni avisar: el resto de la API la rechaza con
403 clave_solo_auditoria. Y al revés: una clave de la API
(deca_live_) o de aviso (deca_aviso_) no lee el registro.
La suscripción la dice la clave; no hay que mandar ningún otro dato.
Va en la cabecera, Authorization: Bearer deca_audit_… o
X-DeCA-Api-Key: deca_audit_…. Nunca en la dirección:
una clave puesta en la URL no se lee, y acabaría en los registros de cualquier
proxy intermedio.
| Parámetro | Qué es |
|---|---|
desde | El cursor: devuelve los eventos con número mayor que este (el propio desde no se repite). Entero de 0 a 9223372036854775807; por defecto, 0 |
limite | Cuántos eventos como mucho, de 1 a 1000; por defecto, 500 |
Cualquier otro parámetro se ignora. Solo GET.
curl -sS -D cabeceras.txt \
-H "Authorization: Bearer deca_audit_xxxxxxxxxxxxxxxx" \
"https://deca.digitaldocu.es/api/deca/v1/actividad?desde=0&limite=500"
200 con Content-Type: application/x-ndjson; charset=utf-8:
un objeto JSON por línea, cada una terminada en salto de línea, en orden de
número de evento. Puede venir vacía: no es un error, es que no hay nada nuevo.
{"@timestamp":"2026-09-28T07:14:03.512907Z","ecs":{"version":"8.11.0"},
"event":{"kind":"event","dataset":"deca.actividad","module":"deca","id":"48213",
"sequence":48213,"action":"doc.descarga","outcome":"success","provider":"proxy",
"category":["file"],"type":["access"]},
"message":"…","organization":{"id":"transportes-ejemplo"},
"user":{"id":"…","email":"…"},"source":{"ip":"203.0.113.24"},
"deca":{"at_local":"…","actor_id":"…","ip":"203.0.113.24","ip_fiable":true,
"ref":null,"detalle":{},
"cadena":{"v":1,"id":48213,"at":"2026-09-28T07:14:03.512907Z",
"tenant":"transportes-ejemplo","atribucion":"sesion","tipo":"doc.descarga",
"resultado":"ok","http":200,"origen":"proxy","actor_tipo":"usuario",
"document_id":91234,"h_pii":"9f…","hash_prev":"3c…","hash":"a7…"}}}
(En la respuesta real, cada evento va en una sola línea; aquí está
partido para que se lea.) source.ip solo va cuando la IP está
verificada; la IP tal cual está siempre en deca.ip, con
deca.ip_fiable. Los apuntes de nuestro personal salen enmascarados,
igual que en la pestaña. deca.detalle cambia de forma según el tipo
de evento: en Elasticsearch, mapéalo como flattened.
Cabeceras de la respuesta:
| Cabecera | Qué dice |
|---|---|
X-DeCA-Siguiente | El desde de tu siguiente llamada: el número del último evento de esta página, o el mismo desde si no ha venido ninguno |
X-DeCA-Mas | 1 si queda más ahora mismo: pide la página siguiente ya, sin esperar. 0 si vas al día |
X-DeCA-Lineas | Cuántos eventos trae la página |
X-DeCA-Ultimo-Hash | La huella del último evento de la página (no va si la página está vacía) |
X-DeCA-Cabeza-Id · X-DeCA-Cabeza-Hash | El último evento de tu registro en el momento de la lectura. Con X-DeCA-Mas: 0, si tu última huella no es esta, falta la cola: avísanos |
X-DeCA-Ancla-Id · X-DeCA-Ancla-Hash | Solo si ya se ha borrado algo por antigüedad (12 meses) y lo borrado llega hasta la activación de la pestaña: el último evento borrado, con el que encadena el primero que se te exporta. Mientras la purga solo haya borrado eventos anteriores a la activación, no va |
Todas las respuestas llevan Cache-Control: no-store.
Guarda X-DeCA-Siguiente y úsalo como desde en la llamada
siguiente. Mientras X-DeCA-Mas sea 1, sigue pidiendo;
con 0, vuelve dentro de unos minutos (cada 5 es un buen ritmo). Si
una llamada falla, repítela con el mismo desde: te
devolvemos lo mismo más lo nuevo, sin huecos ni repetidos. Los números de evento
no son consecutivos (la numeración es común a toda la plataforma): lo que prueba
que no falta nada es la cadena, no el número.
Solo desde que se activó la pestaña. El registro se sirve
desde la primera vez que se activó la pestaña Actividad de tu
suscripción, igual que en la pestaña y en el CSV; lo anterior no se te exporta.
Esa fecha no cambia aunque la pestaña se desactive y se vuelva a activar. Si se
desactiva, la lectura contesta 403 no_visible desde la
llamada siguiente, aunque la clave siga activa.
Empezar desde hoy, sin descargar lo que ya haya: haz una
primera llamada con desde=9223372036854775806. Vuelve sin eventos,
pero con la cabeza: usa X-DeCA-Cabeza-Id como tu desde
y X-DeCA-Cabeza-Hash como la huella anterior que esperas. Así también
el primer evento que recibas se puede comprobar. (Si todavía no hay ningún evento,
no viene cabeza: empieza desde 0.)
Cada evento guarda la huella del anterior (hash_prev) y la suya
(hash), en deca.cadena. Con eso tu SIEM comprueba dos
cosas: que no falta ningún evento entre dos que tiene, y que ninguno ha cambiado
después de copiarlo. Usa siempre los campos de deca.cadena, no los de
ECS: un pipeline puede reformatear @timestamp, y la huella se
calcula sobre el texto exacto de deca.cadena.at (UTC, seis
decimales y Z).
# Python
import hashlib, json
CAMPOS = ("v", "id", "at", "tenant", "atribucion", "tipo",
"resultado", "http", "origen", "actor_tipo", "document_id")
def ns(x):
# null → "-,"; si no, "<octetos UTF-8>:<texto>,"
if x is None:
return "-,"
s = str(x) # los números, en decimal
return f"{len(s.encode('utf-8'))}:{s},"
def comprobar(linea, huella_anterior):
c = json.loads(linea)["deca"]["cadena"]
if huella_anterior is not None and c["hash_prev"] != huella_anterior:
raise ValueError(f"falta algo antes del evento {c['id']}")
nucleo = "".join(ns(c[k]) for k in CAMPOS).encode("utf-8")
prev = bytes.fromhex(c["hash_prev"]) if c["hash_prev"] else bytes(32)
h = hashlib.sha256(prev + nucleo + bytes.fromhex(c["h_pii"])).hexdigest()
if h != c["hash"]:
raise ValueError(f"el evento {c['id']} no casa con su huella")
return c["hash"] # la huella anterior del evento siguiente
hash_prev no lo puedes comprobar y es tu punto de
partida. Por eso la primera llamada del ejemplo pasa
huella_anterior = None, salvo que empieces desde hoy con la
cabeza.h_pii es la huella
de la parte personal (usuario, correo, IP, navegador, referencia y detalle)
calculada con una sal que no sale de la plataforma. Entra en la cadena tal cual,
así que un h_pii cambiado se detecta; pero sin la sal no se puede
comprobar que deca.ip o user.email sean los que se
huellaron.registro.supresion con el número del evento afectado.
Tu copia no se toca sola: aplicar esa supresión en tu SIEM te corresponde a
ti.X-DeCA-Ancla-Id, lo que
falta se borró por antigüedad (12 meses): no es una manipulación. El primer
evento que recibas encadena con X-DeCA-Ancla-Hash. Esas cabeceras
solo van cuando es así: si lo borrado es todo anterior a la activación de la
pestaña, no van, y tu primer evento encadena con uno anterior a la activación
que no se te exporta (el caso de arriba).Siempre en JSON, {"ok": false, "code": "…", "error": "…"}, nunca
como líneas NDJSON.
| Código | Qué pasa | Qué hacer |
|---|---|---|
401 sin_clave | No viene la clave en la cabecera | Mándala en Authorization, no en la URL |
401 clave_no_valida | Clave mal escrita o revocada | Crea otra en la pestaña Actividad |
403 clave_sin_permiso | Es una clave de la API o de aviso, no de auditoría | Usa la deca_audit_ |
403 no_visible | La pestaña Actividad de tu suscripción está desactivada | Pide que se active; mientras, no hay nada que leer |
400 parametro_no_valido | desde o limite fuera de rango, o repetidos | Revisa la dirección |
405 metodo_no_admitido | La llamada no es un GET | Usa GET |
429 demasiadas_consultas | Más de 30 consultas en un minuto. Trae Retry-After | Espera lo que diga y sigue con el mismo desde |
503 no_disponible | No hemos podido leer el registro ahora (fallo nuestro) | Reintenta con el mismo desde |
| Consultas | 30 por minuto y suscripción |
| Eventos por página | 1000 como mucho (500 si no dices nada). Una página puede traer menos si pasa de unos 3,5 MB: entonces viene X-DeCA-Mas: 1 |
| Claves de auditoría | 2 activas por suscripción |
| Antigüedad | Lo que haya en el registro, que se conserva 12 meses, y nunca antes de la primera activación de la pestaña |
Tu copia es tuya. Cada lectura queda apuntada en el propio registro (un resumen cada 15 minutos por clave). Lo que tu SIEM guarde, cuánto tiempo, quién lo ve y cómo aplica las supresiones es responsabilidad de tu empresa, como responsable del tratamiento.