Logistivo CLI:物流オペレーションをターミナルから、そして ERP から実行

1 つのコマンドカタログが 3 つの窓口を支えています。lg ターミナルクライアント、ダッシュボード内の AI アシスタント、そして外部 AI アシスタントが接続するチャネルです。機能は一度書かれると 3 つすべてに現れます。さらに、パネルのすべての書き込み操作に対応するコマンドが揃ったため、チームが画面上で行っている作業を、スクリプトや自社の AI エージェントが実行できます。決済、法的同意、アカウントの閉鎖は意図的にブラウザに残しています。 このページは契約(コントラクト)です。下のコマンド表は手書きではなく、リクエストのたびにライブのコマンドレジストリから生成されます。

Logistivo CLI とは何ですか?

Logistivo CLI(lg)は、Logistivo アカウントの業務(貨物と運行、輸送依頼、見積もりと入札、車両とドライバー、書類、取引先と取引先元帳、請求書と会計、倉庫と在庫、チームメンバー、関税率)を、ターミナル、スクリプト、または自社の AI エージェントから実行するコマンドラインクライアントです。コマンドはクライアントに組み込まれていません。クライアントはサーバーからコマンドカタログを取得し、それをもとに自分自身を組み立てます。同じコマンドは何もインストールせずに HTTP 経由でも呼び出せます(ERP・CRM 連携が使うのはこの経路です)。また、外部 AI アシスタントが接続する MCP チャネルからも呼び出せます。

現在稼働しているもの、そして準備中のもの

このセクションは意図的にページの先頭に置いています。連携ドキュメントが犯しうる最も高くつく誤りは、まだ公開されていないエンドポイントを公開済みであるかのように説明することです。連携担当者がコードを書き、エンドポイントが 404 を返し、信頼は二度と戻りません。

コントラクトは凍結されているため、今日書いた連携コードは、残りの部分が公開されても動き続けます。知らないフィールドは無視しなければなりません。このルールがあるからこそ、互換性を壊さずに追加できるのです。

何が違うのか:クライアントはコマンドを知らず、カタログから描き出す

これは、このページの残りすべてに意味を与える唯一の設計判断です。一般的な CLI ではコマンドはクライアントの中にありますが、Logistivo ではサーバー上にあります。

従来の構成では、サーバーに機能が加わるとクライアントの新しいリリースが必要になり、ユーザーはそれをインストールしなければなりません。その間、両者が知っている内容は食い違います。これこそ連携で最も気づかれにくい失敗パターンです。スクリプトは動き、終了コードは 0 なのに、コマンドはサーバーにできることを正しく表していないのです。

Logistivo では、コマンドの定義は 1 つのレコードです。名前、何をするか、JSON Schema パラメータ、どのロールが見られるか、確認が必要か、その効果を元に戻せるか。lg は起動時にこのカタログを取得し、ヘルプテキスト、フラグ、入力バリデーションをそこから生成します。サーバーでコマンドが追加された瞬間に、クライアントを更新しなくても lg はそれを知っています。

同じカタログが、ダッシュボードの AI アシスタントと、外部 AI アシスタントが接続するチャネルも支えています。機能は一度書かれると 3 か所に現れ、3 つすべてが同じロールゲート、同じ確認ゲート、同じ監査台帳を通ります。CLI 専用のコマンド一覧は別に存在しません。もしあれば、3 つの窓口は半年もしないうちに静かに食い違っていくでしょう。

命名ルール:コマンド名はどのように増えていくか

カタログは今後も成長します。新しいコマンドはその場しのぎで命名されるのではなく、6 つのルールに従います。ここで公開するのは、これらがあなたの連携コードで何年にもわたって書かれ続ける名前であり、その予測可能性があなたにとって価値を持つからです。

カバレッジ:パネルのすべてを、コマンドから

多くのチームが Logistivo を自社の AI エージェントや自動化の仕組みに組み込んでいます。エージェントは、仕事を最後までやり遂げられて初めて役に立ちます。そこでルールは単純です。オフィスのユーザーがパネルで変更できるものは、コマンドでも変更できます。同じロールゲート、同じ確認ゲート、同じ監査台帳のもとで。

これは主張ではなく測定結果です。パネルの背後にあるすべての書き込みエンドポイントをコマンドレジストリと照合しており、あるエンドポイントがカバー済みと数えられるのは、コマンドがそれを自身の HTTP ミラーとして宣言しているか、同じ共有サービスを呼び出している場合だけです。対象範囲内の書き込みエンドポイントは 100% カバーされており、未検証のものはありません。除外したすべてのエンドポイントには、理由が文書で記されています(本人確認と決済のステップ、連携のコントロールプレーン、ドライバーアプリ専用のエンドポイント、クライアントのテレメトリー、呼び出し元のない使われていない複製)。連携担当者にとって重要なものは以下に挙げています。

ファイルを受け取るコマンドも同じルールに従います。表のうち 33 個のコマンドが書類や写真を受け取り、そのすべてが lg、HTTP、MCP から動作します(「ファイルを受け取るコマンド」を参照)。

管理業務も、より強いキーのもとでカバーされています。表のうち 62 個のコマンド(チームメンバーとロール、企業設定、銀行口座、保存済みカード、連携キー)には、manager レベルの連携キーが必要です。デフォルトの employee レベルのキーは日常業務を実行し、これらのコマンドは INSUFFICIENT_KEY_LEVEL で拒否されます。

アフィリエイト(紹介パートナー)アカウントには独自のカタログがあります:20 ドメインにわたる 55 個のコマンドで、紹介、リード、CRM のメモとタスク、営業資料、収益と支払いに加え、すべてのロールが持つアカウントとチームのコマンドを含みます。アフィリエイト専用のコマンドは、パネルの 3 つのロールを掲載する下の公開表には含まれていません。アフィリエイトアカウントでは GET /api/common/commands と lg commands で確認できます。アフィリエイトであると同時に荷主、運送会社、通関業者でもある企業には、両方のセットが表示されます。

エージェントは lg users get(API 名 get_current_user)から始めるべきです。ロール、使用中のキーのアクセスレベル、実効権限、そして保留中の法的同意や未認証の電話番号が現在書き込みを妨げていないかを返すため、エージェントは拒否を集めるのではなく、予測できます。

カバレッジは、コマンドレジストリとパネルの書き込みエンドポイントを比較するレポートで測定されます。コマンドなしで追加されたパネルのエンドポイントは、そのレポートに未検証として現れます。コマンドから到達できないパネルの操作を見つけた場合は、サポートに報告してください。

インストールと認証

lg は依存関係のない単一ファイルの Node スクリプト(Node 18 以上)で、npm で @lgstv/lg として公開されています。パッケージは同一の 2 つのコマンド lg と logistivo をインストールします。お使いのマシンで lg がすでにエイリアスとして使われている場合は logistivo を使ってください。単一ファイルなので、lg.mjs を顧客の ERP サーバーにコピーするだけでも動作します。ERP/CRM 連携の構築にクライアントは必要ありません。同じコマンドが HTTP で利用できます。

1. インストール — 必要なのは Node 18 以上だけで、ほかに依存関係はありません。グローバルにインストールするか、npx でインストールせずに実行します。

2. 承認コードでサインインする — lg login は短い承認コードを表示し、ブラウザで logistivo.com/connect を開きます。SSH 経由や CI の中、または --no-browser を付けた場合はアドレスを表示するだけなので、スマートフォンを含む任意のデバイスでそのアドレスを開いてください。そこでいつもどおりサインインし、ページに表示されたコードがターミナルのコードと同じであることを確認したうえで、「読み取り専用」か「読み取りと操作」のアクセスを選んで承認します。ターミナルにシークレットを入力することは一切ありません。パスワードも、キーも、ワンタイムコードも不要です。その後、セッションはそのコンピューターでサインインしたまま維持されます。ターミナルを閉じてもサインアウトされず、lg はアクセストークンの有効期限が切れる前に自動で更新します。lg logout はサーバー側でもセッションを終了させます。また、パネルの Settings › Integration keys › AI and CLI connections から削除することもできます。lg login --password(メールアドレスとパスワード)は代替手段として引き続き使えます。lg 0.11 以降が必要です。

3. サーバーと CI では:承認コードではなくキーを — サーバーや CI ジョブには、ブラウザでコードを承認する人がいません。パネル(Settings › Integration keys)または lg keys create でキーを発行し(必要に応じて、権限を絞った専用のチームメンバーの代理として)、LOGISTIVO_TOKEN に設定するか、lg login --api-key で保存します。この変数が設定されている間、設定ファイルは一切読み込まれません。詳しくは「API キーで接続する」セクションを参照してください。

4. 確認してカタログを見てみる — lg commands は、自分のロールで見られるすべてのコマンドをドメインごとに一覧表示します。lg help はそのドメインの動詞を表示し、lg help は 1 つのコマンドの全パラメータを、型と必須かどうかとともに表示します。これらのテキストはどれもクライアントの中にはなく、すべてカタログから来ています。一覧はロールによって異なることに注意してください。同じドメインでも、運送会社と荷主とでは動詞が異なります(たとえば loads set-status は運送会社のカタログにしかありません)。動詞が見つからない場合は、インストールを確認する前にロールを確認してください。

キーをバージョン管理、CI ログ、チャット画面に決して置かないでください。承認コードまたは連携キーでサインインした場合、lg logout はサーバー側でもそれを終了させます。パスワードセッション(lg login --password)では「ローカル」のコピーを削除するだけで、そのことを明示的に表示します。監査台帳は引数を保存しますが、名前に token、password、IBAN、card、OTP を含むフィールドと、コマンドがシークレットと宣言したすべてのパラメータを *** でマスクします。

クライアントの使い勝手

これらの機能は `lg` クライアントの日常的な利用を楽にします。どれも HTTP コントラクトを変更しないため、HTTP で直接連携する場合はこのセクションを読み飛ばしてかまいません。

API キーで接続する

ERP、CRM、スクリプト、AI エージェントは、パネルまたは lg keys create で発行した連携キーで Logistivo に接続します。キーは実在するチームメンバーの代理として動作します。その人のロールがコマンド一覧を決め、キーのアクセスレベルが作業の重さを決め、その人がチームを去るとキーも一緒に無効になります。合成の「サービスアカウント」は存在せず、今後も作りません。マルチテナントの分離はユーザーに結びついており、持ち主のいないブリッジこそ、何年も黙って書き込み続けるものだからです。

1. パネルでキーを発行する — Settings › Integration keys › New key を開きます。名前(例:「ERP 夜間同期」)を付け、スコープとアクセスレベルを選び、必要に応じて有効期間を短くします。キーはその画面で一度だけ表示され、サーバーには保存されません。紛失した場合は Rotate で新しいキーが発行され、古いキーは即座に無効になります。

2. ターミナルから接続する — lg login --api-key はキーを検証し、ID とスコープを表示し、コマンドカタログをダウンロードします。lg whoami は、どのユーザー、企業、ロール、スコープで実行しているかをサーバーに問い合わせます。lg logout は、承認コードのセッションをサーバー側で終了させるのと同じように、サーバー側でキーも失効させます(パスワードセッションの lg logout だけはそれができません)。

3. または素の HTTP で — キーは Authorization: Bearer ヘッダーで送るアクセストークンで、3 つのコマンドエンドポイントに対して直接使えます。サーバーや CI では LOGISTIVO_TOKEN に設定してください。この変数が設定されている間、設定ファイルは一切読み込まれません。

4. またはターミナルからキーを管理する:lg keys — lg keys はパネル画面のターミナル版です。エンドポイントも同じ、ゲートも同じです(チーム管理権限と、キーで呼び出す場合は manager レベルのキー)。create、rotate、transfer は新しいトークンを一度だけ返します。--save-profile=NAME を付けるとトークンはそのままローカルのプロファイルに入り、表示されることはありません。付けない場合、lg はトークンを一度だけ表示します。--json モードでは、--show-secret を付けた場合にのみトークンが含まれます。どちらのフラグも付けない --json の呼び出しはリクエスト送信前に拒否されるため、トークンが黙ってスクリプトの出力に紛れ込むことはありません。rotate、transfer、revoke は確認(--yes)を求めます。キーは正確な名前か uuid で指定し、推測で指定されることは決してありません。アクセスレベルは、ローテーションまたは移譲されたキーにそのまま引き継がれます。

スコープはコマンドカタログだけでなく、API の窓口全体で強制されます。読み取り専用のキーは読み取りリクエスト(GET/HEAD/OPTIONS)を通し、それ以外の書き込みリクエストにはすべて 403 forbidden で応答します。意図的な例外が 2 つあり、どちらもより細かいゲートで守られています。コマンド実行エンドポイント(書き込みを伴わないコマンドも POST で呼び出されるため、判断はコマンド自身の read_only フラグに基づきます)と、キーが自分自身を失効させる操作(lg logout。読み取り専用のキーでも許可されます)です。スコープ、ドメイン許可、アクセスレベルは 3 つの独立した軸で、すべてを通過しなければなりません。read キーは manager レベルでも書き込めず、employee の write キーはチームを管理できず、loads:write のドメイン許可で管理業務が開放されることはありません。必要な範囲をカバーする、最も権限の小さいメンバーの代理としてキーを発行してください。そもそもどのレコードを見られるかは、ロールが決めます。

最初の 10 分間:パネルから最初の呼び出しまで

連携で最も高くつくのは最初の 10 分間です。以下の 4 つのステップは、その道のりを順に示しています。同じ順序はパネルにもウィザード(Settings › Integration › Setup wizard)として用意されており、ウィザードはここで説明するエンドポイントをそのまま呼び出します。ウィザードを使えない開発者も、このセクションを読めば同じ地点にたどり着けます。

4 つのステップのどれも「送信した」で終わりません。キーが本当に機能しているかはリクエスト台帳が答え、Webhook が本当に届いているかは配信台帳が答えます。どちらもパネルと lg で確認できます。一度も測定されない設定は、動くと信じられているだけの設定です。

サンドボックス:本番に触れずに書き込みを練習する

プリフライト(dry_run)は、コマンドが何を「するか」を教えてくれます。しかし、冪等性が本当にリプレイされるか、Webhook が本当に配信されるか、エラー分岐がどう見えるか、クレジットがどこから引かれるかは、「実際の」書き込みでしか確かめられません。そのためにあるのがサンドボックスです。自社を映した独立したテナントで、コードもコマンドカタログも同じですが、本番の台帳には決して触れません。

サンドボックスはリクエストのフラグではなく、「テナント」です。自社のために開設される独立した Logistivo 企業で、独自のユーザー、独自のパッケージ、独自のクレジット台帳、独自の Webhook 購読を持ちます。環境はキーの所属企業から「導出」され、呼び出し側が指定することはできません。そのため、誤ったプロファイルを選んでも、リクエストはサーバー側で 403 ENVIRONMENT_MISMATCH となって停止します。「サンドボックスに書き込んだつもりが本番に書き込んでいた」ということは、構造上起こりえません。

窓口は同一です。これは測定済みです。サンドボックスキーで得られるカタログは、本番キーで得られるものと同じです(荷主のキーでは、どちらも 448 個のコマンド)。サンドボックスで動くブリッジは本番でも動きます。学んだことを二度学び直す必要はありません。

サンドボックスは空の状態では開設されません。生成された(コピーでは「ない」)シードデータが入っています:取引先、両方向の貨物、輸送依頼です。名前は明らかに架空のもので、メールアドレスは @sandbox.invalid ドメインにあります。実在するテナントの行が読み取られたり、複製されたり、テンプレートとして使われたりすることは決してありません。

リセットボタンは意図的に「ありません」。レコード、ファイル、書類が削除されることも一切ありません。まっさらな状態から始めたい場合は、更新によって新しい世代が開設され、古い世代は引退した状態で残ります。蓄積された数百行のリハーサルデータのコストは、誤ったテナントにつながった削除処理に比べれば無に等しいものです。

コマンドモデル

文法は 1 つだけで、例外はありません。コマンドの呼び出し方を知るために必要なのは、カタログがそのコマンドについて述べている内容だけです。

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

フラグ名はスキーマのパラメータ名であり、lg はどちらの綴りも受け付けます。--load_code と --load-code は同じフィールドに届きます(lg help は kebab-case 形式で表示します)。つまりフラグを見れば、どの JSON フィールドに入るかがわかり、CLI から HTTP に移る際に名前を変換する必要はありません。値なしで書いたブール型パラメータは true になり(--only_open)、その逆は --no-only_open です。値がダッシュで始まる引数は --flag=value の形で書いてください。そうしないと、書き忘れたフラグが次のフラグを黙って値として飲み込んでしまいます。ファイルを受け取るコマンドでは、ローカルパスの前に @ を付けます (--attachment_uuid=@./cmr.pdf)。「ファイルを受け取るコマンド」を参照してください。

実際の例

以下のコマンドはすべて現在のカタログに存在し、ここにある名前やパラメータに作り話は一切ありません。出力は省略しています。出力は `data.result` の階層を示しています。HTTP では同じオブジェクトが `{success, data:{…}}` エンベロープに包まれて届きます(「HTTP コマンド API」を参照)。

クライアントなしで:HTTP コマンド API

lg は便利な道具であって、入口そのものではありません。カタログと実行はどちらも 3 つの HTTP エンドポイントを通ります(ファイルには 4 つ目としてアップロード用エンドポイントがあります。「ファイルを受け取るコマンド」を参照)。ERP や CRM の連携はここに直接接続します。認証には Authorization: Bearer で送るパーソナルアクセストークンを使います。「すべての」レスポンスは同じエンベロープで返ります:`{ "success": bool, "data": { … }, "code": "…", "message": { … } }`。コントラクト全体は `data` の中にあります。`contract_version`、`command`、`status`、そしてコマンドの出力としての `result`、失敗時には `error_code`、`error_key`、`error` です。このページのレスポンス例は、紙面節約のためエンベロープではなく `data` オブジェクトを示しています。自分のコードで `data` を取り出すのを忘れないでください。`lg --json` もエンベロープ「全体」を出力するため、jq パスは `.data.result…` で始まります。唯一の例外は `/api/public/status` です。このエンドポイントはエンベロープを持たないため、監視ツールはパースせずにアラートを出せます。

200 + ok:false で確認を求めることは禁止されており、決して起こりません。理由は単純です。クライアントは 200 を成功として扱うため、200 で返された確認要求はフローを黙って止めてしまい、誰も気づきません。確認は常に 409 です。

ファイルを受け取るコマンド

表のうち 33 個のコマンドが書類や写真を受け取ります:貨物に CMR を添付する、請求書から HS コードを読み取る、車検証から車両を作成する、写真からパレットを推定する、企業書類をアップロードする、請求書をパッキングリストと照合する、などです。ブラウザではファイルをフォームにドロップしますが、それ以外の場所では 2 つのステップが必要です。ファイルをアップロードし、その id をコマンドに渡します。

名前が attachment_uuid または attachment_uuids で終わるパラメータはファイルを受け取ります。リスト内なら photos[].attachment_uuid、小切手なら front_attachment_uuid です。これを覚えておく必要はありません。カタログの各行には正確なパスを含む file_params があり、スキーマはそれらのフィールドに印を付け(format: uuid、x-logistivo-file: true)、カタログのレスポンスにはアップロード先、受け付ける形式、サイズ上限、有効期間を含む attachments ブロックがあります。lg help でも表示されます。

アップロードは書き込みなので、read スコープのキーではアップロードできません。write スコープのキーなら、どちらのアクセスレベルでもアップロードできます。アップロードしたファイルはキーではなくユーザーに属します。同じユーザーの 2 つのキーは同じアップロードを参照でき、他人のアップロードは誰も使えません。未知の id、期限切れの id、自分のものではない id は単に「見つからない」とされ、その理由は開示されません。

ダッシュボードのチャットの添付ファイルとコマンドのアップロードは別々の領域です。アシスタントチャットで添付したファイルを id でコマンドに渡すことはできず、コマンドのアップロードがチャットのスレッドに現れることもありません。

アップロードはコマンドではなく、それ自体に業務上の効果はありません。コマンドがその id を使うまで、データには何も起こりません。アップロードで確認が求められないのもそのためです。ゲートを持つのは、ファイルを消費するコマンドのほうです。

確認ゲートと取り消せないコマンド

カタログのすべてのコマンドは 2 つのフラグを持ちます。confirmation_needed(実行前に明示的な承認が必要か)と irreversible(その効果を元に戻せるか)です。これらは別々の問いであり、どちらもクライアントの行儀の良さに任せるのではなく、サーバー側で強制されます。

このゲートは業務ルールではなく、責任のためのものです。見積もりを提出することは運送契約への第一歩であり、請求書を発行すれば会計仕訳が生まれ、在庫移動は台帳を変えます。どれも、うっかり 2 回実行できてしまってよいものではありません。

ゲートは「実際に実行される」コマンドから読み取られます。チャットの汎用実行コマンドのように、あるコマンドが別のコマンドをラップする場合、ゲートはラッパーではなく、実際に実行されるコマンドのフラグから決まります。フラグが固定されていたら、2 つの失敗のどちらかが避けられません。請求書コマンドが確認なしで実行されるか、国を検索するたびに確認カードが表示されるかです。

read_only と「ゲートなし」は同じではありません。3 つの異なる状態があります。invoices preview はゲートがなく、何も書き込みません。loads create はゲート付きで、書き込みます。invoices create はゲートがないものの書き込みます(下書きが生まれます)。判断基準は 1 つです。コマンドが戻ったとき、データベースの行が変わっているかどうかです。

一部の操作は、そもそもコマンドではないため、このゲートの対象外です。パッケージの購入、3D Secure によるカードの追加、法的文書への同意、アカウントの閉鎖は、本人のブラウザで行われます。これらについては lg handoff-links generate が logistivo.com のリンクを返し(無料で、何も書き込まず、シークレットも含みません)、本人がそこでステップを完了します。どのフラグをどう設定しても、決済や同意が API 呼び出しになることはありません。

連携側ですべての呼び出しでゲートを自動的に通過させることは技術的には可能ですが、推奨しません。正しいパターンは、元に戻せるとわかっているコマンドに限ってゲートを自動化し、取り消せないコマンドは人間のキューに回すことです。

プリフライト:書き込みコマンドを実行せずに、何が起きるかを確認する

ERP ブリッジの組み込みで最も高くつくのは、リクエストボディが正しいかどうかを、コマンドを「実際に」実行して確かめることです。本番の台帳に下書きの請求書が残り、クレジットが消費され、手作業での後始末が必要になります。dry_run はそれを終わらせます。リクエストにフィールドを 1 つ追加するだけで、コマンドは実行されず、何が起きるかを読み取れます。

レジストリには現在 519 個のコマンドがあり、そのうち 233 個は読み取りのみで何も書き込みません。残りの 286 個の書き込みコマンドのうち 251 個はゲート付きで、confirm なしで呼び出すとすでに 409 を返し、何をするかを説明します。抜け穴は残りの 35 個でした。請求書の下書き、輸出書類の下書き、明細行といった、まさにブリッジが最初に組み込む一連の処理です。これらは、呼び出すことがそのまま実行することでした。

ゲート付きのコマンドにも 2 つ目の抜け穴がありました。スキーマバリデーションはゲートの「後」に実行されます。そのため、引数が欠けた呼び出しはまず確認カードを受け取り、確認し、その後でようやく「このフィールドがありません」と知らされていました。プリフライトは 1 回の往復で両方に答えます。

そして、最もよくあり、かつ最も発見が遅れるブリッジのバグはこれです。スキーマにないフィールドを送る(recipient_contact_id が期待される箇所に customer_id を送る)と、サーバーはそれを黙って捨て、コマンドは 200 を返し、レコードは誤った場所に作られます。プリフライトのレスポンスは、そうしたフィールドを arguments.ignored の下に名前付きで列挙します。エラーメッセージがそれを教えてくれることは決してありません。

プリフライトは何も書き込みません。これは測定結果である以前に、構造上の性質です。プリフライトの経路はコマンドのハンドラーに決して入らないため、後から追加されるものも含め、すべてのコマンドに当てはまります。リリース時には測定も行いました。当時カタログにあったすべての書き込みコマンドをプリフライトした際(2 種類の引数セット、2 つのロール、178 回のプリフライト)に発行されたすべての SQL ステートメントを記録しました。INSERT/UPDATE/DELETE は 1 つもなく、キューに入ったジョブも、メールも 1 通もありませんでした。31 テーブルの行数とクレジット残高も変わりませんでした。

終了コードは実際の呼び出しが返すものと同じです(実行可能なら 0、確認ゲートなら 4、冪等性の衝突なら 2、それ以外は 1)。そのため "lg … --dry-run || exit" は CI ステップで意味のあるゲートになります。

冪等性:再試行しても重複が生まれない理由

ERP はエラーメッセージを読まず、画面も見ず、タイムアウトの後には同じリクエストを再送します。このセクションはまさにその振る舞いのためにあります。

idempotency_key を付けた呼び出しは最大 1 回しか実行されません。2 回目の呼び出しはコマンドを再実行せず、記録された結果を status replayed で返します。ネットワークが切れたとき、リクエストがタイムアウトしたとき、キューが同じジョブを 2 回拾ったときも、重複した請求書ではなく最初の結果が返ります。

すべての書き込みコマンドでキーを使ってください。カタログの read_only フィールドが、その区別を労せず教えてくれます。read_only: false のコマンドにはすべて冪等キーを付けるべきです。

再試行戦略では指数バックオフを使い、毎回「同じ」キーを送ってください。試行ごとに新しいキーを生成するのは、冪等性をまったく使わないのと同じです。同じキーを並列に送らないでください。at-most-once(最大 1 回)が保証されるのは順次の再試行に対してであり、互いに競合する 2 つのリクエストに対してではありません。

増分同期とページネーション

ERP/CRM ブリッジは、毎時すべてを取得するのではなく、変更されたものだけを取得すべきです。すべての一覧コマンド(loads list、demands list-own、contacts list、invoices list、teammates list、load-groups list、reservations list、drivers list、tenders list、products search、warehouses search)は、2 つの共通パラメータを受け付け、1 つの共通フィールドを返します。

offset がない理由:稼働中のテーブルでは offset がずれます。2 つのページの間に挿入された行のせいで、レコードを黙って読み飛ばしたり二重に数えたりします。カーソルは前進のみなので、走査の途中で挿入・更新されたレコードは一覧の末尾に来ます。何も失われず、何も重複しません。

最初の全件取得も同じ仕組みです。updated_since=1970-01-01T00:00:00Z から始め、next_cursor が null になるまでたどります。必要がないため、別の「エクスポート」エンドポイントはありません。

lg でも同じことができます:lg loads list --updated_since=2026-09-01T00:00:00Z --limit=100 --json を実行し、続いて lg loads list --cursor= --json を実行します。

次回の同期では、updated_since に、これまでに見た最大の updated_at を設定します。タイムスタンプの精度は 1 秒なので、境界上のレコードが 2 回届くことがありますが、自分の側で external_ref または code をキーに upsert していれば無害です。updated_since も cursor もない場合、コマンドはチャットモードで実行されます:人間向けの並び順、最大 20 行、next_cursor は null で、truncated が「まだある」ことを示します。このモードを同期に使わないでください。

このセクションの機械可読なエラーキー:INVALID_CURSOR、INVALID_UPDATED_SINCE、DUPLICATE_EXTERNAL_REF、EXTERNAL_REF_TOO_LONG、EXTERNAL_REF_INVALID。いずれも HTTP 422 で応答し、ボディには error_code: "failed" と result.error_key が入ります。テキストではなく、キーで分岐してください。

自社のキー:external_ref

こちらの自動採番 id(invoice_id、contact_id)を ERP の業務キーにしないでください。これらは不透明で、自社内に閉じたものです。代わりに、貨物、輸送依頼、取引先、請求書、製品、倉庫は、あなたのキーを保持できます。

送信 Webhook:何かが起きたら、こちらからあなたへ POST します

ここまでの内容はすべてプル方式でした。あなたが尋ね、こちらが答えます。ERP ブリッジは、貨物のステータスが変わった、請求書が発行された、見積もりが届いたといったことを知るために、1 分ごとに問い合わせる必要がありました。今は違います。https のアドレスを登録し、関心のあるイベントを選べば、それが起きたときに署名付きの POST が届きます。

プッシュはプルの代わりではなく、プルの「隣に」追加されました。増分同期(updated_since + cursor)には手を加えておらず、今も正しい手段です。最初の全件取得、履歴のバックフィル、「何か見落としていないか」の確認は、すべてそちらで行います。Webhook は遅延を数分から数秒に短縮します。健全なブリッジは両方を使います。Webhook をトリガーとして、同期をセーフティネットとして。

ペイロードはレコードのコピーではなく「シグナル」です。識別子(id、あなた自身の external_ref、status、時刻)を運びます。詳細は自分のキーを使って GET で取得し、その呼び出しはすでにあなた自身の認可ゲートを通過します。そのため、露出面は識別子のレベルにとどまります。2 つのフィールドは意図的に含めていません。見積もりを出した企業の身元(ブラインド見積もりはパネルでは別名で表示されます。Webhook に身元を入れると、2 つ目の経路からそのプライバシーを破ることになります)と、予約の連絡先情報(個人データを余計な経路で動かす必要はありません)です。

イベント名は「凍結」されています。名前の意味が変わる場合は新しい名前が作られ、古い名前は非推奨期間のあいだ存続します。ペイロードに新しいフィールドを追加しても、コントラクトバージョンは上がり「ません」。受信側は知らないフィールドを無視しなければなりません。

イベントカタログ

以下の 13 種類のイベントはどれも、「実際に」ディスパッチされるイベントクラスに裏付けられています。カタログに「あったらいいな」という名前はありません。告知されたのに一度も発火しないイベントは、連携担当者が永遠に待ち続けるイベントであり、その失敗は気づかれません。この表も手書きではなく、ページを読み込むたびにライブのカタログから生成されます。

invoice.payment_recorded は意図的に「含めていません」。このイベントクラスはコード上に存在しますが、どこからもディスパッチされていません。支払いイベントは、このクラスが実際に発火するようになった日に追加されます。それより前には追加しません。イベントを早まって告知することは、まったく追加しないことよりも悪いのです。

署名の検証

すべてのリクエストに X-Logistivo-Signature ヘッダーが付きます:t=,v1=。署名対象の文字列は "." で、アルゴリズムは HMAC-SHA256 です。v1 プレフィックスによって方式をバージョン管理できます。将来 2 つ目の方式が加わった場合、ヘッダーは t=…,v1=…,v2=… となり、知っている方式を選べばよいのです。

依存関係のないリファレンス受信側を 2 つ公開しています。1 つは Node 用、もう 1 つは素の PHP 用です。どちらも上記の 4 つのルールを適用し、--selftest で自己テストします:有効な署名、改ざんされたボディ、誤ったシークレット、許容幅外のタイムスタンプ、不正な形式のヘッダー、再シリアライズの落とし穴、そして 2 種類の繰り返し(同じ配信/同じ事実)です。検証ロジックは、私たち自身が使っているコードとまったく同じものです。私たちは自ら公開するコントラクトを自分でも使っています。

配信、再試行、ミュート

受信側が常に稼働しているとは限りません。この仕組みはそれを前提としています。一時的な失敗は再試行スケジュールに入り、恒久的な失敗は購読をミュートします。そしてミュートの「理由」は必ず記録されます。

ミュートの理由は機械可読で安定しており(delivery_failures、endpoint_gone、unsafe_target、key_revoked、by_user)、パネルと `lg webhooks list` の両方に表示されます。理由のないミュートでは、「なぜ何も届かないのか」に答えられなくなります。再有効化するとカウンターと記録時刻がリセットされます。

アドレスを書くのはあなたですが、リクエストを送るのは「こちらの」サーバーです。ゲートがなければ、クラウドのメタデータアドレスを入力したテナントが、こちらのネットワーク内部からリクエストを発行させることができてしまいます。そのため、アドレスは登録時と「毎回の」配信時の両方でチェックされます:

ヘルスとステータス:沈黙したブリッジに、あなたより先に誰かが気づくことがないように

連携ブリッジが派手に停止することはめったにありません。たいていは深夜に静かに止まります。トークンの期限が切れる、キーの代理対象の人がチームを離れる、スコープが狭められる、受信側のエンドポイントが 403 を返し始める。最初に気づくのは、ほとんどの場合顧客です。lg status はその順序を逆転させるためにあります。

1 つのコマンドが 2 つの問いに同時に答えます。障害時にはどちらも同じ瞬間に問われるからです。プラットフォームは稼働しているか、そして自分のブリッジは健全か。プラットフォームの行はトークンなしで動作します。障害時にはトークンそのものが最初の容疑者であり、ステータスの問いをトークンに結びつけてしまうと、答えが最も必要なときに答えが得られなくなるからです。

ヘルスサマリーは新しい台帳を持たず、既存の 3 つの情報源を 1 つの文にまとめます:日次の利用カウンター(どれだけ呼び出し、どれだけ失敗したか)、リクエスト台帳(どのリクエストが、いつ、なぜ拒否されたか)、送信 Webhook の配信台帳(イベントが受信側に届いたか)。エラー率は常に日次カウンターから読み取ります。詳細台帳には上限があり、上限に達した場合はその旨をはっきりと示します。

同じサマリーはパネルにも表示されます。Settings › Integration › Keys の下で、各行にバッジが付き、その下に理由が表示されます。しきい値はクライアント側では計算しません。サーバーが割り当てたレベルを、パネルと lg status の両方がそのまま表示するため、2 つの画面が同じキーについて異なることを言うことはありません。

監視ツールにトークンは不要です。このエンドポイントが返すのはステータスを表す単語だけで、バージョン、ホスト名、キュー名、ジョブ数、エラーテキスト、顧客データは含まれません。含めることもできません。

ヘルスサマリーは、リクエストごとに一定数のクエリで生成されます。クエリ数はキーの数に応じて増えません。詳細は、そのキーの最近のリクエストを lg logs で、送信側のブリッジを lg webhooks list で確認してください。

エラーと終了コード

機械が分岐に使うのは安定した文字列です。error フィールドのテキストは人間向けで、ローカライズされ、予告なく変わります。メッセージのテキストで分岐する連携はサポート対象外であり、それが壊れてもバグではありません。

2 つの分類、2 つのフィールドがあります。error_code はトランスポート層(上の表)で、最初に分岐に使うものです。error_key は failed の中に入る安定した業務ルールのキー(INSUFFICIENT_CREDITS、NOT_FOUND、DUPLICATE_CONTACT、INSUFFICIENT_STOCK)で、カタログ内の辞書(error_keys)と以下で公開されています。まだキーを出力しないコマンドは error_key: null を返します。辞書にないキーは一般的な失敗として扱ってください。

安定性の約束

連携コントラクトで最も重い問いは「今日書いたコードは明日も動くか」です。それに答えるのが 1 つの数値、つまりカタログ内の contract_version と、すべてのレスポンスに付く X-Contract-Version ヘッダーです。この数値が答えるのはただ 1 つの問い、「以前に書いたコードが変更なしで今も動くか」です。数値が増えていなければ、動きます。増えていれば、動きません。あなたが依存していた何かが削除されたか、狭められたのです。

告知は何も削除しないからです。告知の翌日も、既存のコードはまったく以前どおりに動きます。告知でバージョンを上げると、1 つの変更に対して 2 つのシグナルを出すことになり、最初のシグナルを無視する癖をつけさせてしまいます。それは本当に重要なシグナルを弱めます。告知は、あなたが行っている呼び出しにすでに乗っています。カタログの行の deprecated_at と replaced_by、そのコマンドのレスポンスに付く X-Deprecated ヘッダーと deprecated ブロック、そして lg を使っていれば stderr への 1 行の警告です。何かが廃止予定であることを知るために、何かをする必要はありません。それはすでにあなたのログにあります。

告知から削除までには、少なくとも暦の上で 2 四半期が経過します。猶予期間中、古いコマンドは引き続き応答し、呼び出しのたびに警告します。CLI 名が廃止される場合は、後継のコマンドがその名前をエイリアスとして引き継ぎます。猶予期間中は古い名前で古いコマンドに届き、削除された日からは同じ呼び出しが新しいコマンドに届きます。クライアントの新しいリリースを出す必要はありません。

preview_invoice → get_invoice(2026 年 9 月 3 日に告知)。get_invoice は同じ preview ブロックを返し、さらに明細行、税の内訳、記録済みの入金、残高、自社 ERP キー(external_ref)による指定を追加します。preview_invoice は引き続き動作し、lg invoices preview も同様です。どちらも警告を出します。

コマンド識別テーブル

下の表は手書きではありません。このページが配信されるたびに、ライブのコマンドレジストリから生成されます。サーバーで追加されたコマンドは自動的にここに現れます。「ゲート」列は、コマンドが確認を求めるか、その効果を元に戻せるかを示し、「ロール」列は、どのアカウント種別のカタログにそのコマンドが含まれるかを示します。バッジは、manager レベルのキーが必要なコマンド、ファイルを受け取るコマンド、lg からしか実行できないコマンドを示します。

API 名(list_loads)は凍結されており、変わることはありません。HTTP 呼び出しで使うのはこの名前です。CLI 識別子(loads list)はバージョニングポリシーに従います。一度告知されると、バージョンの引き上げと移行期間を伴う場合にのみ変更され、その期間中、カタログは古い組をエイリアスとして提供します。ここにある説明は各コマンド自身のレコードから取られており、短縮されています。全文、パラメータの型、enum 値については、カタログエンドポイントを読むか lg help を実行してください。アフィリエイト専用のコマンドはここには掲載していません。アフィリエイトアカウントでは自分のカタログで確認できます。

ERP・CRM 連携 — 認証主体:サービスアカウントではなく、実在のユーザー

ERP や CRM は、パネルで発行したパーソナルアクセストークンを持つ、実在する企業ユーザーとして Logistivo に接続します。企業に属さず、スコープの制約を受けず、誰も責任を負わない合成のサービスアカウントが作られることは決してありません。

これは好みではなく、アーキテクチャの問題です。マルチテナントの分離は、すべてユーザーの所属企業に依存しています。企業を持たない主体では分離スコープが機能しなくなり、そのトークンでの誤ったクエリ 1 つで、自分のものではないデータを読めてしまいます。2 つ目の理由は監査です。台帳のユーザー列は「誰がこれを実行したか」を人に結びつけますが、サービスアカウントはその列を無意味にし、請求書を発行する連携から責任者をなくしてしまいます。3 つ目は失効です。トークンが人に属していれば、その人が去ったときに連携は止まります。持ち主のいないブリッジこそ、何年も黙って書き込み続けるブリッジなのです。

ERP・CRM 連携 — バージョニングと非推奨化

カタログのレスポンスには整数の contract_version が含まれ、コマンドの各行には since、deprecated_at、replaced_by が含まれます。コントラクトバージョンが上がるのは、互換性を壊す変更のときだけです。

ERP・CRM 連携 — 安定した識別子:自動採番 id を ERP にキーとして渡さない

内部の自動採番 id は不透明で、テナントにスコープされています。別の企業の有効な id を指定しても、データではなく「見つかりません」が返ります。しかし、不透明であることは業務キーであることを意味しません。ERP が自分のレコードに保存するものは、業務キーでなければなりません。

ERP・CRM 連携 — ページネーション、フィルタリング、増分同期

一覧コマンドは limit を受け取り、count を返します。夜間同期のために、コントラクトは以下を約束しています。これを採用した一覧コマンドは JSON Schema で cursor と updated_since を公開します。そうなるまでは、小さなウィンドウとして扱ってください(上限 20 行、カーソルなし)。

ERP・CRM 連携 — レート制限

制限のキーは IP ではなくトークンです。ERP は単一の NAT の背後からアクセスしてくるため、IP ベースの制限では企業全体が 1 人のユーザーとして数えられてしまいます。2 つのキーを持つ 2 つの連携が、互いの割り当てを食い合うことはありません。これが「キー 1 つにつき連携 1 つ」の 2 つ目の理由です。

ERP・CRM 連携 — 私たちがやらないこと

これはしばしば、コントラクトの中で最も役に立つ部分です。決して実現しないものがわかっていれば、それを前提に設計できます。

機械可読なカタログ

このページは人間向けに書かれています。AI アシスタントや自動化されたクライアント向けには、同じ情報が、構造化された認証不要のコピーとして存在します。

GET /api/public/cli/catalog は認証不要で、コマンドの公開識別情報を返します:凍結された API 名、CLI の domain/verb 識別子、説明、JSON Schema パラメータ、read_only、confirmation_needed、irreversible の各フラグ、credit_cost、そしてアクセス関連のフィールド key_level、permission、file_params、secret_parameters、cli_only です。個人データ、テナントデータ、サンプルレコードは一切返しません。返すのは、窓口がどういうものかだけです。

外部エージェントの正しい使い方:カタログを読み、どの作業が可能かを「自分の言葉で」説明し、実行が必要になったら、ユーザー自身の環境でユーザー自身のトークンを使って実行するよう案内します。エージェントがユーザーのトークンを求めたり、生成したり、中継したりすることは決してありません。

会員登録や輸送依頼のために Logistivo を AI アシスタントに接続するのは別のチャネルで、専用のページがあります:

機械可読な定義:OpenAPI 3.1 と Postman

上のカタログは Logistivo 独自の形式です。同じ情報は、お使いのツールがすでに理解できる 2 つの業界標準形式でも公開しています:OpenAPI 3.1 の定義と Postman コレクションです。どちらも手書きではなく、リクエストのたびにライブのコマンドレジストリから生成されるため、古くなることはありません。

定義はカタログ「そのもの」であり、2 つ目の現実ではありません。すべてのコマンドは POST /api/common/commands/ オペレーションとして現れます。ボディのスキーマはそのコマンド自身の JSON Schema(アシスタントが見るものとバイト単位で同一)、レスポンスのスキーマは共通のエンベロープで、ゲートフラグ(read_only、confirmation_needed、irreversible とクレジットバッジ)は x-logistivo 拡張に、業務エラーキーは x-error-keys の下にあります。廃止予定のコマンドには deprecated: true が付き、6 か月の猶予期間中は呼び出し可能なままです。

コマンドの窓口に加えて、連携コントラクトを担う REST エンドポイントも定義に含まれています:増分同期(updated_since + cursor + limit)に対応した 10 個の一覧エンドポイントと、自社のキー(external_ref)を受け付ける 7 個の書き込みエンドポイントです。Logistivo の REST の窓口はそれよりはるかに大きいのですが、残りは「意図的に」除外しています。それらのペイロードは製品自身のコントラクトであり、推測で書いたスキーマは、生成したクライアントを最初の呼び出しで壊してしまいます。そしてあなたは、こちらではなく自分のコードの中にバグを探すことになるでしょう。

定義は、要求した人の「ロール」に合わせて生成されます。別のロールのコマンドがドキュメントに入ることはありません。定義は辞書ではなくコントラクトであり、その中のすべてがそのトークンで呼び出せなければならないからです。認証不要版は 3 つのロールの和集合で、そこでは各オペレーションがどのロールに表示されるかを x-logistivo.roles に記載しています。

私たちの言葉をうのみにせず、検証してください:npx @redocly/cli lint logistivo-openapi.json は警告ゼロで通ります。

コード生成:定義から型付きクライアントを

定義は手に入りました。次はそこから動作するコードを生成しましょう。`lg codegen` は OpenAPI ドキュメントを読み込み、依存関係のない単一ファイルのクライアントを書き出します。TypeScript(型宣言と薄い呼び出しラッパー)または PHP(PHPDoc の array shape を備えたクライアントクラス)です。`npm install` も composer も不要です。TypeScript 側は `fetch`、PHP 側は `ext-curl` を使います。

生成はこちらのサーバーではなく、「あなたの」マシンで行われます。これは好みの問題ではありません。定義を一度リポジトリに固定すれば(`lg spec --out=...`)、同じコマンドがビルドのたびにネットワークもトークンもなしで実行されるため、CI がこちらの稼働状況に依存することはありません。さらに、生成されたコードはあなたの ERP の中で「動く」コードなので、API から取得するのではなくリポジトリに置いておけば、すべての変更が `git diff` に現れます。

生成されたクライアントは Logistivo の 3 つの経路(レール)をすべて正しく扱い、どれがどれかを推測ではなくドキュメントから知ります。すべてのオペレーションは `x-logistivo.surface` で自身の経路を宣言しています。コマンドの経路では `{success, data:{…}}` エンベロープを取り外します。REST の同期一覧はエンベロープに包まれて「おらず」、そのまま返されます(取り外すと `next_cursor` が失われます)。REST の書き込みは、そのエンドポイント自身の製品ボディを返します。汎用のジェネレーターはこの区別を知りませんが、私たちのジェネレーターは知っています。

型は推測ではありません。各コマンドの引数インターフェースは、アシスタントが見るのとまったく同じ JSON Schema から生成されます。必須は必須のまま、enum はユニオン型に、ネストしたオブジェクトはネストした型になります。実際には、サーバーが必須とするフィールドを書き忘れた呼び出しは、本番環境で 422 で失敗するのではなく、コンパイル時に失敗します。

ファイルには生成時刻を書き込みません。同じ定義と同じフラグからは同じバイト列が生成されます。そのため再生成しても `git diff` にノイズは出ず、本当に変わったただ 1 つのもの、つまりコントラクトが見えるままになります。

正直な適用範囲

このセクションは縮めるよりも、むしろ増やしていきたいと考えています。連携コントラクトの価値は、何を約束するかよりも、何を約束しないかをどれだけ明確に述べるかにあります。

よくある質問

Logistivo CLI は何に使うのですか?

Logistivo アカウントの業務を、ターミナル、スクリプト、または自社の AI エージェントから実行します:貨物と運行、輸送依頼、見積もりと入札、車両、ドライバーとその経費、書類、取引先と取引先元帳、請求書と会計、倉庫と在庫、チームメンバー、HS 関税率とアンチダンピング関税。パネルのすべての書き込み操作にコマンドがあり、決済、法的同意、アカウントの閉鎖は意図的にブラウザに残しています。同じコマンドは、ERP/CRM 連携のために HTTP で、また MCP チャネルからも呼び出せます。

Logistivo CLI のコマンドはどのように命名されていますか?

形は lg --parameter=value です。ドメインは複数形のエンティティ(loads、demands、invoices、export-documents)で、動詞は意図的な判断によってのみ増える閉じた語彙(list、get、create、update、search、set-status、generate など)から選ばれます。1 つのエンティティの動詞はすべて 1 つのドメインにあり、(domain, verb)の組はカタログ全体で一意です。

CLI ではどのように認証しますか?

対話的に使う場合は、lg login が承認コードを表示し、Logistivo にサインインしたあと、ブラウザでそのコードを確認して承認します。ターミナルにパスワードやキーを入力することはなく、セッションは lg logout を実行するまでサインインしたまま維持され、自動で更新されます。lg login --password(メールアドレスとパスワード)は代替手段として引き続き使えます。サーバーや CI では、パネルまたは lg keys create で発行した連携キーを使います。lg login --api-key でローカルのプロファイルに保存するか、LOGISTIVO_TOKEN 環境変数に設定してください。HTTP では、トークンは Authorization: Bearer ヘッダーで送られます。

ERP 連携用にサービスアカウントを作成できますか?

いいえ。連携は、パネルで作成した実在の企業ユーザーのトークンで接続します。企業を持たない合成の主体は、マルチテナントの分離を機能させなくし、監査台帳の「誰がこれを実行したか」の列を無意味にし、持ち主が去っても止まらないブリッジを残してしまいます。正しいパターンは、狭いロールと責任者が明確な専用ユーザーです。

Logistivo にはテスト(サンドボックス)環境がありますか?

はい。セルフサービスで利用できます。自社用に独立したサンドボックステナントが開設されます。コードもコマンドカタログも同じで、クレジット台帳は独自のものです。環境はキーの所属企業から導出され、リクエストで指定することはできません。誤ったプロファイルで行った呼び出しは、サーバー側で 403 ENVIRONMENT_MISMATCH となって停止します。サンドボックスで作成した輸送依頼が実在の運送会社に届くことはなく、サンドボックスのイベントが本番の Webhook 購読に配信されることもなく、本番の残高も動きません。エンドポイント:POST /api/common/integration-sandbox (開設)、POST /api/common/integration-sandbox/keys (キー)、GET /api/common/integration-sandbox/go-live (本番移行チェックリスト)。lg 側では、lg --sandbox と lg --live が 1 回の実行ごとのプロファイル選択子です。

スクリプトで lg --json の出力を読むにはどうすればよいですか?

出力は HTTP エンベロープ全体です:{ "success": bool, "data": { "contract_version", "command", "status", "result" }, "code", "message" }。業務ペイロードは .data.result の下にあります。一覧コマンドでは行が .data.result.、件数が .data.result.count、次のページが .data.result.next_cursor です。したがって正しい jq パスは lg loads list --json | jq -r '.data.result.loads[].load_code' のようになります。エンベロープを飛ばしたパスはエラーにならず、黙って null を返します。監視スクリプトでは、それが鳴りやまないアラームになります。唯一の例外は /api/public/status で、そのボディは意図的にエンベロープを持ちません。

同じリクエストを 2 回送ると、レコードが重複しますか?

idempotency_key を送っていれば重複しません。同じキーでの 2 回目の呼び出しはコマンドを再実行せず、最初の結果を status replayed で返します。キーは UUID にすべきで、再試行の間は「同じ」でなければなりません。試行ごとに新しいキーを生成するのは、冪等性を使わないのと同じです。

--yes は何をしますか?

確認ゲートを通過させます。ゲート付きのコマンドを --yes なしで呼び出しても何も変わりません。サーバーはこれから行う内容を 1 文で要約し、取り消せないかどうかを伝えて停止します(HTTP 409、CLI 終了コード 4)。--yes を付けてコマンドを繰り返すと、処理が実行されます。

スクリプトでコマンドの失敗を検出するにはどうすればよいですか?

メッセージのテキストではなく、終了コードを読んでください:0 は成功、1 は業務ルールによる拒否、2 はコマンドまたは引数のエラー、3 は認証またはロールのエラー、4 は確認が必要。HTTP で対応するものは 200、422、404、403、409 で、ボディ内の error_code フィールドは安定しています。エラーの「メッセージ」はローカライズされ、予告なく変わります。

利用可能なコマンドをプログラムから調べるにはどうすればよいですか?

認証不要のディスカバリーには GET /api/public/cli/catalog を、自分のアカウントで実際に見られる一覧には GET /api/common/commands を使います。どちらも各コマンドの JSON Schema パラメータを返します。コマンド一覧をハードコードせず、これらからクライアントを組み立ててください。

新しいコマンドが追加されたら、クライアントを更新する必要がありますか?

いいえ。クライアントはコマンド一覧をカタログから取得するため、サーバーで追加されたコマンドは、クライアントの新しいリリースなしに現れます。更新が必要なのは、コントラクトバージョンを上げる互換性のない変更の場合だけで、その場合も古い形式は少なくとも 6 か月間存続します。

CLI は既存の REST API を置き換えますか?

いいえ。Web アプリとモバイルアプリを支える REST の窓口はそのまま残ります。コマンドカタログはリソースの CRUD を置き換えるものではなく、「動詞」の層、つまりロールゲートと確認ゲートを備え、監査される、操作を実行するための唯一の方法です。

Logistivo の API キーはどうやって取得し、何ができますか?

チーム管理権限を持つマネージャーが、パネルの Settings › Integration keys で、またはターミナルから lg keys create で発行します。キーは実在するチームメンバーの代理として動作し、3 つの独立した設定を持ちます:スコープ(read はカタログと書き込みを伴わないコマンドを、write はすべてのコマンドを開放します。確認ゲートはそのまま残ります)、任意のドメイン許可、そしてアクセスレベル(日常業務向けのデフォルトである employee、またはチーム・設定・キー・お金に関わる操作向けの manager)です。キーは一度だけ表示され、サーバーには保存されません。ローテーション、移譲、失効が可能です。代理対象のメンバーが去ると、キーも閉じられます。サービスアカウントはありません。

冪等キーを別のコマンドで再利用するとどうなりますか?

サーバーは 409 idempotency_conflict で応答し、そのキーが最初に使われたコマンドを示します。何も実行されず、何もリプレイされません。キーは自社にスコープされ、1 つのコマンドに紐づき、24 時間ロックされます。その後は、同じキーで新しい実行が始まります。

連携にはどのようにレート制限が適用されますか?

トークンごと、毎分、3 つのバケットで適用されます:カタログの読み取り 120 回、読み取りコマンド 120 回、書き込みコマンド 30 回。429 には常に Retry-After と、retry_after、limit、bucket を含むボディが付きます。その時間だけ待ってから再試行してください。カタログは現在の数値を rate_limits の下で公開しています。

業務上の拒否とトランスポートエラーはどう見分けますか?

まず error_code で分岐します。failed(422)は業務上の拒否で、INSUFFICIENT_CREDITS や NOT_FOUND といった安定した error_key が付きます。server_error(5xx)とネットワークエラーは同じ冪等キーで再試行します。rate_limited(429)は待機してやり過ごします。エラーのテキストで分岐してはいけません。

Logistivo は Webhook を送信しますか?どのイベントについてですか?

はい。https のアドレスを登録し、関心のあるイベントを選ぶと、それが起きたときに署名付きの POST が届きます。現在購読できるイベントは 13 種類です:貨物の作成/ステータス変更/ドライバー割り当て、輸送依頼の作成とステータス変更、見積もりの提出/修正/承認、請求書の下書き/発行/取り消し、倉庫予約の作成/ステータス変更。すべての名前は、実際にディスパッチされるイベントクラスに裏付けられています。告知したのに一度も発火しないものはありません。そのようなイベントは、連携担当者が永遠に待ち続ける、気づかれない失敗だからです。購読は Settings › Integration › Webhooks で、またはターミナルから lg webhooks create で作成します。

Logistivo から届く Webhook の署名は、どのように検証すればよいですか?

すべてのリクエストに X-Logistivo-Signature ヘッダーが付きます:t=,v1=。署名対象の文字列は "."、アルゴリズムは HMAC-SHA256、鍵は購読のシークレットです。ルールは 4 つあります。(1)「生の」バイト列を検証する:ボディをデコードして再シリアライズする(JSON.stringify / json_encode)と、同じデータが異なるバイト列になり、署名が一致しなくなります。これが最もよくある間違いです。(2) タイムスタンプを確認し、署名が正しくても |now − t| > 300 秒のものは破棄する。(3) hash_equals / timingSafeEqual で定数時間比較する。(4) 重複排除は配信 id ではなく event_id で行う:event_id はリプレイ時にも保持されますが、配信 id は変わります。動作する Node と PHP の受信側はリポジトリの examples/webhook-receiver/ にあり、--selftest で自己テストします。

Webhook を配信できなかった場合はどうなりますか?イベントは失われますか?

失われません。受信側が 2xx を返さない場合、配信は再試行スケジュールに入ります:60 · 300 · 1800 · 7200 · 21600 秒、合計 6 回の試行、期間はおよそ 8.6 時間です。410 Gone を除く「すべての」失敗は、4xx も含めて再試行されます。業務イベントを失う損失は、数回の余分なリクエストよりも大きいからです。再試行を使い切った配信が 10 回連続すると購読はミュートされ、その理由が記録されるため、「なぜ何も届かないのか」に答えられる状態が保たれます。配信台帳はすべての試行(ステータスコード、所要時間、エラー)を保持し、パネルまたは lg webhooks replay で 1 件の配信を再送できます。増分同期(updated_since + cursor)も引き続きセーフティネットとして使えます。

自社の AI エージェントは、ユーザーがパネルでできることをすべて行えますか?

ほぼすべて行えます。パネルのすべての書き込み操作にコマンドがあり(パネルの書き込みエンドポイントと照合した結果、対象範囲内のものは 100% カバーされています)、同じコマンドが lg、HTTP、MCP チャネルに開放されています。意図的な例外は、本人確認とお金に関わるステップです。サインイン、パッケージの購入、3D Secure によるカードの追加、法的文書への同意、アカウントの閉鎖は本人のブラウザに残り、lg handoff-links generate がそのためのリンクを返します。連携キー、Webhook、サンドボックスは、シークレットを一度だけ返すため、lg の組み込みコマンドで管理します。

CLI や API から、PDF や写真などのファイルをコマンドに渡せますか?

はい。lg では、ローカルパスの前に @ を付けると (--attachment_uuid=@./cmr.pdf)、lg が先にファイルをアップロードします。コマンドにファイルフィールドが 1 つしかない場合は --file が近道になり、lg upload はアップロードだけを行います。HTTP では、ファイルを multipart で POST /api/common/commands:upload に送り、返された attachment_uuid をコマンドに渡します。MCP クライアントは upload_attachment ツールを使います。ファイル形式は内容から判定され、アップロードしたファイルはユーザーに属し、24 時間利用できます。同じファイルを再度アップロードすると、同じ id が返ります。

employee と manager の連携キーの違いは何ですか?

アクセスレベルは、キーがどれだけ重い作業をしてよいかを決めます。新しいキーのデフォルトである employee キーは、日常業務を実行します:貨物、輸送依頼、見積もり、運行、車両、書類、取引先。manager キーは、管理業務も行えます:チームメンバーとロール、企業設定、連携キー、銀行口座、保存済みカード、アフィリエイトの支払い。キーが代理対象の本人より多くの権限を持つことはなく、employee キーで manager 用のコマンドを呼び出すと INSUFFICIENT_KEY_LEVEL が返ります。レベルはパネルで、または lg keys update --access-level=manager で変更します。

アフィリエイト(紹介パートナー)も CLI を使えますか?

はい。アフィリエイトアカウントには独自のカタログ(紹介、リード、CRM のメモとタスク、営業資料、収益と支払いのための 55 個のコマンド)があり、ほかのロールと同じように lg、HTTP、MCP から利用できます。支払いの申請や取り消し、支払先銀行口座の変更には、manager レベルのキーと確認が必要です。荷主、運送会社、通関業者でもある企業には、両方のカタログが表示されます。

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.