API d'optimisation de chargement
Soumettez votre cargaison et recevez en retour un plan de chargement optimisé et physiquement valide — emplacements, mètres de chargement, pieds linéaires et positions de palettes facturées. Le même moteur que celui utilisé dans l'application, continuellement validé par rapport aux plans réels des dispatchers. Accès bac à sable sur tous les plans ; accès production sur le plan Enterprise.
Démarrage rapide
- Connectez-vous → cliquez sur votre nom → API → générez une clé.
- Soumettez une tâche d'optimisation (celle-ci s'exécute en bac à sable — gratuit sur tous les plans) :
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-..."
}Interrogez le status_url (avec le même en-tête d'authentification) jusqu'à ce que status soit "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/..."
}
}Points de terminaison
POST /api/v1/pack
Valide et met en file d'attente une tâche d'optimisation. Renvoie 202 avec un job_id. Prend en charge un en-tête Idempotency-Key — les nouvelles tentatives avec la même clé renvoient la tâche d'origine au lieu de recalculer le chargement.
GET /api/v1/jobs/{id}
Statut de la tâche et, une fois terminée, le résultat complet. Les résultats sont conservés pendant 30 jours ; les plans de production restent accessibles en permanence dans votre espace de travail.
GET /api/v1/openapi.json
Le contrat OpenAPI 3.1 lisible par machine — importez-le dans Postman, des générateurs de code, ou utilisez-le avec votre outil d'IA de développement. Les codes de véhicules sont injectés depuis le moteur en temps réel, garantissant qu'il est toujours à jour. Ouvrir la spécification ↗
Champs de réponse
L'objet result d'une tâche terminée :
| Champ | Type | Description |
|---|---|---|
| placed_count / unplaced_count | integer | Nombre d'articles chargés / non chargés. |
| units | "metric" | "imperial" | Unités dans lesquelles sont exprimées toutes les valeurs ci-dessous (reprises de la requête). |
| metrics.ldm_m | number | Mètres de chargement — étendue du bloc de cargaison (max fin − min début). |
| metrics.linear_ft | number | Même étendue en pieds linéaires (messagerie LTL aux États-Unis). |
| metrics.span_mm | integer | Étendue en mm, toujours en métrique. |
| metrics.pallet_positions_eur | integer | null | Positions de palettes EUR facturées. Null si le plan est trop grand pour être mesuré. |
| metrics.total_weight_kg | number | Poids chargé, toujours en kg. |
| metrics.overlaps | integer | Paires d'interpénétration — toujours 0 pour un plan valide. |
| items[].placed | boolean | Indique si l'article a été intégré au plan de chargement. |
| items[].x / y / z | number | null | Position dans les unités de la requête : x depuis l'extrémité porte, y depuis la paroi gauche, z depuis le sol. Les trois valeurs sont null si placed est false. |
| items[].rotation_deg / tilt_deg | 0 | 90 | Orientation : rotation autour de l'axe vertical ; tilt = posé sur le côté. |
| items[].length / width / height / weight | number | Dimensions de l'article telles que soumises, reprises dans les unités de la requête. |
| plan_url | string | null | Le plan créé dans l'application. Null pour les tâches en bac à sable. |
Versionnage et stabilité
Le contrat est versionné dans le chemin (/api/v1/). Au sein de v1, les modifications sont uniquement additives — nouveaux champs optionnels dans la requête, nouveaux champs de réponse, nouveaux codes de véhicules et codes d'erreur peuvent apparaître, et les clients doivent ignorer les champs qu'ils ne reconnaissent pas. Les champs existants ne changent jamais de signification ou de type, et rien n'est supprimé. Une modification incompatible entraîne un nouveau chemin de version, avec v1 maintenu en parallèle pendant au moins 12 mois et un avis de dépréciation envoyé par e-mail à chaque espace de travail disposant d'une clé active.
Champs de la requête
| Champ | Type | Description |
|---|---|---|
| vehicle | string | Un code de véhicule issu des tableaux ci-dessous. Ou transmettez un {customVehicleId} (un véhicule de votre flotte d'espace de travail). |
| objective | "ldm" | "pallets" | Minimiser les mètres de chargement ou les positions de palettes facturées. Par défaut "ldm". |
| units | "metric" | "imperial" | Unités dans lesquelles les dimensions et poids des articles sont lus ET renvoyés : mm/kg (par défaut) ou po/lb. Les positions dans la réponse suivent les mêmes unités. |
| items | array, required | Le manifeste de cargaison (champs ci-dessous). |
| sandbox | boolean | Mode test : résultat complet, rien n'est conservé. Disponible sur tous les plans. |
| name | string | Nom du plan (mode production). |
| ldm_limit_m | number | Limiter le chargement aux N premiers mètres de chargement (camions). |
| budget_ms | number | Budget de temps d'optimisation (production ; par défaut 30000, max 300000). |
| callback_url | https URL | URL appelée par POST avec le résultat lorsque la tâche est terminée, signé (voir Webhooks). |
Champs des articles
| Champ | Type | Description |
|---|---|---|
| length / width / height | number, required | Dimensions de l'article, lues dans les unités de la requête (mm par défaut, pouces avec "units": "imperial"). |
| weight | number, required | Poids de l'article (kg par défaut, lb avec "units": "imperial"). |
| quantity | integer, default 1 | Développe en unités individuelles. |
| name | string, optional | Affiché dans le plan et la réponse. |
| stackable | boolean, default true | Autorise le chargement d'autres articles au-dessus de celui-ci. |
| max_stack | number, optional | Poids maximal que cet article peut supporter au-dessus (mêmes unités que le poids). |
| fragile | boolean, default false | Les articles fragiles sont placés uniquement sur les couches supérieures. |
| tiltable | boolean, default true | Indique si l'article peut être posé sur le côté. |
| destination_stop | integer, default 1 | Arrêt de livraison pour un chargement LIFO multi-arrêts. |
Codes de véhicules
Générés à partir des spécifications du moteur en temps réel. Les véhicules personnalisés de votre flotte d'espace de travail fonctionnent via custom_vehicle_id.
Camions et remorques
| Code | Véhicule | Intérieur (L×l×H) | Charge utile |
|---|---|---|---|
| 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 |
Conteneurs
| Code | Conteneur | Intérieur (L×l×H) | Charge utile |
|---|---|---|---|
| 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 |
Niveaux d'accès et limites
| Bac à sable (tous les plans) | Production (Enterprise) | |
|---|---|---|
| Appels | 25 réussis / mois | 5 000 succès / mois |
| Articles par manifeste | 20 | 5,000 |
| Tâches simultanées | 1 | 10 |
| Budget d'optimisation | 10 s | 30 s par défaut, jusqu'à 300 s |
| Persistance | Aucune donnée stockée | Chaque optimisation crée un plan réel dans votre espace de travail |
Seuls les chargements réussis sont comptabilisés — les échecs de validation et les tâches en erreur ne sont jamais facturés sur un quota. Les deux compteurs sont réinitialisés le 1er (UTC). Les résultats en bac à sable sont complets et non limités — les limites concernent uniquement l'accès, pas la sortie. Besoin de plus de 5 000 chargements par mois ? Contactez-nous pour discuter des tarifs volume et OEM.
Webhooks
Transmettez un callback_url pour recevoir le résultat par POST lorsque la tâche est terminée. Chaque appel inclut une X-Hansatic-Signature : un HMAC-SHA256 du corps brut avec le secret webhook de votre clé (affiché une seule fois à la création de la clé). Vérifiez-la :
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));
}La livraison est effectuée en une seule tentative avec un délai de 5 s — le status_url reste toujours la source de vérité.
Erreurs
Toutes les erreurs sont de forme { "error": { "code", "message", ... } } — code lisible par machine, message lisible par humain, et field lorsqu'un champ spécifique est en cause.
| Code | HTTP | Signification |
|---|---|---|
| invalid_key | 401 | Clé API manquante, mal formée ou révoquée. |
| invalid_json | 400 | Le corps n'est pas un JSON valide. |
| invalid_vehicle | 400 | Code de véhicule ou {customVehicleId} inconnu. |
| invalid_items | 400 | Un champ d'article est manquant, non numérique ou hors plage. |
| suspicious_dimensions | 400 | Toutes les dimensions inférieures à 100 mm — probablement des pouces envoyés en mm. Utilisez "units": "imperial". |
| invalid_units | 400 | units doit être "metric" ou "imperial". |
| invalid_objective | 400 | objective doit être "ldm" ou "pallets". |
| invalid_callback | 400 | callback_url doit être en https. |
| too_many_items | 400 | Le manifeste dépasse la limite d'articles pour votre niveau d'accès. |
| upgrade_required | 402 | Le mode production nécessite le plan Enterprise. Utilisez le bac à sable ou passez à un plan supérieur. |
| sandbox_limit_reached | 402 | Quota mensuel du bac à sable épuisé — réinitialisé le 1er du mois. |
| quota_exceeded | 402 | Quota mensuel de 5 000 chargements atteint — réinitialisation le 1er. Pour un volume supérieur : contactez-nous. |
| too_many_jobs | 429 | Limite de tâches simultanées atteinte. Interrogez les tâches en cours et réessayez. |
| not_found | 404 | Aucune tâche correspondante pour cet espace de travail. |
Des questions, des besoins de limites plus élevées ou un connecteur spécifique (Zapier, Excel, MCP) ? hello@hansatic.com