Documentación técnica
API de Decafly
Para quien conecta un programa de gestión, un ERP o un sistema de transporte con Decafly. Con una llamada HTTP se genera un DeCA con su QR, igual que desde el formulario.
Si no eres programador y buscas cómo conectar tu programa, empieza por la guía para empresas.
Autenticación
Cada empresa crea sus claves en la aplicación, en Ajustes, Integraciones (solo los administradores). La clave se envía en la cabecera Authorization: Bearer dk_live_... y solo da acceso a los documentos de esa empresa. Se muestra una única vez al crearla y se puede revocar en cualquier momento. Todas las llamadas son por HTTPS y están pensadas para hacerse desde un servidor, nunca desde el navegador del usuario, para no exponer la clave.
Endpoints
| POST | /api/v1/documentos | Genera un DeCA y devuelve su identificador, sus enlaces y su huella. |
| GET | /api/v1/documentos | Lista los últimos documentos. Filtros: limite (50 por defecto, máximo 200), referencia (exacta) y estado (vigente o anulado). |
| GET | /api/v1/documentos/{id} | Devuelve los datos y el estado (vigente o anulado) de un documento. |
| GET | /api/v1/documentos/{id}/pdf | Descarga el PDF del documento. |
| POST | /api/v1/documentos/{id}/anular | Anula un documento. Se conserva para trazabilidad. |
Crear un documento
POST /api/v1/documentos con un cuerpo JSON:
| Campo | Tipo | Obligatorio | Notas |
|---|---|---|---|
| cargadorNombre | texto | Sí | Nombre o razón social del cargador. |
| cargadorNif | texto | Sí | NIF o CIF del cargador. |
| transportistaNombre | texto | Sí | Nombre o razón social del transportista. |
| transportistaNif | texto | Sí | NIF o CIF del transportista. |
| origen | texto | Sí | Dirección de origen. |
| destino | texto | Sí | Dirección de destino. |
| naturalezaCarga | texto | Sí | Naturaleza o descripción de la mercancía. |
| pesoKg | número | Sí | Peso en kilogramos, mayor que 0. |
| matricula | texto | Sí | Matrícula del vehículo. |
| referencia | texto | No | Referencia propia (pedido, albarán...). Máximo 60 caracteres. |
| fechaRecogida | AAAA-MM-DD | No | Fecha prevista de recogida. |
| horarioRecogida | texto | No | Por ejemplo 08:00-10:00. |
| fechaEntrega | AAAA-MM-DD | No | Fecha prevista de entrega. |
| horarioEntrega | texto | No | Por ejemplo 14:00-16:00. |
| palets | número | No | Número de palets. |
| metrosLineales | número | No | Metros lineales ocupados. |
La fecha y la hora del documento las pone Decafly al generarlo.
Ejemplos
curl
curl -X POST https://decafly.lucasyleodigital.com/api/v1/documentos \
-H "Authorization: Bearer dk_live_XXXXXXXX" \
-H "Content-Type: application/json" \
-d '{
"referencia": "PED-0001",
"cargadorNombre": "Frutas Ejemplo SL",
"cargadorNif": "B12345678",
"transportistaNombre": "Transportes Ejemplo SL",
"transportistaNif": "B87654321",
"origen": "Calle Mayor 1, Amposta",
"destino": "Mercabarna, Barcelona",
"naturalezaCarga": "Hortalizas",
"pesoKg": 2200,
"matricula": "1234ABC"
}'JavaScript
const respuesta = await fetch("https://decafly.lucasyleodigital.com/api/v1/documentos", {
method: "POST",
headers: {
Authorization: "Bearer " + process.env.DECAFLY_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
referencia: "PED-0001",
cargadorNombre: "Frutas Ejemplo SL",
cargadorNif: "B12345678",
transportistaNombre: "Transportes Ejemplo SL",
transportistaNif: "B87654321",
origen: "Calle Mayor 1, Amposta",
destino: "Mercabarna, Barcelona",
naturalezaCarga: "Hortalizas",
pesoKg: 2200,
matricula: "1234ABC",
}),
});
const documento = await respuesta.json(); // { id, estado, verifyUrl, pdfUrl, hash }Respuesta
HTTP 201
{
"id": "5f577c64-8f04-4b0b-9788-ef827cd26c3d",
"estado": "vigente",
"verifyUrl": "https://decafly.lucasyleodigital.com/verificar/5f577c64-...",
"pdfUrl": "https://decafly.lucasyleodigital.com/verificar/5f577c64-.../pdf",
"hash": "ce37fa7c0caf6dc73a4fb8a4eded6aa..."
}verifyUrl es la página que ve una persona (estado y datos) y pdfUrl es la descarga directa que lleva el QR del documento. hash es la huella SHA-256 del PDF.
Especificación OpenAPI
La API se describe en un archivo OpenAPI 3.0 que puedes importar tal cual en Postman, Insomnia, Make, Zapier, n8n, Power Automate o en el conector HTTP de tu ERP: te crea las operaciones sin escribir código.
Abrir /api/v1/openapi.jsonUn programa con muchas empresas
- Cada empresa tiene su propia cuenta en Decafly y sus propias claves. Una clave solo da acceso a los documentos de su empresa, así que los datos de unas empresas nunca se mezclan con los de otras.
- Si tu programa da servicio a muchas empresas, guarda la clave de Decafly de cada una junto a su configuración, cifrada. La empresa la crea en Ajustes, Integraciones y la pega en tu programa (o se la da a tu soporte).
- Crea una clave por empresa y por entorno: las dk_test_ para desarrollar y las dk_live_ para producción.
- Si una empresa deja de usar tu programa, revoca su clave desde su cuenta y deja de funcionar al instante.
Idempotencia: que un reintento no duplique
Cada documento generado es un documento legal. Si tu programa reintenta una petición tras un corte de red, podría crear dos. Evítalo enviando la cabecera Idempotency-Key con un valor único de esa operación (por ejemplo el identificador del envío en tu programa, máximo 100 caracteres). Si repites la petición con la misma clave, Decafly devuelve el documento ya creado (código 200 y cabecera Idempotent-Replayed: true) en lugar de generar otro.
Modo de pruebas
Las claves que empiezan por dk_test_ generan documentos de prueba: el PDF lleva una banda roja "DOCUMENTO DE PRUEBA - SIN VALIDEZ LEGAL", la página de verificación lo indica y la respuesta incluye "prueba": true. Los documentos de prueba y los reales están separados: una clave de pruebas no ve los documentos reales ni al revés. Desarrolla con una clave de pruebas y cambia a una real solo al pasar a producción.
Avisos automáticos (webhooks)
En lugar de consultar la API cada poco, tu programa puede recibir un aviso. El administrador de la empresa añade una dirección https en Ajustes, Integraciones, y obtiene un secreto de firma (se muestra una sola vez). Decafly hace un POST JSON a esa dirección en estos casos:
- documento.creado Se ha generado un documento (por la web, por importación o por la API).
- documento.anulado Se ha anulado un documento.
- documento.verificado Alguien ha descargado el documento escaneando su QR (por ejemplo en un control de carretera).
Ejemplo de aviso
POST https://tu-programa.com/decafly/avisos
Decafly-Event: documento.anulado
Decafly-Delivery: 3b1c9f0e-...
Decafly-Signature: t=1760000000,v1=9f2c...
{
"id": "evt_8d1b6c52-...",
"tipo": "documento.anulado",
"creado": "2026-10-09T08:15:00.000Z",
"datos": {
"documentoId": "5f577c64-8f04-4b0b-9788-ef827cd26c3d",
"referencia": "PED-0001",
"estado": "anulado",
"prueba": false,
"verifyUrl": "https://decafly.lucasyleodigital.com/verificar/5f577c64-...",
"pdfUrl": "https://decafly.lucasyleodigital.com/verificar/5f577c64-.../pdf"
}
}Comprobar la firma
Cada aviso lleva la cabecera Decafly-Signature: t es la marca de tiempo y v1 el HMAC-SHA256 de "t.cuerpo" con tu secreto. Comprueba siempre la firma y que la marca de tiempo sea reciente.
import { createHmac, timingSafeEqual } from "crypto";
// cuerpoCrudo: el cuerpo EXACTO recibido, sin volver a serializarlo.
function avisoValido(cuerpoCrudo, cabeceraFirma, secreto) {
const partes = Object.fromEntries(cabeceraFirma.split(",").map((p) => p.split("=")));
const esperado = createHmac("sha256", secreto)
.update(partes.t + "." + cuerpoCrudo)
.digest("hex");
const firmaOk =
esperado.length === partes.v1.length &&
timingSafeEqual(Buffer.from(esperado), Buffer.from(partes.v1));
const reciente = Math.abs(Date.now() / 1000 - Number(partes.t)) < 300; // 5 minutos
return firmaOk && reciente;
}- Responde con un código 2xx en menos de 8 segundos. Cualquier otra respuesta se considera fallo.
- Un aviso fallido se reintenta hasta 5 veces. Los reintentos se procesan periódicamente, hoy una vez al día, así que no cuentes con ellos para avisos urgentes: tu programa puede consultar la API para ponerse al día.
- Un mismo aviso puede llegar más de una vez: usa el campo
iddel aviso para ignorar los repetidos. - La dirección debe ser pública y https; no se admiten direcciones de redes internas.
Errores y límites
- 400 Datos incompletos o no válidos. El cuerpo incluye
detallescon el error de cada campo. - 401 Falta la clave o no es válida (o está revocada).
- 404 El documento no existe, es de otra empresa o es de otro entorno (real o de pruebas).
- 409 Ya hay una petición en curso con esa Idempotency-Key. Reintenta en unos segundos.
- 429 Límite alcanzado: 200 documentos por hora y clave (600 descargas de PDF por hora).
Conviene saber
- Cada llamada a
POST /api/v1/documentoscrea un documento nuevo: no hay reintento automático. Guarda eliddevuelto junto al envío de tu programa para no duplicarlo. - Un DeCA no se edita. Para corregirlo, anula el documento y genera uno nuevo.
- Guarda el enlace
verifyUrl(o descarga el PDF) en tu programa para que el conductor pueda recibirlo.