Cargamento
Se connecter

Documentation API

L'API REST de Cargamento permet à une autre application (ERP, logiciel d'entrepôt, TMS, application mobile) de calculer une palettisation, un plan de chargement et un ordre de préparation au quai, et d'enregistrer des plans consultables par un lien.

Le calcul de l'API est exactement celui de l'outil en ligne : mêmes règles, mêmes résultats, mêmes messages.

Authentification

Chaque application reçoit une clé, créée par un administrateur dans Administration › Clés d'API. Envoyez-la dans l'en-tête Authorization :

Authorization: Bearer cg_0123456789abcdef0123456789abcdef01234567

L'en-tête X-Api-Key: cg_… est accepté aussi. La clé ne doit jamais être exposée dans du code exécuté par un navigateur public : appelez l'API depuis votre serveur.

Limites

LimiteValeur
Appels par clé et par minute120 (au-delà : 429, en-tête Retry-After: 60)
Taille du corps de requête2 Mo
Lignes par plan500
Palettes unitaires par calcul2 000

Erreurs

Toute erreur renvoie un objet error :

{
  "error": {
    "code": "validation_error",
    "message": "Saisie invalide.",
    "details": [
      { "field": "carton.length", "message": "Doit être supérieur à 0." },
      { "field": "lines[2].palletCode", "message": "Type de palette inconnu « xyz »." }
    ]
  }
}
HTTPcodeCause
400invalid_jsonCorps absent ou JSON illisible.
401unauthorizedClé absente, invalide ou révoquée.
404not_foundPlan ou route inconnus.
405method_not_allowedMéthode non prévue sur cette route.
413too_largeCorps supérieur à 2 Mo.
422validation_errorSaisie invalide ; details liste chaque champ fautif.
429rate_limitedTrop d'appels.
500server_errorErreur interne (journalisée côté serveur).

Erreur de saisie et impossibilité métier sont distinctes : un colis plus grand que la palette n'est pas une saisie invalide. POST /palletize répond alors 200 avec result.ok = false et un result.error lisible.

Saisie des valeurs

Points d'accès

MéthodeRouteRôle
GET/api/v1Description de l'API (sans clé).
GET/pingVérifie la clé.
GET/referentialsPalettes, véhicules et seuils d'alerte en vigueur.
POST/palletizePalettisation d'une référence de colis.
POST/load-planPlan de chargement et plan de quai, sans enregistrement.
POST/plansCalcule et enregistre un plan, renvoie son lien.
GET/plansListe des plans créés par la clé.
GET/plans/{id}Lit un plan, résultat recalculé.
PUT/plans/{id}Remplace les saisies d'un plan.
DELETE/plans/{id}Supprime un plan.

GET /ping

curl -s https://cargamento.vozit.dev/api/v1/ping -H "Authorization: Bearer $CLE"
{ "ok": true, "key": "ERP Cyprès", "version": "v1", "time": "2026-10-19T14:02:11+02:00" }

GET /referentials

{
  "pallets": [
    { "code": "eur", "label": "EUR / EPAL 120 × 80", "shortLabel": "EUR 120×80", "length": 120, "width": 80, "deckHeight": 14.4, "tare": 25, "active": true }
  ],
  "vehicles": [
    { "code": "semi", "label": "Semi-remorque standard (13,60 m)", "length": 1360, "width": 248, "height": 270, "payload": 24000, "active": true }
  ],
  "thresholds": { "cogMin": 0.3, "cogMax": 0.65, "lateral": 0.05 }
}

Codes livrés : palettes eur, ind, demi, quart, us ; véhicules semi, mega, p19, p12, vl20, c20, c40, c40hc. L'administrateur peut en ajouter ou les modifier : lisez cette route plutôt que de figer les codes.

POST /palletize

Corps :

ChampTypeOblig.Règle
reftextenonRéférence du colis (120 car.). Défaut « Colis ».
carton.length, carton.width, carton.heightnombreoui> 0, cm. height = sens vertical.
carton.weightnombrenon≥ 0, kg. 0 = limite de poids ignorée.
carton.canTiltbooléennonColis couchable. Défaut false.
palletCodetextel’un des deuxCode du référentiel.
palletobjetl’un des deux{ length, width, deckHeight, tare }, surcharge ou remplace palletCode.
maxHeightnombreouiHauteur max palette comprise, cm.
maxWeightnombreouiPoids max palette comprise, kg.
overhangnombrenonDébord autorisé par côté, cm. Défaut 0.
orderQtyentiernonColis commandés. Défaut 0.
stopentiernonArrêt de livraison, ≥ 1. Défaut 1.
stackablebooléennonPalettes gerbables. Défaut false.

Il faut palletCode ou pallet (ou les deux).

curl -s https://cargamento.vozit.dev/api/v1/palletize \
  -H "Authorization: Bearer $CLE" -H "Content-Type: application/json" \
  -d '{"ref":"Carton 40x30x25","carton":{"length":40,"width":30,"height":25,"weight":8},
       "palletCode":"eur","maxHeight":180,"maxWeight":800,"orderQty":500,"stop":2,"stackable":true}'

Réponse (le tableau layout est abrégé) :

{
  "result": {
    "ok": true,
    "ref": "Carton 40x30x25",
    "orientation": "upright",
    "orientationLabel": "debout",
    "footprint": { "length": 40, "width": 30 },
    "cartonHeight": 25,
    "perLayer": 8,
    "layers": 6,
    "total": 48,
    "pattern": "columns",
    "layout": [ { "x": 0, "y": 0, "length": 30, "width": 40 }, "…" ],
    "usable": { "length": 120, "width": 80, "height": 165.6, "weight": 775 },
    "overhang": 0,
    "palletHeight": 164.4,
    "grossWeight": 409,
    "fillVolume": 0.905797,
    "fillArea": 1,
    "palletVolume": 1.57824,
    "cartonsVolume": 1.44,
    "limit": "height",
    "order": {
      "qty": 500, "fullPallets": 10, "pallets": 11,
      "partial": { "cartons": 20, "layers": 3, "height": 89.4, "weight": 185 }
    },
    "loadLines": [
      { "name": "Carton 40x30x25", "palletCode": "eur", "length": 120, "width": 80, "stackable": true, "stop": 2, "height": 164.4, "weight": 409, "qty": 10 },
      { "name": "Carton 40x30x25 (incomplète)", "palletCode": "eur", "length": 120, "width": 80, "stackable": true, "stop": 2, "height": 89.4, "weight": 185, "qty": 1 }
    ]
  },
  "input": { "…": "saisie normalisée" }
}
Champ du résultatSens
orientationupright (debout), side (couché sur le côté), end (couché sur le bout).
perLayer, layers, totalColis par couche, nombre de couches, colis par palette.
patterncolumns ou mixed (deux sens de colis : alterner les couches en miroir).
layoutRectangles de la couche vue de dessus, origine au coin de la surface utile (débord compris).
palletHeight, grossWeightHauteur (cm) et poids brut (kg) d'une palette complète.
fillVolume, fillAreaRemplissage, ratio de 0 à 1 (peut dépasser 1 avec un débord).
limitheight ou weight : ce qui arrête l'empilement.
ordernull sans quantité ; sinon palettes complètes et palette incomplète.
loadLinesLignes prêtes à envoyer telles quelles dans lines de /load-plan.

Cas d'impossibilité (200) :

{ "result": { "ok": false, "error": { "code": "tooBig", "message": "Le colis est plus grand que la palette." } } }

Codes possibles : dimensions, pallet, weight, maxHeight, maxWeight, tooBig, tooHigh, tooHeavy.

POST /load-plan

Corps :

ChampTypeOblig.Règle
vehicleCodetextel’un des deuxCode du référentiel.
vehicleobjetl’un des deux{ length, width, height, payload, label? }, cotes intérieures utiles. Surcharge vehicleCode.
gapnombrenonJeu entre palettes, cm. Défaut 0.
maxStackentiernonNiveaux de gerbage, 1 à 4. Défaut 2.
thresholdsobjetnon{ cogMin, cogMax, lateral } en ratios (0,30 = 30 %). Défaut : seuils de l'administration.
linestableauouiLignes de palettes ou commandes, voir ci-dessous. 500 max.

Il faut vehicleCode ou vehicle (ou les deux).

Ligne de palettes (palettes déjà constituées) :

ChampTypeOblig.Règle
idtextenonVotre identifiant (60 car.), repris dans les résultats. Défaut L1, L2…
nametextenonDésignation affichée.
palletCodetextenonComplète length et width s'ils sont absents.
length, widthnombresans palletCodeEmprise d'une palette chargée (débord compris), cm.
heightnombreouiHauteur d'une palette chargée, cm.
weightnombrenonPoids d'une palette chargée, kg.
qtyentiernonNombre de palettes. Défaut 1.
stackablebooléennonDéfaut false.
stopentiernonArrêt, 1 = premier livré. Défaut 1.

Ligne de commande : une ligne qui contient carton est une commande de colis. Elle accepte tous les champs de /palletize (plus id et name), est palettisée puis remplacée par ses palettes complètes (identifiant id) et sa palette incomplète (identifiant id-partial). Le résumé de chaque palettisation est renvoyé dans orders.

curl -s https://cargamento.vozit.dev/api/v1/load-plan \
  -H "Authorization: Bearer $CLE" -H "Content-Type: application/json" \
  -d '{
    "vehicleCode": "p19",
    "maxStack": 2,
    "lines": [
      { "id": "CMD-1", "name": "Commande Dupont", "stop": 2,
        "carton": { "length": 40, "width": 30, "height": 25, "weight": 8 },
        "palletCode": "eur", "maxHeight": 180, "maxWeight": 800, "orderQty": 500 },
      { "id": "P-2", "name": "Palettes prêtes", "palletCode": "demi",
        "height": 90, "weight": 150, "qty": 4, "stackable": true, "stop": 1 }
    ]
  }'

Réponse (tableaux abrégés au premier élément) :

{
  "plan": {
    "vehicle": { "code": "p19", "label": "Porteur 19 t (7,50 m)", "length": 750, "width": 248, "height": 260, "payload": 10000 },
    "gap": 0,
    "maxStack": 2,
    "thresholds": { "cogMin": 0.3, "cogMax": 0.65, "lateral": 0.05 },
    "lines": [ { "index": 0, "id": "CMD-1", "name": "Commande Dupont", "stop": 2 } ],
    "rows": [ { "row": 1, "x": 0, "depth": 120, "stop": 2, "steps": [1, 2, 3] } ],
    "placements": [
      {
        "step": 1, "row": 1, "x": 0, "y": 4, "alongL": 120, "acrossW": 80,
        "side": "Gauche", "crosswise": false,
        "stack": {
          "units": ["CMD-1"], "levels": 1, "length": 120, "width": 80,
          "height": 164.4, "weight": 409, "volume": 1578240, "stop": 2,
          "baseLineIndex": 0, "topLineIndex": 0, "mixed": false, "content": "Commande Dupont"
        }
      }
    ],
    "overflow": [],
    "indicators": {
      "palletsRequested": 15, "palletsLoaded": 15, "floorSpots": 13, "rows": 5,
      "loadingMeters": 500, "remainingLength": 250,
      "weight": 4875, "payload": 10000, "volume": 18.3686, "boxVolume": 48.36,
      "cog": { "x": 242.5, "y": 124, "xRatio": 0.323333, "yOffset": 0 },
      "gauges": {
        "weight": { "ratio": 0.4875, "level": "ok" },
        "loadingMeters": { "ratio": 0.666667, "level": "ok" },
        "floor": { "ratio": 0.619355, "level": "ok" },
        "volume": { "ratio": 0.379831, "level": "ok" }
      }
    },
    "alerts": [ { "level": "ok", "code": "ok", "message": "Tout est chargé, dans les limites de poids et de dimensions." } ],
    "dock": {
      "width": 248, "depth": 500, "area": 12.4,
      "rows": [ { "row": 1, "y": 0, "depth": 120 } ],
      "spots": [ { "step": 1, "row": 1, "x": 4, "y": 0, "width": 80, "depth": 120, "levels": 1, "lineIndex": 0 } ],
      "sequence": [ { "step": 1, "row": 1, "side": "Gauche", "content": "Commande Dupont", "crosswise": false, "height": 164.4, "weight": 409, "stop": 2, "lineIndex": 0 } ]
    }
  },
  "orders": [
    { "lineIndex": 0, "id": "CMD-1", "ref": "Colis", "perLayer": 8, "layers": 6, "perPallet": 48,
      "palletHeight": 164.4, "grossWeight": 409, "limit": "height",
      "order": { "qty": 500, "fullPallets": 10, "pallets": 11, "partial": { "cartons": 20, "layers": 3, "height": 89.4, "weight": 185 } } }
  ],
  "input": { "vehicle": { "…": "…" }, "lines": [ { "id": "CMD-1", "…": "…" }, { "id": "CMD-1-partial", "…": "…" }, { "id": "P-2", "…": "…" } ] }
}

Repère : origine à l'avant gauche du plancher (côté cabine, côté conducteur). x le long du camion vers les portes, y en travers vers la droite.

ChampSens
placements[].stepNuméro d'ordre de chargement (1 = premier chargé, au fond côté cabine).
placements[].x, y, alongL, acrossWEmprise au sol de la pile, cm.
placements[].sideGauche, Centre ou Droite (tiers de largeur ; Centre si seule sur sa rangée).
placements[].crosswisetrue si la palette est tournée en travers.
modeauto pour un plan calculé par l'API.
stack.unitsIdentifiants des lignes, de la palette du bas à celle du haut.
stack.unitKeysIdentifiant de chaque palette unitaire, « id de ligne # rang » (ex. CMD-1#3), du bas vers le haut.
placements[].issuesContrôles de disposition ; toujours vide pour un plan calculé (rempli dans le chargement manuel de l'outil).
stack.volumeVolume des palettes de la pile, en cm³.
stack.mixedLa pile mélange deux lignes.
overflow[]Palettes non chargées : lineId, lineName, unitKey, reason (height, width, space), reasonLabel.
indicators.loadingMetersFin de la dernière rangée, cm (mètres de plancher × 100).
indicators.cogCentre de gravité (cm), xRatio = position en part de la longueur, yOffset = écart au milieu (négatif = gauche).
indicators.gauges.*.levelok, warn (orange) ou bad (rouge), seuils de la fiche §6.1.
alerts[].levelblocking, warning ou ok. Codes : notLoaded, overweight, cogLength, cogLateral, ok.
dockPlan de quai : plancher pivoté, y = distance depuis la porte de quai. sequence = tableau d'ordre de chargement.

POST /plans

Même corps que /load-plan, avec deux champs facultatifs : name (nom affiché) et reference (votre numéro de tournée ou de commande, 190 car., filtrable). Réponse 201 :

{
  "id": "Hk3mQ7pXa2Lr9TbN",
  "name": "Tournée Lyon du 21/10",
  "reference": "TRN-2026-1021",
  "url": "https://cargamento.vozit.dev/p/Hk3mQ7pXa2Lr9TbN",
  "createdAt": "2026-10-19T14:05:00+02:00",
  "updatedAt": "2026-10-19T14:05:00+02:00",
  "input": { "…": "saisie normalisée enregistrée" },
  "plan": { "…": "même structure que /load-plan" },
  "orders": []
}

url ouvre l'outil sur l'écran Camion avec ce plan : à envoyer au chef de quai ou à afficher dans votre application. Il faut un compte Cargamento pour l'ouvrir : sans session, la personne passe par la page de connexion puis arrive sur le plan. L'identifiant est aléatoire (16 caractères) et la page n'est pas indexée.

Seules les saisies sont enregistrées ; le résultat est recalculé à chaque lecture. Le véhicule est enregistré avec ses cotes : modifier ensuite le référentiel ne change pas le plan.

GET /plans

Paramètres : limit (1 à 200, défaut 50), offset, reference (égalité exacte). Ne renvoie que les plans créés par la clé appelante.

{ "data": [ { "id": "Hk3mQ7pXa2Lr9TbN", "name": "Tournée Lyon du 21/10", "reference": "TRN-2026-1021", "url": "…", "createdAt": "…", "updatedAt": "…" } ], "limit": 50, "offset": 0 }

GET /plans/{id}, PUT /plans/{id}, DELETE /plans/{id}

Un plan créé par une autre clé répond 404.

Recette d'intégration type

  1. Au démarrage, GET /referentials pour proposer les véhicules et palettes à l'utilisateur.
  2. À la préparation d'une tournée, envoyez POST /plans avec une ligne de commande par référence (champ carton) ou une ligne de palettes par lot déjà constitué, stop = rang de livraison, reference = votre numéro de tournée.
  3. Si plan.alerts contient une alerte blocking, proposez un autre véhicule ou de scinder la tournée.
  4. Enregistrez id et url ; affichez le lien au chef de quai.
  5. Si la tournée change, PUT /plans/{id}.

Exemple en PHP

$ch = curl_init('https://cargamento.vozit.dev/api/v1/load-plan');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('CARGAMENTO_KEY'), 'Content-Type: application/json'],
    CURLOPT_POSTFIELDS => json_encode([
        'vehicleCode' => 'semi',
        'lines' => [['id' => 'A', 'palletCode' => 'eur', 'height' => 150, 'weight' => 500, 'qty' => 33]],
    ]),
]);
$res = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
echo $res['plan']['indicators']['loadingMeters'] / 100, " m de plancher\n";   // 13.2

Exemple en JavaScript (Node 18+, côté serveur)

const res = await fetch('https://cargamento.vozit.dev/api/v1/palletize', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.CARGAMENTO_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ carton: { length: 40, width: 30, height: 25, weight: 8 }, palletCode: 'eur', maxHeight: 180, maxWeight: 800 }),
});
const { result } = await res.json();
console.log(result.ok ? `${result.total} colis par palette` : result.error.message);

Versions

La version v1 est stable : on n'y retire ni ne renomme aucun champ. Les ajouts (nouveaux champs, nouvelles routes) sont annoncés dans le journal des versions. Une rupture ferait l'objet d'une v2 en parallèle.