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

Inventory Levels — 在庫

在庫を Last Mile の在庫マスターに取り込みます。用途別に 2 エンドポイントに分かれます: 定期的な全量同期は snapshot、販売・入荷など変動イベントの都度反映は available を使います(外部倉庫の拠点で販売チャネル側の販売可能数を伝える channel-available もあります)。両方の併用も可能です(古い読み値の上書き防止により、古いバッチ読み値がリアルタイム更新済みの値を上書きすることはありません)。商品は barcode、拠点は location_id または外部識別子で解決します。

Base URL https://api.lmile.io · 認証 x-ingest-secret ヘッダー(詳細) · shop はリクエストボディに含めます(省略するとキーのアカウント)

エンドポイント

Method
Path
用途
こんなとき

POST

/api/ingest/inventory-snapshot

1 拠点の現在庫を絶対値で一括投入(全在庫を上書き)

初回投入 / 1 日 1 回などの定期全量同期 / ズレ補正

POST

/api/ingest/inventory-available

販売可能在庫 (available_qty) のリアルタイム更新(absolute / delta)

販売・入荷・返品などの変動をその場で反映

POST

/api/ingest/channel-available

販売チャネル(Shopify など)での販売可能数を伝える

在庫数を外部倉庫が送ってくる拠点で、チャネルで売っていない商品を売れない数にする

取込により出荷元の合算在庫が変わると Source Inventory Webhook が発火します。

レート制限はありません。上限は 1 リクエストあたり lines 最大 10,000 行です(超過は 400)。

商品(barcode)の指定

両エンドポイント共通。商品は行の barcode(JAN コード)で商品マスターと照合します。SKU や商品名では照合できません。未登録の barcode はリクエスト全体を失敗させず、HTTP 200 のまま該当行だけ反映されず unresolved[] に返ります(→ 行単位エラー)。事前に Products で商品(barcode)を登録してください。

拠点(location)の指定

両エンドポイント共通。location_id(内部 UUID)か location(外部識別子)のいずれかを指定します。

"location_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
// または
"location": { "identifier_type": "smaregi_store_id", "identifier_value": "4" }

古い読み値の上書き防止

両エンドポイント共通。**「古い在庫の読み値で、新しい値を上書きしない」**ための仕組みです。

いつ関係するか — 在庫を複数の経路で送っている場合です。典型は「販売のたびに送るリアルタイム更新」と「数時間おきのバッチ全量同期」の併用。バッチは在庫を読み取ってから届くまでに時間差があるため、対策がないとバッチの古い在庫数が直近のリアルタイム更新を巻き戻してしまいます(売り切れた商品が「在庫あり」に戻る事故)。送信経路が 1 つだけなら、この節は読み飛ばして構いません(as_of 省略のままで正しく動きます)。

具体例 — 1 時間おきのバッチとリアルタイム更新を併用しているとき:

時刻
起きること

14:00

バッチが在庫システムから読み取り: barcode 1234567890123 = 5 個

14:10

店頭で 5 個売れ、リアルタイム更新が available_qty: 0 を送信 → 在庫 0(読み値の時刻 14:10 として反映)

14:30

14:00 に読んだバッチが届き、available_qty: 5 を as_of: 14:00 付きで送信

14:30 の行は「14:00 の読み値 < 反映済みの 14:10」なので適用されず、在庫は 0 のまま守られます。この仕組みが無ければ在庫が 5 に巻き戻り、実際には無い在庫が売れてしまいます。

判定ルール — 各行の as_of(省略時はリクエストの inventory_as_of、それも省略なら受信時刻)を、反映済みの時刻と比較します。

行の時刻 vs 反映済みの時刻
結果

新しい / 同じ / 初回(既存行なし)

適用します(同時刻を許すので、同じリクエストの再送は成功します)

古い

その行だけ適用しません。HTTP 200 のまま stale_skipped[] に返ります

レスポンスの見方 — 上の 14:30 のリクエストにはこう返ります:

送信側がやること

  • バッチ連携では、各行の as_of に在庫システム側でその値を読み取った時刻を入れてください(例: スマレジ /stock の updDateTime)。送信時刻ではありません。

  • 送信経路が 1 つだけなら as_of は省略できます(受信時刻が使われ、古い読み値として弾かれることはありません)。

  • stale_skipped[] は「より新しい値が反映済み」の合図です。再送は不要です。恒常的に大量に返る場合だけ、as_of に入れている時刻が正しいか確認してください。


POST /api/ingest/inventory-snapshot

「今この拠点にこの商品が何個あるか」を絶対値で送信します。送るたびに現在庫が送った値で丸ごと置き換わります(追記や加算ではありません)。当日の日次履歴と変更ログも同時に更新されます。

リクエスト

ボディパラメータ

パラメータ
型
必須
説明

shop

string

店舗ドメイン(省略するとキーのアカウント)

source_system

string

✓

取込元。ログの change_source に記録

location_id / location

string / object

✓

いずれか必須(上記参照)

lines

array

✓

在庫行の配列(最大 10,000 件)

inventory_as_of

ISO 8601

読み取り時刻の既定値。行の as_of 省略時に使われます(それも省略なら受信時刻。→ 古い読み値の上書き防止)

idempotency_key

string

監査用の任意キー。このエンドポイントでは重複排除に使いません(再送の安全性は絶対値の置き換えで担保されます)

lines[].barcode

string

✓

商品 barcode

lines[].on_hand_qty

integer (≥0)

✓

物理在庫(拠点に実在する数)

lines[].available_qty

integer

販売可能在庫(省略時は on_hand_qty)

lines[].allocated_qty reserved_qty incoming_qty

integer

引当 / 予約 / 入荷予定(default: 0)

lines[].as_of

ISO 8601

その行の読み値を在庫システム側で読み取った時刻。省略時は inventory_as_of(→ 古い読み値の上書き防止)

レスポンス

フィールド
説明

lines_processed

受け取った行数

lines_upserted

在庫に反映した行数

lines_unresolved

反映しなかった行数(内訳は unresolved[])

lines_stale_skipped

読み値が古く反映しなかった行数(内訳は stale_skipped[])

logs_written

変更ログを書いた行数(値が変わった行のみ)

unresolved[]

反映しなかった行と理由(→ 行単位エラー)

stale_skipped[]

読み値が古く反映しなかった行(→ 古い読み値の上書き防止)

挙動・冪等性

  • 在庫は店舗 × 商品 × 拠点ごとに 1 行で管理されます。送信すると該当行が送った値で丸ごと置き換わり、無ければ新規作成されます(追記や加算はされません)。

  • 同時に、その日の日次履歴も同じ値に更新されます(1 日 1 行。同じ日に何度送っても増えません)。

  • 変更ログは値が変わった行だけ記録されます。同じ値の再送でログは増えません。

  • 同じリクエストは何度再送しても安全です(冪等)。タイムアウト等で結果が不明なときは、そのまま再送してください。

  • 未登録の barcode の行はその行だけ反映されず unresolved[] に返ります(→ 行単位エラー)。

  • 行の読み値が反映済みより古い場合はその行だけ反映されません(→ 古い読み値の上書き防止)。


POST /api/ingest/inventory-available

販売可能在庫 (available_qty) を即時更新します。1 リクエストで 1〜数件の (barcode × 拠点) を更新する用途です。

任意で committed_qty(注文済み・未出荷)と on_hand_qty(物理在庫)も同時に送れます。送った行だけ上書きされ、省略した値は現状維持です(allocated_qty / reserved_qty はこのエンドポイントでは変わりません)。

なぜ committed_qty も送るのか — Shopify では注文が入った瞬間、その分の在庫は available から committed(注文済み・未出荷)へ移ります。例: 在庫 1 個の商品が売れると available: 0, committed: 1。このとき available_qty: 0 だけを送ると Last Mile からは「在庫なし」に見え、いま入ったその注文にすら引き当てられません。committed_qty: 1 を併せて送ると、引き当て可能数は available + committed = 1 と判定され、その注文のために確保済みの 1 個へ正しく引き当てられます。

available_qty の送り方は mode で選びます:

mode

送る値

こんなとき

再送したら

absolute

在庫数そのもの(available_qty: 3 = 3 個にする)

その時点の在庫数を知っている場合。可能ならこちら推奨

同じ値で上書きされるだけ。安全(冪等)

delta

増減量(available_delta: -2 = 2 個減らす)

販売・返品などの変動イベントしか取れない場合

二重に増減してしまうため、イベントごとに一意な idempotency_key を必ず付けてください(同一キーの 2 回目は適用されません)

delta の結果が負になる場合は 0 で止まり、clamped[] に返ります(エラーにはなりません)。committed_qty / on_hand_qty は mode に関わらず常に「在庫数そのもの」で送ります。

リクエスト

ボディパラメータ

パラメータ
型
必須
説明

shop

string

店舗ドメイン(省略するとキーのアカウント)

source_system

string

✓

取込元

mode

string

✓

absolute または delta

location_id / location

string / object

✓

いずれか必須

lines

array

✓

更新行の配列(最大 10,000 件)

inventory_as_of

ISO 8601

読み取り時刻の既定値。行の as_of 省略時に使われます(それも省略なら受信時刻。→ 古い読み値の上書き防止)

idempotency_key

string

delta ではイベントごとに一意な値を必ず(同一キーの 2 回目は適用されません)。absolute では不要

lines[].barcode

string

✓

商品 barcode

lines[].available_qty

integer (≥0)

absolute 時 ✓

上書きする販売可能在庫

lines[].available_delta

integer

delta 時 ✓

変動量(負可)

lines[].committed_qty

integer (≥0)

注文済み・未出荷の数を上書き(省略時は現状維持)。→ 上の「なぜ committed_qty も送るのか」

lines[].on_hand_qty

integer (≥0)

物理在庫を上書き(省略時は現状維持)

lines[].as_of

ISO 8601

その行の読み値を在庫システム側で読み取った時刻。省略時は inventory_as_of(→ 古い読み値の上書き防止)

レスポンス

フィールド
説明

lines_processed

受け取った行数

lines_updated

在庫に反映した行数

lines_unresolved

反映しなかった行数(内訳は unresolved[])

lines_clamped

delta の結果が負になり 0 に丸めた行数(内訳は clamped[])

lines_deduped

同一 idempotency_key の二重適用として skip した行数

lines_stale_skipped

読み値が古く反映しなかった行数(内訳は stale_skipped[])

unresolved[]

反映しなかった行と理由(→ 行単位エラー)

clamped[]

0 に丸めた行({ barcode, computed, set })

stale_skipped[]

読み値が古く反映しなかった行(→ 古い読み値の上書き防止)

挙動・冪等性

  • 送った行の available_qty が更新されます。在庫行が無ければ新規作成されます(送らなかった数量は 0 で作成)。

  • committed_qty / on_hand_qty は送った行だけ上書きされ、省略した値は現状維持です(allocated_qty / reserved_qty はこのエンドポイントでは変わりません)。

  • 変更ログは値が変わった行だけ記録されます。

  • 日次履歴は更新しません(日次は snapshot エンドポイントの役割です)。

  • absolute は同じ値の再送で上書きされるだけなので安全です(冪等)。delta の重複排除は連続リトライを吸収する best-effort です。イベントごとに一意な idempotency_key を必ず付けてください。

  • 行の読み値が反映済みより古い場合はその行だけ反映されません(→ 古い読み値の上書き防止)。

POST /api/ingest/channel-available

販売チャネル(Shopify など)で、ある拠点の商品がいくつ売れる状態かを伝えます。在庫の数そのものは外部倉庫など別の送り手が送ってくる拠点(外部倉庫の拠点)のためのエンドポイントです。

送った値が 0 以下の商品は、その拠点の販売可能在庫が 0 になります。倉庫に現物があってもチャネルで売っていない商品を、Last Mile でも「売れない」として扱うためです。1 以上を送ると、倉庫から届いた在庫数にもとづく通常の計算に戻ります。

例: 倉庫から「在庫 3」が届いている商品について

送った channel_available_qty

販売可能在庫

未送信

倉庫の数にもとづく通常の計算(例: 3)

0 または負(売り越し)

0

1 以上

通常の計算に戻る(例: 3。送った数そのものにはなりません)

  • 倉庫から届いた在庫数は書き換えません。倉庫の数は従来どおり inventory-available で送ってください

  • 受注の引き当ては止まりません。外部倉庫の拠点は在庫数に関わらず受注を引き受けます。0 で変わるのは販売可能在庫の数(お届け予定日の表示・入荷通知など)です

  • 外部倉庫の拠点以外に送ると 422(location_not_derived)で拒否します

リクエスト

ボディパラメータ

パラメータ
型
必須
説明

shop

string

✓

店舗ドメイン

source_system

string

✓

送り元(変更ログに残ります)

location_id

string (UUID)

✓

外部倉庫の拠点の内部 UUID(外部識別子での指定はできません)

lines

array

✓

更新行の配列(最大 10,000 件)

inventory_as_of

ISO 8601

読み取り時刻の既定値。行の as_of 省略時に使われます(それも省略なら受信時刻)

lines[].barcode

string

✓

商品 barcode

lines[].channel_available_qty

integer

✓

チャネルでの販売可能数。負の値もそのまま送ってください(0 以下はすべて「売れない」)

lines[].as_of

ISO 8601

その行の値を読み取った時刻

レスポンス

フィールド
説明

lines_processed

受け取った行数

lines_updated

チャネルの販売可能数が前回から変わった行数

unresolved[]

反映しなかった行と理由(→ 行単位エラー)

stale_skipped[]

読み値が古く反映しなかった行({ barcode, as_of, current_as_of })

changed[]

その結果、販売可能在庫が変わった行({ locationId, barcode, qtyBefore, qtyAfter })

挙動・冪等性

  • 同じ値を再送しても結果は変わりません(冪等)。そのまま再送して構いません。

  • 受け取った時刻より新しい値がすでに届いている行は反映しません(stale_skipped[])。順序が入れ替わって届いても、新しい値が勝ちます。

  • 在庫行がまだ無い商品(倉庫から一度も在庫数が届いていない商品)は、在庫 0 の行を作ってから反映します。例: 新商品に channel_available_qty: 0 を送ると、その拠点の販売可能在庫が 0 になります。あとから倉庫が在庫数を送ると、その数で通常どおり計算されます。

  • 販売可能在庫が変わると、Source Inventory Webhook の対象になります(取込と同じ扱い)。


エラー

エラーは 2 種類あります: リクエスト全体のエラー(HTTP 4xx で全行不採用)と、行単位エラー(HTTP 200 のまま該当行だけ不採用)。

リクエスト全体のエラー

エンドポイント固有:

Status
条件
レスポンス例

422

ok:false(error_kind 付き)

{ "ok": false, "error_kind": "location_unresolved", "error": "no location for smaregi_store_id=999999" }

400

lines が 10,000 行を超過

{ "ok": false, "error": "max 10000 lines per request" }

error_kind: invalid_payload / location_unresolved / location_ambiguous。channel-available では location_not_found(shop にその拠点が無い)/ location_not_derived(外部倉庫の拠点ではない)も返ります。共通エラー(401 / 400 / 403 / 405 / 500 / 503)は Error Codes を参照。

行単位エラー(HTTP 200)

解決できない行があってもリクエスト全体はエラーになりません。HTTP 200・ok: true のまま該当行だけ在庫に反映されず、レスポンスの配列に理由付きで返ります(silent skip はしません)。「200 が返るのに在庫が変わらない」場合は、まず unresolved[] / stale_skipped[] を確認してください。接続テスト時はこれらが空であることの確認を推奨します。

配列

reason

意味

unresolved[]

product_not_found

barcode が商品マスター未登録(最頻出。先に Products で登録)

unresolved[]

missing_barcode

行に barcode が無い

unresolved[]

missing_on_hand_qty

snapshot: 行に on_hand_qty が無い

unresolved[]

missing_available_qty / missing_available_delta

available: mode に対応する値(absolute → available_qty / delta → available_delta)が行に無い

unresolved[]

invalid_as_of

行の as_of が日時として解釈できない

unresolved[]

invalid_channel_available_qty

channel-available: channel_available_qty が整数でない

stale_skipped[]

—({ barcode, as_of, current_as_of } を返却)

反映済みより古い読み値のため適用せず。再送不要(→ 古い読み値の上書き防止)

データモデル

  • sot.inventory_levels — 現在庫マスター(shop + product_id + location_id で一意)。channel-available の値は channel_available_qty / channel_available_as_of

  • sot.inventory_daily_snapshots — 日次履歴(+ snapshot_date)

  • sot.inventory_update_logs — 変更監査ログ(change_type: snapshot / available_set / available_delta / committed_set / on_hand_set / channel_available_set)

  • read は agent_read.inventory_levels / inventory_daily_snapshots / inventory_update_logs(safe view)経由

Last updated

Was this helpful?