Logistivo CLI: prowadź operacje logistyczne z terminala i ze swojego ERP

Jeden katalog poleceń zasila trzy powierzchnie: terminalowego klienta lg, asystenta AI w panelu oraz kanał, przez który łączą się zewnętrzni asystenci AI. Funkcję pisze się raz i pojawia się ona we wszystkich trzech miejscach — a każda operacja zapisu w panelu ma już swoje polecenie, więc twoje skrypty i twoi własni agenci AI mogą wykonywać pracę, którą twój zespół wykonuje na ekranie. Płatności, zgody prawne i zamknięcie konta celowo pozostają w przeglądarce. Ta strona jest kontraktem. Poniższa tabela poleceń nie jest pisana ręcznie; jest generowana z bieżącego rejestru poleceń przy każdym żądaniu.

Czym jest Logistivo CLI?

Logistivo CLI (lg) to klient wiersza poleceń, który wykonuje pracę na twoim koncie Logistivo — ładunki i kursy, zapytania transportowe, oferty i przetargi, flota i kierowcy, dokumenty, kontakty i rozrachunki, faktury i księgowość, magazyn i stany magazynowe, członkowie zespołu oraz taryfy celne — z terminala, ze skryptu lub z twojego własnego agenta AI. Polecenia nie są wbudowane w klienta: klient pobiera katalog poleceń z serwera i na jego podstawie sam się rysuje. Te same polecenia można wywołać przez HTTP bez instalowania czegokolwiek — tej drogi używają integracje ERP i CRM — oraz przez kanał MCP, z którym łączą się zewnętrzni asystenci AI.

Co działa dziś, a co jest w drodze

Ta sekcja celowo znajduje się na górze strony. Najdroższy błąd, jaki może popełnić dokument integracyjny, to opisanie endpointu, który nie jest jeszcze otwarty, tak jakby już działał: integrator pisze kod, endpoint zwraca 404, a zaufanie już nie wraca.

Ponieważ kontrakt jest zamrożony, kod integracji, który piszesz dziś, będzie działał dalej, gdy otworzą się pozostałe elementy. Pola, których nie znasz, musisz ignorować; to właśnie ta reguła pozwala nam dodawać bez psucia.

Co tu jest inaczej: klient nie zna poleceń, tylko je rysuje

To jedna decyzja, która nadaje sens reszcie tej strony. W zwykłym CLI polecenia mieszkają w kliencie; w Logistivo mieszkają na serwerze.

W klasycznym układzie, gdy serwer zyskuje nową funkcję, klient potrzebuje nowego wydania, a użytkownik musi je zainstalować. W międzyczasie obie strony wiedzą co innego — i to jest najcichszy tryb awarii w integracjach: skrypt działa, kod wyjścia to 0, a polecenie po prostu nie opisuje tego, co potrafi serwer.

W Logistivo definicja polecenia to jeden rekord: nazwa, co robi, parametry JSON Schema, które role mogą je widzieć, czy wymaga potwierdzenia i czy jego efekt jest odwracalny. Przy starcie lg pobiera ten katalog i generuje z niego tekst pomocy, flagi i walidację danych wejściowych. W chwili, gdy polecenie zostaje dodane na serwerze, lg je zna — bez aktualizacji klienta.

Ten sam katalog zasila asystenta AI w panelu oraz kanał, z którym łączą się zewnętrzni asystenci AI. Funkcję pisze się raz, a pojawia się w trzech miejscach — i wszystkie trzy przechodzą przez tę samą bramkę ról, tę samą bramkę potwierdzenia i ten sam rejestr audytu. Nie istnieje osobny spis poleceń CLI; gdyby istniał, te trzy powierzchnie po cichu rozjechałyby się w ciągu sześciu miesięcy.

Reguła nazewnictwa — jak rosną nazwy poleceń

Katalog będzie rósł. Nowe polecenia nie są nazywane doraźnie; obowiązuje sześć reguł. Publikujemy je tutaj, bo to nazwy, które twoja integracja będzie pisać przez lata, a ich przewidywalność ma dla ciebie wartość.

Pokrycie: wszystko z panelu dostępne poleceniem

Wiele zespołów podłącza Logistivo do własnego agenta AI lub środowiska automatyzacji, a agent jest użyteczny tylko wtedy, gdy potrafi dokończyć zadanie. Zasada jest więc prosta: wszystko, co użytkownik biurowy może zmienić w panelu, może zmienić także polecenie — za tą samą bramką ról, tą samą bramką potwierdzenia i z tym samym rejestrem audytu.

To jest zmierzone, a nie deklarowane. Każdy endpoint zapisu stojący za panelem jest sprawdzany względem rejestru poleceń, a endpoint uznaje się za pokryty tylko wtedy, gdy polecenie deklaruje go jako swoje lustro HTTP lub wywołuje tę samą współdzieloną usługę. Spośród endpointów zapisu objętych zakresem pokrytych jest 100%, a żaden nie pozostaje niepotwierdzony. Każdy pominięty endpoint ma spisany powód — kroki tożsamości i płatności, płaszczyzna sterowania integracją, własne endpointy aplikacji kierowcy, telemetria klienta, martwe kopie bez wywołujących — a te, które mają znaczenie dla integratora, są wymienione poniżej.

Polecenia przyjmujące plik podlegają tej samej zasadzie: do 33 poleceń w tabeli przekazujesz dokument lub zdjęcie i wszystkie działają z lg, przez HTTP i przez MCP (zob. Polecenia przyjmujące pliki).

Prace zarządcze również są pokryte, za mocniejszym kluczem: klucz integracyjny na poziomie manager jest potrzebny do 62 poleceń w tabeli — członkowie zespołu i role, ustawienia firmy, konta bankowe, zapisane karty, klucze integracyjne. Klucz na poziomie employee, domyślny, wykonuje codzienne operacje i przy tych poleceniach dostaje odmowę z INSUFFICIENT_KEY_LEVEL.

Konta afiliantów (partnerów polecających) mają własny katalog z 55 poleceniami w 20 domenach — rekomendacje (referrals), leady, notatki i zadania CRM, prezentacje sprzedażowe, zarobki i wypłaty oraz polecenia konta i zespołu, które ma każda rola. Polecenia dostępne tylko dla afiliantów nie są w publicznej tabeli poniżej, która obejmuje trzy role panelu; konto afilianta widzi je w GET /api/common/commands i w lg commands. Firma, która jest jednocześnie afiliantem oraz nadawcą, przewoźnikiem lub agencją celną, widzi oba zestawy.

Agent powinien zacząć od lg users get (nazwa API get_current_user): zwraca rolę, poziom dostępu używanego klucza, efektywne uprawnienia oraz informację, czy oczekująca akceptacja prawna lub niezweryfikowany numer telefonu blokują obecnie zapisy — dzięki temu agent może przewidzieć odmowę, zamiast ją zbierać.

Pokrycie jest mierzone raportem, który porównuje rejestr poleceń z endpointami zapisu panelu; endpoint panelu dodany bez polecenia pojawia się w nim jako niepotwierdzony. Jeśli znajdziesz w panelu działanie, do którego nie da się dotrzeć poleceniem, zgłoś to do wsparcia.

Instalacja i tożsamość

lg to jednoplikowy skrypt Node bez zależności (Node 18+), opublikowany w npm jako @lgstv/lg. Pakiet instaluje dwa identyczne polecenia, lg i logistivo — użyj logistivo, jeśli lg jest już aliasem na twoim komputerze. Ponieważ to jeden plik, skopiowanie lg.mjs na serwer ERP klienta również działa. Do zbudowania integracji ERP/CRM nie potrzebujesz klienta — te same polecenia są dostępne przez HTTP.

1. Instalacja — Potrzebujesz tylko Node 18 lub nowszego; nie ma innych zależności. Zainstaluj globalnie albo uruchom bez instalacji przez npx.

2. Logowanie kodem zatwierdzenia — lg login wyświetla krótki kod zatwierdzenia i otwiera w przeglądarce adres logistivo.com/connect; przez SSH, w CI lub z --no-browser tylko wypisuje adres, który możesz otworzyć na dowolnym urządzeniu, także na telefonie. Zaloguj się tam jak zwykle, sprawdź, czy kod na stronie jest tym samym kodem co w terminalu, wybierz dostęp tylko do odczytu albo do odczytu i wykonywania działań, a następnie zatwierdź. Do terminala nie wpisuje się niczego tajnego: ani hasła, ani klucza, ani kodu jednorazowego. Sesja pozostaje potem zalogowana na tym komputerze — zamknięcie terminala nie wylogowuje, a lg sam odnawia token dostępu, zanim ten wygaśnie. lg logout kończy sesję również na serwerze; możesz ją też usunąć w panelu w Settings › Integration keys › AI and CLI connections. lg login --password (e-mail i hasło) pozostaje jako rozwiązanie awaryjne. Wymaga lg 0.11 lub nowszego.

3. Dla serwerów i CI: klucz, nie kod zatwierdzenia — Na serwerze ani w zadaniu CI nie ma nikogo, kto zatwierdziłby kod w przeglądarce. Wydaj klucz w panelu (Settings › Integration keys) albo poleceniem lg keys create — opcjonalnie w imieniu dedykowanego członka zespołu o wąskim zakresie uprawnień — i umieść go w LOGISTIVO_TOKEN albo zapisz przez lg login --api-key. Gdy zmienna jest ustawiona, plik konfiguracyjny nigdy nie jest odczytywany. Szczegóły: sekcja „Połączenie za pomocą klucza API”.

4. Sprawdź instalację i przejrzyj katalog — lg commands wyświetla wszystkie polecenia, które widzi twoja rola, pogrupowane według domen; lg help pokazuje czasowniki tej domeny, a lg help — pełną listę parametrów jednego polecenia z typami i informacją, które są wymagane. Żaden z tych tekstów nie mieszka w kliencie — wszystko pochodzi z katalogu. Pamiętaj, że lista zależy od twojej roli: czasowniki jednej domeny nie są takie same dla przewoźnika i dla nadawcy (na przykład loads set-status jest tylko w katalogu przewoźnika). Jeśli brakuje czasownika, sprawdź swoją rolę, zanim sprawdzisz instalację.

Nigdy nie umieszczaj klucza w systemie kontroli wersji, logu CI ani oknie czatu. Jeśli logowanie odbyło się kodem zatwierdzenia lub kluczem integracyjnym, lg logout kończy sesję również na serwerze; w sesji hasłowej (lg login --password) usuwa tylko LOKALNĄ kopię i wyraźnie to komunikuje. Rejestr audytu przechowuje twoje argumenty, ale maskuje jako *** każde pole, którego nazwa zawiera token, password, IBAN, card lub OTP, oraz każdy parametr, który polecenie deklaruje jako tajny.

Ergonomia klienta

Te funkcje ułatwiają codzienną pracę z klientem `lg`. Żadna z nich nie zmienia kontraktu HTTP — pomiń tę sekcję, jeśli integrujesz się bezpośrednio przez HTTP.

Połączenie za pomocą klucza API

ERP, CRM, skrypt lub agent AI łączy się z Logistivo kluczem integracyjnym wydanym w panelu albo poleceniem lg keys create. Klucz działa w imieniu prawdziwego członka zespołu: rola tej osoby decyduje o liście poleceń, poziom dostępu klucza — o tym, jak poważna może być praca, a gdy ta osoba odchodzi z zespołu, klucz umiera razem z nią. Nie ma syntetycznego „konta serwisowego” i nie będzie — izolacja multi-tenant jest związana z użytkownikiem, a most bez właściciela to taki, który latami po cichu dalej zapisuje.

1. Wydaj klucz w panelu — Settings › Integration keys › New key. Nadaj mu nazwę („Nocna synchronizacja ERP”), wybierz zakres i poziom dostępu oraz opcjonalnie skróć okres ważności. Klucz jest pokazywany raz, tylko na tym ekranie; serwer go nie przechowuje. Jeśli go zgubisz, Rotate wydaje nowy — stary natychmiast traci ważność.

2. Połącz się z terminala — lg login --api-key weryfikuje klucz, wypisuje tożsamość i zakres oraz pobiera katalog poleceń. lg whoami pyta serwer, jako który użytkownik, w jakiej firmie, roli i zakresie działasz. lg logout unieważnia klucz również na serwerze, tak samo jak kończy tam sesję otwartą kodem zatwierdzenia (tylko lg logout w sesji hasłowej tego nie potrafi).

3. Albo zwykłe HTTP — Klucz to token dostępu przesyłany w nagłówku Authorization: Bearer i działa bezpośrednio z trzema endpointami poleceń. Na serwerze lub w CI umieść go w LOGISTIVO_TOKEN; gdy ta zmienna jest ustawiona, plik konfiguracyjny nigdy nie jest odczytywany.

4. Albo zarządzaj kluczami z terminala: lg keys — lg keys to terminalowy odpowiednik ekranu w panelu: te same endpointy, ta sama bramka (uprawnienie do zarządzania zespołem oraz klucz na poziomie manager, gdy wywołujesz to kluczem). create, rotate i transfer zwracają nowy token jednorazowo: z --save-profile=NAME trafia on od razu do lokalnego profilu i nigdy nie jest wypisywany; w przeciwnym razie lg wypisuje go raz. W trybie --json token jest dołączany tylko wtedy, gdy dodasz --show-secret, a wywołanie --json bez żadnej z tych flag jest odrzucane przed wysłaniem żądania, więc token nie może po cichu trafić do wyjścia skryptu. rotate, transfer i revoke proszą o potwierdzenie (--yes); klucz jest wskazywany dokładną nazwą lub uuid, nigdy przez zgadywanie. Poziom dostępu przechodzi bez zmian na klucz po rotacji lub przeniesieniu.

Zakres jest egzekwowany na całej powierzchni API, nie tylko w katalogu poleceń: klucz tylko do odczytu przepuszcza żądania odczytu (GET/HEAD/OPTIONS), a każde inne, zapisujące żądanie dostaje 403 forbidden. Są dwa celowe wyjątki i obu strzeże dokładniejsza bramka: endpoint wykonywania poleceń (decyzja wynika z własnej flagi read_only polecenia, bo polecenia niezapisujące też wywołuje się przez POST) oraz klucz unieważniający samego siebie (lg logout, dozwolone także dla klucza tylko do odczytu). Zakres, uprawnienia domenowe i poziom dostępu to trzy niezależne osie i wszystkie muszą przepuścić żądanie: klucz read nie może zapisywać nawet na poziomie manager, klucz write na poziomie employee nie może zarządzać zespołem, a uprawnienie domenowe loads:write nie otwiera prac zarządczych. Wydaj klucz w imieniu członka z najmniejszymi uprawnieniami, które pokrywają twoje potrzeby: rola decyduje o tym, które rekordy w ogóle możesz zobaczyć.

Pierwsze dziesięć minut: od panelu do pierwszego wywołania

Najdroższe minuty integracji to pierwsze dziesięć. Cztery poniższe kroki układają tę ścieżkę w kolejności; ta sama kolejność istnieje w panelu jako kreator (Settings › Integration › Setup wizard), a kreator wywołuje dokładnie opisane tu endpointy. Deweloper bez dostępu do kreatora dociera w to samo miejsce, czytając tę sekcję.

Żaden z czterech kroków nie kończy się na „wysłane”. Na pytanie, czy klucz naprawdę działa, odpowiada rejestr żądań; na pytanie, czy webhook naprawdę dociera — rejestr dostarczeń. Oba są dostępne w panelu i w lg. Konfiguracja, której nikt nie mierzy, to konfiguracja, o której tylko się wierzy, że działa.

Sandbox: ćwicz zapisy bez dotykania produkcji

Przebieg próbny (dry_run) mówi ci, co polecenie BY zrobiło; ale to, czy idempotencja naprawdę odtwarza wynik, czy webhook naprawdę zostaje dostarczony, jak wyglądają gałęzie błędów i skąd pochodzą kredyty, widać dopiero przy PRAWDZIWYM zapisie. Do tego służy sandbox (środowisko testowe): osobny tenant odzwierciedlający twoją firmę — ten sam kod, ten sam katalog poleceń — który nigdy nie dotyka twojego produkcyjnego rejestru.

Sandbox to nie flaga żądania, tylko TENANT. Osobna firma Logistivo otwarta dla twojej: własny użytkownik, własny pakiet, własny rejestr kredytów, własne subskrypcje webhooków. Środowisko jest WYPROWADZANE z firmy, do której należy klucz, i wywołujący nie może go podać, więc nawet jeśli wybierzesz zły profil, żądanie zatrzymuje się po stronie serwera z 403 ENVIRONMENT_MISMATCH. Sytuacja „miało pójść do sandboxa, a poszło na produkcję” jest strukturalnie niemożliwa.

Powierzchnia jest identyczna — zmierzono to: katalog, który pobiera klucz sandboxowy, jest tym samym katalogiem, który pobiera twój klucz produkcyjny (dla klucza nadawcy po obu stronach to katalog z 448 poleceń). Most, który działa w sandboxie, działa na produkcji; niczego, czego się nauczysz, nie trzeba uczyć się dwa razy.

Sandbox nie otwiera się pusty: zawiera wygenerowane (NIE skopiowane) dane startowe — kontakty, ładunki w obu kierunkach i zapytania transportowe. Nazwy są jawnie zmyślone, a adresy e-mail należą do domeny @sandbox.invalid; żaden wiersz prawdziwego tenanta nigdy nie jest odczytywany, klonowany ani używany jako szablon.

Celowo NIE ma przycisku resetu i żaden rekord, plik ani dokument nigdy nie jest usuwany. Jeśli chcesz zacząć od czysta, odnowienie otwiera nową generację, a stara pozostaje — wycofana. Kilkaset nagromadzonych wierszy próbnych nic nie kosztuje w porównaniu z procedurą usuwania podpiętą do niewłaściwego tenanta.

Model poleceń

Jest jedna gramatyka i nie ma od niej wyjątków. Aby wiedzieć, jak wywołać polecenie, potrzebujesz tylko tego, co mówi o nim katalog.

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

Nazwy flag to nazwy parametrów ze schematu, a lg akceptuje obie pisownie: --load_code i --load-code trafiają do tego samego pola (lg help wypisuje formę kebab-case). Widząc flagę, wiesz więc, do którego pola JSON trafi, a przejście z CLI na HTTP nie wymaga tłumaczenia nazw. Parametr logiczny zapisany bez wartości oznacza true (--only_open); przeciwieństwem jest --no-only_open. Argument, którego wartość zaczyna się od myślnika, zapisuj jako --flag=value, inaczej zapomniana flaga po cichu połknie następną jako swoją wartość. W poleceniu przyjmującym plik poprzedź ścieżkę lokalną znakiem @ (--attachment_uuid=@./cmr.pdf) — zob. Polecenia przyjmujące pliki.

Prawdziwe przykłady

Każde poniższe polecenie istnieje dziś w katalogu; żadna nazwa ani parametr nie są tu wymyślone. Wyniki są skrócone. Pokazują poziom `data.result`; przez HTTP ten sam obiekt przychodzi wewnątrz koperty (envelope) `{success, data:{…}}` (zob. API poleceń przez HTTP).

Bez klienta: API poleceń przez HTTP

lg to wygoda, a nie jedyne drzwi. Katalog i wykonanie przechodzą przez trzy endpointy HTTP (pliki mają czwarty, endpoint przesyłania — zob. Polecenia przyjmujące pliki); integracja ERP lub CRM łączy się tutaj bezpośrednio. Tożsamością jest osobisty token dostępu przesyłany w Authorization: Bearer. KAŻDA odpowiedź przychodzi w tej samej kopercie: `{ "success": bool, "data": { … }, "code": "…", "message": { … } }`. Cały kontrakt mieszka wewnątrz `data` — `contract_version`, `command`, `status` oraz, jako wynik polecenia, `result`; w razie niepowodzenia `error_code`, `error_key` i `error`. Przykłady odpowiedzi na tej stronie, dla oszczędności miejsca, pokazują obiekt `data`, a nie kopertę; nie zapomnij rozpakować `data` we własnym kodzie. `lg --json` również wypisuje CAŁĄ kopertę, dlatego ścieżki jq zaczynają się od `.data.result…`. Jedynym wyjątkiem jest `/api/public/status`: ten endpoint nie ma koperty, aby watchdogi mogły alarmować bez jej parsowania.

Proszenie o potwierdzenie przez 200 + ok:false jest zabronione i nigdy się nie zdarzy. Powód jest prosty: klienci traktują 200 jako sukces, więc prośba o potwierdzenie zwrócona jako 200 po cichu przerwałaby przepływ i nikt by tego nie zauważył. Potwierdzenie to zawsze 409.

Polecenia przyjmujące pliki

Do 33 poleceń w tabeli przekazujesz dokument lub zdjęcie: dołączenie CMR do ładunku, odczytanie kodów HS z faktury, utworzenie pojazdu z dowodu rejestracyjnego, oszacowanie palety ze zdjęć, przesłanie dokumentu firmy, porównanie faktury z listą pakowania (packing list). W przeglądarce upuszczasz plik do formularza; wszędzie indziej trwa to dwa kroki — prześlij plik, a potem przekaż jego identyfikator poleceniu.

Parametr przyjmuje plik, gdy jego nazwa kończy się na attachment_uuid lub attachment_uuids — photos[].attachment_uuid wewnątrz listy, front_attachment_uuid na czeku. Nie musisz tego pamiętać: każdy wiersz katalogu zawiera file_params z dokładnymi ścieżkami, schemat oznacza te pola (format: uuid, x-logistivo-file: true), a odpowiedź katalogu zawiera blok attachments z adresem przesyłania, akceptowanymi typami, limitami rozmiaru i czasem życia. lg help również je pokazuje.

Przesyłanie to zapis, więc klucz z zakresem odczytu nie może przesyłać plików; może to zrobić każdy klucz z zakresem zapisu, na dowolnym poziomie dostępu. Przesłane pliki należą do użytkownika, a nie do klucza: dwa klucze tego samego użytkownika widzą te same pliki i nikt nie może użyć cudzych. Identyfikator nieznany, wygasły lub nienależący do ciebie po prostu nie zostaje znaleziony, a powód nie jest ujawniany.

Załączniki czatu w panelu i pliki przesłane dla poleceń to oddzielne przestrzenie: pliku dołączonego w czacie asystenta nie można przekazać poleceniu po identyfikatorze, a plik przesłany dla polecenia nigdy nie pojawia się w wątku czatu.

Przesłanie nie jest poleceniem i samo w sobie nie ma skutku biznesowego: z twoimi danymi nic się nie dzieje, dopóki polecenie nie użyje identyfikatora. Dlatego też przesyłanie nigdy nie prosi o potwierdzenie — bramkę niesie polecenie, które zużywa plik.

Bramka potwierdzenia i polecenia nieodwracalne

Każde polecenie w katalogu ma dwie flagi: confirmation_needed (czy przed uruchomieniem wymaga wyraźnej zgody) i irreversible (czy jego efekt można cofnąć). To osobne pytania i oba są egzekwowane na serwerze, a nie zostawione dobrym manierom klienta.

Bramka dotyczy odpowiedzialności, a nie reguł biznesowych. Złożenie oferty to pierwszy krok do umowy przewozowej; wystawienie faktury tworzy zapis księgowy; ruch magazynowy zmienia księgę. Żadnej z tych rzeczy nie powinno się dać uruchomić dwa razy przez przypadek.

Bramka jest odczytywana z polecenia FAKTYCZNIE wykonywanego. Gdy jedno polecenie opakowuje inne — jak robi to ogólny wykonawca w czacie — bramka pochodzi z flag polecenia, które naprawdę się wykona, a nie z opakowania. Przy stałej fladze jedna z dwóch awarii byłaby nieunikniona: albo polecenie faktury wykonałoby się bez potwierdzenia, albo każde wyszukanie kraju wywoływałoby kartę potwierdzenia.

read_only i brak bramki to nie to samo. Istnieją trzy różne stany: invoices preview nie ma bramki i niczego nie zapisuje; loads create ma bramkę i zapisuje; invoices create nie ma bramki, ale zapisuje — powstaje szkic. Rozstrzyga jeden test: czy po powrocie polecenia zmienił się jakiś wiersz w bazie danych?

Niektóre działania nie stoją za tą bramką, bo w ogóle nie są poleceniami: zakup pakietu, dodanie karty z 3D Secure, akceptacja tekstów prawnych i zamknięcie konta odbywają się we własnej przeglądarce danej osoby. Dla nich lg handoff-links generate zwraca link do logistivo.com — bezpłatnie, niczego nie zapisując, bez żadnego sekretu — a osoba kończy krok tam. Żadna flaga, niezależnie od ustawienia, nie zamienia płatności ani zgody w wywołanie API.

Technicznie integracja może automatycznie przechodzić przez bramkę przy każdym wywołaniu, ale nie jest to zalecane. Właściwy wzorzec to automatyzowanie bramki tylko dla poleceń, o których wiesz, że są odwracalne, i kierowanie nieodwracalnych do kolejki człowieka.

Przebieg próbny: zobacz, co zrobiłoby polecenie zapisu, bez jego uruchamiania

Najdroższy krok przy podłączaniu mostu ERP to sprawdzanie poprawności treści żądania przez FAKTYCZNE uruchomienie polecenia: szkic faktury w produkcyjnej księdze, spalony kredyt, ręczne sprzątanie. dry_run z tym kończy — dodajesz jedno pole do żądania, polecenie się nie wykonuje, a ty czytasz, co by się stało.

Rejestr składa się dziś z 519 poleceń; spośród nich 233 to polecenia wyłącznie do odczytu, które niczego nie zapisują. Spośród pozostałych 286 poleceń zapisu 251 to polecenia objęte bramką: wywołane bez confirm już odpowiadają 409 i opisują, co by zrobiły. Luka dotyczyła pozostałych 35: szkiców faktur, szkiców dokumentów eksportowych, pozycji — dokładnie tego łańcucha, który most podłącza jako pierwszy. W ich przypadku wywołanie oznaczało wykonanie.

Nawet w poleceniach z bramką była druga luka: walidacja schematu odbywa się PO bramce. Wywołanie z brakującym argumentem dostawało więc najpierw kartę potwierdzenia, było potwierdzane i dopiero wtedy słyszało „brakuje tego pola”. Przebieg próbny odpowiada na oba pytania w jednej wymianie.

A najczęstszy — i najpóźniej wykrywany — błąd mostu jest taki: wysyłasz pole, którego nie ma w schemacie (customer_id tam, gdzie oczekiwane jest recipient_contact_id), serwer po cichu je pomija, polecenie zwraca 200, a rekord ląduje w złym miejscu. Odpowiedź przebiegu próbnego wymienia takie pola z nazwy pod arguments.ignored; żaden komunikat błędu tego nie zrobi.

Przebieg próbny niczego nie zapisuje — i wynika to ze struktury, zanim jeszcze z pomiaru: ścieżka przebiegu próbnego nigdy nie wchodzi do handlera polecenia, więc dotyczy to każdego polecenia, także dodanych później. Zostało to też zmierzone przy wdrożeniu: przechwycono każdą instrukcję SQL wykonaną podczas przebiegów próbnych wszystkich ówczesnych poleceń zapisu w katalogu (dwa zestawy argumentów, dwie role, 178 przebiegów próbnych). Ani jednego INSERT/UPDATE/DELETE, ani jednego zadania w kolejce, ani jednego e-maila; liczby wierszy w 31 tabelach i saldo kredytów pozostały bez zmian.

Kod wyjścia jest taki sam, jaki dałoby prawdziwe wywołanie — wykonałoby się: 0, bramka potwierdzenia: 4, konflikt idempotencji: 2, wszystko inne: 1 — więc „lg … --dry-run || exit” jest sensowną bramką w kroku CI.

Idempotencja: dlaczego ponawianie nie tworzy duplikatów

ERP nie czyta twojego komunikatu o błędzie, nie widzi twojego ekranu i po przekroczeniu limitu czasu wyśle to samo żądanie ponownie. Ta sekcja istnieje właśnie z powodu tego zachowania.

Wywołanie z idempotency_key wykonuje się najwyżej raz. Drugie wywołanie nie wykonuje polecenia ponownie; zwraca zapisany wynik ze statusem replayed. Gdy zerwie się sieć, gdy żądanie przekroczy limit czasu albo gdy kolejka podejmie to samo zadanie dwa razy, dostajesz pierwszy wynik zamiast zduplikowanej faktury.

Używaj klucza w każdym poleceniu zapisującym. Pole read_only w katalogu daje ci to rozróżnienie za darmo: każde polecenie z read_only: false zasługuje na klucz idempotencji.

Twoja strategia ponowień powinna stosować wykładnicze wydłużanie odstępów (exponential backoff) i wysyłać TEN SAM klucz przy każdej próbie. Generowanie nowego klucza dla każdej próby to to samo, co nieużywanie idempotencji. Nie wysyłaj tego samego klucza równolegle: gwarancja „najwyżej raz” obowiązuje dla kolejnych ponowień, a nie dla dwóch żądań ścigających się ze sobą.

Synchronizacja przyrostowa i paginacja

Most ERP/CRM powinien pobierać to, co się zmieniło, a nie wszystko co godzinę. Każde polecenie listujące — loads list, demands list-own, contacts list, invoices list, teammates list, load-groups list, reservations list, drivers list, tenders list, products search, warehouses search — przyjmuje dwa wspólne parametry i zwraca jedno wspólne pole.

Dlaczego nie ma offsetu: na żywej tabeli offset się przesuwa. Wiersz wstawiony między dwiema stronami sprawia, że po cichu pomijasz rekord albo liczysz go dwa razy. Kursor przesuwa się tylko do przodu, więc rekord wstawiony lub zaktualizowany w trakcie przechodzenia ląduje na końcu listy: nic nie ginie, nic się nie dubluje.

Pełne zasilenie początkowe to ten sam mechanizm: zacznij od updated_since=1970-01-01T00:00:00Z i podążaj za next_cursor, aż będzie null. Nie ma osobnego endpointu „eksportu”, bo nie jest potrzebny.

To samo z lg: lg loads list --updated_since=2026-09-01T00:00:00Z --limit=100 --json, a potem lg loads list --cursor= --json.

Przy następnej synchronizacji ustaw updated_since na największą zaobserwowaną wartość updated_at. Znaczniki czasu mają precyzję jednej sekundy, więc rekord na granicy może przyjść dwa razy — to nieszkodliwe, o ile po swojej stronie wykonujesz upsert według external_ref lub code. Bez updated_since ani cursor polecenie działa w trybie czatu: kolejność czytelna dla ludzi, najwyżej 20 wierszy, next_cursor równe null i truncated sygnalizujące, że „jest więcej”. Nie używaj tego trybu do synchronizacji.

Maszynowo czytelne klucze błędów dla tej sekcji: INVALID_CURSOR, INVALID_UPDATED_SINCE, DUPLICATE_EXTERNAL_REF, EXTERNAL_REF_TOO_LONG, EXTERNAL_REF_INVALID. Wszystkie odpowiadają HTTP 422 z error_code: "failed" i result.error_key w treści. Opieraj rozgałęzienia na kluczu, nigdy na tekście.

Twój własny klucz: external_ref

Nie rób z naszych autoinkrementowanych identyfikatorów (invoice_id, contact_id) klucza biznesowego w swoim ERP; są nieprzezroczyste i prywatne dla twojej firmy. Ładunki, zapytania transportowe, kontakty, faktury, produkty i magazyny niosą zamiast tego twój klucz.

Webhooki wychodzące: wysyłamy do ciebie POST, gdy coś się wydarzy

Wszystko do tego miejsca działało w trybie pull: ty pytasz, my odpowiadamy. Most ERP musiał pytać co minutę, żeby dowiedzieć się, że ładunek zmienił status, wystawiono fakturę albo wpłynęła oferta. Już nie: zarejestruj adres https, wybierz zdarzenia, które cię interesują, a gdy się wydarzą, przyjdzie podpisany POST.

Push dodano OBOK pull, a nie zamiast niego. Synchronizacja przyrostowa (updated_since + cursor) pozostaje nietknięta i nadal jest właściwym narzędziem: pierwsze pełne zasilenie, uzupełnianie historii i odpowiedź na pytanie „czy coś mi umknęło” — wszystko to odbywa się tam. Webhooki skracają opóźnienie z minut do sekund. Zdrowy most używa obu — webhooka jako wyzwalacza, synchronizacji jako siatki bezpieczeństwa.

Payload to SYGNAŁ, a nie kopia rekordu. Zawiera identyfikatory (id, twój własny external_ref, status, czas); szczegóły pobierasz własnym kluczem przez GET, a to wywołanie przechodzi już przez twoje własne bramki autoryzacji. Powierzchnia ekspozycji pozostaje więc na poziomie identyfikatorów. Dwóch pól celowo brak: tożsamości firmy, która złożyła ofertę (oferty niejawne pojawiają się w panelu pod aliasem; umieszczenie tożsamości w webhooku przebiłoby tę prywatność drugim kanałem), oraz danych kontaktowych rezerwacji (nie ma potrzeby przesyłać danych osobowych dodatkowym kanałem).

Nazwy zdarzeń są ZAMROŻONE. Jeśli zmienia się znaczenie nazwy, otwierana jest nowa nazwa, a stara żyje przez okno wycofania. Dodanie nowego pola do payloadu NIE podnosi wersji kontraktu — konsument musi ignorować pola, których nie zna.

Katalog zdarzeń

Za każdym z 13 poniższych zdarzeń stoi klasa zdarzenia, która jest FAKTYCZNIE wysyłana. W katalogu nie ma nazw typu „byłoby miło”: zdarzenie ogłoszone, ale nigdy niewywołane, to zdarzenie, na które integrator czeka w nieskończoność, a ta awaria jest cicha. Ta tabela również nie jest pisana ręcznie — przy każdym załadowaniu strony jest rysowana z bieżącego katalogu.

invoice.payment_recorded jest celowo NIEOBECNE: ta klasa zdarzenia istnieje w kodzie, ale nie jest nigdzie wysyłana. Zdarzenie płatności zostanie dodane w dniu, w którym klasa będzie naprawdę wywoływana — nie wcześniej. Przedwczesne ogłoszenie zdarzenia jest gorsze niż niedodanie go nigdy.

Weryfikacja podpisu

Każde żądanie zawiera nagłówek X-Logistivo-Signature: t=,v1=. Podpisywany ciąg to ".", a algorytm to HMAC-SHA256. Prefiks v1 pozwala wersjonować schemat: jeśli kiedyś pojawi się drugi schemat, nagłówek będzie zawierał t=…,v1=…,v2=…, a ty wybierzesz ten, który znasz.

Publikujemy dwa referencyjne odbiorniki bez zależności, jeden dla Node i jeden dla czystego PHP. Oba stosują cztery powyższe reguły i testują się same przez --selftest: prawidłowy podpis, zmanipulowana treść, zły sekret, znacznik czasu poza oknem, nieprawidłowy nagłówek, pułapka ponownej serializacji i oba rodzaje powtórzeń (to samo dostarczenie / ten sam fakt). Weryfikator jest dokładnym odpowiednikiem kodu, którego sami używamy — korzystamy z kontraktu, który publikujemy.

Dostarczanie, ponowienia i wyciszanie

Twój odbiornik nie zawsze będzie dostępny. Mechanizm to zakłada: przejściowa awaria trafia do harmonogramu ponowień, trwała wycisza subskrypcję — a POWÓD wyciszenia jest zawsze zapisywany.

Powód wyciszenia jest maszynowo czytelny i stabilny (delivery_failures, endpoint_gone, unsafe_target, key_revoked, by_user) i widać go zarówno w panelu, jak i w `lg webhooks list`. Wyciszenie bez powodu sprawiłoby, że na pytanie „dlaczego nic nie przychodzi” nie byłoby odpowiedzi. Ponowne włączenie zeruje licznik i znacznik.

Adres piszesz ty; żądanie wykonuje NASZ serwer. Bez bramki tenant, który wpisałby adres metadanych chmury, mógłby zmusić nas do wysyłania żądań z wnętrza naszej własnej sieci. Dlatego adres jest sprawdzany zarówno przy rejestracji, jak i przy KAŻDYM dostarczeniu:

Stan i status: nikt nie powinien zauważyć cichego mostu przed tobą

Most integracyjny rzadko umiera z hukiem. Zwykle cichnie nad ranem: wygasa token, osoba, w imieniu której działa klucz, odchodzi z zespołu, zakres zostaje zawężony albo endpoint odbierający zaczyna zwracać 403. Pierwszy zauważa to prawie zawsze klient. lg status istnieje po to, by odwrócić tę kolejność.

Jedno polecenie odpowiada na dwa pytania naraz, bo podczas awarii oba padają w tej samej chwili: czy platforma działa i czy mój most jest zdrowy. Linia platformy działa bez tokenu — podczas awarii to właśnie token jest pierwszym podejrzanym, a uzależnienie od niego pytania o status zostawiłoby cię bez odpowiedzi dokładnie wtedy, gdy jej potrzebujesz.

Podsumowanie stanu nie prowadzi nowego rejestru; składa w jedno zdanie trzy istniejące źródła: dzienny licznik użycia (ile wywołań i ile nieudanych), rejestr żądań (które żądanie, kiedy i dlaczego zostało odrzucone) oraz rejestr dostarczeń webhooków wychodzących (czy zdarzenie dotarło do odbiornika). Współczynnik błędów jest zawsze odczytywany z dziennego licznika; rejestr szczegółowy ma limit i wyraźnie to komunikuje, gdy limit zostanie osiągnięty.

To samo podsumowanie pojawia się w panelu: w Settings › Integration › Keys każdy wiersz ma plakietkę, a pod nią powód. Progi nie są liczone po stronie klienta; poziom przypisany przez serwer jest tym, co rysują zarówno panel, jak i lg status, więc dwa ekrany nigdy nie mogą mówić różnych rzeczy o tym samym kluczu.

Twój watchdog nie potrzebuje tokenu. Endpoint zwraca tylko słowa statusu; nie zawiera wersji, nazwy hosta, nazwy kolejki, liczby zadań, tekstu błędu ani danych klientów — i nie może ich zawierać.

Podsumowanie stanu powstaje przy stałej liczbie zapytań na żądanie; liczba zapytań nie rośnie wraz z liczbą kluczy. Po szczegóły sięgnij do ostatnich żądań danego klucza przez lg logs, a do mostu wychodzącego — przez lg webhooks list.

Błędy i kody wyjścia

To, na czym maszyna opiera rozgałęzienia, jest stabilnym ciągiem znaków. Tekst w polu error jest przeznaczony dla człowieka, jest zlokalizowany i zmienia się bez uprzedzenia — integracja, która opiera rozgałęzienia na tekście komunikatu, nie jest wspierana, a jej zepsucie się nie jest błędem.

Dwie taksonomie, dwa pola. error_code to warstwa transportowa (tabela powyżej) i to na nim opierasz rozgałęzienia w pierwszej kolejności. error_key to stabilny klucz reguły biznesowej wewnątrz failed — INSUFFICIENT_CREDITS, NOT_FOUND, DUPLICATE_CONTACT, INSUFFICIENT_STOCK — publikowany jako słownik w katalogu (error_keys) i poniżej. Polecenie, które jeszcze nie emituje klucza, zwraca error_key: null; klucz nieobecny w słowniku traktuj jak ogólne niepowodzenie.

Obietnica stabilności

Najdroższe pytanie w kontrakcie integracyjnym brzmi: „czy kod, który piszę dziś, będzie działał jutro?”. Odpowiada na nie jedna liczba: contract_version w katalogu i nagłówek X-Contract-Version w każdej odpowiedzi. Odpowiada ona dokładnie na jedno pytanie — czy wcześniej napisany kod nadal działa bez zmian? Jeśli liczba nie wzrosła — tak. Jeśli wzrosła — nie: coś, na czym opierał się twój kod, zostało usunięte lub zawężone.

Bo ogłoszenie niczego nie usuwa: twój istniejący kod działa dokładnie tak jak wcześniej, także dzień później. Podnoszenie wersji przy ogłoszeniu dałoby dwa sygnały dla jednej zmiany i nauczyłoby cię ignorować pierwszy — osłabiając sygnał, który naprawdę ma znaczenie. Ogłoszenie i tak dociera wraz z wywołaniem, które wykonujesz: deprecated_at i replaced_by w wierszu katalogu, nagłówek X-Deprecated plus blok deprecated w odpowiedzi tego polecenia oraz jednowierszowe ostrzeżenie na stderr, jeśli używasz lg. Nie musisz nic robić, by dowiedzieć się, że coś zniknie; to już jest w twoim logu.

Między ogłoszeniem a usunięciem mijają co najmniej dwa kwartały kalendarzowe. Przez całe okno stare polecenie nadal odpowiada i ostrzega przy każdym wywołaniu. Gdy nazwa CLI zostaje wycofana, jej następca przejmuje tę nazwę jako alias: w trakcie okna stara nazwa trafia do starego polecenia, a w dniu usunięcia to samo wywołanie trafia do nowego — bez wydawania przez ciebie nowej wersji klienta.

preview_invoice → get_invoice (ogłoszone 3 września 2026). get_invoice zwraca ten sam blok podglądu i dodaje pozycje, rozbicie podatków, zarejestrowane płatności, saldo pozostałe do zapłaty oraz adresowanie twoim własnym kluczem ERP (external_ref). preview_invoice nadal działa, podobnie jak lg invoices preview — oba ostrzegają.

Tabela tożsamości poleceń

Poniższa tabela nie jest pisana ręcznie: jest generowana z bieżącego rejestru poleceń za każdym razem, gdy ta strona jest serwowana. Polecenie dodane na serwerze pojawia się tu samo. Kolumna „Bramka” mówi, czy polecenie prosi o potwierdzenie i czy jego efekt można cofnąć; kolumna „Role” mówi, które typy kont znajdą je w swoim katalogu. Plakietki oznaczają polecenia wymagające klucza na poziomie manager, polecenia przyjmujące plik oraz te, które działają tylko z lg.

Nazwa API (list_loads) jest zamrożona i nie zmieni się; to jej używasz w wywołaniach HTTP. Tożsamość CLI (loads list) podlega polityce wersjonowania: po ogłoszeniu zmienia się tylko wraz z podniesieniem wersji i oknem migracji, w trakcie którego katalog serwuje starą parę jako alias. Opisy tutaj pochodzą z własnego rekordu każdego polecenia i są skrócone — pełny tekst, typy parametrów i wartości enum znajdziesz w endpoincie katalogu albo po uruchomieniu lg help . Polecenia dostępne tylko dla afiliantów nie są tu wymienione; konto afilianta widzi je we własnym katalogu.

Integracja z ERP i CRM — Tożsamość: żadnych kont serwisowych, zamiast tego prawdziwy użytkownik

ERP lub CRM łączy się z Logistivo jako prawdziwy użytkownik firmy, posiadający osobisty token dostępu wygenerowany w panelu. Syntetyczne konto serwisowe — bez firmy, wyłączone z ograniczania zakresu, bez nikogo odpowiedzialnego — nigdy nie jest tworzone.

To architektura, a nie preferencja. Izolacja multi-tenant opiera się w całości na firmie użytkownika; dla tożsamości bez firmy zakres izolacji przestaje działać, a jedno błędne zapytanie z takim tokenem odczytuje dane, które do niego nie należą. Drugim powodem jest audyt: kolumna użytkownika w rejestrze wiąże „kto to uruchomił” z osobą; konto serwisowe czyni tę kolumnę bezużyteczną i zostawia integrację wystawiającą faktury bez właściciela. Trzecim jest unieważnianie: jeśli token należy do osoby, integracja milknie, gdy ta osoba odchodzi. Most bez właściciela to most, który latami po cichu dalej zapisuje.

Integracja z ERP i CRM — Wersjonowanie i wycofywanie

Odpowiedź katalogu zawiera liczbę całkowitą contract_version; wiersze poleceń zawierają since, deprecated_at i replaced_by. Wersja kontraktu rośnie tylko przy zmianach łamiących zgodność.

Integracja z ERP i CRM — Stabilne identyfikatory: nigdy nie przekazuj swojemu ERP autoinkrementowanego id jako klucza

Wewnętrzne autoinkrementowane identyfikatory są nieprzezroczyste i ograniczone do tenanta: prawidłowy identyfikator innej firmy zwraca ci „nie znaleziono”, a nie dane. Ale nieprzezroczystość nie czyni z nich kluczy biznesowych. To, co ERP przechowuje we własnym rekordzie, musi być kluczem biznesowym.

Integracja z ERP i CRM — Paginacja, filtrowanie i synchronizacja przyrostowa

Polecenia listujące przyjmują limit i zwracają count. Na potrzeby nocnej synchronizacji kontrakt zobowiązuje się do poniższego; polecenie listujące, które to wdrożyło, ogłasza cursor i updated_since w swoim JSON Schema, a dopóki tego nie zrobi, traktuj je jako małe okno (maksymalnie 20 wierszy, bez kursora).

Integracja z ERP i CRM — Limity żądań

Kluczem limitu jest token, a nie IP: ERP przychodzi zza jednego NAT-u, a limit oparty na IP liczyłby całą firmę jako jednego użytkownika. Dwie integracje z dwoma kluczami nigdy nie zjadają sobie nawzajem limitu — to drugi powód zasady „jedna integracja na klucz”.

Integracja z ERP i CRM — Czego nie zrobimy

To często najbardziej przydatna część kontraktu: wiedza o tym, co nigdy nie nadejdzie, pozwala zaprojektować rozwiązanie z uwzględnieniem tego.

Katalog do odczytu maszynowego

Ta strona została napisana dla ludzi. Dla asystenta AI lub zautomatyzowanego klienta te same informacje istnieją jako ustrukturyzowana kopia dostępna bez uwierzytelnienia.

GET /api/public/cli/catalog nie wymaga uwierzytelnienia i zwraca publiczną tożsamość poleceń: zamrożoną nazwę API, tożsamość CLI domena/czasownik, opis, parametry JSON Schema, flagi read_only, confirmation_needed i irreversible, credit_cost oraz pola dostępu key_level, permission, file_params, secret_parameters i cli_only. Nie zwraca żadnych danych osobowych, danych tenantów ani przykładowych rekordów — tylko to, czym jest ta powierzchnia.

Właściwy sposób korzystania z niego przez zewnętrznego agenta: przeczytać katalog, wyjaśnić WŁASNYMI słowami, jakie zadania są możliwe, a gdy potrzebne jest wykonanie — wskazać użytkownikowi, jak uruchomić polecenie we własnym środowisku z własnym tokenem. Agent nigdy nie prosi o token użytkownika, nie generuje go ani go nie przekazuje.

Podłączenie Logistivo do asystenta AI w celu rejestracji i składania zapytań transportowych to osobny kanał z własną stroną:

Definicja do odczytu maszynowego: OpenAPI 3.1 i Postman

Powyższy katalog to własny format Logistivo. Te same informacje są też publikowane w dwóch standardowych formatach branżowych, które twoje narzędzia już rozumieją: jako definicja OpenAPI 3.1 i jako kolekcja Postman. Żadna z nich nie jest pisana ręcznie — obie są generowane z bieżącego rejestru poleceń przy każdym żądaniu, więc nie mogą się zdezaktualizować.

Definicja JEST katalogiem, a nie drugą rzeczywistością. Każde polecenie występuje jako operacja POST /api/common/commands/; schemat treści to własny JSON Schema polecenia (bajt w bajt to, co widzi asystent), schemat odpowiedzi to wspólna koperta, flagi bramek (read_only, confirmation_needed, irreversible i plakietka kredytów) znajdują się w rozszerzeniu x-logistivo, a biznesowe klucze błędów — pod x-error-keys. Wycofane polecenie jest oznaczone jako deprecated: true i pozostaje wywoływalne przez sześciomiesięczne okno.

Obok powierzchni poleceń w definicji są też endpointy REST, które niosą kontrakt integracyjny: endpointy listujące z synchronizacją przyrostową (updated_since + cursor + limit) w liczbie 10 oraz endpointy zapisu przyjmujące twój własny klucz (external_ref) w liczbie 7. Powierzchnia REST Logistivo jest znacznie większa; resztę pominięto CELOWO. Te payloady są własnym kontraktem produktu, a schemat napisany na podstawie domysłów zepsułby twojego wygenerowanego klienta przy pierwszym wywołaniu — a błędu szukałoby się w twoim kodzie, nie w naszym.

Definicja jest generowana dla ROLI pytającego: polecenie innej roli nigdy nie trafia do dokumentu, bo definicja jest kontraktem, a nie słownikiem, i wszystko w niej musi być wywoływalne tym tokenem. Wersja bez uwierzytelnienia to suma trzech ról i tam każda operacja podaje pod x-logistivo.roles, które role ją widzą.

Nie wierz nam na słowo — sprawdź: npx @redocly/cli lint logistivo-openapi.json przechodzi bez żadnych ostrzeżeń.

Generowanie kodu: typowany klient z definicji

Masz definicję; teraz wygeneruj z niej działający kod. `lg codegen` czyta dokument OpenAPI i zapisuje jednoplikowego klienta bez zależności — TypeScript (deklaracje typów plus cienka nakładka wywołań) lub PHP (klasa klienta z kształtami tablic w PHPDoc). Bez `npm install`, bez composera: `fetch` po stronie TypeScript, `ext-curl` po stronie PHP.

Generowanie odbywa się na TWOIM komputerze, a nie na naszym serwerze. To nie kwestia preferencji: przypnij definicję w swoim repozytorium raz (`lg spec --out=...`), a to samo polecenie działa przy każdym buildzie bez sieci i bez tokenu, więc twoje CI nigdy nie zależy od dostępności naszych usług. A ponieważ wygenerowany kod to kod, który DZIAŁA w twoim ERP, trzymanie go w repozytorium zamiast pobierania z API sprawia, że każda zmiana jest widoczna w `git diff`.

Wygenerowany klient poprawnie obsługuje wszystkie trzy tory Logistivo i dowiaduje się, który jest który, z dokumentu, a nie przez zgadywanie: każda operacja deklaruje swój tor w `x-logistivo.surface`. Tor poleceń rozpakowuje kopertę `{success, data:{…}}`; lista synchronizacji REST NIE ma koperty i jest zwracana bez zmian (rozpakowanie usunęłoby `next_cursor`); zapis REST zwraca własną treść produktową endpointu. Generator ogólnego przeznaczenia nie zna tego rozróżnienia — nasz zna.

Typy nie są zgadywane: interfejs argumentów każdego polecenia jest generowany z tego samego JSON Schema, które widzi asystent. Wymagane pozostaje wymagane, enumy stają się typami unii, zagnieżdżone obiekty — zagnieżdżonymi typami. W praktyce: jeśli serwer wymaga pola, wywołanie, które o nim zapomni, nie zawiedzie z 422 na produkcji — zawiedzie podczas kompilacji.

Do pliku nie jest zapisywany znacznik czasu generowania: ta sama definicja z tymi samymi flagami daje te same bajty. Ponowne generowanie nie robi więc szumu w `git diff`, a jedyna rzecz, która naprawdę się zmieniła — kontrakt — pozostaje widoczna.

Uczciwy zakres

Wolimy tę sekcję rozbudowywać niż skracać. Wartość kontraktu integracyjnego tkwi nie tyle w tym, co obiecuje, ile w tym, jak jasno mówi, czego nie obiecuje.

Najczęściej zadawane pytania

Do czego służy Logistivo CLI?

Wykonuje pracę na twoim koncie Logistivo z terminala, ze skryptu lub z twojego własnego agenta AI: ładunki i kursy, zapytania transportowe, oferty i przetargi, flota, kierowcy i ich wydatki, dokumenty, kontakty i rozrachunki, faktury i księgowość, magazyn i stany magazynowe, członkowie zespołu, taryfy HS i cła antydumpingowe. Każda operacja zapisu w panelu ma swoje polecenie; płatności, zgody prawne i zamknięcie konta celowo pozostają w przeglądarce. Te same polecenia można wywołać przez HTTP w integracjach ERP/CRM oraz przez kanał MCP.

Jak nazywane są polecenia w Logistivo CLI?

Kształt to lg --parameter=value. Domena to encja w liczbie mnogiej (loads, demands, invoices, export-documents), a czasownik pochodzi z zamkniętego słownika, który rośnie tylko w wyniku świadomej decyzji (list, get, create, update, search, set-status, generate i tak dalej). Wszystkie czasowniki jednej encji mieszkają w jednej domenie, a para (domena, czasownik) jest unikalna w całym katalogu.

Jak uwierzytelnić się w CLI?

Interaktywnie lg login wyświetla kod zatwierdzenia, który potwierdzasz w przeglądarce po zalogowaniu się do Logistivo — do terminala nie wpisuje się ani hasła, ani klucza, a sesja pozostaje zalogowana i sama się odnawia, dopóki nie uruchomisz lg logout. lg login --password (e-mail i hasło) pozostaje jako rozwiązanie awaryjne. Na serwerach i w CI używaj klucza integracyjnego wydanego w panelu lub poleceniem lg keys create: lg login --api-key zapisuje go w lokalnym profilu, możesz też umieścić go w zmiennej środowiskowej LOGISTIVO_TOKEN. Przez HTTP token jest przesyłany w nagłówku Authorization: Bearer.

Czy mogę utworzyć konto serwisowe dla integracji z ERP?

Nie. Integracja łączy się tokenem prawdziwego użytkownika firmy utworzonego w panelu. Syntetyczna tożsamość bez firmy wyłącza izolację multi-tenant, czyni bezużyteczną kolumnę „kto to uruchomił” w rejestrze audytu i zostawia most, który nie milknie, gdy jego właściciel odchodzi. Właściwy wzorzec to dedykowany użytkownik z wąską rolą i konkretnym, wskazanym z nazwiska właścicielem.

Czy Logistivo ma środowisko testowe (sandbox)?

Tak, i jest ono samoobsługowe: dla twojej firmy otwierany jest osobny tenant sandbox — ten sam kod, ten sam katalog poleceń, własny rejestr kredytów. Środowisko wynika z firmy, do której należy klucz, i nie można go podać w żądaniu; wywołanie z niewłaściwym profilem zatrzymuje się po stronie serwera z 403 ENVIRONMENT_MISMATCH. Zapytanie transportowe otwarte w sandboxie nigdy nie trafia do prawdziwych przewoźników, zdarzenie z sandboxa nigdy nie jest dostarczane do twojej produkcyjnej subskrypcji webhooków, a twoje produkcyjne saldo się nie zmienia. Endpointy: POST /api/common/integration-sandbox (otwarcie), POST /api/common/integration-sandbox/keys (klucz), GET /api/common/integration-sandbox/go-live (lista kontrolna go-live). Po stronie lg flagi lg --sandbox i lg --live wybierają profil dla pojedynczego uruchomienia.

Jak odczytać wyjście lg --json w skrypcie?

Wyjście to cała koperta HTTP: { "success": bool, "data": { "contract_version", "command", "status", "result" }, "code", "message" }. Dane biznesowe (payload) znajdują się pod .data.result; w poleceniach listujących wiersze to .data.result., liczba to .data.result.count, a następna strona to .data.result.next_cursor. Poprawna ścieżka jq wygląda więc tak: lg loads list --json | jq -r '.data.result.loads[].load_code'. Ścieżka pomijająca kopertę nie zgłasza błędu, tylko po cichu zwraca null — w skrypcie typu watchdog staje się to alarmem, który nigdy nie przestaje się włączać. Jedynym wyjątkiem jest /api/public/status, którego treść celowo nie ma koperty.

Jeśli wyślę to samo żądanie dwa razy, czy powstaną zduplikowane rekordy?

Nie, jeśli wysłano idempotency_key. Drugie wywołanie z tym samym kluczem nie uruchamia polecenia ponownie; zwraca pierwszy wynik ze statusem replayed. Klucz powinien być UUID i musi pozostać TEN SAM przy kolejnych ponowieniach — generowanie nowego klucza dla każdej próby to to samo, co nieużywanie idempotencji.

Co robi --yes?

Przechodzi przez bramkę potwierdzenia. Gdy polecenie objęte bramką zostanie wywołane bez --yes, nic się nie zmienia: serwer w jednym zdaniu podsumowuje, co zamierza zrobić, mówi, czy jest to nieodwracalne, i się zatrzymuje (HTTP 409, kod wyjścia CLI 4). Powtórzenie polecenia z --yes wykonuje pracę.

Jak wykryć w skrypcie nieudane polecenie?

Czytaj kod wyjścia, nigdy tekst komunikatu: 0 sukces, 1 odrzucenie przez regułę biznesową, 2 błąd polecenia lub argumentu, 3 błąd uwierzytelnienia lub roli, 4 wymagane potwierdzenie. Przez HTTP odpowiednikami są 200, 422, 404, 403 i 409, a pole error_code w treści jest stabilne. KOMUNIKAT błędu jest zlokalizowany i zmienia się bez uprzedzenia.

Jak programowo odkryć dostępne polecenia?

GET /api/public/cli/catalog do wykrywania bez uwierzytelnienia oraz GET /api/common/commands dla listy, którą twoje konto faktycznie widzi. Oba zwracają parametry JSON Schema każdego polecenia; buduj klienta na ich podstawie, zamiast kodować listę poleceń na sztywno.

Czy muszę aktualizować klienta, gdy dodawane jest nowe polecenie?

Nie. Klient pobiera listę poleceń z katalogu, więc polecenie dodane na serwerze pojawia się bez nowego wydania klienta. Aktualizacja jest potrzebna tylko przy zmianie łamiącej zgodność, która podnosi wersję kontraktu, a wtedy stary kształt pozostaje aktywny przez co najmniej sześć miesięcy.

Czy CLI zastępuje istniejące REST API?

Nie. Powierzchnia REST, na której działają aplikacje webowe i mobilne, pozostaje na miejscu. Katalog poleceń nie zastępuje CRUD-a zasobów; to warstwa czasowników — jedyny sposób wykonania działania, objęty bramką ról, bramką potwierdzenia i audytem.

Jak uzyskać klucz API Logistivo i co może on robić?

Menedżer z uprawnieniem do zarządzania zespołem wydaje go w panelu w Settings › Integration keys albo z terminala poleceniem lg keys create. Klucz działa w imieniu prawdziwego członka zespołu i ma trzy niezależne ustawienia: zakres (read otwiera katalog i polecenia niezapisujące, write otwiera każde polecenie — bramka potwierdzenia pozostaje bez zmian), opcjonalne uprawnienia domenowe oraz poziom dostępu — employee, domyślny, do codziennych operacji, lub manager do spraw zespołu, ustawień, kluczy i pieniędzy. Jest pokazywany raz i nie jest przechowywany na serwerze; można go rotować, przenosić i unieważniać. Gdy członek, w imieniu którego działa, odchodzi, klucz również zostaje zamknięty. Nie ma konta serwisowego.

Co się stanie, jeśli użyję klucza idempotencji ponownie z innym poleceniem?

Serwer odpowiada 409 idempotency_conflict i podaje polecenie, z którym klucz został użyty po raz pierwszy; nic się nie wykonuje i nic nie jest odtwarzane. Klucze są ograniczone do twojej firmy, powiązane z jednym poleceniem i zablokowane na 24 godziny — potem ten sam klucz rozpoczyna nowe wykonanie.

Jak limity żądań są stosowane do mojej integracji?

Per token, na minutę, w trzech pulach: odczyty katalogu 120, polecenia odczytu 120, polecenia zapisujące 30. 429 zawsze zawiera Retry-After oraz treść z retry_after, limit i bucket; odczekaj tyle i ponów. Katalog publikuje aktualne wartości pod rate_limits.

Jak odróżnić odrzucenie biznesowe od błędu transportowego?

Najpierw opieraj rozgałęzienia na error_code: failed (422) to odrzucenie biznesowe i niesie stabilny error_key, taki jak INSUFFICIENT_CREDITS lub NOT_FOUND; server_error (5xx) i błędy sieci ponawiasz z tym samym kluczem idempotencji; przy rate_limited (429) odczekujesz. Nigdy nie opieraj rozgałęzień na tekście błędu.

Czy Logistivo wysyła webhooki? Dla jakich zdarzeń?

Tak. Rejestrujesz adres https i wybierasz zdarzenia, które cię interesują; gdy się wydarzą, przychodzi podpisany POST. Liczba zdarzeń, które można dziś subskrybować: 13 — ładunek: utworzenie/zmiana statusu/przypisanie kierowcy; zapytanie transportowe: utworzenie i zmiana statusu; oferta: złożenie/korekta/akceptacja; faktura: szkic/wystawienie/anulowanie; rezerwacja magazynowa: utworzenie/zmiana statusu. Za każdą nazwą stoi klasa zdarzenia, która jest faktycznie wysyłana — nic nie jest ogłaszane, by potem nigdy nie zostać wywołane, bo takie zdarzenie to cicha awaria, na którą integrator czeka w nieskończoność. Subskrypcje tworzy się w Settings › Integration › Webhooks albo z terminala poleceniem lg webhooks create.

Jak zweryfikować podpis webhooka Logistivo?

Każde żądanie zawiera nagłówek X-Logistivo-Signature: t=,v1=. Podpisywany ciąg to ".", algorytm to HMAC-SHA256, a kluczem jest sekret subskrypcji. Cztery zasady: (1) weryfikuj SUROWE bajty — dekodowanie i ponowna serializacja treści (JSON.stringify / json_encode) zamienia te same dane w inne bajty i podpis przestaje się zgadzać; to najczęstszy błąd; (2) sprawdzaj znacznik czasu i odrzucaj wszystko, dla czego |now − t| > 300 s, nawet jeśli podpis jest poprawny; (3) porównuj w stałym czasie za pomocą hash_equals / timingSafeEqual; (4) deduplikuj po event_id, a nie po id dostarczenia — event_id jest zachowywany przy ponownym wysłaniu, a id dostarczenia się zmienia. Działające odbiorniki w Node i PHP znajdują się w repozytorium w examples/webhook-receiver/ i testują się same przez --selftest.

Co się stanie, jeśli webhooka nie da się dostarczyć? Czy stracę zdarzenie?

Nie. Jeśli twój odbiornik nie zwróci 2xx, dostarczenie trafia do harmonogramu ponowień: 60 · 300 · 1800 · 7200 · 21600 s, łączna liczba prób: 6, okno około 8,6 h. KAŻDA awaria poza 410 Gone jest ponawiana, łącznie z 4xx — utrata zdarzenia biznesowego kosztuje więcej niż kilka dodatkowych żądań. Po 10 kolejnych dostarczeniach, które wyczerpały ponowienia, subskrypcja jest wyciszana, a powód zapisywany, więc na pytanie „dlaczego nic nie przychodzi” wciąż da się odpowiedzieć. Rejestr dostarczeń przechowuje każdą próbę (kod statusu, czas trwania, błąd), a pojedyncze dostarczenie możesz wysłać ponownie z panelu lub poleceniem lg webhooks replay. Synchronizacja przyrostowa (updated_since + cursor) nadal pozostaje siatką bezpieczeństwa.

Czy mój agent AI może zrobić wszystko, co użytkownik może zrobić w panelu?

Prawie wszystko. Każda operacja zapisu w panelu ma swoje polecenie — w pomiarze względem endpointów zapisu panelu pokrytych jest 100% endpointów objętych zakresem — a te same polecenia są dostępne z lg, przez HTTP i przez kanał MCP. Celowe wyjątki to kroki dotyczące tożsamości i pieniędzy: logowanie, zakup pakietu, dodanie karty z 3D Secure, akceptacja tekstów prawnych i zamknięcie konta pozostają w przeglądarce danej osoby, a lg handoff-links generate zwraca dla nich link. Kluczami integracyjnymi, webhookami i sandboxem zarządza się wbudowanymi poleceniami lg, bo zwracają one sekrety jednorazowo.

Czy mogę przekazywać do poleceń pliki, takie jak PDF-y lub zdjęcia, z CLI lub API?

Tak. W lg poprzedź ścieżkę lokalną znakiem @ (--attachment_uuid=@./cmr.pdf), a lg najpierw prześle plik; --file to skrót, gdy polecenie ma jedno pole plikowe, a lg upload tylko przesyła plik. Przez HTTP wyślij plik jako multipart do POST /api/common/commands:upload i przekaż zwrócony attachment_uuid poleceniu; klienci MCP używają narzędzia upload_attachment. Typ jest rozpoznawany na podstawie zawartości, przesłane pliki należą do użytkownika i pozostają użyteczne przez 24 h, a ponowne przesłanie tego samego pliku zwraca ten sam identyfikator.

Czym różni się klucz integracyjny employee od klucza manager?

Poziom dostępu decyduje o tym, jak poważną pracę może wykonywać klucz. Klucz employee, domyślny dla nowych kluczy, wykonuje codzienne operacje: ładunki, zapytania transportowe, oferty, kursy, flota, dokumenty i kontakty. Klucz manager może też wykonywać prace zarządcze — członkowie zespołu i role, ustawienia firmy, klucze integracyjne, konta bankowe, zapisane karty, wypłaty dla afiliantów. Klucz nigdy nie ma większych uprawnień niż osoba, w imieniu której działa, a klucz employee wywołujący polecenie na poziomie manager dostaje INSUFFICIENT_KEY_LEVEL. Poziom zmienisz w panelu lub poleceniem lg keys update --access-level=manager.

Czy afiliant (partner polecający) może korzystać z CLI?

Tak. Konto afilianta dostaje własny katalog z 55 poleceniami — dla rekomendacji (referrals), leadów, notatek i zadań CRM, prezentacji sprzedażowych, zarobków i wypłat — i korzysta z niego przez lg, HTTP lub MCP jak każda inna rola. Zlecenie lub anulowanie wypłaty oraz zmiana konta bankowego do wypłat wymagają klucza na poziomie manager i potwierdzenia. Firma, która jest też nadawcą, przewoźnikiem lub agencją celną, widzi oba katalogi.

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.