Cargo Marshal
Dashboard
Cargo Marshal
Dashboard
Cargo Marshal Docs
AchslastenErsten Load Plan erstellenNotfallzugang bei SSOSingle Sign-On mit Microsoft Entra IDSingle Sign-On mit Google WorkspaceSingle Sign-On mit OktaWebhooks
Anleitungen

Single Sign-On mit Okta

Verbinden Sie Okta per SAML mit Cargo Marshal, verifizieren Sie Ihre E-Mail-Domain und lassen Sie Ihr Team mit seinen Okta-Konten anmelden.

Einführung

Was Cargo Marshal ist, und für wen.

Anleitungen

Webhooks

Erhalten Sie einen signierten POST, wenn eine Berechnung endet, prüfen Sie ihn und gehen Sie mit Wiederholungen um, damit Ihr System nicht abfragen muss.

Ein Webhook ist ein signierter HTTPS-POST, den Cargo Marshal an Ihren Server sendet, wenn eine Berechnung in Ihrer Organisation endet. Nutzen Sie ihn, um den nächsten Schritt Ihres Ablaufs zu starten, sobald ein Load Plan fertig ist, ohne GET /calculates/{id} abzufragen.

Bevor Sie beginnen

Webhooks brauchen den Enterprise-Plan (dasselbe apiAccess wie die Persistenz-API) und die Rolle Owner oder Admin, die Rollen, die API-Schlüssel verwalten. Außerdem brauchen Sie einen öffentlichen HTTPS-Endpunkt, der den rohen Request-Body lesen kann.

Endpunkt einrichten

Öffnen Sie Einstellungen → Webhooks, um einen Endpunkt zu registrieren, die Ereignisse zu wählen, die er erhält, und sein Signaturgeheimnis zu kopieren. Das Geheimnis beginnt mit whsec_. Es wird einmal angezeigt, beim Erstellen des Endpunkts und beim Rotieren. Speichern Sie es dort, wo Ihr Server es lesen kann.

Auf derselben Seite können Inhaber und Admins einen Endpunkt deaktivieren und löschen, das Geheimnis rotieren, ein Testereignis senden, das Zustellprotokoll der letzten 30 Tage lesen und ein Ereignis erneut zustellen.

Ereignisse

Ein Endpunkt abonniert beliebige der drei Ereignistypen. Jede Berechnung in der Organisation endet in genau einem davon, egal ob die API oder ein Planner in der App sie gestartet hat.

EreignisWird ausgelöst, wenn
calculate.completedDie Berechnung fertig war und ihren Load Plan gespeichert hat.
calculate.failedDie Berechnung fehlgeschlagen ist oder die Zeit überschritten hat. failureCode sagt, was zutrifft.
calculate.cancelledDie Berechnung abgebrochen wurde, zum Beispiel von einem Planner in der App.

Eine Berechnung mit Zeitüberschreitung kommt als calculate.failed mit failureCode: "timed_out" an. Ein eigenes Timeout-Ereignis gibt es nicht.

Ein Endpunkt erhält nur Ereignisse für Berechnungen, die nach seiner Aktivierung endeten. Ein neuer Endpunkt bekommt keine Vorgeschichte.

Die meisten Ereignisse kommen innerhalb von Sekunden nach dem Ende der Berechnung an. Gelegentlich wartet ein Ereignis auf den nächsten stündlichen Durchlauf.

Die Anfrage

Cargo Marshal sendet einen POST mit JSON-Body. Der Body ist mit Absicht klein. Er nennt Berechnung, Load Plan und Project und enthält keine Plandaten. Lesen Sie den Plan über die API.

{
  "id": "evt_cm9x2k_completed",
  "type": "calculate.completed",
  "createdAt": "2026-10-11T09:00:00.000Z",
  "data": {
    "calculateId": "cm9x2k",
    "loadPlanId": "cm9w7p",
    "projectId": "cm9w1a",
    "revision": 7,
    "source": "api",
    "failureCode": null
  }
}
FeldBedeutung
idDie Ereignis-Id. Sie ist bei jeder Wiederholung des Ereignisses gleich.
typeEiner der drei Ereignistypen oben.
createdAtWann die Berechnung endete, als RFC-3339-Zeitstempel in UTC.
data.calculateIdDer Berechnungslauf. Übergeben Sie ihn an GET /calculates/{id}.
data.loadPlanIdDer Load Plan, zu dem der Lauf gehört. Übergeben Sie ihn an GET /load-plans/{id}.
data.projectIdDas Project, das den Load Plan enthält.
data.revisionBei calculate.completed die revision, die der Lauf gespeichert hat. Bei den anderen Ereignissen die revision, von der der Lauf ausging. Verwerfen Sie ein Ereignis, das älter ist als der Plan, den Sie halten.
data.sourceapi, wenn ein API-Schlüssel die Berechnung gestartet hat, app, wenn ein Planner es tat.
data.failureCodenull, außer das Ereignis ist calculate.failed. Dann ist es der Fehlercode des Laufs oder timed_out.

Das OpenAPI-Dokument unter /api/v1/spec.json listet dieselben Ereignisse unter webhooks, sodass Sie Typen für den Body erzeugen können.

Header

Anfragen folgen der Spezifikation Standard Webhooks.

HeaderWert
webhook-idDie Nachrichten-Id der Zustellung. Sie ist bei jeder Wiederholung gleich.
webhook-timestampUnixzeit in Sekunden.
webhook-signatureEine oder mehrere durch Leerzeichen getrennte Signaturen, je als v1,<base64>.

Signatur prüfen

Prüfen Sie bei jeder Anfrage die Signatur, bevor Sie ihr vertrauen. Wer Ihre URL findet, kann einen POST dorthin senden.

Mit einer Standard-Webhooks-Bibliothek übergeben Sie den rohen Body und die Header. Parsen Sie das JSON nicht vorher und serialisieren Sie es nicht neu, denn die Signatur deckt genau die gesendeten Bytes ab.

import { Webhook } from "standardwebhooks";

const wh = new Webhook(process.env.CARGO_MARSHAL_WEBHOOK_SECRET!); // whsec_…

export async function POST(request: Request) {
  const body = await request.text();
  let event;
  try {
    event = wh.verify(body, Object.fromEntries(request.headers)) as {
      id: string;
      type: string;
      data: { calculateId: string; loadPlanId: string; revision: number };
    };
  } catch {
    return new Response("invalid signature", { status: 400 });
  }

  // Ereignis festhalten, dann schnell antworten.
  return new Response(null, { status: 204 });
}

Die Bibliothek lehnt außerdem einen Zeitstempel ab, der weit von der aktuellen Zeit abweicht. Das verhindert die Wiedergabe einer alten Anfrage.

So prüfen Sie ohne Bibliothek:

  1. Bilden Sie den signierten Inhalt als <webhook-id>.<webhook-timestamp>.<body> mit dem rohen Body.
  2. Nehmen Sie den Teil des Geheimnisses nach whsec_ und dekodieren Sie ihn aus Base64. Diese Bytes sind der HMAC-Schlüssel.
  3. Berechnen Sie HMAC-SHA256 über den signierten Inhalt mit diesem Schlüssel und kodieren Sie das Ergebnis als Base64.
  4. Vergleichen Sie es zeitkonstant mit jeder v1,-Signatur in webhook-signature. Der Header kann mehrere enthalten, durch Leerzeichen getrennt. Akzeptieren Sie die Anfrage, wenn eine davon passt.

Geheimnis rotieren

Wenn Sie ein Geheimnis rotieren, signiert das alte weitere 24 Stunden. In dieser Zeit trägt jede Anfrage zwei v1,-Signaturen, eine je Geheimnis. Spielen Sie das neue Geheimnis innerhalb dieses Fensters auf Ihrem Server ein. Die Prüfung funktioniert die ganze Zeit, weil ein Treffer bei einer der beiden Signaturen genügt.

Zustellgarantien

Die Zustellung erfolgt mindestens einmal und ohne feste Reihenfolge. Bauen Sie den Empfänger für beides.

  • Entdoppeln Sie über webhook-id. Eine Wiederholung oder ein erneutes Senden nach einem Absturz trägt dieselbe webhook-id und denselben Body. Merken Sie sich die verarbeiteten Ids und überspringen Sie Wiederholungen.
  • Verwerfen Sie veraltete Ereignisse. Zwei Ereignisse zum selben Load Plan können in vertauschter Reihenfolge ankommen. Vergleichen Sie data.revision mit der revision, die Sie halten, und ignorieren Sie eine ältere.

Antworten

Antworten Sie innerhalb von 10 Sekunden mit einem beliebigen 2xx-Status. Erledigen Sie langsame Arbeit nach der Antwort, zum Beispiel indem Sie das Ereignis in Ihre eigene Warteschlange legen.

Als Fehler zählen:

  • Ein Status außerhalb von 2xx.
  • Eine Weiterleitung. Cargo Marshal folgt ihr nicht, registrieren Sie also die endgültige URL.
  • Keine Antwort innerhalb von 10 Sekunden.
  • Ein Verbindungs- oder TLS-Fehler.

Cargo Marshal liest von Ihrer Antwort nur die ersten 4 KiB, und das Zustellprotokoll speichert die ersten 1 KiB. Senden Sie einen kurzen Body oder keinen.

Wiederholungen

Eine fehlgeschlagene Zustellung wird nach diesem Plan wiederholt, mit etwa 10 % Jitter. Jede Wartezeit zählt ab dem Fehlschlag des vorherigen Versuchs.

VersuchWartezeit davor
21 Minute
35 Minuten
430 Minuten
52 Stunden
66 Stunden
712 Stunden
824 Stunden

Acht Versuche sind das Limit. Nach dem letzten bleibt die Zustellung im Zustellprotokoll als fehlgeschlagen stehen, und Sie können sie unter Einstellungen → Webhooks erneut zustellen.

Wenn ein Endpunkt dauerhaft fehlschlägt

Ein Endpunkt, der drei Tage lang fehlschlägt, wird deaktiviert, und Cargo Marshal sendet nichts mehr an ihn. Jede 2xx-Antwort setzt die Zählung zurück. Die Inhaber und Admins der Organisation erhalten dann eine E-Mail. Beheben Sie den Fehler im Empfänger und aktivieren Sie den Endpunkt unter Einstellungen → Webhooks wieder.

Eine Organisation, die den Enterprise-Plan verlässt, behält ihre Endpunkte, aber es wird nichts zugestellt, bis der API-Zugriff zurückkehrt.

Testen und erneut zustellen

Senden Sie vom Endpunkt aus ein Testereignis, um Ihren Empfänger zu prüfen, bevor eine echte Berechnung läuft. Der Test nutzt denselben Signatur- und Zustellweg wie ein echtes Ereignis, mit type gleich webhook.test, und prüft damit auch Ihre Signaturprüfung. Beantworten Sie ihn wie jedes andere Ereignis mit 2xx.

Um ein Ereignis erneut zu senden, zum Beispiel nach einer Fehlerbehebung, stellen Sie es im Zustellprotokoll erneut zu. Eine erneute Zustellung behält dieselbe webhook-id, Ihre Entdoppelung greift also weiter.

Das Zustellprotokoll zeigt je Zustellung den letzten Versuch und die Zahl der Versuche. Es listet nicht jeden Versuch.

Netzwerkregeln

Cargo Marshal prüft die Endpunkt-URL beim Speichern und bei jeder Anfrage erneut:

  • Die URL muss https nutzen und darf weder Benutzernamen noch Passwort enthalten.
  • Der Port muss 443 oder mindestens 1024 sein.
  • Der Host muss ein Name sein. Eine IP-Adresse, etwa https://203.0.113.7/, und localhost werden abgelehnt.
  • Der Name darf nicht zu einer privaten, Loopback-, Link-Local- oder Cloud-Metadaten-Adresse auflösen. Eine solche Adresse unter den Ergebnissen genügt, um die Anfrage zu verweigern.

Um Webhooks während der Entwicklung zu empfangen, machen Sie Ihren lokalen Server über einen Tunnel mit öffentlichem HTTPS-Hostnamen erreichbar.

Empfohlener Ablauf

Reihen Sie eine Berechnung über die API ein, warten Sie auf den Webhook und lesen Sie dann den Load Plan.

  1. POST /load-plans/{loadPlanId}/calculate mit einem Idempotency-Key. Merken Sie sich die Lauf-id aus der 202-Antwort und kehren Sie zurück.
  2. Prüfen Sie in Ihrem Empfänger die Signatur und entdoppeln Sie über webhook-id.
  3. Ordnen Sie data.calculateId dem Lauf zu, den Sie eingereiht haben. Ein Endpunkt hört auch von Berechnungen, die Planner in der App starten. Ignorieren Sie also die, die Sie nicht eingereiht haben, oder unterscheiden Sie sie über data.source.
  4. Rufen Sie bei calculate.completed GET /load-plans/{id} mit data.loadPlanId für die Placements auf.
  5. Lesen Sie bei calculate.failed den data.failureCode. Bei revision_conflict hat sich der Load Plan nach Ihrem Lesen geändert. Lesen Sie ihn erneut und reihen Sie eine neue Berechnung ein.

GET /calculates/{id} bleibt verfügbar, wenn Sie einen Lauf selbst prüfen wollen. Die Seite Persistenz-API behandelt jede Route.

Single Sign-On mit Okta

Verbinden Sie Okta per SAML mit Cargo Marshal, verifizieren Sie Ihre E-Mail-Domain und lassen Sie Ihr Team mit seinen Okta-Konten anmelden.

Einführung

Was Cargo Marshal ist, und für wen.

Auf dieser Seite

Endpunkt einrichtenEreignisseDie AnfrageHeaderSignatur prüfenGeheimnis rotierenZustellgarantienAntwortenWiederholungenWenn ein Endpunkt dauerhaft fehlschlägtTesten und erneut zustellenNetzwerkregelnEmpfohlener Ablauf