Packing API
Geben Sie Fracht auf und erhalten Sie einen optimierten, physikalisch validierten Ladeplan — mit Positionen, Lademetern, Linearfuß und abgerechneten Palettenplätzen. Derselbe Algorithmus wie in der App, kontinuierlich gegen reale Disponenten-Layouts validiert. Sandbox-Zugang in jedem Tarif; Produktionszugang im Enterprise-Tarif.
Schnellstart
- Melden Sie sich an → klicken Sie auf Ihren Namen → API → generieren Sie einen Schlüssel.
- Senden Sie einen Pack-Job ab (dieser läuft in der Sandbox — kostenlos in jedem Tarif):
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-..."
}Fragen Sie die status_url ab (gleicher Auth-Header), bis status "done" ist:
{
"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/..."
}
}Endpunkte
POST /api/v1/pack
Validiert und stellt einen Pack-Job in die Warteschlange. Gibt 202 mit einer job_id zurück. Unterstützt einen Idempotency-Key-Header — Wiederholungen mit demselben Schlüssel geben den ursprünglichen Job zurück, statt doppelt zu packen.
GET /api/v1/jobs/{id}
Job-Status und, wenn abgeschlossen, das vollständige Ergebnis. Ergebnisse werden 30 Tage lang gespeichert; Produktionspläne bleiben dauerhaft in Ihrem Arbeitsbereich.
GET /api/v1/openapi.json
Der maschinenlesbare OpenAPI 3.1-Vertrag — importieren Sie ihn in Postman, Code-Generatoren oder nutzen Sie ihn mit Ihrem KI-Coding-Tool. Fahrzeugcodes werden aus dem Live-System übernommen, sodass er immer aktuell ist. Spec öffnen ↗
Antwortfelder
Das result-Objekt eines abgeschlossenen Jobs:
| Feld | Typ | Beschreibung |
|---|---|---|
| placed_count / unplaced_count | integer | Anzahl der platzierten / nicht platzierten Artikel. |
| units | "metric" | "imperial" | Die Einheiten, in denen alle nachfolgenden Zahlen angegeben sind (entspricht der Anfrage). |
| metrics.ldm_m | number | Lademeter — die Ausdehnung des Ladungsblocks (max. Ende − min. Anfang). |
| metrics.linear_ft | number | Dieselbe Ausdehnung in Linearfuß (US-LTL). |
| metrics.span_mm | integer | Die Ausdehnung in mm, immer metrisch. |
| metrics.pallet_positions_eur | integer | null | Abgerechnete EUR-Palettenpositionen. null, wenn das Layout zu groß für eine Messung ist. |
| metrics.total_weight_kg | number | Platziertes Gewicht, immer in kg. |
| metrics.overlaps | integer | Sich durchdringende Paare — immer 0 bei einem gültigen Layout. |
| items[].placed | boolean | Ob der Artikel im Layout platziert wurde. |
| items[].x / y / z | number | null | Position in den Anfrageeinheiten: x vom Türende, y von der linken Wand, z vom Boden. Alle drei sind null, wenn placed false ist. |
| items[].rotation_deg / tilt_deg | 0 | 90 | Ausrichtung: Drehung um die vertikale Achse; tilt = seitlich abgelegt. |
| items[].length / width / height / weight | number | Der Artikel wie in der Anfrage übermittelt, in den Anfrageeinheiten. |
| plan_url | string | null | Der erstellte Plan in der App. null für Sandbox-Jobs. |
Versionierung & Stabilität
Der Vertrag ist in der Pfadangabe (/api/v1/) versioniert. Innerhalb von v1 sind Änderungen nur additiv — neue optionale Anfragefelder, neue Antwortfelder, neue Fahrzeugcodes und Fehlercodes können hinzugefügt werden, und Clients sollten Felder ignorieren, die sie nicht kennen. Bestehende Felder ändern niemals ihre Bedeutung oder ihren Typ, und nichts wird entfernt. Eine Breaking Change führt zu einem neuen Versionspfad, wobei v1 mindestens 12 Monate parallel gepflegt und die Abschaltung per E-Mail an jeden Arbeitsbereich mit einem aktiven Schlüssel angekündigt wird.
Anfragefelder
| Feld | Typ | Beschreibung |
|---|---|---|
| vehicle | string | Ein Fahrzeugcode aus den untenstehenden Tabellen. Oder geben Sie {customVehicleId} an (ein Fahrzeug aus Ihrer Arbeitsbereichsflotte). |
| objective | "ldm" | "pallets" | Minimierung der Lademeter oder der abgerechneten Palettenpositionen. Standardmäßig "ldm". |
| units | "metric" | "imperial" | Wie Artikelabmessungen und -gewichte gelesen UND zurückgegeben werden: mm/kg (Standard) oder in/lb. Positionen in der Antwort folgen denselben Einheiten. |
| items | array, required | Das Frachtmanifest (Felder siehe unten). |
| sandbox | boolean | Testmodus: vollständiges Ergebnis, nichts wird gespeichert. Verfügbar in jedem Tarif. |
| name | string | Name des Plans (Produktionsmodus). |
| ldm_limit_m | number | Beschränkung der Ladung auf die ersten N Lademeter (LKW). |
| budget_ms | number | Optimierungszeitbudget (Produktion; Standard 30000, Maximum 300000). |
| callback_url | https URL | Wird per POST mit dem Ergebnis aufgerufen, wenn der Job abgeschlossen ist, signiert (siehe Webhooks). |
Artikelfelder
| Feld | Typ | Beschreibung |
|---|---|---|
| length / width / height | number, required | Artikelabmessungen, gelesen in den Einheiten der Anfrage (Standard mm, Zoll mit "units": "imperial"). |
| weight | number, required | Artikelgewicht (Standard kg, lb mit "units": "imperial"). |
| quantity | integer, default 1 | Wird in einzelne Einheiten aufgelöst. |
| name | string, optional | Wird im Plan und in der Antwort angezeigt. |
| stackable | boolean, default true | Andere Fracht darf auf diesem Artikel gestapelt werden. |
| max_stack | number, optional | Maximales Gewicht, das dieser Artikel tragen kann (gleiche Einheiten wie Gewicht). |
| fragile | boolean, default false | Zerbrechliche Artikel werden nur in den oberen Lagen platziert. |
| tiltable | boolean, default true | Darf der Artikel seitlich abgelegt werden. |
| destination_stop | integer, default 1 | Lieferstopp für Mehrstopp-LIFO-Ladung. |
Fahrzeugcodes
Generiert aus den Live-Engine-Spezifikationen. Benutzerdefinierte Fahrzeuge aus Ihrer Arbeitsbereichsflotte funktionieren über custom_vehicle_id.
LKW & Anhänger
| Code | Fahrzeug | Innenmaße (L×B×H) | Nutzlast |
|---|---|---|---|
| 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 |
Container
| Code | Container | Innenmaße (L×B×H) | Nutzlast |
|---|---|---|---|
| 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 |
Zugriffsebenen & Limits
| Sandbox (jeder Tarif) | Produktion (Enterprise) | |
|---|---|---|
| Aufrufe | 25 erfolgreiche / Monat | 5.000 erfolgreiche Packungen / Monat |
| Artikel pro Manifest | 20 | 5,000 |
| Gleichzeitige Jobs | 1 | 10 |
| Optimierungsbudget | 10 s | 30 s Standard, bis zu 300 s |
| Persistenz | Nichts gespeichert | Jeder Pack-Vorgang erstellt einen echten Plan in Ihrem Arbeitsbereich |
Nur erfolgreiche Packungen werden gezählt – fehlgeschlagene Validierungen und Jobs belasten das Kontingent nicht. Beide Zähler werden am 1. (UTC) zurückgesetzt. Sandbox-Ergebnisse sind vollständig und uneingeschränkt – die Limits sind die Grenze, nicht die Ausgabe. Brauchen Sie mehr als 5.000 Packungen pro Monat? Sprechen Sie mit uns über Volumen- und OEM-Preise.
Webhooks
Geben Sie callback_url an, um das Ergebnis per POST zu erhalten, wenn der Job abgeschlossen ist. Jeder Callback enthält eine X-Hansatic-Signature: ein HMAC-SHA256 der Rohdaten mit dem Webhook-Geheimnis Ihres Schlüssels (wird einmalig bei der Schlüsselerstellung angezeigt). Überprüfen Sie diese:
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));
}Die Zustellung erfolgt in einem Versuch mit 5s Timeout — die status_url bleibt immer die Quelle der Wahrheit.
Fehler
Alle Fehler haben die Form { "error": { "code", "message", ... } } — maschinenlesbarer Code, menschenlesbare Nachricht und field, wenn ein bestimmtes Eingabefeld fehlerhaft ist.
| Code | HTTP | Bedeutung |
|---|---|---|
| invalid_key | 401 | Fehlender, falsch formatierter oder widerrufener API-Schlüssel. |
| invalid_json | 400 | Der Body ist kein gültiges JSON. |
| invalid_vehicle | 400 | Unbekannter Fahrzeugcode / {customVehicleId}. |
| invalid_items | 400 | Ein Artikelfeld fehlt, ist nicht numerisch oder außerhalb des gültigen Bereichs. |
| suspicious_dimensions | 400 | Alle Abmessungen unter 100 mm — vermutlich Zoll als mm gesendet. Geben Sie "units": "imperial" an. |
| invalid_units | 400 | units muss "metric" oder "imperial" sein. |
| invalid_objective | 400 | objective muss "ldm" oder "pallets" sein. |
| invalid_callback | 400 | callback_url muss https sein. |
| too_many_items | 400 | Manifest überschreitet das Artikel-Limit für Ihre Zugriffsebene. |
| upgrade_required | 402 | Produktionsmodus erfordert den Enterprise-Tarif. Nutzen Sie die Sandbox oder upgraden Sie. |
| sandbox_limit_reached | 402 | Monatliches Sandbox-Kontingent aufgebraucht — wird am 1. zurückgesetzt. |
| quota_exceeded | 402 | Monatliches Produktionskontingent von 5.000 Packungen aufgebraucht – wird am 1. zurückgesetzt. Bei höherem Volumen: Kontaktieren Sie uns. |
| too_many_jobs | 429 | Limit für gleichzeitige Jobs erreicht. Fragen Sie laufende Jobs ab und versuchen Sie es erneut. |
| not_found | 404 | Kein solcher Job für diesen Arbeitsbereich vorhanden. |
Fragen, höhere Limits oder ein benötigter Connector (Zapier, Excel, MCP)? hello@hansatic.com