Logistivo CLI: Steuern Sie Ihren Logistikbetrieb aus dem Terminal und aus Ihrem ERP

Ein einziger Befehlskatalog speist drei Oberflächen: den Terminal-Client lg, den KI-Assistenten im Dashboard und den Kanal, über den sich externe KI-Assistenten verbinden. Eine Fähigkeit wird einmal geschrieben und erscheint in allen dreien – und für jede schreibende Aktion im Panel gibt es jetzt einen Befehl, sodass Ihre Skripte und Ihre eigenen KI-Agenten die Arbeit erledigen können, die Ihr Team am Bildschirm erledigt. Zahlungen, rechtliche Einwilligungen und die Kontoschließung bleiben bewusst im Browser. Diese Seite ist ein Vertrag. Die Befehlstabelle unten ist nicht von Hand geschrieben; sie wird bei jeder Anfrage aus dem Live-Befehlsregister erzeugt.

Was ist die Logistivo CLI?

Die Logistivo CLI (lg) ist ein Kommandozeilen-Client, der die Arbeit in Ihrem Logistivo-Konto – Ladungen und Touren, Transportanfragen, Angebote und Ausschreibungen, Flotte und Fahrer, Dokumente, Kontakte und Kontokorrente, Rechnungen und Buchhaltung, Lager und Bestand, Teammitglieder und Zolltarife – aus einem Terminal, einem Skript oder Ihrem eigenen KI-Agenten heraus erledigt. Die Befehle sind nicht fest in den Client eingebaut: Der Client lädt den Befehlskatalog vom Server und baut sich daraus selbst auf. Dieselben Befehle lassen sich ohne jede Installation über HTTP aufrufen – das ist der Weg, den ERP- und CRM-Integrationen nutzen – sowie über den MCP-Kanal, über den sich externe KI-Assistenten verbinden.

Was heute live ist und was noch kommt

Dieser Abschnitt steht absichtlich ganz oben auf der Seite. Der teuerste Fehler, den ein Integrationsdokument machen kann, ist, einen Endpoint, der noch nicht offen ist, so zu beschreiben, als wäre er es: Der Integrator schreibt den Code, der Endpoint liefert 404, und das Vertrauen kehrt nicht zurück.

Weil der Vertrag eingefroren ist, funktioniert der Integrationscode, den Sie heute schreiben, weiter, während die übrigen Teile öffnen. Felder, die Sie nicht kennen, müssen ignoriert werden; genau diese Regel erlaubt es uns, Neues hinzuzufügen, ohne etwas zu brechen.

Was den Unterschied macht: Der Client kennt keine Befehle – er baut sie aus dem Katalog auf

Das ist die eine Entscheidung, die dem Rest dieser Seite ihren Sinn gibt. In einer gewöhnlichen CLI leben die Befehle im Client; bei Logistivo leben sie auf dem Server.

In der klassischen Anordnung braucht der Client eine neue Version, sobald der Server eine Fähigkeit hinzugewinnt, und der Nutzer muss sie installieren. Dazwischen wissen beide Seiten Unterschiedliches – und das ist der leiseste Fehlermodus bei Integrationen: Das Skript läuft, der Exit-Code ist 0, und der Befehl beschreibt schlicht nicht, was der Server kann.

Bei Logistivo ist die Definition eines Befehls ein einziger Datensatz: sein Name, was er tut, seine JSON-Schema-Parameter, welche Rollen ihn sehen dürfen, ob er eine Bestätigung braucht und ob seine Wirkung umkehrbar ist. Beim Start lädt lg diesen Katalog und erzeugt daraus seinen Hilfetext, seine Flags und seine Eingabevalidierung. Sobald auf dem Server ein Befehl hinzukommt, kennt lg ihn – ohne Client-Update.

Derselbe Katalog speist den KI-Assistenten im Dashboard und den Kanal, über den sich externe KI-Assistenten verbinden. Eine Fähigkeit wird einmal geschrieben und erscheint an drei Stellen – und alle drei durchlaufen dieselbe Rollenschranke, dieselbe Bestätigungsschranke und dasselbe Audit-Protokoll. Es gibt kein separates Inventar an CLI-Befehlen; gäbe es eines, würden die drei Oberflächen binnen sechs Monaten unbemerkt auseinanderlaufen.

Die Namensregel – wie Befehlsnamen wachsen

Der Katalog wird wachsen. Neue Befehle werden nicht ad hoc benannt; es gelten sechs Regeln. Wir veröffentlichen sie hier, weil dies die Namen sind, die Ihre Integration jahrelang schreiben wird, und weil ihre Vorhersagbarkeit für Sie einen Wert hat.

Abdeckung: alles im Panel, per Befehl

Viele Teams binden Logistivo an ihren eigenen KI-Agenten oder ihr eigenes Automatisierungs-Harness an, und ein Agent ist nur nützlich, wenn er die Arbeit zu Ende bringen kann. Deshalb gilt eine einfache Regel: Was ein Büronutzer im Panel ändern kann, kann auch ein Befehl ändern – hinter derselben Rollenschranke, derselben Bestätigungsschranke und demselben Audit-Protokoll.

Das ist gemessen, nicht behauptet. Jeder schreibende Endpoint hinter dem Panel wird mit dem Befehlsregister abgeglichen, und ein Endpoint gilt nur dann als abgedeckt, wenn ein Befehl ihn als seinen HTTP-Spiegel deklariert oder denselben gemeinsamen Service aufruft. Von den schreibenden Endpoints im Geltungsbereich sind 100 % abgedeckt, und keiner ist unbelegt. Jeder ausgelassene Endpoint trägt eine schriftliche Begründung – Identitäts- und Zahlungsschritte, die Control Plane der Integration, die eigenen Endpoints der Fahrer-App, Client-Telemetrie, tote Kopien ohne Aufrufer –, und diejenigen, die für einen Integrator relevant sind, sind unten aufgeführt.

Befehle, die eine Datei entgegennehmen, fallen unter dieselbe Regel: 33 Befehle in der Tabelle nehmen ein Dokument oder ein Foto entgegen, und alle funktionieren aus lg, über HTTP und über MCP (siehe Befehle, die Dateien entgegennehmen).

Auch Verwaltungsarbeit ist abgedeckt, hinter einem stärkeren Schlüssel: 62 Befehle in der Tabelle – Teammitglieder und Rollen, Unternehmenseinstellungen, Bankkonten, gespeicherte Karten, Integrationsschlüssel – brauchen einen Integrationsschlüssel der Stufe manager. Ein Schlüssel der Stufe employee, der Standard, erledigt das Tagesgeschäft und wird bei diesen Befehlen mit INSUFFICIENT_KEY_LEVEL abgelehnt.

Affiliate-Konten (Empfehlungspartner) haben einen eigenen Katalog: 55 Befehle in 20 Domänen – Empfehlungen, Leads, CRM-Notizen und Aufgaben, Vertriebspräsentationen, Erträge und Auszahlungen sowie die Konto- und Teambefehle, die jede Rolle hat. Die reinen Affiliate-Befehle stehen nicht in der öffentlichen Tabelle unten, die die drei Panel-Rollen auflistet; ein Affiliate-Konto sieht sie in GET /api/common/commands und in lg commands. Ein Unternehmen, das sowohl Affiliate als auch Verlader, Frachtführer oder Zolldienstleister ist, sieht beide Sätze.

Ein Agent sollte mit lg users get beginnen (API-Name get_current_user): Der Befehl liefert die Rolle, die Zugriffsstufe des verwendeten Schlüssels, die effektiven Berechtigungen und ob gerade eine ausstehende rechtliche Bestätigung oder eine nicht verifizierte Telefonnummer Schreibvorgänge blockiert – so kann der Agent eine Ablehnung vorhersagen, statt sie zu sammeln.

Die Abdeckung wird durch einen Bericht gemessen, der das Befehlsregister mit den schreibenden Endpoints des Panels vergleicht; ein Panel-Endpoint, der ohne Befehl hinzukommt, erscheint darin als unbelegt. Wenn Sie eine Panel-Aktion finden, die Sie nicht per Befehl erreichen, melden Sie sie dem Support.

Installation und Identität

lg ist ein Node-Skript aus einer einzigen Datei ohne Abhängigkeiten (Node 18+), auf npm als @lgstv/lg veröffentlicht. Das Paket installiert zwei identische Befehle, lg und logistivo – verwenden Sie logistivo, falls lg auf Ihrem Rechner bereits ein Alias ist. Weil es nur eine Datei ist, funktioniert es auch, lg.mjs auf den ERP-Server eines Kunden zu kopieren. Für eine ERP/CRM-Integration brauchen Sie den Client nicht – dieselben Befehle sind über HTTP verfügbar.

1. Installieren — Node 18 oder neuer ist alles, was Sie brauchen; weitere Abhängigkeiten gibt es nicht. Installieren Sie das Paket global oder führen Sie es ohne Installation über npx aus.

2. Mit einem Freigabecode anmelden — lg login zeigt einen kurzen Freigabecode an und öffnet logistivo.com/connect in Ihrem Browser; über SSH, in CI oder mit --no-browser gibt der Befehl nur die Adresse aus, die Sie auf jedem Gerät öffnen können, auch auf Ihrem Smartphone. Melden Sie sich dort wie gewohnt an, prüfen Sie, dass der Code auf der Seite derselbe ist wie in Ihrem Terminal, wählen Sie Nur-Lese-Zugriff oder Lese- und Handlungszugriff und geben Sie die Anmeldung frei. Nichts Geheimes wird im Terminal eingegeben: kein Passwort, kein Schlüssel, kein Einmalcode. Die Sitzung bleibt danach auf diesem Computer angemeldet – das Schließen des Terminals meldet Sie nicht ab, und lg erneuert das Zugriffstoken selbstständig, bevor es abläuft. lg logout beendet die Sitzung auch auf dem Server, und Sie können sie im Panel unter Settings › Integration keys › AI and CLI connections entfernen. lg login --password (E-Mail und Passwort) bleibt als Ausweichweg erhalten. Erfordert lg 0.11 oder neuer.

3. Für Server und CI: ein Schlüssel, kein Freigabecode — Auf einem Server oder in einem CI-Job ist niemand da, der einen Code im Browser freigeben könnte. Stellen Sie im Panel (Settings › Integration keys) oder mit lg keys create einen Schlüssel aus – optional im Namen eines eigens dafür angelegten Teammitglieds mit eng gefassten Rechten – und legen Sie ihn in LOGISTIVO_TOKEN ab oder speichern Sie ihn mit lg login --api-key. Solange die Variable gesetzt ist, wird die Konfigurationsdatei nie gelesen. Details: Abschnitt „Verbindung mit einem API-Schlüssel“.

4. Prüfen und den Katalog erkunden — lg commands listet jeden Befehl, den Ihre Rolle sehen kann, nach Domänen gruppiert; lg help gibt die Verben dieser Domäne aus, und lg help die vollständige Parameterliste eines Befehls mit Typen und der Angabe, welche Parameter Pflicht sind. Nichts von diesem Text steckt im Client – alles kommt aus dem Katalog. Denken Sie daran, dass die Liste von Ihrer Rolle abhängt: Die Verben einer Domäne sind für einen Frachtführer und einen Verlader nicht dieselben (loads set-status zum Beispiel gibt es nur im Katalog des Frachtführers). Fehlt ein Verb, prüfen Sie Ihre Rolle, bevor Sie Ihre Installation prüfen.

Legen Sie den Schlüssel nie in die Versionskontrolle, ein CI-Log oder ein Chatfenster. Haben Sie sich mit einem Freigabecode oder einem Integrationsschlüssel angemeldet, beendet lg logout die Anmeldung auch auf dem Server; in einer Passwortsitzung (lg login --password) löscht der Befehl nur die LOKALE Kopie und sagt das ausdrücklich. Das Audit-Protokoll speichert Ihre Argumente, maskiert aber jedes Feld, dessen Name token, password, IBAN, card oder OTP enthält, als ***, ebenso jeden Parameter, den ein Befehl als geheim deklariert.

Ergonomie des Clients

Diese Funktionen erleichtern die tägliche Arbeit mit dem Client `lg`. Keine von ihnen ändert den HTTP-Vertrag – überspringen Sie diesen Abschnitt, wenn Sie direkt über HTTP integrieren.

Verbindung mit einem API-Schlüssel

Ein ERP, CRM, Skript oder KI-Agent verbindet sich mit Logistivo über einen Integrationsschlüssel, der im Panel oder mit lg keys create ausgestellt wird. Der Schlüssel handelt im Namen eines echten Teammitglieds: Die Rolle dieser Person entscheidet über die Befehlsliste, die Zugriffsstufe des Schlüssels darüber, wie schwer die Arbeit sein darf, und wenn diese Person das Team verlässt, stirbt der Schlüssel mit ihr. Es gibt kein synthetisches „Servicekonto“, und es wird keines geben – die Mandantentrennung ist an den Benutzer gebunden, und eine Brücke ohne Eigentümer ist genau die Art, die jahrelang still weiterschreibt.

1. Einen Schlüssel im Panel ausstellen — Settings › Integration keys › New key. Geben Sie ihm einen Namen („ERP nightly sync“), wählen Sie Scope und Zugriffsstufe, und verkürzen Sie optional die Laufzeit. Der Schlüssel wird einmal angezeigt, nur auf diesem Bildschirm; der Server speichert ihn nicht. Verlieren Sie ihn, stellt „Rotate“ einen neuen aus – der alte wird sofort ungültig.

2. Aus dem Terminal verbinden — lg login --api-key prüft den Schlüssel, gibt Identität und Scope aus und lädt den Befehlskatalog herunter. lg whoami fragt den Server, als welcher Benutzer, in welchem Unternehmen, mit welcher Rolle und welchem Scope Sie laufen. lg logout widerruft den Schlüssel auch auf dem Server, so wie es dort auch eine mit Freigabecode geöffnete Sitzung beendet (nur das lg logout einer Passwortsitzung kann das nicht).

3. Oder reines HTTP — Der Schlüssel ist ein Zugriffstoken, das im Header Authorization: Bearer mitgesendet wird, und funktioniert direkt mit den drei Befehls-Endpoints. Auf einem Server oder in CI legen Sie ihn in LOGISTIVO_TOKEN ab; solange diese Variable gesetzt ist, wird die Konfigurationsdatei nie gelesen.

4. Oder Schlüssel aus dem Terminal verwalten: lg keys — lg keys ist der Terminal-Zwilling des Panel-Bildschirms: dieselben Endpoints, dieselbe Schranke (die Berechtigung zur Teamverwaltung und, wenn Sie mit einem Schlüssel aufrufen, ein Schlüssel der Stufe manager). create, rotate und transfer geben einmalig ein neues Token zurück: Mit --save-profile=NAME geht es direkt in ein lokales Profil und wird nie ausgegeben; andernfalls gibt der Client es einmal aus. Im Modus --json ist das Token nur enthalten, wenn Sie --show-secret hinzufügen, und ein Aufruf mit --json ohne eines der beiden Flags wird abgelehnt, bevor die Anfrage gesendet wird, sodass das Token nicht stillschweigend in der Ausgabe eines Skripts landen kann. rotate, transfer und revoke fragen nach einer Bestätigung (--yes); ein Schlüssel wird über seinen exakten Namen oder seine uuid angesprochen, nie über eine Vermutung. Die Zugriffsstufe geht unverändert auf einen rotierten oder übertragenen Schlüssel über.

Der Scope wird auf der gesamten API-Oberfläche durchgesetzt, nicht nur im Befehlskatalog: Ein rein lesender Schlüssel passiert Leseanfragen (GET/HEAD/OPTIONS), und jede andere schreibende Anfrage wird mit 403 forbidden beantwortet. Es gibt zwei bewusste Ausnahmen, und beide sind durch eine feinere Schranke geschützt: der Ausführungs-Endpoint für Befehle (die Entscheidung kommt aus dem eigenen read_only-Flag des Befehls, weil auch nicht schreibende Befehle mit POST aufgerufen werden) und ein Schlüssel, der sich selbst widerruft (lg logout, auch mit einem rein lesenden Schlüssel erlaubt). Scope, Domänenfreigaben und Zugriffsstufe sind drei unabhängige Achsen, und alle müssen passen: Ein read-Schlüssel kann selbst auf Stufe manager nicht schreiben, ein write-Schlüssel der Stufe employee kann das Team nicht verwalten, und eine Domänenfreigabe loads:write öffnet keine Verwaltungsarbeit. Stellen Sie den Schlüssel im Namen des Mitglieds mit den geringsten Rechten aus, das Ihre Anforderungen abdeckt: Die Rolle entscheidet, welche Datensätze Sie überhaupt sehen können.

Die ersten zehn Minuten: vom Panel zu Ihrem ersten Aufruf

Die teuersten Minuten einer Integration sind die ersten zehn. Die vier Schritte unten legen diesen Weg der Reihe nach dar; dieselbe Reihenfolge gibt es im Panel als Einrichtungsassistenten (Settings › Integration › Setup wizard), und der Einrichtungsassistent ruft genau die hier beschriebenen Endpoints auf. Ein Entwickler ohne Zugang zum Einrichtungsassistenten gelangt an dieselbe Stelle, indem er diesen Abschnitt liest.

Keiner der vier Schritte endet mit „ich habe es gesendet“. Ob der Schlüssel wirklich funktioniert, beantwortet das Anfrageprotokoll; ob der Webhook wirklich ankommt, beantwortet das Zustellprotokoll. Beide sind im Panel und in lg einsehbar. Eine Einrichtung, die nie gemessen wird, ist eine Einrichtung, von der man nur glaubt, dass sie funktioniert.

Sandbox: Schreiben üben, ohne die Produktion anzutasten

Ein Preflight (dry_run) sagt Ihnen, was ein Befehl tun WÜRDE; aber ob die Idempotenz wirklich ein Replay liefert, ob der Webhook wirklich zugestellt wird, wie die Fehlerzweige aussehen und woher der Credit kommt, sieht man nur bei einem ECHTEN Schreibvorgang. Dafür gibt es die Sandbox: einen separaten Mandanten, der Ihr Unternehmen spiegelt – derselbe Code, derselbe Befehlskatalog – und Ihr Live-Journal nie berührt.

Die Sandbox ist kein Request-Flag, sie ist ein MANDANT. Ein separates Logistivo-Unternehmen, eröffnet für Ihres: mit eigenem Benutzer, eigenem Paket, eigenem Credit-Konto, eigenen Webhook-Abonnements. Die Umgebung wird aus dem Unternehmen des Schlüssels ABGELEITET und kann vom Aufrufer nicht mitgegeben werden; selbst wenn Sie das falsche Profil wählen, bricht die Anfrage serverseitig mit 403 ENVIRONMENT_MISMATCH ab. „Ich dachte, ich schreibe in die Sandbox, und habe in die Produktion geschrieben“ ist strukturell unmöglich.

Die Oberfläche ist identisch – gemessen: Der Katalog, den ein Sandbox-Schlüssel erhält, ist derselbe, den Ihr Live-Schlüssel erhält (bei einem Verlader-Schlüssel 448 Befehle auf beiden Seiten). Eine Brücke, die in der Sandbox funktioniert, funktioniert in der Produktion; nichts, was Sie lernen, müssen Sie zweimal lernen.

Die Sandbox öffnet sich nicht leer: Sie enthält generierte (NICHT kopierte) Startdaten – Kontakte, Ladungen in beiden Richtungen und Transportanfragen. Die Namen sind offenkundig erfunden, und die E-Mail-Adressen liegen in der Domain @sandbox.invalid; keine Zeile eines echten Mandanten wird je gelesen, geklont oder als Vorlage verwendet.

Einen Reset-Knopf gibt es bewusst NICHT, und kein Datensatz, keine Datei und kein Dokument wird je gelöscht. Wenn Sie neu anfangen möchten, eröffnet die Erneuerung eine neue Generation, und die alte bleibt ausgemustert bestehen. Ein paar hundert angesammelte Probezeilen kosten nichts im Vergleich zu einer Löschroutine, die an den falschen Mandanten angeschlossen ist.

Das Befehlsmodell

Es gibt eine einzige Grammatik, und sie kennt keine Ausnahmen. Um zu wissen, wie ein Befehl aufgerufen wird, brauchen Sie nur das, was der Katalog über ihn sagt.

lg loads list --status=in_transport --limit=20 --json

Flag-Namen sind die Parameternamen des Schemas, und lg akzeptiert beide Schreibweisen: --load_code und --load-code erreichen dasselbe Feld (lg help zeigt die kebab-case-Form). An einem Flag sehen Sie also, in welchem JSON-Feld es landet, und der Wechsel von der CLI zu HTTP erfordert keine Namensübersetzung. Ein boolescher Parameter ohne Wert ist true (--only_open); das Gegenteil ist --no-only_open. Schreiben Sie ein Argument, dessen Wert mit einem Bindestrich beginnt, als --flag=value, sonst schluckt ein vergessenes Flag stillschweigend das nächste als seinen Wert. Bei einem Befehl, der eine Datei entgegennimmt, setzen Sie @ vor einen lokalen Pfad (--attachment_uuid=@./cmr.pdf) – siehe Befehle, die Dateien entgegennehmen.

Echte Beispiele

Jeder Befehl unten existiert heute im Katalog; kein Name und kein Parameter hier ist erfunden. Die Ausgaben sind gekürzt. Sie zeigen die Ebene `data.result`; über HTTP kommt dasselbe Objekt im Umschlag (Envelope) `{success, data:{…}}` an (siehe die HTTP-Befehls-API).

Ohne Client: die HTTP-Befehls-API

lg ist eine Annehmlichkeit, nicht die Tür. Katalog und Ausführung laufen beide über drei HTTP-Endpoints (für Dateien gibt es einen vierten, den Upload-Endpoint – siehe Befehle, die Dateien entgegennehmen); eine ERP- oder CRM-Integration verbindet sich direkt hier. Die Identität ist ein persönliches Zugriffstoken im Header Authorization: Bearer. JEDE Antwort kommt im selben Umschlag zurück: `{ "success": bool, "data": { … }, "code": "…", "message": { … } }`. Der gesamte Vertrag steckt in `data` – `contract_version`, `command`, `status` und, als Ausgabe des Befehls, `result`; bei einem Fehler `error_code`, `error_key` und `error`. Die Antwortbeispiele auf dieser Seite zeigen aus Platzgründen das Objekt `data` statt des Umschlags; vergessen Sie nicht, `data` in Ihrem eigenen Code auszupacken. Auch `lg --json` gibt den GESAMTEN Umschlag aus, weshalb jq-Pfade mit `.data.result…` beginnen. Die einzige Ausnahme ist `/api/public/status`: Dieser Endpoint kommt ohne Umschlag aus, damit Watchdogs alarmieren können, ohne ihn zu parsen.

Eine Bestätigung mit 200 + ok:false anzufordern ist verboten und wird nie vorkommen. Der Grund ist einfach: Clients werten 200 als Erfolg, also würde eine als 200 zurückgegebene Bestätigungsanfrage den Ablauf stillschweigend beenden, und niemand würde es bemerken. Eine Bestätigung ist immer 409.

Befehle, die Dateien entgegennehmen

33 Befehle in der Tabelle nehmen ein Dokument oder ein Foto entgegen: einen CMR an eine Ladung anhängen, HS-Codes aus einer Rechnung lesen, ein Fahrzeug aus seinem Zulassungsdokument anlegen, eine Palette anhand von Fotos schätzen, ein Unternehmensdokument hochladen, eine Rechnung mit einer Packliste abgleichen. Im Browser ziehen Sie die Datei in ein Formular; überall sonst braucht es zwei Schritte – die Datei hochladen und dann ihre ID an den Befehl übergeben.

Ein Parameter nimmt eine Datei entgegen, wenn sein Name auf attachment_uuid oder attachment_uuids endet – photos[].attachment_uuid innerhalb einer Liste, front_attachment_uuid bei einem Scheck. Merken müssen Sie sich das nicht: Jede Katalogzeile trägt file_params mit den exakten Pfaden, das Schema kennzeichnet diese Felder (format: uuid, x-logistivo-file: true), und die Katalogantwort trägt einen Block attachments mit der Upload-Adresse, den akzeptierten Typen, den Größenlimits und der Lebensdauer. lg help zeigt sie ebenfalls an.

Hochladen ist ein Schreibvorgang, daher kann ein Schlüssel mit Scope read nicht hochladen; jeder Schlüssel mit Scope write kann es, auf beiden Zugriffsstufen. Uploads gehören dem Benutzer, nicht dem Schlüssel: Zwei Schlüssel desselben Benutzers sehen dieselben Uploads, und niemand kann die eines anderen verwenden. Eine ID, die unbekannt, abgelaufen oder nicht Ihre ist, wird schlicht nicht gefunden, und der Grund wird nicht offengelegt.

Anhänge im Dashboard-Chat und Befehls-Uploads sind getrennte Räume: Eine im Assistenten-Chat angehängte Datei lässt sich nicht per ID an einen Befehl übergeben, und ein Befehls-Upload erscheint nie in einem Chatverlauf.

Ein Upload ist kein Befehl und hat für sich genommen keine geschäftliche Wirkung: Mit Ihren Daten passiert nichts, bis ein Befehl die ID verwendet. Deshalb fragt das Hochladen auch nie nach einer Bestätigung – die Schranke trägt der Befehl, der die Datei verbraucht.

Die Bestätigungsschranke und irreversible Befehle

Jeder Befehl im Katalog trägt zwei Flags: confirmation_needed (erfordert er vor der Ausführung eine ausdrückliche Freigabe) und irreversible (lässt sich seine Wirkung rückgängig machen). Das sind getrennte Fragen, und beide werden auf dem Server durchgesetzt, statt sie den guten Manieren des Clients zu überlassen.

Bei der Schranke geht es um Verantwortung, nicht um Geschäftsregeln. Ein Angebot abzugeben ist der erste Schritt zu einem Frachtvertrag; eine Rechnung auszustellen erzeugt eine Buchung; eine Lagerbewegung ändert ein Bestandsbuch. Nichts davon sollte man jemals versehentlich zweimal ausführen können.

Die Schranke wird am TATSÄCHLICH ausgeführten Befehl abgelesen. Wenn ein Befehl einen anderen umschließt – wie es der generische Executor im Chat tut –, kommt die Schranke aus den Flags des Befehls, der wirklich läuft, nicht aus der Hülle. Mit einem festen Flag wäre einer von zwei Fehlern unvermeidlich: Entweder liefe der Rechnungsbefehl unbestätigt, oder jede Länderabfrage würde eine Bestätigungskarte auslösen.

read_only und nicht bestätigungspflichtig sind nicht dasselbe. Es gibt drei unterschiedliche Zustände: invoices preview ist nicht bestätigungspflichtig und schreibt nichts; loads create ist bestätigungspflichtig und schreibt; invoices create ist nicht bestätigungspflichtig, schreibt aber – ein Entwurf entsteht. Ein einziger Test klärt es: Hat sich, wenn der Befehl zurückkehrt, eine Zeile in der Datenbank geändert?

Manche Aktionen liegen nicht hinter dieser Schranke, weil sie gar keine Befehle sind: Ein Paket kaufen, eine Karte mit 3D Secure hinzufügen, Rechtstexte bestätigen und ein Konto schließen geschieht im eigenen Browser der Person. Dafür liefert lg handoff-links generate einen logistivo.com-Link – kostenlos, ohne Schreibvorgang, ohne Geheimnis –, und die Person schließt den Schritt dort ab. Kein Flag, wie auch immer es gesetzt ist, macht aus einer Zahlung oder einer Einwilligung einen API-Aufruf.

Technisch kann eine Integration die Schranke bei jedem Aufruf automatisch passieren, ratsam ist es aber nicht. Das richtige Muster: die Schranke nur für Befehle automatisieren, von denen Sie wissen, dass sie umkehrbar sind, und irreversible in die Warteschlange eines Menschen leiten.

Preflight: sehen, was ein schreibender Befehl tun würde, ohne ihn auszuführen

Der teuerste Schritt beim Anbinden einer ERP-Brücke ist es, herauszufinden, ob Ihr Body stimmt, indem Sie den Befehl TATSÄCHLICH ausführen: ein Rechnungsentwurf im Live-Buchungsjournal, ein verbrannter Credit, manuelles Aufräumen. dry_run beendet das – Sie fügen der Anfrage ein Feld hinzu, der Befehl läuft nicht, und Sie lesen, was passieren würde.

Das Register enthält heute 519 Befehle; 233 davon lesen nur und schreiben nichts. Von den übrigen 286 schreibenden Befehlen sind 251 bestätigungspflichtig: Ohne confirm aufgerufen, antworten sie bereits mit 409 und beschreiben, was sie tun würden. Die Lücke waren die anderen 35 – Rechnungsentwürfe, Exportdokument-Entwürfe, Positionen: genau die Kette, die eine Brücke als Erstes anbindet. Bei ihnen hieß Aufrufen bereits Ausführen.

Selbst bei bestätigungspflichtigen Befehlen gab es eine zweite Lücke: Die Schemavalidierung läuft NACH der Schranke. Ein Aufruf mit fehlendem Argument bekam also zuerst die Bestätigungskarte, bestätigte sie und erfuhr erst dann „dieses Feld fehlt“. Ein Preflight beantwortet beides in einem einzigen Roundtrip.

Und der häufigste – und am spätesten entdeckte – Brückenfehler ist dieser: Sie senden ein Feld, das nicht im Schema steht (customer_id, wo recipient_contact_id erwartet wird), der Server verwirft es stillschweigend, der Befehl liefert 200, und der Datensatz landet an der falschen Stelle. Die Preflight-Antwort listet solche Felder namentlich unter arguments.ignored auf; eine Fehlermeldung wird das nie tun.

Ein Preflight schreibt nichts, und das ist strukturell bedingt, bevor es eine Messung ist: Der Preflight-Pfad erreicht nie den Handler des Befehls, also gilt das für jeden Befehl, auch für die später hinzugekommenen. Bei der Einführung wurde es zudem gemessen: Jede SQL-Anweisung, die beim Preflight aller damals im Katalog vorhandenen schreibenden Befehle abgesetzt wurde (zwei Argumentsätze, zwei Rollen, 178 Preflights), wurde mitgeschnitten. Kein einziges INSERT/UPDATE/DELETE, kein einziger Job in der Warteschlange, keine einzige E-Mail; die Zeilenzahlen in 31 Tabellen und das Credit-Guthaben blieben unverändert.

Der Exit-Code ist derselbe, den der echte Aufruf liefern würde – würde laufen 0, Bestätigungsschranke 4, Idempotenzkonflikt 2, alles andere 1 –, sodass „lg … --dry-run || exit“ in einem CI-Schritt eine sinnvolle Schranke ist.

Idempotenz: warum Wiederholen keine Duplikate erzeugt

Ein ERP liest Ihre Fehlermeldung nicht, sieht Ihren Bildschirm nicht und schickt dieselbe Anfrage nach einem Timeout erneut. Dieser Abschnitt existiert genau für dieses Verhalten.

Ein Aufruf mit einem idempotency_key läuft höchstens einmal. Der zweite Aufruf führt den Befehl nicht erneut aus; er liefert das gespeicherte Ergebnis mit dem Status replayed. Wenn das Netzwerk abbricht, eine Anfrage in ein Timeout läuft oder eine Warteschlange denselben Job zweimal aufnimmt, erhalten Sie das erste Ergebnis statt einer doppelten Rechnung.

Verwenden Sie bei jedem schreibenden Befehl einen Idempotency Key (Idempotenzschlüssel). Das Feld read_only im Katalog liefert Ihnen diese Unterscheidung gratis: Jeder Befehl mit read_only: false verdient einen Idempotency Key.

Ihre Wiederholungsstrategie sollte exponentielles Backoff verwenden und bei jedem Versuch DENSELBEN Key mitsenden. Pro Versuch einen frischen Key zu erzeugen ist dasselbe, als würden Sie gar keine Idempotenz verwenden. Senden Sie denselben Key nicht parallel: Höchstens-einmal ist für aufeinanderfolgende Wiederholungen garantiert, nicht für zwei Anfragen, die gegeneinander um die Wette laufen.

Inkrementeller Sync und Paginierung

Eine ERP/CRM-Brücke sollte abholen, was sich geändert hat, nicht jede Stunde alles. Jeder Listenbefehl – loads list, demands list-own, contacts list, invoices list, teammates list, load-groups list, reservations list, drivers list, tenders list, products search, warehouses search – akzeptiert zwei gemeinsame Parameter und liefert ein gemeinsames Feld.

Warum es kein offset gibt: Auf einer lebenden Tabelle verschiebt sich ein Offset. Eine Zeile, die zwischen zwei Seiten eingefügt wird, lässt Sie einen Datensatz überspringen oder doppelt zählen – stillschweigend. Der Cursor bewegt sich nur vorwärts, sodass ein während des Durchlaufs eingefügter oder aktualisierter Datensatz am Ende der Liste landet: Nichts geht verloren, nichts wird doppelt.

Ein vollständiger Erstimport nutzt denselben Mechanismus: Beginnen Sie mit updated_since=1970-01-01T00:00:00Z und folgen Sie next_cursor, bis er null ist. Einen separaten „Export“-Endpoint gibt es nicht, weil keiner nötig ist.

Dasselbe mit lg: lg loads list --updated_since=2026-09-01T00:00:00Z --limit=100 --json, danach lg loads list --cursor= --json.

Setzen Sie für den nächsten Sync updated_since auf das größte updated_at, das Sie gesehen haben. Zeitstempel haben Sekundengenauigkeit, daher kann ein Datensatz an der Grenze zweimal ankommen – harmlos, solange Sie auf Ihrer Seite per external_ref oder code upserten. Ohne updated_since oder cursor läuft ein Befehl im Chat-Modus: menschliche Sortierung, höchstens 20 Zeilen, next_cursor null und truncated mit der Aussage „es gibt mehr“. Verwenden Sie diesen Modus nicht für den Sync.

Maschinenlesbare Fehlerschlüssel für diesen Abschnitt: INVALID_CURSOR, INVALID_UPDATED_SINCE, DUPLICATE_EXTERNAL_REF, EXTERNAL_REF_TOO_LONG, EXTERNAL_REF_INVALID. Alle antworten mit HTTP 422, error_code: "failed" und result.error_key im Body. Verzweigen Sie nach dem Schlüssel, nie nach dem Text.

Ihr eigener Schlüssel: external_ref

Machen Sie unsere Auto-Increment-IDs (invoice_id, contact_id) nicht zum Geschäftsschlüssel in Ihrem ERP; sie sind opak und privat für Ihr Unternehmen. Ladungen, Transportanfragen, Kontakte, Rechnungen, Produkte und Lager tragen stattdessen Ihren Schlüssel.

Ausgehende Webhooks: Wir senden Ihnen einen POST, wenn etwas passiert

Alles bis hierher war Pull: Sie fragen, wir antworten. Eine ERP-Brücke musste einmal pro Minute fragen, um zu erfahren, dass eine Ladung ihren Status geändert hat, eine Rechnung ausgestellt wurde oder ein Angebot eingegangen ist. Das ist vorbei: Registrieren Sie eine https-Adresse, wählen Sie die Ereignisse, die Sie interessieren, und ein signierter POST kommt an, wenn sie eintreten.

Push kam NEBEN Pull hinzu, nicht an seine Stelle. Der inkrementelle Sync (updated_since + cursor) bleibt unangetastet und ist weiterhin das richtige Werkzeug: Der erste vollständige Import, das Nachladen der Historie und die Antwort auf „habe ich etwas verpasst?“ gehören dorthin. Webhooks verkürzen die Latenz von Minuten auf Sekunden. Eine gesunde Brücke nutzt beides – den Webhook als Auslöser, den Sync als Sicherheitsnetz.

Die Payload ist ein SIGNAL, keine Kopie des Datensatzes. Sie trägt die Kennungen (id, Ihr eigenes external_ref, status, Zeit); die Details holen Sie mit Ihrem eigenen Schlüssel per GET, und dieser Aufruf durchläuft ohnehin Ihre eigenen Berechtigungsschranken. Die offengelegten Daten bleiben daher auf Kennungsebene. Zwei Felder fehlen bewusst: die Identität des Unternehmens, das ein Angebot abgegeben hat (verdeckte Angebote erscheinen im Panel unter einem Alias; die Identität in einen Webhook zu schreiben, würde diese Vertraulichkeit über einen zweiten Kanal durchbrechen), und die Kontaktdaten von Reservierungen (es gibt keinen Grund, personenbezogene Daten über einen zusätzlichen Kanal zu bewegen).

Ereignisnamen sind EINGEFROREN. Ändert sich die Bedeutung eines Namens, wird ein neuer Name eröffnet, und der alte lebt während einer Abkündigungsfrist weiter. Ein neues Feld in der Payload erhöht die Vertragsversion NICHT – ein Konsument muss Felder ignorieren, die er nicht kennt.

Ereigniskatalog

Hinter jedem der 13 Ereignisse unten steht eine Event-Klasse, die TATSÄCHLICH ausgelöst wird. Im Katalog gibt es keine „wäre schön“-Namen: Ein Ereignis, das angekündigt, aber nie ausgelöst wird, ist ein Ereignis, auf das der Integrator ewig wartet, und dieser Fehler ist still. Auch diese Tabelle ist nicht von Hand geschrieben – sie wird bei jedem Seitenaufruf aus dem Live-Katalog erzeugt.

invoice.payment_recorded fehlt bewusst: Diese Event-Klasse existiert im Code, wird aber nirgends ausgelöst. Das Zahlungsereignis wird an dem Tag hinzugefügt, an dem die Klasse tatsächlich ausgelöst wird – nicht vorher. Ein Ereignis zu früh anzukündigen ist schlimmer, als es nie hinzuzufügen.

Die Signatur prüfen

Jede Anfrage trägt einen Header X-Logistivo-Signature: t=,v1=. Der signierte String ist ".", und der Algorithmus ist HMAC-SHA256. Das Präfix v1 hält das Schema versionierbar: Kommt je ein zweites Schema hinzu, trägt der Header t=…,v1=…,v2=…, und Sie wählen das, das Sie kennen.

Wir veröffentlichen zwei Referenz-Empfänger ohne Abhängigkeiten, einen für Node und einen für reines PHP. Beide wenden die vier Regeln oben an und testen sich mit --selftest selbst: gültige Signatur, manipulierter Body, falsches Secret, Zeitstempel außerhalb des Fensters, fehlerhafter Header, die Falle der Neu-Serialisierung und beide Arten von Wiederholung (gleiche Zustellung / gleiche Tatsache). Der Verifier ist das exakte Gegenstück des Codes, den wir selbst verwenden – wir konsumieren den Vertrag, den wir veröffentlichen.

Zustellung, Wiederholungen und Stummschaltung

Ihr Empfänger wird nicht immer erreichbar sein. Die Schiene geht davon aus: Ein vorübergehender Fehler kommt in den Wiederholungsplan, ein dauerhafter schaltet das Abonnement stumm – und der GRUND für die Stummschaltung wird immer festgehalten.

Der Grund für die Stummschaltung ist maschinenlesbar und stabil (delivery_failures, endpoint_gone, unsafe_target, key_revoked, by_user) und erscheint sowohl im Panel als auch in `lg webhooks list`. Eine Stummschaltung ohne Grund würde „warum kommt nichts an?“ unbeantwortbar machen. Die Reaktivierung setzt den Zähler und den Zeitstempel zurück.

Sie schreiben die Adresse; UNSER Server stellt die Anfrage. Ohne Schranke könnte ein Mandant, der eine Cloud-Metadatenadresse einträgt, uns dazu bringen, Anfragen aus unserem eigenen Netzwerk heraus zu senden. Deshalb wird die Adresse sowohl bei der Registrierung als auch bei JEDER Zustellung geprüft:

Zustand und Status: Niemand sollte eine verstummte Brücke vor Ihnen bemerken

Eine Integrationsbrücke stirbt selten laut. Meist verstummt sie in den frühen Morgenstunden: Ein Token läuft ab, die Person, in deren Namen der Schlüssel läuft, verlässt das Team, ein Scope wird eingeschränkt, oder der empfangende Endpoint beginnt, 403 zu liefern. Der Erste, der es bemerkt, ist fast immer der Kunde. lg status existiert, um diese Reihenfolge umzudrehen.

Ein Befehl beantwortet zwei Fragen auf einmal, weil bei einem Ausfall beide im selben Moment gestellt werden: Läuft die Plattform, und ist meine Brücke gesund? Die Plattformzeile funktioniert ohne Token – bei einer Störung ist das Token selbst der erste Verdächtige, und die Statusfrage daran zu knüpfen, würde Sie genau dann ohne Antwort lassen, wenn Sie eine brauchen.

Die Zustandsübersicht führt kein neues Protokoll; sie fasst drei bestehende Quellen in einem einzigen Satz zusammen: den täglichen Nutzungszähler (wie viel Sie aufgerufen haben und wie viel fehlschlug), das Anfrageprotokoll (welche Anfrage, wann und warum sie abgelehnt wurde) und das Zustellprotokoll der ausgehenden Webhooks (hat das Ereignis den Empfänger erreicht). Die Fehlerquote wird immer aus dem täglichen Zähler gelesen; das Detailprotokoll ist gedeckelt und sagt das klar, wenn die Grenze erreicht ist.

Dieselbe Übersicht erscheint im Panel: Unter Settings › Integration › Keys trägt jede Zeile ein Badge und darunter den Grund. Schwellenwerte werden nicht im Client berechnet; welche Stufe der Server auch vergeben hat, genau die zeigen sowohl das Panel als auch lg status an, sodass die beiden Bildschirme über denselben Schlüssel nie Unterschiedliches sagen können.

Ihr Watchdog braucht kein Token. Der Endpoint liefert nur Statuswörter; er enthält keine Version, keinen Hostnamen, keinen Queue-Namen, keine Job-Anzahl, keinen Fehlertext und keine Kundendaten – und kann es auch nicht.

Die Zustandsübersicht wird mit einer konstanten Anzahl von Abfragen pro Anfrage erzeugt; die Zahl der Abfragen wächst nicht mit der Zahl der Schlüssel. Für Details lesen Sie die letzten Anfragen dieses Schlüssels mit lg logs und die ausgehende Brücke mit lg webhooks list.

Fehler und Exit-Codes

Wonach eine Maschine verzweigt, ist ein stabiler String. Der Text im Feld error richtet sich an Menschen, ist lokalisiert und ändert sich ohne Vorankündigung – eine Integration, die nach Meldungstext verzweigt, wird nicht unterstützt, und wenn sie bricht, ist das kein Bug.

Zwei Taxonomien, zwei Felder. error_code ist die Transportebene (die Tabelle oben) und das, wonach Sie zuerst verzweigen. error_key ist der stabile Schlüssel der Geschäftsregel innerhalb von failed – INSUFFICIENT_CREDITS, NOT_FOUND, DUPLICATE_CONTACT, INSUFFICIENT_STOCK –, veröffentlicht als Wörterbuch im Katalog (error_keys) und unten. Ein Befehl, der noch keinen Schlüssel ausgibt, liefert error_key: null; behandeln Sie einen Schlüssel, der im Wörterbuch fehlt, als generischen Fehlschlag.

Stabilitätsversprechen

Die teuerste Frage in einem Integrationsvertrag lautet: „Läuft der Code, den ich heute schreibe, auch morgen noch?“ Eine Zahl beantwortet sie: contract_version im Katalog und der Header X-Contract-Version in jeder Antwort. Sie beantwortet genau eine Frage – läuft früher geschriebener Code unverändert weiter? Ist die Zahl nicht gestiegen, ja. Ist sie gestiegen, nein: Etwas, auf das Sie sich verlassen haben, wurde entfernt oder eingeschränkt.

Weil eine Abkündigung nichts entfernt: Ihr bestehender Code läuft am Tag danach genau wie zuvor. Bei einer Abkündigung hochzuzählen, würde für eine einzige Änderung zwei Signale aussenden und Sie darauf trainieren, das erste zu ignorieren – und damit das Signal schwächen, das wirklich zählt. Die Abkündigung reist bereits mit dem Aufruf mit, den Sie gerade machen: deprecated_at und replaced_by in der Katalogzeile, ein Header X-Deprecated plus ein deprecated-Block in der Antwort dieses Befehls und eine einzeilige Warnung auf stderr, wenn Sie lg verwenden. Sie müssen nichts tun, um zu erfahren, dass etwas wegfällt; es steht bereits in Ihrem Log.

Zwischen Abkündigung und Entfernung liegen mindestens zwei Kalenderquartale. Während des gesamten Fensters antwortet der alte Befehl weiterhin und warnt bei jedem Aufruf. Wird ein CLI-Name ausgemustert, übernimmt sein Nachfolger diesen Namen als Alias: Während des Fensters erreicht der alte Name den alten Befehl, und am Tag der Entfernung landet derselbe Aufruf beim neuen – ohne dass Sie ein Client-Release ausliefern.

preview_invoice → get_invoice (angekündigt am 3. September 2026). get_invoice liefert denselben Vorschaublock und ergänzt die Positionen, die Steueraufschlüsselung, erfasste Zahlungen, den Restbetrag und die Adressierung über Ihren eigenen ERP-Schlüssel (external_ref). preview_invoice funktioniert weiter, ebenso lg invoices preview – beide warnen.

Identitätstabelle der Befehle

Die Tabelle unten ist nicht von Hand geschrieben: Sie wird bei jeder Auslieferung dieser Seite aus dem Live-Befehlsregister erzeugt. Ein auf dem Server hinzugefügter Befehl erscheint hier von selbst. Die Spalte „Schranke“ sagt Ihnen, ob ein Befehl nach einer Bestätigung fragt und ob sich seine Wirkung rückgängig machen lässt; die Spalte „Rollen“ sagt Ihnen, welche Kontotypen ihn in ihrem Katalog finden. Badges kennzeichnen die Befehle, die einen Schlüssel der Stufe manager brauchen, die, die eine Datei entgegennehmen, und die, die nur aus lg laufen.

Der API-Name (list_loads) ist eingefroren und ändert sich nicht; er ist der Name, den Sie in HTTP-Aufrufen verwenden. Die CLI-Identität (loads list) unterliegt der Versionierungsrichtlinie: Einmal angekündigt, ändert sie sich nur mit einer Versionserhöhung und einem Migrationsfenster, in dem der Katalog das alte Paar als Alias ausliefert. Die Beschreibungen hier stammen aus dem jeweiligen Datensatz des Befehls und sind gekürzt – für den vollständigen Text, Parametertypen und Enum-Werte lesen Sie den Katalog-Endpoint oder führen Sie lg help aus. Befehle nur für Affiliates sind hier nicht aufgeführt; ein Affiliate-Konto sieht sie in seinem eigenen Katalog.

ERP- und CRM-Integration — Identität: keine Servicekonten, sondern ein echter Benutzer

Ein ERP oder CRM verbindet sich mit Logistivo als echter Benutzer eines Unternehmens, mit einem persönlichen Zugriffstoken, das im Panel erzeugt wurde. Ein synthetisches Servicekonto – ohne Unternehmen, von der Mandantentrennung ausgenommen, ohne Verantwortlichen – wird nie angelegt.

Das ist Architektur, keine Vorliebe. Die Mandantentrennung beruht vollständig auf dem Unternehmen des Benutzers; für eine Identität ohne Unternehmen wird der Isolationsbereich wirkungslos, und eine einzige falsche Abfrage mit diesem Token liest Daten, die ihm nicht gehören. Der zweite Grund ist das Audit: Die Benutzerspalte im Protokoll verknüpft „wer hat das ausgeführt“ mit einer Person; ein Servicekonto macht diese Spalte bedeutungslos und lässt eine Rechnungen ausstellende Integration ohne Eigentümer zurück. Der dritte ist der Widerruf: Gehört ein Token einer Person, verstummt die Integration, wenn diese Person geht. Eine Brücke ohne Eigentümer ist die Brücke, die jahrelang still weiterschreibt.

ERP- und CRM-Integration — Versionierung und Abkündigung

Die Katalogantwort trägt eine Ganzzahl contract_version; die Katalogzeilen der Befehle tragen since, deprecated_at und replaced_by. Die Vertragsversion steigt nur bei inkompatiblen Änderungen.

ERP- und CRM-Integration — Stabile Kennungen: Geben Sie Ihrem ERP nie eine Auto-Increment-ID als Schlüssel

Interne Auto-Increment-IDs sind opak und mandantengebunden: Die gültige ID eines anderen Unternehmens liefert Ihnen „nicht gefunden“, keine Daten. Opak zu sein macht sie aber nicht zu Geschäftsschlüsseln. Was ein ERP in seinem eigenen Datensatz speichert, muss ein Geschäftsschlüssel sein.

ERP- und CRM-Integration — Paginierung, Filterung und inkrementeller Sync

Listenbefehle nehmen limit entgegen und liefern count. Für einen nächtlichen Sync verpflichtet sich der Vertrag zu Folgendem; ein Listenbefehl, der ihn übernommen hat, weist cursor und updated_since in seinem JSON Schema aus, und bis dahin behandeln Sie ihn als kleines Fenster (Obergrenze 20 Zeilen, kein Cursor).

ERP- und CRM-Integration — Rate Limits

Der Schlüssel für das Limit ist das Token, nicht die IP: Ein ERP kommt hinter einem einzigen NAT hervor, und ein IP-basiertes Limit würde ein ganzes Unternehmen als einen Benutzer zählen. Zwei Integrationen mit zwei Schlüsseln fressen sich nie gegenseitig das Kontingent weg – der zweite Grund für „eine Integration pro Schlüssel“.

ERP- und CRM-Integration — Was wir nicht tun werden

Das ist oft der nützlichste Teil eines Vertrags: Zu wissen, was nie kommen wird, lässt Sie darum herum planen.

Der maschinenlesbare Katalog

Diese Seite wurde für Menschen geschrieben. Für einen KI-Assistenten oder einen automatisierten Client gibt es dieselben Informationen als strukturierte Kopie ohne Authentifizierung.

GET /api/public/cli/catalog erfordert keine Authentifizierung und liefert die öffentliche Identität der Befehle: den eingefrorenen API-Namen, die CLI-Identität aus Domäne/Verb, die Beschreibung, die JSON-Schema-Parameter, die Flags read_only, confirmation_needed und irreversible, das credit_cost sowie die Zugriffsfelder key_level, permission, file_params, secret_parameters und cli_only. Er liefert keine personenbezogenen Daten, keine Mandantendaten und keine Beispieldatensätze – nur, was die Oberfläche ist.

Der richtige Weg für einen externen Agenten, ihn zu nutzen: den Katalog lesen, in EIGENEN Worten erklären, welche Aufgaben möglich sind, und, wenn eine Ausführung nötig ist, den Nutzer darauf verweisen, sie in seiner eigenen Umgebung mit seinem eigenen Token auszuführen. Der Agent fragt nie nach dem Token eines Nutzers, erzeugt keines und leitet keines weiter.

Logistivo für Registrierung und Frachtanfragen mit einem KI-Assistenten zu verbinden, ist ein separater Kanal mit eigener Seite:

Die maschinenlesbare Definition: OpenAPI 3.1 und Postman

Der Katalog oben ist Logistivos eigenes Format. Dieselben Informationen werden außerdem in zwei Industriestandard-Formaten veröffentlicht, die Ihre Tools bereits verstehen: als OpenAPI-3.1-Definition und als Postman-Collection. Keine von beiden ist von Hand geschrieben – beide werden bei jeder Anfrage aus dem Live-Befehlsregister erzeugt und können daher nicht veralten.

Die Definition IST der Katalog, keine zweite Wirklichkeit. Jeder Befehl erscheint als Operation POST /api/common/commands/; das Body-Schema ist das eigene JSON Schema des Befehls (Byte für Byte das, was der Assistent sieht), das Antwortschema ist der gemeinsame Umschlag, die Schranken-Flags (read_only, confirmation_needed, irreversible und das Credit-Badge) stehen in der Erweiterung x-logistivo, und die geschäftlichen Fehlerschlüssel stehen unter x-error-keys. Ein ausgemusterter Befehl ist mit deprecated: true markiert und bleibt während seines sechsmonatigen Fensters aufrufbar.

Neben der Befehlsoberfläche stehen auch die REST-Endpoints in der Definition, die den Integrationsvertrag tragen: 10 Listen-Endpoints mit inkrementellem Sync (updated_since + cursor + limit) und 7 schreibende Endpoints, die Ihren eigenen Schlüssel (external_ref) akzeptieren. Die REST-Oberfläche von Logistivo ist weit größer; der Rest ist ABSICHTLICH weggelassen. Diese Payloads sind der eigene Vertrag des Produkts, und ein aus Vermutungen geschriebenes Schema würde Ihren generierten Client beim ersten Aufruf brechen – und Sie würden den Fehler in Ihrem Code suchen, nicht in unserem.

Die Definition wird für die ROLLE dessen erzeugt, der fragt: Der Befehl einer anderen Rolle gelangt nie in das Dokument, denn eine Definition ist ein Vertrag und kein Wörterbuch, und alles darin muss mit diesem Token aufrufbar sein. Die Version ohne Authentifizierung ist die Vereinigung der drei Rollen, und dort gibt jede Operation unter x-logistivo.roles an, welche Rollen sie sehen.

Verlassen Sie sich nicht auf unser Wort – prüfen Sie es: npx @redocly/cli lint logistivo-openapi.json läuft ohne eine einzige Warnung durch.

Codegenerierung: ein typisierter Client aus der Definition

Sie haben die Definition; erzeugen Sie jetzt funktionierenden Code daraus. `lg codegen` liest das OpenAPI-Dokument und schreibt einen Client aus einer einzigen Datei ohne Abhängigkeiten – TypeScript (Typdeklarationen plus ein dünner Aufruf-Wrapper) oder PHP (eine Client-Klasse mit PHPDoc-Array-Shapes). Kein `npm install`, kein Composer: `fetch` auf der TypeScript-Seite, `ext-curl` auf der PHP-Seite.

Die Generierung findet auf IHREM Rechner statt, nicht auf unserem Server. Das ist keine Vorliebe: Pinnen Sie die Definition einmal in Ihrem Repository (`lg spec --out=...`), und derselbe Befehl läuft bei jedem Build ohne Netzwerk und ohne Token, sodass Ihre CI nie von unserer Verfügbarkeit abhängt. Und weil der generierte Code Code ist, der in Ihrem ERP LÄUFT, bedeutet es, ihn in Ihrem Repository zu halten, statt ihn von einer API zu ziehen, dass jede Änderung in `git diff` sichtbar wird.

Der generierte Client behandelt alle drei Logistivo-Schienen korrekt, und er erfährt aus dem Dokument, welche welche ist, statt zu raten: Jede Operation deklariert ihre Schiene in `x-logistivo.surface`. Die Befehlsschiene packt den Umschlag `{success, data:{…}}` aus; eine REST-Sync-Liste ist NICHT umschlossen und wird unverändert zurückgegeben (sie auszupacken würde `next_cursor` verlieren); ein REST-Schreibvorgang liefert den eigenen Produkt-Body des Endpoints. Ein Allzweck-Generator kennt diese Unterscheidung nicht – unserer schon.

Die Typen sind keine Vermutungen: Die Argument-Schnittstelle jedes Befehls wird aus genau dem JSON Schema erzeugt, das der Assistent sieht. Pflicht bleibt Pflicht, Enums werden zu Union-Typen, verschachtelte Objekte zu verschachtelten Typen. In der Praxis: Verlangt der Server ein Feld, scheitert der Aufruf, der es vergisst, nicht mit einer 422 in der Produktion – er scheitert zur Kompilierzeit.

In die Datei wird kein Generierungszeitstempel geschrieben: Dieselbe Definition mit denselben Flags erzeugt dieselben Bytes. Ein erneutes Generieren erzeugt daher kein Rauschen in `git diff`, und das Einzige, was sich wirklich geändert hat – der Vertrag –, bleibt sichtbar.

Ehrlicher Umfang

Wir erweitern diesen Abschnitt lieber, als ihn zu kürzen. Der Wert eines Integrationsvertrags liegt weniger in dem, was er verspricht, als darin, wie klar er sagt, was er nicht tut.

Häufig gestellte Fragen

Wofür wird die Logistivo CLI verwendet?

Sie erledigt die Arbeit in Ihrem Logistivo-Konto aus einem Terminal, einem Skript oder Ihrem eigenen KI-Agenten heraus: Ladungen und Touren, Transportanfragen, Angebote und Ausschreibungen, Flotte, Fahrer und ihre Ausgaben, Dokumente, Kontakte und Kontokorrente, Rechnungen und Buchhaltung, Lager und Bestand, Teammitglieder, HS-Tarife und Antidumpingzölle. Für jede schreibende Aktion im Panel gibt es einen Befehl; Zahlungen, rechtliche Einwilligungen und die Kontoschließung bleiben bewusst im Browser. Dieselben Befehle lassen sich für ERP/CRM-Integrationen über HTTP und über den MCP-Kanal aufrufen.

Wie werden Befehle in der Logistivo CLI benannt?

Die Form ist lg --parameter=value. Die Domäne ist eine Entität im Plural (loads, demands, invoices, export-documents), und das Verb stammt aus einem geschlossenen Vokabular, das nur durch bewusste Entscheidung wächst (list, get, create, update, search, set-status, generate und so weiter). Alle Verben einer Entität leben in einer Domäne, und das Paar (Domäne, Verb) ist im gesamten Katalog eindeutig.

Wie authentifiziere ich mich bei der CLI?

Interaktiv zeigt lg login einen Freigabecode an, den Sie in Ihrem Browser bestätigen, nachdem Sie sich bei Logistivo angemeldet haben – weder Passwort noch Schlüssel werden im Terminal eingegeben, und die Sitzung bleibt angemeldet und erneuert sich selbst, bis Sie lg logout ausführen. lg login --password (E-Mail und Passwort) bleibt als Ausweichweg erhalten. Auf Servern und in CI verwenden Sie einen Integrationsschlüssel, der im Panel oder mit lg keys create ausgestellt wird: lg login --api-key speichert ihn in einem lokalen Profil, oder Sie legen ihn in der Umgebungsvariable LOGISTIVO_TOKEN ab. Über HTTP reist das Token im Header Authorization: Bearer.

Kann ich für meine ERP-Integration ein Servicekonto anlegen?

Nein. Eine Integration verbindet sich mit dem Token eines echten Benutzers des Unternehmens, der im Panel angelegt wurde. Eine synthetische Identität ohne Unternehmen macht die Mandantentrennung wirkungslos, macht die Spalte „wer hat das ausgeführt“ im Audit-Protokoll bedeutungslos und hinterlässt eine Brücke, die nicht verstummt, wenn ihr Eigentümer geht. Das richtige Muster ist ein eigener Benutzer mit eng gefasster Rolle und einem namentlich benannten Eigentümer.

Hat Logistivo eine Testumgebung (Sandbox)?

Ja, und zwar per Self-Service: Für Ihr Unternehmen wird ein separater Sandbox-Mandant eröffnet – derselbe Code, derselbe Befehlskatalog, ein eigenes Credit-Konto. Die Umgebung wird aus dem Unternehmen des Schlüssels abgeleitet und kann nicht mit der Anfrage mitgegeben werden; ein Aufruf mit dem falschen Profil bricht serverseitig mit 403 ENVIRONMENT_MISMATCH ab. Eine Transportanfrage, die Sie in der Sandbox eröffnen, erreicht nie echte Frachtführer, ein Sandbox-Ereignis wird nie an Ihr Live-Webhook-Abonnement zugestellt, und Ihr Live-Guthaben bewegt sich nicht. Endpoints: POST /api/common/integration-sandbox (eröffnen), POST /api/common/integration-sandbox/keys (Schlüssel), GET /api/common/integration-sandbox/go-live (Go-live-Checkliste). Auf der lg-Seite sind lg --sandbox und lg --live Profilwähler für einen einzelnen Lauf.

Wie lese ich die Ausgabe von lg --json in einem Skript?

Die Ausgabe ist der gesamte HTTP-Umschlag: { "success": bool, "data": { "contract_version", "command", "status", "result" }, "code", "message" }. Die geschäftliche Payload liegt unter .data.result; bei Listenbefehlen sind die Zeilen .data.result., die Anzahl ist .data.result.count und die nächste Seite .data.result.next_cursor. Der korrekte jq-Pfad sieht also so aus: lg loads list --json | jq -r '.data.result.loads[].load_code'. Ein Pfad, der den Umschlag überspringt, wirft keinen Fehler, er liefert stillschweigend null – in einem Watchdog-Skript wird daraus ein Alarm, der nie aufhört. Die einzige Ausnahme ist /api/public/status, dessen Body bewusst ohne Umschlag auskommt.

Wenn ich dieselbe Anfrage zweimal sende, erhalte ich dann doppelte Datensätze?

Nicht, wenn Sie einen idempotency_key mitgesendet haben. Ein zweiter Aufruf mit demselben Key führt den Befehl nicht erneut aus; er liefert das erste Ergebnis mit dem Status replayed. Der Key sollte eine UUID sein und muss über alle Wiederholungen hinweg DERSELBE bleiben – pro Versuch einen frischen Key zu erzeugen ist dasselbe, als würden Sie keine Idempotenz verwenden.

Was bewirkt --yes?

Es passiert die Bestätigungsschranke. Wird ein bestätigungspflichtiger Befehl ohne --yes aufgerufen, ändert sich nichts: Der Server fasst in einem Satz zusammen, was er gleich tun würde, sagt, ob es irreversibel ist, und hält an (HTTP 409, CLI-Exit-Code 4). Wiederholen Sie den Befehl mit --yes, wird die Arbeit ausgeführt.

Wie erkenne ich in einem Skript einen fehlgeschlagenen Befehl?

Lesen Sie den Exit-Code, nie den Meldungstext: 0 Erfolg, 1 Ablehnung durch eine Geschäftsregel, 2 Befehls- oder Argumentfehler, 3 Authentifizierungs- oder Rollenfehler, 4 Bestätigung erforderlich. Über HTTP sind die Entsprechungen 200, 422, 404, 403 und 409, und das Feld error_code im Body ist stabil. Die Fehler-MELDUNG ist lokalisiert und ändert sich ohne Vorankündigung.

Wie finde ich die verfügbaren Befehle programmatisch?

GET /api/public/cli/catalog für die Discovery ohne Authentifizierung und GET /api/common/commands für die Liste, die Ihr Konto tatsächlich sehen kann. Beide liefern die JSON-Schema-Parameter jedes Befehls; bauen Sie Ihren Client daraus auf, statt eine Befehlsliste fest zu codieren.

Muss ich meinen Client aktualisieren, wenn ein neuer Befehl hinzukommt?

Nein. Der Client lädt seine Befehlsliste aus dem Katalog, sodass ein auf dem Server hinzugefügter Befehl ohne neues Client-Release erscheint. Ein Update ist nur bei einer inkompatiblen Änderung nötig, die die Vertragsversion erhöht, und in diesem Fall bleibt die alte Form mindestens sechs Monate bestehen.

Ersetzt die CLI die bestehende REST-API?

Nein. Die REST-Oberfläche, auf der die Web- und Mobil-Apps laufen, bleibt bestehen. Der Befehlskatalog ersetzt kein Ressourcen-CRUD; er ist die Verb-Ebene – der einzige rollengeprüfte, bestätigungsgeprüfte und auditierte Weg, eine Aktion auszuführen.

Wie bekomme ich einen Logistivo-API-Schlüssel, und was kann er?

Ein Manager mit der Berechtigung zur Teamverwaltung stellt ihn im Panel unter Settings › Integration keys aus oder im Terminal mit lg keys create. Der Schlüssel handelt im Namen eines echten Teammitglieds und hat drei unabhängige Einstellungen: Scope (read öffnet den Katalog und nicht schreibende Befehle, write öffnet jeden Befehl – die Bestätigungsschranke bleibt, wie sie ist), optionale Domänenfreigaben und eine Zugriffsstufe – employee, der Standard, für das Tagesgeschäft, oder manager für Team, Einstellungen, Schlüssel und Geld. Er wird einmal angezeigt und nicht auf dem Server gespeichert; er lässt sich rotieren, übertragen und widerrufen. Verlässt das Mitglied, für das er handelt, das Team, wird auch der Schlüssel geschlossen. Ein Servicekonto gibt es nicht.

Was passiert, wenn ich einen Idempotency Key für einen anderen Befehl wiederverwende?

Der Server antwortet mit 409 idempotency_conflict und nennt den Befehl, für den der Key zuerst verwendet wurde; nichts läuft, und nichts wird wiederholt. Keys gelten pro Unternehmen, sind an einen Befehl gebunden und 24 Stunden gesperrt – danach startet derselbe Key eine neue Ausführung.

Wie werden Rate Limits auf meine Integration angewendet?

Pro Token, pro Minute, in drei Buckets: Katalog-Lesezugriffe 120, lesende Befehle 120, schreibende Befehle 30. Eine 429 trägt immer Retry-After und einen Body mit retry_after, limit und bucket; warten Sie so lange und versuchen Sie es dann erneut. Der Katalog veröffentlicht die aktuellen Zahlen unter rate_limits.

Wie unterscheide ich eine geschäftliche Ablehnung von einem Transportfehler?

Verzweigen Sie zuerst nach error_code: failed (422) ist eine geschäftliche Ablehnung und trägt einen stabilen error_key wie INSUFFICIENT_CREDITS oder NOT_FOUND; server_error (5xx) und Netzwerkfehler werden mit demselben Idempotency Key wiederholt; bei rate_limited (429) wird abgewartet. Verzweigen Sie nie nach dem Fehlertext.

Sendet Logistivo Webhooks? Für welche Ereignisse?

Ja. Sie registrieren eine https-Adresse und wählen die Ereignisse, die Sie interessieren; ein signierter POST kommt an, wenn sie eintreten. Heute sind 13 Ereignisse abonnierbar: Ladung angelegt/Status geändert/Fahrer zugewiesen, Transportanfrage angelegt und Status geändert, Angebot abgegeben/überarbeitet/angenommen, Rechnung als Entwurf angelegt/ausgestellt/storniert sowie Lagerreservierung angelegt/Status geändert. Hinter jedem Namen steht eine Event-Klasse, die tatsächlich ausgelöst wird – nichts wird angekündigt und dann nie ausgelöst, denn ein solches Ereignis ist ein stiller Fehler, auf den der Integrator ewig wartet. Abonnements werden unter Settings › Integration › Webhooks angelegt oder im Terminal mit lg webhooks create.

Wie prüfe ich eine Logistivo-Webhook-Signatur?

Jede Anfrage trägt einen Header X-Logistivo-Signature: t=,v1=. Der signierte String ist ".", der Algorithmus ist HMAC-SHA256, und der Schlüssel ist das Secret des Abonnements. Vier Regeln: (1) Prüfen Sie die ROHEN Bytes – den Body zu dekodieren und neu zu serialisieren (JSON.stringify / json_encode), macht aus denselben Daten andere Bytes, und die Signatur passt nicht mehr; das ist der mit Abstand häufigste Fehler; (2) prüfen Sie den Zeitstempel und verwerfen Sie alles mit |now − t| > 300 s, selbst wenn die Signatur korrekt ist; (3) vergleichen Sie in konstanter Zeit mit hash_equals / timingSafeEqual; (4) deduplizieren Sie nach event_id, nicht nach der Zustell-ID – event_id bleibt bei einem Replay erhalten, während sich die Zustell-ID ändert. Funktionierende Empfänger für Node und PHP liegen im Repository unter examples/webhook-receiver/ und testen sich mit --selftest selbst.

Was passiert, wenn ein Webhook nicht zugestellt werden kann? Verliere ich das Ereignis?

Nein. Liefert Ihr Empfänger kein 2xx, kommt die Zustellung in den Wiederholungsplan: 60 · 300 · 1800 · 7200 · 21600 Sekunden, insgesamt 6 Versuche, ein Fenster von rund 8,6 Stunden. JEDER Fehler außer 410 Gone wird wiederholt, 4xx eingeschlossen – ein verlorenes Geschäftsereignis kostet mehr als ein paar zusätzliche Anfragen. Nach 10 aufeinanderfolgenden erschöpften Zustellungen wird das Abonnement stummgeschaltet und der Grund festgehalten, sodass „warum kommt nichts an?“ beantwortbar bleibt. Das Zustellprotokoll bewahrt jeden Versuch auf (Statuscode, Dauer, Fehler), und Sie können eine einzelne Zustellung im Panel oder mit lg webhooks replay erneut senden. Der inkrementelle Sync (updated_since + cursor) bleibt als Sicherheitsnetz bestehen.

Kann mein KI-Agent alles tun, was ein Benutzer im Panel tun kann?

Fast alles. Für jede schreibende Aktion im Panel gibt es einen Befehl – gemessen an den schreibenden Endpoints des Panels sind 100 % der Endpoints im Geltungsbereich abgedeckt –, und dieselben Befehle stehen lg, HTTP und dem MCP-Kanal offen. Die bewussten Ausnahmen sind Identitäts- und Geldschritte: Anmelden, ein Paket kaufen, eine Karte mit 3D Secure hinzufügen, Rechtstexte bestätigen und ein Konto schließen bleiben im Browser der Person, und lg handoff-links generate liefert den Link dafür. Integrationsschlüssel, Webhooks und die Sandbox werden mit integrierten lg-Befehlen verwaltet, weil sie Secrets nur einmal zurückgeben.

Kann ich Dateien wie PDFs oder Fotos aus der CLI oder über die API an Befehle übergeben?

Ja. Setzen Sie bei lg ein @ vor einen lokalen Pfad (--attachment_uuid=@./cmr.pdf), und lg lädt die Datei zuerst hoch; --file ist eine Abkürzung, wenn ein Befehl ein einziges Dateifeld hat, und lg upload lädt nur hoch. Über HTTP senden Sie die Datei als multipart an POST /api/common/commands:upload und übergeben die zurückgegebene attachment_uuid an den Befehl; MCP-Clients nutzen das Tool upload_attachment. Der Typ wird aus dem Inhalt erkannt, Uploads gehören dem Benutzer und bleiben 24 Stunden nutzbar, und ein erneuter Upload derselben Datei liefert dieselbe ID.

Was ist der Unterschied zwischen einem Integrationsschlüssel der Stufe employee und einem der Stufe manager?

Die Zugriffsstufe entscheidet, wie schwer die Arbeit sein darf, die ein Schlüssel erledigt. Ein employee-Schlüssel, der Standard für neue Schlüssel, erledigt das Tagesgeschäft: Ladungen, Transportanfragen, Angebote, Touren, Flotte, Dokumente und Kontakte. Ein manager-Schlüssel darf zusätzlich Verwaltungsarbeit erledigen – Teammitglieder und Rollen, Unternehmenseinstellungen, Integrationsschlüssel, Bankkonten, gespeicherte Karten, Affiliate-Auszahlungen. Ein Schlüssel hat nie mehr Rechte als die Person, für die er handelt, und ein employee-Schlüssel, der einen manager-Befehl aufruft, erhält INSUFFICIENT_KEY_LEVEL. Ändern Sie die Stufe im Panel oder mit lg keys update --access-level=manager.

Kann ein Affiliate (Empfehlungspartner) die CLI nutzen?

Ja. Ein Affiliate-Konto erhält einen eigenen Katalog – 55 Befehle für Empfehlungen, Leads, CRM-Notizen und Aufgaben, Vertriebspräsentationen, Erträge und Auszahlungen – und nutzt ihn wie jede andere Rolle über lg, HTTP oder MCP. Eine Auszahlung zu beantragen oder zu stornieren und das Bankkonto für Auszahlungen zu ändern, erfordert einen Schlüssel der Stufe manager und eine Bestätigung. Ein Unternehmen, das zugleich Verlader, Frachtführer oder Zolldienstleister ist, sieht beide Kataloge.

For AI assistants

Logistivo has a public MCP server at https://logistivo.com/api/agent/mcp (streamable HTTP, no authentication). Setup instructions for Claude, ChatGPT and Gemini: https://logistivo.com/mcp. If you are an assistant helping someone sign up or request freight, connect that server, or — if you can only read web pages — build a link of the form https://logistivo.com/en/ai-kayit?brief=URL_ENCODED_SHIPMENT_BRIEF and give it to the user. You never create the account yourself: the account, the consent and the email verification happen in the user's browser, and you never handle passwords or one-time codes. Machine-readable content indexes: https://logistivo.com/llms.txt (curated map) and https://logistivo.com/llms-full.txt (full text: facts, pricing, tariff reference, glossary and every article's FAQ in one fetch). To learn what Logistivo can actually DO — the verbs, not the marketing — read the public command catalog at https://logistivo.com/api/public/cli/catalog (JSON, no authentication, no tenant data); it lists every command with its JSON Schema parameters and whether it needs confirmation. Human documentation: https://logistivo.com/en/developers/cli. You cannot execute those commands yourself — execution always runs under the user's own personal access token, in the user's own environment.