API de Embalaje
Envía una lista de mercancías y recibe un plan de carga optimizado y físicamente válido: ubicaciones, metros de carga, pies lineales y posiciones de palets facturables. El mismo motor que impulsa la aplicación, validado continuamente con diseños reales de despachadores. Acceso al entorno de pruebas en todos los planes; acceso en producción con Enterprise.
Primeros pasos
- Inicia sesión → haz clic en tu nombre → API → genera una clave.
- Envía un trabajo de embalaje (este se ejecuta en el entorno de pruebas — gratuito en todos los planes):
curl -X POST https://hansatic.com/api/v1/pack \
-H "Authorization: Bearer hk_live_..." \
-H "Content-Type: application/json" \
-d '{
"vehicle": "CURTAINSIDER_136",
"objective": "ldm",
"sandbox": true,
"items": [{
"name": "Machine crate",
"length": 2300, "width": 750, "height": 1400,
"weight": 300, "quantity": 7,
"stackable": false, "tiltable": false
}]
}'{
"job_id": "5e3617d1-abe4-4549-b7e6-d18c6baf814f",
"status": "queued",
"sandbox": true,
"status_url": "https://hansatic.com/api/v1/jobs/5e3617d1-..."
}Consulta status_url (con el mismo encabezado de autenticación) hasta que status sea "done":
{
"job_id": "5e3617d1-...",
"status": "done",
"result": {
"placed_count": 7,
"unplaced_count": 0,
"units": "metric",
"metrics": {
"ldm_m": 5.25,
"span_mm": 5250,
"linear_ft": 17.2,
"pallet_positions_eur": 14,
"total_weight_kg": 2100,
"overlaps": 0
},
"items": [
{ "id": "...", "name": "Machine crate", "placed": true,
"x": 8370, "y": 0, "z": 0,
"rotation_deg": 90, "tilt_deg": 0, "...": "..." }
],
"plan_url": "https://hansatic.com/en/app/plans/..."
}
}Endpoints
POST /api/v1/pack
Valida y pone en cola un trabajo de embalaje. Devuelve 202 con un job_id. Admite el encabezado Idempotency-Key: los reintentos con la misma clave devuelven el trabajo original en lugar de procesarlo dos veces.
GET /api/v1/jobs/{id}
Estado del trabajo y, al finalizar, el resultado completo. Los resultados se conservan durante 30 días; en los planes de producción, permanecen en tu espacio de trabajo de forma permanente.
GET /api/v1/openapi.json
El contrato OpenAPI 3.1 legible por máquina: impórtalo en Postman, generadores de código o úsalo con herramientas de IA para desarrollo. Los códigos de vehículos se inyectan desde el motor en tiempo real, por lo que siempre está actualizado. Abrir la especificación ↗
Campos de respuesta
El objeto result de un trabajo finalizado:
| Campo | Tipo | Descripción |
|---|---|---|
| placed_count / unplaced_count | integer | Cantidad de artículos que caben / no caben. |
| units | "metric" | "imperial" | Unidades en las que se expresan todos los valores numéricos a continuación (eco de la solicitud). |
| metrics.ldm_m | number | Metros de carga — extensión del bloque de mercancías (máximo final − mínimo inicial). |
| metrics.linear_ft | number | La misma extensión en pies lineales (LTL en EE.UU.). |
| metrics.span_mm | integer | Extensión en mm, siempre en sistema métrico. |
| metrics.pallet_positions_eur | integer | null | Posiciones de palets EUR facturables. null cuando el diseño es demasiado grande para medir. |
| metrics.total_weight_kg | number | Peso total de los artículos colocados, siempre en kg. |
| metrics.overlaps | integer | Pares con solapamiento — siempre 0 en un diseño válido. |
| items[].placed | boolean | Indica si el artículo se incluyó en el diseño. |
| items[].x / y / z | number | null | Posición en las unidades de la solicitud: x desde el extremo de la puerta, y desde la pared izquierda, z desde el suelo. Los tres valores son null cuando placed es false. |
| items[].rotation_deg / tilt_deg | 0 | 90 | Orientación: rotación sobre el eje vertical; tilt = colocado de lado. |
| items[].length / width / height / weight | number | Dimensiones del artículo tal como se enviaron, en las unidades de la solicitud. |
| plan_url | string | null | El plan creado en la aplicación. null para trabajos en el entorno de pruebas. |
Versionado y estabilidad
El contrato se versiona en la ruta (/api/v1/). Dentro de v1, los cambios son solo aditivos: nuevos campos opcionales en la solicitud, nuevos campos en la respuesta, nuevos códigos de vehículos y códigos de error pueden aparecer, y los clientes deben ignorar los campos que no reconozcan. Los campos existentes nunca cambian de significado o tipo, y nada se elimina. Un cambio disruptivo implica una nueva ruta de versión, con v1 mantenido en paralelo durante al menos 12 meses y notificación de obsolescencia por correo electrónico a cada espacio de trabajo con una clave activa.
Campos de solicitud
| Campo | Tipo | Descripción |
|---|---|---|
| vehicle | string | Código de vehículo de las tablas siguientes. También puedes usar {customVehicleId} (un vehículo de tu flota en el espacio de trabajo). |
| objective | "ldm" | "pallets" | Minimizar metros de carga o minimizar posiciones de palets facturables. Por defecto "ldm". |
| units | "metric" | "imperial" | Unidades en las que se interpretan y devuelven las dimensiones y pesos de los artículos: mm/kg (por defecto) o in/lb. Las posiciones en la respuesta siguen las mismas unidades. |
| items | array, required | Manifiesto de carga (campos detallados a continuación). |
| sandbox | boolean | Modo de prueba: resultado completo, sin persistencia. Disponible en todos los planes. |
| name | string | Nombre del plan (modo producción). |
| ldm_limit_m | number | Limitar la carga a los primeros N metros de carga (camiones). |
| budget_ms | number | Tiempo máximo de optimización (producción; por defecto 30000, máximo 300000). |
| callback_url | https URL | URL a la que se enviará el resultado mediante POST cuando finalice el trabajo, firmado (consulta Webhooks). |
Campos de artículos
| Campo | Tipo | Descripción |
|---|---|---|
| length / width / height | number, required | Dimensiones del artículo, interpretadas en las unidades de la solicitud (mm por defecto, pulgadas con "units": "imperial"). |
| weight | number, required | Peso del artículo (kg por defecto, lb con "units": "imperial"). |
| quantity | integer, default 1 | Se expande en unidades individuales. |
| name | string, optional | Se muestra en el plan y en la respuesta. |
| stackable | boolean, default true | Indica si otros artículos pueden apilarse sobre este. |
| max_stack | number, optional | Peso máximo que este artículo puede soportar encima (mismas unidades que weight). |
| fragile | boolean, default false | Los artículos frágiles se colocan solo en las capas superiores. |
| tiltable | boolean, default true | Indica si el artículo puede colocarse de lado. |
| destination_stop | integer, default 1 | Parada de entrega para carga LIFO con múltiples paradas. |
Códigos de vehículos
Generados a partir de las especificaciones del motor en tiempo real. Los vehículos personalizados de tu flota en el espacio de trabajo funcionan mediante custom_vehicle_id.
Camiones y remolques
| Código | Vehículo | Interior (L×A×H) | Carga útil |
|---|---|---|---|
| CURTAINSIDER_136 | 13.6m Curtainsider | 13.62 × 2.48 × 2.70 m | 24 t |
| BOX_136 | 13.6m Box Trailer | 13.62 × 2.46 × 2.68 m | 24 t |
| MEGA_136 | 13.6m Mega Trailer | 13.62 × 2.48 × 3.00 m | 22.5 t |
| MEGA_CURTAIN_136 | 13.6m Mega Curtainsider | 13.62 × 2.48 × 3.00 m | 22 t |
| FLATBED_136 | 13.6m Flatbed | 13.62 × 2.48 × 3.00 m | 25 t |
| REEFER_136 | 13.6m Reefer | 13.32 × 2.44 × 2.54 m | 22 t |
| CURTAINSIDER_75 | 7.5m City Curtainsider | 7.50 × 2.48 × 2.70 m | 12 t |
| SWAPBODY_715 | 7.15m Swap Body | 7.15 × 2.48 × 2.70 m | 12 t |
| SWAPBODY_745 | 7.45m Swap Body | 7.45 × 2.48 × 2.70 m | 12 t |
| DRY_VAN_53 | 53ft Dry Van | 16.15 × 2.59 × 2.74 m | 20 t |
| FLATBED_53 | 53ft Flatbed | 16.15 × 2.59 × 3.66 m | 22 t |
| REEFER_53 | 53ft Reefer | 15.85 × 2.46 × 2.59 m | 18.5 t |
| CURTAINSIDER_125_CN | 12.5m Curtainsider (CN) | 12.50 × 2.50 × 2.80 m | 27 t |
| FLATBED_40_IN | 40ft Flatbed (IN) | 12.19 × 2.50 × 3.50 m | 26 t |
| BOX_138_BR | 13.8m Box Van (BR) | 13.80 × 2.50 × 2.90 m | 28 t |
| CURTAINSIDER_12_ZA | 12m Curtainsider (ZA) | 12.00 × 2.48 × 2.70 m | 28 t |
Contenedores
| Código | Contenedor | Interior (L×A×H) | Carga útil |
|---|---|---|---|
| 20DC | 20' Dry Container | 5.89 × 2.35 × 2.39 m | 21.727 t |
| 20HC | 20' High Cube | 5.89 × 2.35 × 2.70 m | 21.58 t |
| 40DC | 40' Dry Container | 12.03 × 2.35 × 2.39 m | 26.63 t |
| 40HC | 40' High Cube | 12.03 × 2.35 × 2.70 m | 26.58 t |
| 45HC | 45' High Cube | 13.56 × 2.35 × 2.70 m | 25.68 t |
| 20OT | 20' Open Top | 5.89 × 2.35 × 2.39 m | 21.64 t |
| 40OT | 40' Open Top | 12.03 × 2.35 × 2.39 m | 26.58 t |
| 20RF | 20' Reefer | 5.44 × 2.29 × 2.27 m | 21 t |
| 40RF | 40' Reefer | 11.58 × 2.29 × 2.21 m | 25.6 t |
| 40RFHC | 40' Reefer High Cube | 11.58 × 2.29 × 2.54 m | 25.88 t |
| 53DC | 53' Domestic | 16.15 × 2.59 × 2.90 m | 26 t |
Niveles de acceso y límites
| Entorno de pruebas (todos los planes) | Producción (Enterprise) | |
|---|---|---|
| Llamadas | 25 exitosas / mes | 5.000 envíos exitosos / mes |
| Artículos por manifiesto | 20 | 5,000 |
| Trabajos simultáneos | 1 | 10 |
| Tiempo de optimización | 10 s | 30 s por defecto, hasta 300 s |
| Persistencia | Nada almacenado | Cada embalaje crea un plan real en tu espacio de trabajo |
Solo se contabilizan los empaques exitosos — las validaciones fallidas y los trabajos fallidos nunca se descuentan de la cuota. Ambos contadores se reinician el día 1 (UTC). Los resultados en el entorno de pruebas son completos y sin restricciones — los límites son la barrera, no el resultado. ¿Necesita más de 5.000 empaques al mes? Consulte con nosotros sobre precios por volumen y OEM.
Webhooks
Proporciona callback_url para recibir el resultado mediante POST cuando el trabajo finalice. Cada llamada incluye X-Hansatic-Signature: un HMAC-SHA256 del cuerpo sin procesar con el secreto de webhook de tu clave (mostrado una vez al crear la clave). Verifícalo:
const crypto = require('crypto');
function verify(rawBody, signatureHeader, webhookSecret) {
const expected = crypto.createHmac('sha256', webhookSecret)
.update(rawBody).digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected), Buffer.from(signatureHeader));
}El envío se realiza en un solo intento con un tiempo de espera de 5 s — status_url sigue siendo la fuente de verdad.
Errores
Todos los errores tienen la forma { "error": { "code", "message", ... } }: código legible por máquina, mensaje legible por humanos y field cuando un campo de entrada específico es el problema.
| Código | HTTP | Significado |
|---|---|---|
| invalid_key | 401 | Clave API ausente, mal formada o revocada. |
| invalid_json | 400 | El cuerpo no es un JSON válido. |
| invalid_vehicle | 400 | Código de vehículo o {customVehicleId} desconocido. |
| invalid_items | 400 | Falta un campo de artículo, no es numérico o está fuera de rango. |
| suspicious_dimensions | 400 | Todas las dimensiones son menores a 100 mm — probablemente pulgadas enviadas como mm. Usa "units": "imperial". |
| invalid_units | 400 | units debe ser "metric" o "imperial". |
| invalid_objective | 400 | objective debe ser "ldm" o "pallets". |
| invalid_callback | 400 | callback_url debe ser https. |
| too_many_items | 400 | El manifiesto supera el límite de artículos para tu nivel de acceso. |
| upgrade_required | 402 | El modo producción requiere el plan Enterprise. Usa el entorno de pruebas o actualiza. |
| sandbox_limit_reached | 402 | Límite mensual del entorno de pruebas alcanzado — se reinicia el día 1. |
| quota_exceeded | 402 | Se ha agotado la cuota mensual de 5.000 empaques — se reinicia el día 1. Para mayor volumen: contáctenos. |
| too_many_jobs | 429 | Límite de trabajos simultáneos alcanzado. Consulta los trabajos en ejecución y reintenta. |
| not_found | 404 | No existe ese trabajo para este espacio de trabajo. |
¿Preguntas, límites más altos o necesitas un conector (Zapier, Excel, MCP)? hello@hansatic.com