EtiQali API
Integra tu ERP con EtiQali para registrar albaranes de venta y generar trazabilidad alimentaria normativa automáticamente. Un único endpoint, REST sobre HTTPS, autenticación por clave.
Empezar en 3 pasos
Solicita acceso
Escribe a soporte@etiqali.com indicando el nombre de tu empresa, CIF y el ERP o software desde el que vas a integrar. Recibirás una clave de API personal y, opcionalmente, acceso al entorno de pruebas.
Configura tu ERP
Tu sistema debe poder realizar una llamada HTTPS POST con cuerpo JSON cada vez que se emita un albarán de venta. La mayoría de ERPs modernos lo soportan de forma nativa o mediante un script.
Envía tu primer albarán
Realiza una llamada al endpoint POST /api/v1/albara con los datos del albarán. EtiQali devuelve el número de lote único para cada línea, que puedes imprimir en el albarán físico junto al código QR para tus clientes.
Autenticación
Todas las peticiones a la API requieren una clave en la cabecera X-EtiQali-Key. Cada clave está autorizada para operar sobre un conjunto de empresas identificadas por CIF: una clave directa cubre una sola empresa (tu ERP emite lo tuyo); una clave de plataforma —una central de mercado, caja gremial o sistema que emite en nombre de varias empresas— cubre todo el conjunto que tiene autorizado. La regla es la misma en ambos casos: el cif_vendedor de cada albarán debe estar dentro del ámbito de la clave.
X-EtiQali-Key: etq_live_xxxxxxxxxxxxxxxxxxxxxxxx
Confidencialidad. Tu clave API identifica tu empresa frente a EtiQali. No la incluyas en código del lado del cliente, repositorios públicos o capturas de pantalla. Si crees que se ha visto comprometida, contacta inmediatamente con soporte para rotarla.
Entornos
EtiQali ofrece dos entornos. Ambos comparten la misma especificación de API. Las llamadas al entorno de pruebas no generan trazabilidad real ni etiquetas oficiales.
| Entorno | Uso | Base URL |
|---|---|---|
| Producción | Datos reales, etiquetas válidas legalmente | https://api.etiqali.com |
| Pruebas | Integración inicial y desarrollo | Facilitado por soporte bajo petición |
Registrar un albarán
Registra un albarán de venta en EtiQali y obtén el número de lote único asignado a cada línea. La llamada debe realizarse en el momento de emisión del albarán por parte del vendedor.
Estructura de petición
El cuerpo de la petición debe ser un objeto JSON con los siguientes campos:
| Campo | Tipo | Descripción | |
|---|---|---|---|
num_albara | string | REQ | Identificador único del albarán dentro de tu numeración. Máximo 64 caracteres. Se usa como clave de idempotencia (ver sección Idempotencia). |
data | string | REQ | Fecha del albarán en formato ISO 8601 (YYYY-MM-DD). |
cif_vendedor | string | REQ | CIF / NIF de la empresa vendedora. Debe estar autorizado para tu clave API (ver Autenticación). |
cif_comprador | string | REQ | CIF / NIF de la empresa compradora destinataria del albarán. |
seccio | string | OPT | Sección de fresco del comprador, cuando el destinatario tiene una cuenta por sección (supermercados). Valores: "pesca", "carn", "fruta", "xarcuteria". Ver Compradores con secciones. |
linies | array | REQ | Líneas del albarán. Cada línea representa un producto o lote vendido. Mínimo 1, máximo 500 por llamada. |
Campos de línea
Cada elemento del array linies describe un producto o lote vendido. Los campos opcionales aplican a sectores regulados específicamente (pesca, carne, etc.).
Campos comunes
| Campo | Tipo | Descripción | |
|---|---|---|---|
producte | string | REQ | Nombre comercial del producto. |
quantitat | number | REQ | Cantidad vendida, en la unidad especificada en unitat. |
seccio | string | OPT | Sección de fresco a la que va esta línea. Solo si el albarán mezcla secciones; prevalece sobre el seccio del albarán. |
cod_art | string | OPT | Código de artículo en tu ERP. Muy recomendado para mantener la coherencia entre tu inventario y EtiQali. |
lot | string | OPT | Lote de trazabilidad del vendedor para esta línea. Muy recomendado: es lo que permite enlazar el recorrido del producto hacia atrás y hacia adelante en la cadena. |
unitat | string | OPT | Unidad de medida. Valores: "kg", "ud", "caja", "l". Por defecto: "kg". |
data_caducitat | string | OPT | Fecha de caducidad o consumo preferente en formato YYYY-MM-DD. |
conservacio | string | OPT | Condiciones de conservación. Ej: "Refrigerado 0–4 °C". |
pais_origen | string | OPT | País de origen. Solo para acuicultura o producto transformado. En pescado salvaje no aplica: el origen es la zona de captura (zona_fao), conforme al Reglamento UE 1379/2013. |
Campos pesca (Reglamento UE 1379/2013)
| Campo | Tipo | Descripción | |
|---|---|---|---|
nom_cientific | string | OPT | Nombre científico en latín. Ej: "Merluccius merluccius". |
zona_fao | string | OPT | Zona de captura FAO. Ej: "27" (Atlántico NE), "34" (Atlántico CE), "37" (Mediterráneo). |
metode_produccio | string | OPT | Método de producción (Reg. UE 1379/2013 art. 35b). Valores: "Captura" (salvaje) o "Acuicultura" (cría). |
art_pesca | string | OPT | Arte de pesca (Reg. UE 1379/2013 art. 35c). Ej: "Arrastre de fondo", "Palangre de superficie", "Redes de enmalle". |
llotja | string | OPT | Lonja o puerto de desembarque. |
barco_nom | string | OPT | Nombre del buque pesquero. |
barco_matricula | string | OPT | Matrícula oficial del buque. |
data_captura | string | OPT | Fecha de captura en formato YYYY-MM-DD. |
estat | string | OPT | Estado de conservación. Valores: "Fresc", "Congelat", "Descongelat". |
Campos carne (RD 1698/2003, RD 142/2026)
| Campo | Tipo | Descripción | |
|---|---|---|---|
especie | string | OPT | Especie animal. Ej: "Vacuno", "Porcino", "Ovino". |
pais_nacimiento | string | OPT | País de nacimiento del animal. |
pais_engorde | string | OPT | País de cría / engorde. |
pais_sacrificio | string | OPT | País del matadero. |
matadero_id | string | OPT | Número de autorización del matadero. |
despiece_id | string | OPT | Número de autorización de la sala de despiece. |
lote_sacrificio | string | OPT | Lote de sacrificio asignado por el matadero. |
Respuesta
Una respuesta exitosa devuelve HTTP 200 con un objeto JSON que confirma el registro y devuelve los números de lote generados.
{
"ok": true,
"num_albara": "ALB-2026-001234",
"lots": [
{
"producte": "Merluza europea",
"num_lot": "ALB-2026-001234-1",
"qr_url": "https://app.etiqali.com/lot.html?lot=ALB-2026-001234-1"
}
]
}
Campos de respuesta
| Campo | Tipo | Descripción |
|---|---|---|
ok | boolean | true si el albarán se ha procesado correctamente. |
num_albara | string | Confirmación del identificador enviado. |
lots | array | Lotes generados, uno por línea, en el mismo orden que las líneas enviadas. |
lots[].num_lot | string | Número de lote único generado por EtiQali. Debe imprimirse en el albarán físico junto al producto. |
lots[].qr_url | string | URL pública del código QR de trazabilidad. Cualquier consumidor con esta URL puede ver los datos públicos del lote. |
Imprime el num_lot en el albarán físico. El comprador y el consumidor final pueden escanear el QR derivado para acceder a la información de trazabilidad.
Compradores con secciones
Un supermercado tiene una cuenta de EtiQali por cada sección de fresco — pescadería, carnicería, frutería, charcutería — porque cada una lleva su propia trazabilidad, sus propios lotes y su propia cartelería. Todas comparten el mismo CIF de la sociedad.
Si envías solo el cif_comprador, el albarán entra en la sección predeterminada. Para dirigirlo a la que corresponde, añade seccio:
| Valor | Sección |
|---|---|
"pesca" | Pescadería |
"carn" | Carnicería |
"fruta" | Frutería |
"xarcuteria" | Charcutería |
Lo normal es enviarlo a nivel de albarán, porque un albarán suele ser de una sola sección. Si un albarán mezcla productos de varias, cada línea puede llevar su propio seccio, que prevalece sobre el del albarán:
{
"num_albara": "A-1234",
"data": "2026-09-01",
"cif_vendedor": "B00000001",
"cif_comprador": "B00000002",
"seccio": "carn",
"linies": [
{ "producte": "Solomillo de ternera", "cod_art": "512", "quantitat": 8 },
{ "producte": "Merluza", "cod_art": "235", "quantitat": 12, "seccio": "pesca" }
]
}
La respuesta incluye seccions con las secciones a las que ha ido el albarán, para que puedas verificar el encaminamiento desde tu ERP.
Compatibilidad. El campo es opcional. Si no lo envías, el comportamiento es el de siempre: el albarán se asigna a la empresa del CIF exacto. Las integraciones existentes no requieren ningún cambio.
Si el comprador no tiene secciones —una pescadería, un restaurante, un mayorista— el campo se ignora. No hace falta condicionarlo en tu código: puedes enviarlo siempre.
Una clave, varias empresas
Lo habitual es que una clave emita en nombre de una sola empresa. Pero hay casos en los que un mismo sistema centraliza los albaranes de muchas: un mercado mayorista cuyo software recibe los albaranes de todos sus asociados, un grupo de empresas, o un ERP en modo multiempresa.
Para esos casos, una clave puede estar autorizada sobre N empresas. No hace falta una clave por cada una, ni que cada empresa configure nada por su cuenta.
El funcionamiento es el mismo de siempre: en cada albarán envías el cif_vendedor correspondiente, y EtiQali comprueba que tu clave está autorizada para ese CIF.
// Un mismo sistema, dos vendedores distintos
{ "cif_vendedor": "B11111111", "num_albara": "1001", ... }
{ "cif_vendedor": "B22222222", "num_albara": "2001", ... }
Si envías un cif_vendedor para el que tu clave no está autorizada, la respuesta es empresa_not_authorized con código 422. La autorización se concede empresa por empresa, con su consentimiento, al dar de alta la clave.
Para integradores de mercados y plataformas centrales. Una sola integración da de alta a todos los operadores conectados a tu sistema. Escríbenos a soporte@etiqali.com indicando cuántas empresas prevés y te preparamos la clave con su ámbito.
Webhooks · recibir albaranes
Hasta aquí hemos visto cómo enviar lo que vendes. Los webhooks son el camino inverso: cuando un proveedor tuyo registra un albarán en EtiQali, EtiQali llama a tu ERP y le entrega la trazabilidad completa.
Tu sistema no tiene que preguntar nada ni programar ninguna sincronización. Solo necesita una URL que reciba peticiones POST.
Qué recibes
{
"event": "compra.proposada",
"estat": "pendent_acceptacio",
"versio": 2,
"rectificacio": true,
"albara": { "num_albara": "A-1234", "data": "2026-09-01" },
"proveidor": { "cif": "B11111111", "nom": "Mayorista Ejemplo, S.L." },
"comprador": { "cif": "B22222222", "nom": "Pescadería Ejemplo" },
"linies": [
{
"producte": "Merluza",
"cod_art": "235",
"quantitat": 12,
"num_lot": "LNK-B11111111-A-1234-1",
"nom_cientific": "Merluccius merluccius",
"zona_fao": "27",
"art_pesca": "Arrastre",
"qr_url": "https://app.etiqali.com/lot.html?lot=..."
}
],
"emes_at": "2026-09-01T06:12:00.000Z"
}
Dos eventos posibles
| Evento | estat | Qué significa |
|---|---|---|
compra.proposada | pendent_acceptacio | El proveedor ha registrado un albarán a tu nombre. Está a la espera de que lo aceptes. |
compra.registrada | acceptada | Ya habías declarado que aceptas automáticamente a este proveedor. No hay que hacer nada más. |
El campo versio
Un albarán puede rectificarse varias veces antes de darse por bueno — en el sector mayorista es lo habitual. Cada envío lleva un número de versión que siempre crece.
Tu ERP debe guardar la última versión aplicada de cada albarán y descartar cualquier mensaje con versión igual o menor. Es la única forma de garantizar que un reintento retrasado no sobrescriba una rectificación posterior.
Verificar que la llamada es nuestra
Cada petición incluye estas cabeceras:
| Cabecera | Contenido |
|---|---|
X-EtiQali-Event | Tipo de evento. Hoy siempre albara.rebut. |
X-EtiQali-Delivery | Identificador único de esta entrega. Útil para tus registros. |
X-EtiQali-Timestamp | Marca de tiempo Unix del envío. |
X-EtiQali-Signature | sha256= seguido del HMAC-SHA256 de timestamp + "." + cuerpo, con tu secreto. |
Calcula el HMAC con tu secreto y compáralo con la cabecera. Si no coincide, descarta la petición. Rechaza también las que lleguen con un timestamp de más de cinco minutos.
Qué debe responder tu ERP
Cualquier código 2xx se considera entregado. Responde rápido —lo ideal es aceptar y procesar después—: si tardas más de 15 segundos, se considera fallido.
Si tu servidor no responde, EtiQali reintenta al minuto, a los 5, a los 15, a la hora, a las 6 horas y a las 24. Un servidor apagado durante la noche no pierde ningún albarán.
Consultar el estado de tus entregas
GET https://api.etiqali.com/api/v1/webhooks/entregues
X-EtiQali-Key: tu_clave
Devuelve las últimas entregas con su estado (pendent, entregat, fallit), el número de intentos y el último error. Acepta ?estat=fallit para filtrar y ?cif= si tu clave cubre varias empresas.
Para activarlo, escríbenos a soporte@etiqali.com con la URL donde quieres recibir. Te devolvemos el secreto de firma. La URL debe ser https y accesible desde internet.
Aceptar una compra
Cuando un proveedor registra un albarán a tu nombre, EtiQali no da por hecho que lo asumes. La compra queda a la espera de que la aceptes.
No es burocracia: el artículo 18 del Reglamento (UE) 178/2002 hace que tu trazabilidad sea tu responsabilidad, y no es delegable. Si un tercero registrara compras a tu nombre sin que tú intervengas, estaría firmando declaraciones por ti. Tu aceptación —con fecha y autor— es lo que convierte una información recibida en un registro tuyo, defendible ante una inspección.
Aceptar o rechazar
POST https://api.etiqali.com/api/v1/compres/acceptar
X-EtiQali-Key: tu_clave
Content-Type: application/json
{
"cif": "B00000002",
"num_albara": "A-1234",
"accepta": true,
"usuari": "id-del-responsable"
}
Para rechazar —mercancía que no llegó, cantidades que no cuadran— envía "accepta": false y un "motiu". El rechazo también queda registrado: es una declaración tan válida como la aceptación, y suele ser la que interesa conservar.
Aceptación automática por proveedor
Si recibes a diario del mismo proveedor y no quieres confirmar cada albarán, puedes declarar una sola vez que aceptas sus compras por adelantado. Esa declaración queda registrada con fecha y autor, y es revocable en cualquier momento.
A partir de entonces sus albaranes llegan como compra.registrada y no requieren ninguna acción. Sigue siendo una declaración tuya —hecha antes, no dejada de hacer—, que es lo que la sostiene.
Para activarla, escríbenos a soporte@etiqali.com indicando tu CIF y el del proveedor. Próximamente será autogestionable desde la aplicación.
Idempotencia y rectificaciones
El campo num_albara actúa como clave de idempotencia. Puedes reenviar el mismo albarán múltiples veces sin riesgo de duplicación:
- Mismo albarán, mismas líneas: EtiQali devuelve la respuesta original sin reprocesar. Útil para reintentos en caso de error de red.
- Mismo albarán, líneas distintas: EtiQali interpreta que se trata de una rectificación y actualiza el contenido manteniendo el identificador.
Si tu ERP emite un albarán y posteriormente modifica las cantidades, basta con reenviar la llamada con el mismo num_albara. No necesitas eliminar ni anular el registro previo.
Ejemplo básico
Ejemplo mínimo en tres lenguajes:
curl -X POST https://api.etiqali.com/api/v1/albara \ -H "Content-Type: application/json" \ -H "X-EtiQali-Key: etq_live_xxxxxxxxxxxxxxxxxxxxxxxx" \ -d '{ "num_albara": "ALB-2026-001234", "data": "2026-06-24", "cif_vendedor": "B00000001", "cif_comprador": "B00000002", "linies": [ { "producte": "Merluza europea", "cod_art": "MER-EUR-1", "quantitat": 12.5 } ] }'
const response = await fetch('https://api.etiqali.com/api/v1/albara', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-EtiQali-Key': 'etq_live_xxxxxxxxxxxxxxxxxxxxxxxx' }, body: JSON.stringify({ num_albara: 'ALB-2026-001234', data: '2026-06-24', cif_vendedor: 'B00000001', cif_comprador: 'B00000002', linies: [ { producte: 'Merluza europea', cod_art: 'MER-EUR-1', quantitat: 12.5 } ] }) }); const data = await response.json(); // data.lots[0].num_lot → imprimir en el albarán físico
import requests response = requests.post( "https://api.etiqali.com/api/v1/albara", headers={ "Content-Type": "application/json", "X-EtiQali-Key": "etq_live_xxxxxxxxxxxxxxxxxxxxxxxx", }, json={ "num_albara": "ALB-2026-001234", "data": "2026-06-24", "cif_vendedor": "B00000001", "cif_comprador": "B00000002", "linies": [ {"producte": "Merluza europea", "cod_art": "MER-EUR-1", "quantitat": 12.5} ], }, ) data = response.json() # data["lots"][0]["num_lot"] → imprimir en el albarán físico
$ch = curl_init('https://api.etiqali.com/api/v1/albara'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'X-EtiQali-Key: etq_live_xxxxxxxxxxxxxxxxxxxxxxxx', ], CURLOPT_POSTFIELDS => json_encode([ 'num_albara' => 'ALB-2026-001234', 'data' => '2026-06-24', 'cif_vendedor' => 'B00000001', 'cif_comprador' => 'B00000002', 'linies' => [ ['producte' => 'Merluza europea', 'cod_art' => 'MER-EUR-1', 'quantitat' => 12.5], ], ]), ]); $data = json_decode(curl_exec($ch), true); // $data['lots'][0]['num_lot'] → imprimir en el albarán físico
Ejemplo sector pesquero
Albarán completo con campos normativos según Reglamento UE 1379/2013:
{
"num_albara": "ALB-2026-001235",
"data": "2026-06-24",
"cif_vendedor": "B00000001",
"cif_comprador": "B00000002",
"linies": [{
"producte": "Merluza europea",
"cod_art": "MER-EUR-1",
"lot": "LL-2026-00871",
"nom_cientific": "Merluccius merluccius",
"quantitat": 18.5,
"unitat": "kg",
"zona_fao": "27",
"metode_produccio": "Captura",
"art_pesca": "Arrastre de fondo",
"llotja": "Vigo",
"barco_nom": "Nuevo Galicia",
"barco_matricula": "3-VI-2-1-08",
"data_captura": "2026-06-22",
"estat": "Fresc",
"conservacio": "0–4 °C",
"data_caducitat": "2026-06-28"
}]
}
Ejemplo sector cárnico
Albarán completo con campos normativos según RD 1698/2003 y RD 142/2026:
{
"num_albara": "ALB-2026-001236",
"data": "2026-06-24",
"cif_vendedor": "B00000001",
"cif_comprador": "B00000002",
"linies": [{
"producte": "Jarrete Vacuno Añojo",
"cod_art": "JAR-VAC-1",
"quantitat": 8.2,
"unitat": "kg",
"especie": "Vacuno",
"pais_nacimiento": "España",
"pais_engorde": "España",
"pais_sacrificio": "España",
"matadero_id": "ES10.00001/B",
"despiece_id": "ES10.00002/B",
"lote_sacrificio": "S-26-06-001",
"conservacio": "Refrigerado 0–4 °C",
"data_caducitat": "2026-07-01"
}]
}
Códigos de error
La API utiliza códigos de estado HTTP estándar. En caso de error, la respuesta incluye un objeto JSON con detalle:
{
"ok": false,
"error": "Campos obligatorios manquen",
"detail": "Falta el campo cif_comprador"
}
| HTTP | Causa | Acción recomendada |
|---|---|---|
| 400 | Cuerpo JSON inválido o faltan campos obligatorios. | Revisar la estructura de la petición. Validar contra esta documentación. |
| 401 | Clave API ausente o inválida. | Verificar la cabecera X-EtiQali-Key. Solicitar nueva clave si la actual está revocada. |
| 403 | El cif_vendedor no corresponde a tu clave API. | Verificar que el CIF enviado coincide con el de tu empresa. |
| 429 | Has superado el límite de peticiones por minuto de tu clave API. | Espaciar las llamadas y reintentar tras unos segundos. Respeta la cabecera Retry-After si está presente. |
| 500 | Error interno del servidor. | Reintentar con espera exponencial: 1s, 2s, 4s, 8s. Si persiste, contactar con soporte. |
Límites de uso
Para garantizar el servicio a todos los integradores, la API aplica límites razonables:
| Recurso | Límite |
|---|---|
| Peticiones por minuto y clave API | 600 |
| Líneas por albarán | 500 |
| Tamaño máximo del cuerpo | 1 MB |
| Tiempo máximo de respuesta | 30 segundos |
Si tu volumen previsto supera estos límites, contacta con soporte para revisar una configuración a medida.
Buenas prácticas
- Envía el albarán en el momento de emisión. Cuanto antes se registre, antes está disponible la trazabilidad para tu comprador.
- Usa siempre
cod_art. Mantener el código de artículo de tu ERP en cada línea facilita el cruce posterior y la generación de etiquetas coherentes. - Implementa reintentos. Para errores
5xxusa espera exponencial. Para4xxno reintentes: revisa la petición. - Guarda el
num_lotdevuelto. Es el identificador que vincula tu albarán físico con la trazabilidad de EtiQali. - Trata la clave API como una contraseña. Almacénala en variables de entorno o un gestor de secretos, nunca en código fuente.
Versionado
La versión está incluida en la URL del endpoint (/api/v1/...). Cualquier cambio que rompa la compatibilidad dará lugar a una nueva versión (/api/v2/...) manteniendo la versión anterior durante al menos 12 meses.
Los cambios compatibles con la versión actual (nuevos campos opcionales, nuevos códigos de error) se introducen sin cambio de versión. Te recomendamos ignorar campos desconocidos en las respuestas.
Soporte para integradores
Para solicitar acceso, abrir una incidencia o consultar el estado del servicio:
| soporte@etiqali.com | |
| Tiempo de respuesta | Menos de 24 horas laborables |
| Entorno de pruebas | Disponible bajo petición |
| Acompañamiento técnico | Incluido en el plan Enterprise |
