For the complete documentation index, see llms.txt. This page is also available as Markdown.

Idempotency

全公開 API の冪等性の一覧です。リトライ・二重送信時の挙動はここを正とします。

Ingestion API

Products — /api/ingest/products

shop + barcode で UPSERT。同じ barcode を何回送っても安全です。

  • 値に変更がなければ unchanged

  • 値に変更があれば updated + product_update_logs に changed_fields を記録

  • GET /api/ingest/accessory-services(read)は書き込みが無いため冪等性の対象外です。何回呼んでも安全です。

Inventory Snapshot — /api/ingest/inventory-snapshot

店舗 × 商品 × 拠点ごとの 1 行を絶対値で丸ごと置き換えます。同じ値の再送は在庫・日次とも重複せず、変更ログも値が変わった行のみ追記されます(再送に冪等)。読み値が反映済みより古い行は適用されず stale_skipped[] で返ります(同時刻は適用 = 再送冪等を維持。→ 古い読み値の上書き防止)。

Inventory Available — /api/ingest/inventory-available

  • mode=absolute: available_qty = N の上書きのため再送に冪等。

  • mode=delta: 積算のため本質的に非冪等。idempotency_key を付与すると、同一キー × 同一 (product, location) の available_delta ログが既存なら適用を skip(lines_deduped)。順次リトライを吸収する best-effort であり、完全な並行重複保証ではないため、イベント単位で一意な idempotency_key を付与してください。

  • committed_qty / on_hand_qty(任意): 送られた行だけ absolute で上書きするため再送に冪等。省略した行は据え置き(mode に依らず常に absolute)。ルーティングは available_qty + committed_qty で引き当てを判定します。

  • as_of(任意): 行の読み値を在庫システム側で読み取った時刻。反映済みより古い行は適用されず stale_skipped[] で返ります(同時刻は適用 = 再送冪等を維持。→ 古い読み値の上書き防止)。

Channel Available — /api/ingest/channel-available

  • shop + location_id + barcode の行を、送った channel_available_qty で上書きするため再送に冪等です。

  • 行の as_of(省略時は inventory_as_of → 受信時刻)が、反映済みの値の時刻より古い行は適用されず stale_skipped[] で返ります。順序が入れ替わって届いても新しい値が勝ちます。

  • 倉庫から届いた在庫数(on_hand_qty)は変わりません。販売可能在庫は、チャネルの値が 0 以下なら 0、それ以外は通常の計算で決まります。

MFC Inventory — /api/ingest/mfc-inventory

shop + fulfillment_source_location_id + shelf_code + barcode で棚別 qty を反映し、同一トランザクションで barcode を全棚合算し sot.inventory_levels.available_qty を更新します。

  • mode=absolute(既定 / 省略時): 絶対値 set のため同一ボディの再送に冪等です(棚在庫・合算とも重複しません)。

  • mode=adjust: 符号付き差分の加減算のため本質的に非冪等です。冪等キーは持たないため、リトライ時は適用済みかを呼び出し側で確認してください(既存 routing write の secure / oversell と同方針)。

barcode 未登録(product_not_found)の行や adjust で在庫が負になる行は、silent skip せずバッチ全体を reject します(部分適用なし)。

Stocktake — /api/operator/stocktake/*

  • open: 拠点ごとに進行中のセッションは 1 つ(部分 UNIQUE)。進行中があれば 409 session_already_open でその id を返す(新しく作らない)。

  • shelf-start: 数えている途中の棚への再 start は何もしない。締めた棚への start は再計数(前の計数は superseded として残す)。

  • shelf-submit: 棚単位の置き換え。同じ内容の再送は同じ結果(二重にならない)。帳簿数(expected)は締めた時点で固定。

  • transition: 状態機械。許されない遷移は 409 invalid_state。location(店舗)の submit は商品ごとの帳簿数の行を作り直す(取り下げ → 再提出で置き換わる)。

  • commit(location): 商品単位で applied_at を付け、外部システムへの調整は UNIQUE (session_id, product_id) で 1 行だけ作る。再 commit で二重に作らない。

  • commit(shelf): 商品(barcode)単位の部分反映で冪等。反映した行には applied_at が付き、再 commit は未反映の行だけを処理する。差分(counted − expected)を当てるので、締めたあとの出荷・入庫と二重にならない。保留(counted_below_active / negative_after_delta)は再 commit で再評価する。committed への再 commit は alreadyCommitted: true の no-op。商品ごとに (shop, location, barcode) の advisory lock を取り、引き当て・再計算・数量修正と直列化する。

Inventory Holds — /api/inventory/hold / /api/inventory/release

hold は shop + reference_id + location + barcode の取り置き数を絶対値で設定します(quantity: 0 は削除)。同じ内容の再送は同じ結果に収束し、履歴(inventory_ledger)も数が変わったときだけ追記します。reference_id を省略すると毎回新しい参照 ID が生成されるため、省略した再送は二重の取り置きになります。1 リクエスト内の全行は 1 トランザクションで、1 行でも失敗すれば全行を取り消します。release は shop + reference_id の取り置きを削除し、該当なしの再送も ok: true(空配列)です。

Order Line Master — /api/order-line-master/bulk-upsert

shop + source_system + transaction_id + transaction_line_id + line_instance_no で UPSERT。同一行の再送は差分があれば更新(無ければ実質 no-op)、order_line_master_update_logs に記録します。

Share Analytics — /api/ingest/share-analytics

source + source_share_id(shares)/ source + source_item_id(items)/ source + source_event_id(events)の UNIQUE への upsert。同一行の再送・backfill 重複に冪等です。行単位の不備(必須欠落・許可外 event_type・不正/未来 occurred_at)は skipped[](reason 付き)で返し、他行の処理は継続します。

Routing API

Orders — /api/routing/orders

  • 入口冪等: shop + sourceSystem + externalOrderRef で routing order 1 行に収束。status = dispatched の注文を再 POST すると再ルーティングせず、既存の出荷指示を idempotent: true で返します(外部への再送なし)。

  • 出口冪等: 出荷指示は shop + idempotencyKey(省略時 {sourceSystem}:{externalOrderRef})で UPSERT し、外部 fulfillment への二重送信を防ぎます。

  • まだ dispatch されていない注文の再 POST は、前回の decision を破棄して再計算します。

Orders (shipped) — /api/routing/orders/shipped

  • shop + sourceSystem + externalOrderRef の注文に、同じ拠点の出荷が既にあれば何も変えずに idempotent: true。そのまま再送してよいです。

  • 同じ注文で別の拠点を送ると、その拠点の出荷が追加されます。取消済みの注文は 409 order_cancelled。

Cancel / Update — /api/routing/cancel / /api/routing/update

  • cancel: 既に cancelled の注文は再 dispatch せず idempotent: true。取消は SoT 側で常に確定(WMS への cancel 通知は best-effort)。

  • update: 全量再ルーティング。出荷指示は元注文の idempotencyKey を維持するため、shop + idempotencyKey の同一行を更新(create→update で二重作成しない)。

Inventory Adjustments — /api/routing/inventory-adjustments/*

  • claim: 取った行を同じ呼び出しの中で claimed にする(FOR UPDATE SKIP LOCKED)。同時に呼んでも同じ行は 1 つにしか渡らない。claimed のまま reclaimAfterMinutes(既定 30 分)を過ぎた行は取り直す(attempts が増える)。

  • result: claimed / unknown の行だけ確定する。applied / failed への再報告は recorded: false の no-op(結果を上書きしない)。

  • 差分は相対値なので、外部システムへ 2 回当てると二重になる。結果が分からないときは unknown で返し、自動では再送しない。

WMS Shipping Request — /api/routing/wms-shipping-request

冪等キーはありません。1 回の呼び出し = 1 件の送信記録 +(live 時)1 回の送信です。

★同じ伝票番号での送り直しは内容が反映されません。 WMS は伝票番号ごとに 1 伝票を持ち、2 回目以降を無視します(上書きではありません)。重複送信は無害ですが、初回に送った内容がそのまま出荷されるため、送る前に内容を確定させてください。あとから変えたい場合は取り下げてから別の伝票として作り直します。1 注文を複数の荷物に分けるときは荷物ごとに別の伝票番号(2 本目以降は連番)で送ります。

送信が失敗したときは WMS に届いていないので、そのまま再送して構いません。shadow は記録のみで送信しません。リトライはこの API 自身は行わず、送信側のループが担います。

Fulfillment API

Secure — /api/routing/secure

  • 単位は shop + routing_order_id + line_instance_no + barcode で一意。確保対象は allocated の単位のみで、既に secured の単位は再確保せず alreadySecured に計上(再 POST は no-op)。

  • 出荷元ヘッダの全単位が確保済になった遷移時のみラベル生成。既に確保済ヘッダの再 secure はラベルを再生成しません。

Picking List — /api/routing/picking-list

  • 非冪等(再発行前提): 生成のたびに新しい PDF をタイムスタンプ付きファイル名で Storage 保存し、sot.lm_picking_lists に 1 行追記します(再印刷履歴の append-only ログ)。

  • 出力は常に最新の routing データ(sot.lm_routing_decisions の decision_status = applied)から組み立てるため、冪等なのは入力側の routing です。売越し・再引き当て後の再生成は最新の引き当て先を反映します。

  • バッチ(/api/routing/picking-list/batch) も同様に非冪等(生成のたびに Storage 上書き保存)。ただし複数注文にまたがるため sot.lm_picking_lists への追記は行いません(生成物は公開 URL が成果物)。

Address Recovery — /api/routing/shipment/address / /api/routing/shipment/retry-label

  • 住所修正: 注文 1 件(shop + sourceSystem + externalOrderRef または routingOrderId)の sot.lm_routing_orders.ship_to_* を部分上書き。同じ住所での再 POST は同値で収束(冪等)。住所はラベル生成の単一ソースのため修正が即反映されます。

  • 変更履歴: 住所の更新と変更履歴(sot.lm_routing_order_ship_to_changes)の記録は同一トランザクション。実際に値が変わったときだけ 1 行 append するため、同値の再 POST(冪等)では履歴を増やしません(before/after を残す append-only ログ)。

  • dry-run: validateOnly: true は読み取りのみ(保存・履歴・webhook なし)。何度呼んでも状態を変えません。

  • ラベル再発行: 出荷元(shipment)単位。既に tracking_number がある出荷元は already_issued で skip し、force: true のときだけ再発行します(二重発行ガード)。確保前(unallocated / allocated)は not_secured で skip。

  • 再発行成功で last_label_error をクリア、失敗で更新。action=label 設定が無い出荷元は no_label_config で skip(silent skip しません)。

  • 自動再発行(opt-in): 住所修正の autoRetryLabel: true は、ラベルエラー中の出荷元に加え、ラベル面に影響する項目が実際に変わった(changedFields)ときだけ発行済み出荷元(secured / oversell_secured・未破棄)を再発行します。同値の再 POST では発行済み出荷元を再発行しません(冪等)。

  • 再発行成功時の自動印刷: 印刷イベント(fulfillment_shipment_status_changed)を印刷アプリへ自動配信します。イベントの冪等キーは routingOrderId:shipmentId:status:transitionedAt(transitionedAt = 再発行時刻)のため、初回確保イベントと重複起票しません。label_printed_at / last_print_error は再発行成功時にリセットされます(新ラベルは未印刷)。

  • 非冪等(再印刷前提): 呼ぶたびに保存済みラベル PDF を出荷元 print server へ送信します(複数回 POST = 複数枚出力)。意図的な再印刷操作としてのみ使用してください。

  • ラベル生成時の自動印刷(Fulfillment)はラベル生成と同じトリガーに従うため、確保済への遷移時のみ 1 回実行されます(リトライなし。失敗は last_print_error に記録し本 API で回収)。

Shipment Memo — /api/routing/shipment/memo

  • 追記のみ(非冪等): 呼ぶたびに sot.lm_shipment_memos へ 1 行 append します(同じ body の再 POST も別行)。編集・削除の API はありません(完全な記録 / 監査ログ)。タイムラインは created_at 降順で表示します。

Shipment Delivery — /api/routing/shipment/delivery

  • 冪等(部分更新): 解決キーは routingOrderId。sot.lm_routing_orders の desired_delivery_date / desired_delivery_time の 2 列だけを最新値で上書きします(同じ値の再 POST は副作用なし・updated_at のみ前進)。他のヘッダ列は触らず、再ルーティング・再 dispatch も行いません。null で指定解除。値はラベル生成時に Ship&Co setup.date / setup.time へ載るため、既発行の送り状へ反映するには retry-label(force)で再発行します。

  • 遅延のお知らせ: notifyDelay: true で遅延になった変更 1 回につき 1 通です。同じ値の再 POST は遅延ではないので送りません。遅延の変更を繰り返すと、そのたびに 1 通送ります。

External Fulfillment — /api/routing/shipment/fulfill-job / /fulfill-result / /fulfill-pending / /api/ingest/sku-mappings

  • fulfill-job / fulfill-pending / sku-mappings(read): SoT への書き込みが無いため冪等性の対象外です。何回呼んでも安全(同じ入力には同じ時点のスナップショットを返します)。

  • fulfill-result: 解決キーは shipmentId。accepted は external_fulfillment_id / 受理時刻の列上書きのため再送に冪等です(externalFulfillmentId は同一 shipment で常に同じ決定的な値を推奨。例 LM-{shipmentId})。failed はエラー記録の上書きのみ(受理マーカーは既存値を保持)。

  • 二重発注ガード: 受理後の fulfill-job は alreadyAccepted: true を返します。イベント再配信で同じ shipment のジョブを再取得しても、呼び出し側はこれを見て外部への再発注を skip できます。

Shipment Ship — /api/routing/shipment/ship

  • 冪等: 既に出荷済みの shipment への再 POST は 200 + noop: true(更新も webhook の再発火もしない = 二重 fulfill 防止)。

  • 出荷済みへの遷移は 1 回だけ副作用(配下単位への cascade / 棚紐付け解除 / 出荷ステータス webhook)を実行します。trackingNumber / carrierCode 指定時は shipped と同一の書き込みで保存されます(未指定は保存済みの値を使用)。

  • MFC 集約グループの一員を指定した場合はグループ全体を一括で shipped にします(既 shipped グループは noop: true)。

Shipment Collect — /api/routing/shipment/collect

  • 冪等(残りだけ前進): 解決キーは shipmentId。集荷済みの単位はそのまま維持し、未集荷の単位だけを集荷済みにします。全単位が集荷済みなら alreadyCollected: true を返します(集荷の更新なし)。

  • issueLabel: true は発行済みラベル(追跡番号あり)を再発行しません(already_issued)。ラベル発行だけが失敗した出荷は、同じリクエストの再送がそのまま再試行になります。

  • 棚の減算処理が途中で失敗した場合、処理済みの単位は集荷済みのまま残ります。再送すれば残りの単位だけが前進します。

Shipment Unsecure — /api/routing/shipment/unsecure

  • 冪等: 解決キーは shipmentId(+ unitId)。既に確保前の単位はそのまま維持し、unsecured: 0 を返します。そのまま再送して構いません。

  • 対象に解除できない単位が 1 つでもあれば、何も変えずに 409 を返します(一部だけ解除されることはありません)。

Carrier Pickup — /api/routing/pickup / /api/routing/pickup/cancel

  • 冪等ではありません(重複依頼は拒否): 解決キーは出荷の pickup_id 列。既に生きた集荷依頼に入っている出荷を含むリクエストは 409 pickup_already_requested で拒否し、既存の受付番号を返します。同じ荷物に受付番号が 2 つ立つことを構造的に防ぐためで、再送しても新しい依頼にはなりません。

  • キャリアへ問い合わせる前に対象の出荷を予約します。同時に 2 か所から依頼しても、あとから来た方はキャリアに触れずに 409 で止まります。キャリアへの依頼が失敗した場合は予約が自動で解除され、対象の出荷は未依頼に戻ります(そのまま再送できます)。

  • 取消はキャリア側が成功してからこちらを解除します。キャリアの取消が失敗すると 502 を返し、出荷は依頼済みのまま残ります(未依頼に戻して二重に呼ばないため)。取消済みの依頼の再取消は 409。

  • 時間帯や対象の変更は「取消 → 依頼し直し」で行います(依頼の書き換えはできません)。

Oversell API

Reallocate — /api/routing/reallocate

  • 売越し(oversold)decision を同一出荷元の別メンバーへ再引き当て。旧 decision は superseded にし後継へリンク(重複適用なし)。

  • 出荷指示は shop + idempotencyKey で UPSERT されるため、再 dispatch は同一行を更新(外部への二重送信を防止)。

Oversell Cases — /api/routing/oversell-case

  • 1 単位につきケース 1 件(shop + fulfillment_unit_id で一意)。初回呼び出しで起票、以降は前進。

  • 同一 status への再呼び出しは changed: false の no-op(timestamp 再設定なし)。不正な遷移は 409 invalid_transition。

Source Change — /api/routing/change-source

  • 1 単位につき変更 1 件(shop + fulfillment_unit_id で一意)。初回呼び出しで起票(change_requested、対象 unit は secured のみ=LM-913)、以降は前進。前回が cancelled(終端で前進不可)の単位は同一行を再オープンして再起票します(reopened: true。LM-956。前回の進行・バルク・チェック・返送ラベル列は初期化)。

  • secured からの起票は確保を解除(secured / secured_at クリア)し、旧出荷元ヘッダが空になった場合のみヘッダ cancelled・ラベル運用破棄(label_voided_at)・棚紐付け解放(released_reason="source_changed")を行います(残アクティブ unit があるヘッダは触りません = 誤破棄なし)。

  • 起票時に旧 decision を superseded にし後継(出荷元=B)へリンク(重複適用なし)。出荷指示の dispatch(A への cancel / B への create)は WMS 設定が無ければ skipped。

  • 同一 status への再呼び出しは changed: false の no-op。不正な遷移は 409 invalid_transition。cancelled は SoT を起票前の出荷元へ巻き戻します(WMS 再連絡は best-effort)。在庫は状態のみで inventory_levels は不変。

  • mode=switch(即時切替): ケースを起票せず同一レグの出荷元だけを更新します。切替完了後に同じボディを再送すると新旧出荷元が同一のため 422 same_source で弾かれ、重複適用されません(自己在庫 available の再計算は派生量のため冪等)。

Transfer API

Transfer Batches — /api/operator/transfer/batches (+ add-unit / remove-unit)

  • バルク作成は呼び出しごとに新規(同一経路の複数 open バルク = 複数箱は許容)。一覧は read 専用。

  • add-unit: 同一バルクへの再スキャンは 200 の no-op(added: false, alreadyInBatch: true)。別バルク所属は 409 already_in_batch。同 barcode の複数単位は未所属の先頭(lineInstanceNo 昇順)から 1 件ずつ。

  • remove-unit: 既に外れていれば 200 の no-op(alreadyRemoved: true)。いずれも open 中のみ(それ以外は 409 batch_not_open)。

Transfer Dispatch — /api/operator/transfer/batches/dispatch

  • dispatched / received 済みへの再実行は 200 + 既存 pdfUrl の no-op(alreadyDispatched: true)。

  • 処理順は PDF → 単位遷移 → バルク確定。途中失敗時はバルクが open のまま残り、再実行が change_requested の残りだけ遷移して回復(遷移済みは skip・PDF は同一パスへ上書き)。

  • 旧拠点の棚 −1 はピック済み(picked_at あり)単位を skip(ピック時に減算済み = 二重減算なし)。

Transfer Receive — /api/operator/transfer/batches/receive

  • 1 スキャン = 1 単位。受領済み単位の再スキャンは 200 の no-op(alreadyReceived: true)。

  • 受領遷移は Source Change の return_received と同一実装(重複適用なし)。全所属単位の終端でバルクが received に締まる(再判定は冪等)。

Transfer Inbound — /api/routing/transfer/inbound

  • 解決キーは shipmentId。その出荷の有効な単位に生きた入荷転送がすでにあれば作り直さず、created: false と既存の batchId を返す(イベント再配信で移動が二重に増えない)。そのまま再送してよい。

  • 中止済みの移動しか残っていない場合は、新しく登録し直せる。

  • 受け取りは配送状況の配達完了検知で自動的に確定する(同じ配達完了を再検知しても遷移は 1 回だけ)。倉庫アプリのスキャン受領はこの移動には使えない(409 inbound_batch_not_manually_receivable)。

Stock Transfer API

拠点間在庫移動(Stock Transfer)。返送バルク(上記 Transfer API)とは別機能です。

Create / Create Request — /api/routing/stock-transfer/create / /create-request

  • 作成は冪等: idempotencyKey を付けて同じ値で再送すると、新しい移動を作らず既存の移動を idempotent: true で返します(shop + idempotency_key で一意)。イベント単位で一意なキーを付与してください。

  • idempotencyKey を省略すると毎回新しい移動になります。

  • 再送で既存の移動が返ったとき、最初に決めた送り状の出し方(labelMethod)は変わりません。

Set Lines / Remove Lines / Update — /api/routing/stock-transfer/set-lines / /remove-lines / /update

  • set-lines: 同一 barcode は数量を置換(行を増やしません)。同じボディの再送は同値で収束(冪等)。出庫後は not_editable。

  • remove-lines: 既に無い barcode の削除は数に含まれないだけで安全(removed に計上しない)。

  • update: 指定したキー(identificationNo / note)だけを上書き。同値の再送は副作用なし。

Ship — /api/routing/stock-transfer/ship

  • 冪等: 既に出庫済み(shipped 以降)への再 ship は noop: true(棚を二重に減らしません)。自社在庫の棚 − は明細ごとの適用済みマーカーで一度きりに保たれます。

  • 移動可能量(棚合計 − 引き当て済み)を超える shippedQty は insufficient_transferable_qty で拒否(売越防止)。承認前は approval_required。

  • print-label は何回でも呼べます: 発行済みのラベルを印刷し直すだけで、発行し直しません。

  • 配送ラベルは 1 回だけ発行: ラベルを発行する移動で、発行後に出庫が失敗して呼び直しても、前回のラベルを使います(二重に発行しません)。

Receive — /api/routing/stock-transfer/receive

  • 絶対値で冪等: acceptedQty は「受け入れた総数」として上書きします。同じ値の再送は棚を二重に増やしません(適用済みを超えた差分だけ棚を増やします)。既に received への再送は noop: true。

  • 自社在庫の受領は destinationShelfCode 必須(destination_shelf_required)。acceptedQty + rejectedQty は出庫数を超えられません(invalid_quantity)。修正依頼中は modification_requested_locked。

Approve / Reject / Cancel / Delete — /api/routing/stock-transfer/approve / /reject / /cancel / /delete

  • approve: 既に approved は noop: true。既定(autoShip: true)は承認後に ship まで実行し、出庫が失敗しても承認は確定済み(ship を呼び直して回復)。

  • reject: 既に rejected は noop: true。承認待ち以外は illegal_transition。

  • cancel: 既に canceled は noop: true。出庫済みは出庫元の棚を復元してから取消(復元失敗は restore_failed で取消を確定しない)。receiving / received は not_cancelable。

  • delete: 既に存在しない移動の削除は noop: true(200)。削除できるのは出庫前のみ(not_deletable)。

Modification Request — /api/routing/stock-transfer/modification-request(+ /complete / /delete)

  • 1 つの移動に開ける依頼は 1 件(active は partial unique)。既に開いていれば modification_request_already_open。開いた依頼が無い状態での complete / delete は no_open_modification_request。

  • 起票は入庫検品中(receiving)のみ(not_receiving)。棚・在庫は動かしません(実数量は receive の受領数で確定)。

List / Get / Lines / Modification Requests(read)— /api/routing/stock-transfer/list / /get / /lines / /modification-request(s)

  • read 専用: SoT への書き込みが無いため冪等性の対象外です。何回呼んでも安全(同じ入力には同じ時点のスナップショットを返します)。

Customer Orders API

Customer Orders — /api/customer-orders/list / /api/customer-orders/detail

  • read 専用: SoT への書き込みが無いため冪等性の対象外です。何回呼んでも安全(同じ入力には同じ時点のスナップショットを返します)。

Operator API

Operator Login — /api/operator/login / /api/operator/login/google / /api/operator/refresh

  • 非冪等(トークン発行)だが安全: 呼ぶたびに新しい JWT / refresh token を発行します(毎回同じトークンは返しません)。SoT の業務状態は変えないため、再試行に害はありません(古いトークンも失効まで有効です)。

  • /api/operator/login/otp/request は呼ぶたびに確認コードのメールを送ります。再送は短い間隔だと 429 rate_limited になるので、少し待ってから送り直してください。

  • 試行回数の上限超過は 429 rate_limited、認証基盤に到達できない場合は 503 auth_unavailable(フォールバックしません)。

Operator Me / Lookup / Queues / Locations(read)— /api/operator/me / /api/operator/shipments / /api/operator/shipments/by-tracking / /api/operator/products/by-barcode / /api/operator/oversell-cases / /api/operator/unallocated-orders / /api/operator/locations

  • read 専用: SoT への書き込みが無いため冪等性の対象外です。何回呼んでも安全(同じ入力には同じ時点のスナップショットを返します)。

Account Delete — /api/operator/account/delete

  • 解決キー: JWT の sub(本人の auth.users.id)。削除対象は常に本人で、外部から指定するキーはありません。

  • 冪等: 既に削除済みのアカウント(有効だが失効済みの JWT を保持したケース等)への再実行は 200 + deleted: true / alreadyDeleted: true を返します(エラーにしません)。

  • 監査: 実行のたびに sot.lm_operator_account_deletions に監査行を 1 件記録します(削除完了時刻を含む)。削除自体は冪等ですが、監査行は試行ごとに残します(トレイル目的)。

  • デモ保護: デモアカウントは状態を変えず 403 demo_account_protected を返します。

Secure Scan — /api/operator/secure-scan

  • 順次確保: ユニットQR(<barcode>|<shipmentId>)は同一 barcode の複数 unit を区別しないため、スキャンごとに「allocated かつ 未 picked の line_instance_no 昇順先頭 1 件」を確保します(数量 2 なら 2 回のスキャンで全確保)。

  • 冪等: 確保対象が無く既確保 unit が居る再スキャンは 200 + secured: 0 / alreadySecured: N(二重確保しません)。ヘッダ遷移・ラベル生成は /api/routing/secure と同一で、既に確保済ヘッダはラベルを再生成しません。

  • 確保可能でも既確保でもない unit(取消済 / 売越し / picked 済のみ等)は 409 unit_not_securable で表面化します(silent skip しません)。

Secure by Tracking — /api/operator/secure-by-tracking

  • 解決キー: 配送ラベルの trackingNumber(sot.lm_fulfillment_shipments.tracking_number・shipment 単位)。同一追跡番号が複数 shipment に当たりうるため該当 shipment を全件処理し、各 shipment 配下の status=allocated ユニットを一括確保します。案 A(pick→secure 順)のため picked 済も含めて確保します(Secure Scan の「未 picked のみ」と異なります)。

  • 冪等: 確保対象(allocated)が無く既確保ユニットが居る再スキャンは 200 + 当該 shipment の secured: 0 / alreadySecured: N(二重確保しません)。全ユニット確保でヘッダ secured に遷移しますが、先行生成済みラベルは再生成しません。

  • 追跡番号に該当 shipment が無ければ 404 shipment_not_found。確保可能でも既確保でもないユニットしか無い shipment は 409 unit_not_securable で表面化します(silent skip しません)。

  • operator 記録: operator JWT 経由の確保は secured_by_staff_user_id に操作者(auth.users.id)を記録します。

Shelf API

  • 再送冪等: shipment(shop + trackingNumber で 1 件に解決)に既にアクティブ紐付けがあり、棚コード・ロケーションが同一なら 200 + idempotent: true(同値で収束)。別棚への紐付けは自動で付け替えず 409 shipment_already_linked。

  • 付け替え(relink): relink: true 指定時のみ、別棚への既存紐付けを manual_release で解除して新棚へ付け替える(200 + relinked: true + previousShelfCode)。解除行は履歴として残る。

  • アクティブ一意性: 同一棚に 1 shipment / 同一 shipment に 1 棚(released_at IS NULL の partial unique index)。並行 INSERT 競合は 409。

  • 自動解除: 出荷完了(shipped 遷移 → released_reason="shipped")/ 取消(注文 cancel → released_reason="cancelled")/ 出荷元変更(secured からの起票で旧ヘッダが空 → released_reason="source_changed")で解除。解除は DELETE せず行を履歴として残します。

Shelf Inventory — /api/shelf/inventory/receive / pick / move / adjust

  • 非冪等(積算): 入庫・ピックは qty を増減する操作のため自然冪等ではありません。冪等キーは持たないため、リトライ時はクライアント側で適用済みかを確認してください(既存 routing write の secure / oversell と同方針)。

  • qty 更新と増減ログ追記は write RPC が同一トランザクションで実行(部分適用なし)。pick の在庫不足は 409 insufficient_shelf_qty、同一棚の別 barcode 占有は 409 shelf_occupied_by_different_barcode。

  • move は非冪等(差分): 再送すると二重に動く。移動元の不足は 409 insufficient_shelf_qty。

  • adjust は冪等(絶対値): 同じ値の再送は delta: 0 で何もしない(ログも書かない)。

  • adjust と move(barcode の付け替え)で、減らした結果その商品の棚の合計が注文に引き当て済みの数を下回る操作は 409 shelf_qty_below_active で何も変えない。

Shelf Condition Move — /api/shelf/inventory/condition-move

  • 非冪等(差分): 商品状態の間で数を動かす操作のため、再送すると二重に動く。元の数の不足は 409 insufficient_shelf_qty / 409 insufficient_condition_qty。

  • 良品を減らした結果が注文に引き当て済みの数を下回る移動は 409 shelf_qty_below_active で何も変えない。

  • 2 表の更新・ログ 2 行・販売可能数の再計算は write RPC が同一トランザクションで実行(部分適用なし)。

Shelves — /api/shelf/master/upsert

  • 冪等(絶対値): (shop, locationId, shelfCode) で upsert。同じ内容の再送は同じ結果。

Shelf Pick — /api/operator/shelf/pick

  • 順次 pick: ユニットQR(<barcode>|<shipmentId>)は同一 barcode の複数 unit を区別しないため、pick はスキャンごとに「未 picked の line_instance_no 昇順先頭 1 件」を picked にします。unpick は「picked の降順先頭 1 件」(直近の pick から LIFO)を戻します。

  • 冪等: barcode 一致 unit が全て picked の状態での再 pick は 200 + alreadyPicked: true(上書きしません)。全て未 picked での unpick も同様(alreadyUnpicked: true)。状態の記録は picked_at / picked_by のみ。

  • 棚 qty 減算の冪等(LM-769): 自己在庫 MFC ユニットの pick(−1 reason=pick)/ unpick(+1 reason=receive)と続く available_qty 再計算は picked_at の遷移時のみ 実行します。既に picked の再 pick / 既に未 picked の再 unpick は遷移しないため棚 qty を増減しません(二重減算なし)。再計算は派生量(max(0, 棚合計 − 引き当て済み未出荷数))なので冪等です。棚操作はベストエフォートで、失敗しても pick 本体は成功し後続の再計算が整合させます。

Shelf Lookup(read)— /api/operator/shelf/lookup / /api/operator/shelf/by-code

  • read 専用: SoT への書き込みが無いため冪等性の対象外です。何回呼んでも安全(同じ入力には同じ時点のスナップショットを返します)。

Configuration

Star Printer — /api/routing/star-printer

  • 設定 upsert: (shop, locationId) で 1 件に収束(1 ロケーション 1 プリンター)。同じ deviceId の再送は同値で収束(冪等)。

  • detach: 物理削除。該当無しの再 POST は deleted: 0 の no-op(冪等)。

Webhooks

Source Inventory Webhook — /api/routing/source-webhook-config

  • 設定 upsert: (shop, fulfillment_source_location_id) で1件に収束。authSecret 省略時は既存の暗号化値を維持。

  • 変動の冪等: 出荷元の barcode(product)単位の合算 available_qty を sot.lm_fulfillment_source_inventory_state に「前回送信値」として保持し、異なるときだけ送信します。同値の在庫再取込では送信しません。送信ペイロードは合算の絶対値(差分ではない)のため、受け手は冪等に上書きできます。

  • 再送: 送信前にプロセスが落ちても event は outbox(sot.lm_fulfillment_source_inventory_events)に pending / failed で残り、回収・再送できます(合算の絶対値を再送するため二重適用の害なし)。

Routing Allocation Webhook — /api/routing/allocation-webhook-config

  • 設定 upsert: shop で1件に収束。authSecret 省略時は既存の暗号化値を維持。

  • 発火の冪等: /api/routing/orders の入口冪等(dispatched 済み注文の同一キー再 POST)では発火しません。未 dispatch の同一注文を再 POST した場合は再ルーティングされ、最新の引き当て結果を改めて送信します(引き当て結果の絶対状態を送るため、受け手は最新イベントで冪等に上書きできます)。受け手の冪等キーは shipmentId(unallocated イベントは routingOrderId + allocationStatus)。

  • 再送: 送信前にプロセスが落ちても event は outbox(sot.lm_routing_allocation_events)に pending / failed で残り、回収・再送できます(明細は起票時 snapshot を再送するため、後続の再引き当てに影響されません)。

Fulfillment Status Webhook — /api/routing/status-webhook-config

  • 設定 upsert: shop ×取込元(sourceSystem。省略時はその shop の既定宛先)で1件に収束。authSecret 省略時は既存の暗号化値を維持。

  • 遷移の冪等: /api/routing/fulfillment が各 shipment(shop + routing_order_id + from_location_id)の status を更新する際、直前の値と異なる shipment だけ event を起票・送信します。同じ status の再報告では送信しません。ペイロードには遷移後の絶対状態(status / previousStatus / trackingNumber)と shipment の出荷明細(shipmentId / lines: barcode 単位集約)を含むため、受け手は shipmentId を冪等キーに同一 shipment の再送を弾きつつ、該当明細だけを冪等に反映できます。

  • 再送: 送信前にプロセスが落ちても event は outbox(sot.lm_fulfillment_status_events)に pending / failed で残り、回収・再送できます。

Oversell Status Webhook — /api/routing/oversell-status-webhook-config

  • 設定 upsert: shop で1件に収束。authSecret 省略時は既存の暗号化値を維持。

  • 発行の冪等: /api/routing/oversell-case でケースが warehouse_received に遷移した時に 1 通発行します。outbox は UNIQUE (oversell_case_id) のため、同一ケースの event は 1 件しか起票されません(二重起票は無視して no-op)。warehouse_received は終端ステップで再遷移できないため、通常 1 ケース = 1 通です。受け手の冪等キーは oversellCaseId。

  • 再送: 送信前にプロセスが落ちても event は outbox(sot.lm_oversell_status_events)に pending / failed で残り、回収・再送できます(明細は起票時 snapshot を再送します)。

Order Update Webhook — /api/routing/order-update-webhook-config

  • 設定 upsert: shop で1件に収束。authSecret 省略時は既存の暗号化値を維持。

  • 発行の冪等: /api/routing/shipment/address で配送先住所の手修正が成功するたびに 1 通発行します。1 注文に複数回の修正が起こり得るため outbox は UNIQUE 制約を持たず 1 修正 = 1 event を起票します。ペイロードは修正後の shipTo 全体(差分ではなく絶対状態)を含むため、受け手は routingOrderId を冪等キーに、correctedAt で順序判断して最新の配送先で冪等に上書きできます。

  • 再送: 送信前にプロセスが落ちても event は outbox(sot.lm_order_update_events)に pending / failed で残り、回収・再送できます(修正後 shipTo は起票時 snapshot を再送します)。

Apps

イベント配信 — LMile ホスト型ミニアプリ

  • 起票の冪等: 同一ルーティング実行内の二重発火は配信キー(routingOrderId × 出荷先 × decidedAt)で 1 配信に収束します。再ルーティング(decidedAt が変わる)は新しいイベントとして配信されます。

  • handler の冪等契約: 配信は at-least-once / 順序保証なし。同じイベントが複数回届くことがあるため、handler は冪等・順序非依存で書きます。ctx.idempotencyKey は同一イベントの再配信で同じ値のため、action 呼び出しの重複実行抑止に流用できます。

  • 再送: 配信失敗(実行環境の未起動 / handler 例外 / timeout)は outbox(sot.lm_app_deliveries)に pending / failed で残り、回収・再送できます。

Last updated

Was this helpful?