decadocument

API del documento de control

Emite y gestiona el DeCA desde tu ERP, TMS o tienda online. Cumple la Orden FOM/2861/2012 y la Resolución DGTCF de 5 de junio de 2026.

Especificación OpenAPI 3.1

1. Autenticación

Crea un token en Empresa → Integraciones (API) y envíalo en cada petición. Cada token tiene permisos: deca:leer, deca:emitir y deca:modificar. Todas las respuestas son JSON.

Authorization: Bearer 1|xxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

2. Emitir un DeCA

Los 8 datos del artículo 6 de la Orden. Si el transportista no existe, se da de alta con su NIF y su nombre. Puedes incluir varios envíos en un mismo documento (apartado sexto de la Resolución).

POST https://decadocument.com/api/v1/decas
Idempotency-Key: ALB-2026-0915

{
  "fecha_transporte": "2026-10-04",
  "transportista": { "nif": "B25000001", "nombre": "Transportes Noguera SL" },
  "envios": [
    { "origen": "Balaguer (Lleida)", "destino": "Mercabarna, 08040 Barcelona",
      "naturaleza": "Quesos curados", "peso": 350.5, "peso_unidad": "kg", "bultos": 14, "referencia": "ALB-2026-0915" }
  ],
  "matricula_tractor": "1234BCD",
  "matricula_remolque": null,
  "autorizacion_especial": null,
  "observaciones": "Mantener cadena de frío",
  "referencia_externa": "ALB-2026-0915"
}

Respuesta 201. El campo url es la dirección del QR: descarga el PDF directamente, sin inicio de sesión. Es la que hay que enviar al conductor antes de salir.

{
  "data": {
    "id": "01a0f84b-dc8c-7028-92e1-7f99949a4c49",
    "estado": "EMITIDO",
    "url": "https://…/d/4krMDy33sataKe1ucVtk1IyicxvBumh1XxWVJjcj",
    "pdf_url": "https://decadocument.com/api/v1/decas/01a0f84b-…/pdf",
    "referencia_externa": "ALB-2026-0915",
    "version": 1,
    …
  }
}

Idempotencia

Si envías la cabecera Idempotency-Key y repites la petición (por un corte de red, por ejemplo), recibirás el mismo documento y no se emitirá otro. Recomendamos usar tu nº de albarán o de expedición.

Reglas

La fecha no puede ser anterior a hoy (el DeCA se emite antes de iniciar el transporte). Los errores de validación devuelven 422 con el detalle por campo.

3. Consultar

GET https://decadocument.com/api/v1/decas?desde=2026-10-01&hasta=2026-10-31&estado=EMITIDO&referencia_externa=ALB-2026-0915&matricula=1234BCD
GET https://decadocument.com/api/v1/decas/{id}
GET https://decadocument.com/api/v1/decas/{id}/pdf

4. Cambios durante el servicio

Siguen el apartado quinto de la Resolución. El PDF original se conserva siempre.

# Cambio de vehículo: misma URL, versión nueva del PDF
POST https://decadocument.com/api/v1/decas/{id}/vehiculo
{ "matricula_tractor": "5678FGH", "motivo": "Avería del camión" }

# Rectificar: documento nuevo con URL nueva; el original queda SUSTITUIDO
POST https://decadocument.com/api/v1/decas/{id}/rectificar
{ …mismos campos que al emitir…, "motivo": "Error en el peso" }

# Anular: misma URL; el PDF indica que no tiene validez
POST https://decadocument.com/api/v1/decas/{id}/anular
{ "motivo": "Servicio cancelado" }

5. Transportistas

GET  https://decadocument.com/api/v1/transportistas
POST https://decadocument.com/api/v1/transportistas   { "nif": "B25000001", "nombre": "Transportes Noguera SL", "email": null, "telefono": null }

6. Webhooks

Configúralos en Empresa → Integraciones (API). Enviamos un POST JSON a tu dirección con estos eventos:

POST https://tu-erp.com/webhooks/deca
X-Deca-Event: deca.emitido
X-Deca-Delivery: 9b1c…  (identificador único del aviso)
X-Deca-Timestamp: 1790884800
X-Deca-Signature: sha256=…

{ "id": "9b1c…", "evento": "deca.emitido", "creado": "2026-10-01T10:00:00Z", "data": { "documento": { …igual que en la API… } } }

Verificar la firma

Calcula HMAC-SHA256(secreto, timestamp + "." + cuerpo) y compáralo con la cabecera. Rechaza avisos con más de 5 minutos de antigüedad. Responde 2xx para confirmar; si no, reintentamos durante unas 9 horas (1 min, 5 min, 30 min, 2 h y 6 h).

// PHP
$cuerpo = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_DECA_TIMESTAMP'];
$esperada = 'sha256=' . hash_hmac('sha256', $ts . '.' . $cuerpo, $secreto);
if (!hash_equals($esperada, $_SERVER['HTTP_X_DECA_SIGNATURE']) || abs(time() - (int) $ts) > 300) {
    http_response_code(401); exit;
}

// Node.js
const crypto = require('crypto');
const esperada = 'sha256=' + crypto.createHmac('sha256', secreto).update(ts + '.' + cuerpo).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(esperada), Buffer.from(req.get('X-Deca-Signature')));

7. Códigos de respuesta