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

  1. Connectez-vous → cliquez sur votre nom → API → générez une clé.
  2. 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 :

ChampTypeDescription
placed_count / unplaced_countintegerNombre 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_mnumberMètres de chargement — étendue du bloc de cargaison (max fin − min début).
metrics.linear_ftnumberMême étendue en pieds linéaires (messagerie LTL aux États-Unis).
metrics.span_mmintegerÉtendue en mm, toujours en métrique.
metrics.pallet_positions_eurinteger | nullPositions de palettes EUR facturées. Null si le plan est trop grand pour être mesuré.
metrics.total_weight_kgnumberPoids chargé, toujours en kg.
metrics.overlapsintegerPaires d'interpénétration — toujours 0 pour un plan valide.
items[].placedbooleanIndique si l'article a été intégré au plan de chargement.
items[].x / y / znumber | nullPosition 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_deg0 | 90Orientation : rotation autour de l'axe vertical ; tilt = posé sur le côté.
items[].length / width / height / weightnumberDimensions de l'article telles que soumises, reprises dans les unités de la requête.
plan_urlstring | nullLe 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

ChampTypeDescription
vehiclestringUn 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.
itemsarray, requiredLe manifeste de cargaison (champs ci-dessous).
sandboxbooleanMode test : résultat complet, rien n'est conservé. Disponible sur tous les plans.
namestringNom du plan (mode production).
ldm_limit_mnumberLimiter le chargement aux N premiers mètres de chargement (camions).
budget_msnumberBudget de temps d'optimisation (production ; par défaut 30000, max 300000).
callback_urlhttps URLURL appelée par POST avec le résultat lorsque la tâche est terminée, signé (voir Webhooks).

Champs des articles

ChampTypeDescription
length / width / heightnumber, requiredDimensions de l'article, lues dans les unités de la requête (mm par défaut, pouces avec "units": "imperial").
weightnumber, requiredPoids de l'article (kg par défaut, lb avec "units": "imperial").
quantityinteger, default 1Développe en unités individuelles.
namestring, optionalAffiché dans le plan et la réponse.
stackableboolean, default trueAutorise le chargement d'autres articles au-dessus de celui-ci.
max_stacknumber, optionalPoids maximal que cet article peut supporter au-dessus (mêmes unités que le poids).
fragileboolean, default falseLes articles fragiles sont placés uniquement sur les couches supérieures.
tiltableboolean, default trueIndique si l'article peut être posé sur le côté.
destination_stopinteger, default 1Arrê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

CodeVéhiculeIntérieur (L×l×H)Charge utile
CURTAINSIDER_13613.6m Curtainsider13.62 × 2.48 × 2.70 m24 t
BOX_13613.6m Box Trailer13.62 × 2.46 × 2.68 m24 t
MEGA_13613.6m Mega Trailer13.62 × 2.48 × 3.00 m22.5 t
MEGA_CURTAIN_13613.6m Mega Curtainsider13.62 × 2.48 × 3.00 m22 t
FLATBED_13613.6m Flatbed13.62 × 2.48 × 3.00 m25 t
REEFER_13613.6m Reefer13.32 × 2.44 × 2.54 m22 t
CURTAINSIDER_757.5m City Curtainsider7.50 × 2.48 × 2.70 m12 t
SWAPBODY_7157.15m Swap Body7.15 × 2.48 × 2.70 m12 t
SWAPBODY_7457.45m Swap Body7.45 × 2.48 × 2.70 m12 t
DRY_VAN_5353ft Dry Van16.15 × 2.59 × 2.74 m20 t
FLATBED_5353ft Flatbed16.15 × 2.59 × 3.66 m22 t
REEFER_5353ft Reefer15.85 × 2.46 × 2.59 m18.5 t
CURTAINSIDER_125_CN12.5m Curtainsider (CN)12.50 × 2.50 × 2.80 m27 t
FLATBED_40_IN40ft Flatbed (IN)12.19 × 2.50 × 3.50 m26 t
BOX_138_BR13.8m Box Van (BR)13.80 × 2.50 × 2.90 m28 t
CURTAINSIDER_12_ZA12m Curtainsider (ZA)12.00 × 2.48 × 2.70 m28 t

Conteneurs

CodeConteneurIntérieur (L×l×H)Charge utile
20DC20' Dry Container5.89 × 2.35 × 2.39 m21.727 t
20HC20' High Cube5.89 × 2.35 × 2.70 m21.58 t
40DC40' Dry Container12.03 × 2.35 × 2.39 m26.63 t
40HC40' High Cube12.03 × 2.35 × 2.70 m26.58 t
45HC45' High Cube13.56 × 2.35 × 2.70 m25.68 t
20OT20' Open Top5.89 × 2.35 × 2.39 m21.64 t
40OT40' Open Top12.03 × 2.35 × 2.39 m26.58 t
20RF20' Reefer5.44 × 2.29 × 2.27 m21 t
40RF40' Reefer11.58 × 2.29 × 2.21 m25.6 t
40RFHC40' Reefer High Cube11.58 × 2.29 × 2.54 m25.88 t
53DC53' Domestic16.15 × 2.59 × 2.90 m26 t

Niveaux d'accès et limites

Bac à sable (tous les plans)Production (Enterprise)
Appels25 réussis / mois5 000 succès / mois
Articles par manifeste205,000
Tâches simultanées110
Budget d'optimisation10 s30 s par défaut, jusqu'à 300 s
PersistanceAucune donnée stockéeChaque 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.

CodeHTTPSignification
invalid_key401Clé API manquante, mal formée ou révoquée.
invalid_json400Le corps n'est pas un JSON valide.
invalid_vehicle400Code de véhicule ou {customVehicleId} inconnu.
invalid_items400Un champ d'article est manquant, non numérique ou hors plage.
suspicious_dimensions400Toutes les dimensions inférieures à 100 mm — probablement des pouces envoyés en mm. Utilisez "units": "imperial".
invalid_units400units doit être "metric" ou "imperial".
invalid_objective400objective doit être "ldm" ou "pallets".
invalid_callback400callback_url doit être en https.
too_many_items400Le manifeste dépasse la limite d'articles pour votre niveau d'accès.
upgrade_required402Le mode production nécessite le plan Enterprise. Utilisez le bac à sable ou passez à un plan supérieur.
sandbox_limit_reached402Quota mensuel du bac à sable épuisé — réinitialisé le 1er du mois.
quota_exceeded402Quota mensuel de 5 000 chargements atteint — réinitialisation le 1er. Pour un volume supérieur : contactez-nous.
too_many_jobs429Limite de tâches simultanées atteinte. Interrogez les tâches en cours et réessayez.
not_found404Aucune 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