DecaflyCentro de ayuda

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/documentosGenera un DeCA y devuelve su identificador, sus enlaces y su huella.
GET/api/v1/documentosLista 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}/pdfDescarga el PDF del documento.
POST/api/v1/documentos/{id}/anularAnula un documento. Se conserva para trazabilidad.

Crear un documento

POST /api/v1/documentos con un cuerpo JSON:

CampoTipoObligatorioNotas
cargadorNombretextoSíNombre o razón social del cargador.
cargadorNiftextoSíNIF o CIF del cargador.
transportistaNombretextoSíNombre o razón social del transportista.
transportistaNiftextoSíNIF o CIF del transportista.
origentextoSíDirección de origen.
destinotextoSíDirección de destino.
naturalezaCargatextoSíNaturaleza o descripción de la mercancía.
pesoKgnúmeroSíPeso en kilogramos, mayor que 0.
matriculatextoSíMatrícula del vehículo.
referenciatextoNoReferencia propia (pedido, albarán...). Máximo 60 caracteres.
fechaRecogidaAAAA-MM-DDNoFecha prevista de recogida.
horarioRecogidatextoNoPor ejemplo 08:00-10:00.
fechaEntregaAAAA-MM-DDNoFecha prevista de entrega.
horarioEntregatextoNoPor ejemplo 14:00-16:00.
paletsnúmeroNoNúmero de palets.
metrosLinealesnúmeroNoMetros 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.json

Un programa con muchas empresas

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:

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;
}

Errores y límites

Conviene saber

API para desarrolladores | Decafly