> For the complete documentation index, see [llms.txt](https://lmile.gitbook.io/lmile-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lmile.gitbook.io/lmile-docs/ingestion-api/inventory-holds.md).

# Inventory Holds — 在庫の取り置き

拠点の在庫の一部を**取り置き**にして、販売可能在庫（available）から外します。取り置いた分は受注の引き当てに使われず、Shopify など販売チャネルの販売可能在庫からも減ります。取り置きをやめると元に戻ります。ERP の引当・店頭での顧客取り置き・展示用の確保などに使います。

> **Base URL** `https://api.lmile.io` · **認証** `x-ingest-secret` ヘッダー または operator JWT（`Authorization: Bearer`）（[詳細](/lmile-docs/getting-started/authentication.md)） · `shop` はリクエストボディに含めます（API キーで呼ぶ場合は省略できます）

## エンドポイント

| Method | Path                     | 用途                                 | こんなとき                   |
| ------ | ------------------------ | ---------------------------------- | ----------------------- |
| `POST` | `/api/inventory/hold`    | 参照 ID ごとに取り置き数を設定（増やす・減らす・`0` で解除） | 受注の引当・店頭での取り置き・取り置き数の変更 |
| `POST` | `/api/inventory/release` | 参照 ID の取り置きをまとめて解除                 | 受け取り完了・キャンセル            |

### しくみ（両エンドポイント共通）

販売可能在庫が 10 個の拠点での例:

| 操作                    | 取り置き | 販売可能在庫 |
| --------------------- | :--: | :----: |
| 3 個を取り置く              |   3  |    7   |
| POS から在庫 12 個（絶対値）が届く |   3  |    9   |
| 在庫が 2 個に減る            |   3  |    0   |
| 取り置きを解除する             |   0  |    2   |

* 取り置きは**在庫の取込とは別に保存**されます。POS・倉庫・ERP から在庫の絶対値が届いても、取り置き分を引き直すので消えません。
* 減るのは販売可能在庫だけです。物理在庫（`on_hand_qty`）は変わりません。
* 在庫が取り置きより少なくなると、販売可能在庫は `0` で止まります（マイナスにはなりません）。
* 取り置き分は受注ルーティングの引き当てに使われません。
* 拠点が出荷元に属していれば、販売可能在庫の変化は [Source Inventory Webhook](/lmile-docs/webhooks/source-inventory.md) で通知されます。Shopify 連携はこの通知で Shopify の販売可能在庫を更新します。
* 店舗・倉庫など、どの拠点の在庫でも取り置けます。

### 参照 ID（`reference_id`）

* 取り置きの持ち主を表す ID です（受注番号・取り置き伝票番号など）。
* 1 つの参照 ID に、複数の商品・複数の拠点の取り置きをまとめられます。
* 省略すると LastMile が生成し、レスポンスの `reference_id` で返します。**変更・解除に必要なので保存してください**。
* 顧客名などの個人情報は入れないでください。

### 拠点（`location`）の指定（両エンドポイント共通）

内部の拠点 ID か、外部システムの識別子のいずれかで指定します。

```jsonc
{ "locationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }   // 内部 UUID
// または
{ "shopifyLocationId": "gid://shopify/Location/1234567890" }
{ "smaregiStoreId": "4" }
{ "locationCode": "TOKYO-DC" }                              // 拠点コード
{ "identifier": { "type": "smaregi_store_id", "value": "4" } }
```

***

## `POST` /api/inventory/hold

参照 ID × 拠点の取り置き数を、`lines` の値に**揃えます**（加算ではありません）。

### リクエスト

```http
POST https://api.lmile.io/api/inventory/hold
x-ingest-secret: {INGEST_SECRET}
Content-Type: application/json
```

```json
{
  "shop": "demo-store.myshopify.com",
  "location": { "smaregiStoreId": "4" },
  "reference_id": "SO-2026-0916-001",
  "reason": "店頭取り置き",
  "source_system": "odoo",
  "expires_at": "2026-09-23T10:00:00+09:00",
  "lines": [
    { "barcode": "1234567890123", "quantity": 2 },
    { "barcode": "1234567890456", "quantity": 1 }
  ]
}
```

### ボディパラメータ

| パラメータ              | 型                     |  必須 | 説明                                              |
| ------------------ | --------------------- | :-: | ----------------------------------------------- |
| `shop`             | string                |  ✓  | 店舗ドメイン（API キーで呼ぶ場合は省略できます）                      |
| `location`         | object                |  ✓  | 取り置く拠点（→ 上の「拠点の指定」）                             |
| `lines`            | array                 |  ✓  | 取り置く商品（最大 1,000 件。同じ `barcode` は 1 行まで）         |
| `reference_id`     | string                |     | 参照 ID（最大 200 文字）。省略すると生成して返します                  |
| `reason`           | string                |     | 理由（記録用・最大 500 文字）                               |
| `source_system`    | string                |     | 呼び出し元のシステム名（記録用・最大 64 文字）                       |
| `expires_at`       | string (ISO 8601)     |     | 期限。過ぎると 5 分以内に自動で解除されます。未来の時刻のみ。省略すると解除するまで残ります |
| `lines[].barcode`  | string                |  ✓  | 商品 barcode                                      |
| `lines[].quantity` | integer (0〜1,000,000) |  ✓  | この参照 ID で取り置く数（絶対値）。`0` でこの商品の取り置きを解除           |

### レスポンス

```json
{
  "ok": true,
  "reference_id": "SO-2026-0916-001",
  "reference_id_generated": false,
  "location_id": "c1cdb7d4-d435-40a6-9c7f-95108f99b35e",
  "lines": [
    { "barcode": "1234567890123", "quantity": 2, "held_qty": 2, "available_qty": 8 },
    { "barcode": "1234567890456", "quantity": 1, "held_qty": 1, "available_qty": 4 }
  ]
}
```

| フィールド                    | 説明                              |
| ------------------------ | ------------------------------- |
| `reference_id`           | 参照 ID（省略したときは生成した値）             |
| `reference_id_generated` | 参照 ID を LastMile が生成したら `true`  |
| `location_id`            | 解決した拠点の内部 ID                    |
| `lines[].quantity`       | この参照 ID の取り置き数                  |
| `lines[].held_qty`       | この拠点 × 商品の取り置き合計（他の参照 ID の分を含む） |
| `lines[].available_qty`  | 取り置きを引いた後の販売可能在庫                |

### 挙動・冪等性

* 同じ内容を再送しても結果は変わりません。そのまま再送してよいです。
* 参照 ID を省略した再送は、**別の取り置きとして二重に取り置かれます**。再送する可能性があるなら参照 ID を付けてください。
* 再送すると `reason` / `source_system` / `expires_at` も送った値に置き換わります（省略した項目は空になります）。
* 取り置き数を減らす・`0` にする操作は、在庫が減っていても常に成功します。
* 増やせるのは「取り置き前の販売可能在庫 − 他の参照 ID の取り置き」までです。例: 在庫 10 個・別の参照 ID が 3 個取り置き中なら、8 個は `409 insufficient_available`（`detail.holdable` = `7`）。
* 1 行でもエラーになると、そのリクエストの取り置きは 1 件も反映されません。途中まで取り置かれた状態は残りません。

***

## `POST` /api/inventory/release

参照 ID の取り置きを、全拠点・全商品まとめて解除します。一部の商品だけ解除するときは `/api/inventory/hold` で `quantity: 0` を送ります。

### リクエスト

```http
POST https://api.lmile.io/api/inventory/release
x-ingest-secret: {INGEST_SECRET}
Content-Type: application/json
```

```json
{
  "shop": "demo-store.myshopify.com",
  "reference_id": "SO-2026-0916-001",
  "reason": "受け取り済み",
  "source_system": "odoo"
}
```

### ボディパラメータ

| パラメータ           | 型      |  必須 | 説明                         |
| --------------- | ------ | :-: | -------------------------- |
| `shop`          | string |  ✓  | 店舗ドメイン（API キーで呼ぶ場合は省略できます） |
| `reference_id`  | string |  ✓  | 解除する参照 ID                  |
| `reason`        | string |     | 理由（記録用・最大 500 文字）          |
| `source_system` | string |     | 呼び出し元のシステム名（記録用・最大 64 文字）  |

### レスポンス

```json
{
  "ok": true,
  "reference_id": "SO-2026-0916-001",
  "released": [
    {
      "location_id": "c1cdb7d4-d435-40a6-9c7f-95108f99b35e",
      "barcode": "1234567890123",
      "released_qty": 2,
      "held_qty": 0,
      "available_qty": 10
    }
  ]
}
```

| フィールド                      | 説明                                |
| -------------------------- | --------------------------------- |
| `released[].released_qty`  | 解除した数                             |
| `released[].held_qty`      | 解除後の、この拠点 × 商品の取り置き合計（他の参照 ID の分） |
| `released[].available_qty` | 解除後の販売可能在庫                        |

### 挙動・冪等性

* 該当する取り置きが無くても `ok: true`（`released` は空）です。そのまま再送してよいです。
* 解除した分は販売可能在庫に戻ります。

## エラー

エンドポイント固有:

| Status | 条件                                           | レスポンス例                                                                                                                             |
| :----: | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|  `409` | 取り置ける上限を超えた（`hold`）                          | `{ "ok": false, "error_kind": "insufficient_available", "detail": { "barcode": "1234567890123", "requested": 8, "holdable": 7 } }` |
|  `422` | 拠点が解決できない（`hold`）                            | `{ "ok": false, "error_kind": "location_unresolved", "error": "location not found for shop" }`                                     |
|  `422` | `barcode` が商品マスター未登録（`hold`）                 | `{ "ok": false, "error_kind": "product_not_found", "detail": { "barcode": "1234567890123" } }`                                     |
|  `400` | 入力不備（同じ `barcode` が 2 行・過去の `expires_at` など） | `{ "ok": false, "error_kind": "invalid_payload", "error": "lines[1].barcode is duplicated" }`                                      |

共通エラー（`401` / `400` / `403` / `405` / `500` / `503`）は [Error Codes](/lmile-docs/reference/error-codes.md) を参照。

## データモデル

* `sot.lm_inventory_holds` — 取り置き台帳（`shop + reference_id + location_id + product_id` で一意・`quantity > 0`。解除した行は削除）
* `sot.inventory_levels.held_qty` — 拠点 × 商品の取り置き合計。`available_qty` はこれを引いた後の値
* `sot.inventory_levels.available_qty_gross` — 取り置きを引く前の取込値（取り置きのある行だけ。それ以外は `NULL`）
* `sot.inventory_ledger` — 取り置き・解除の履歴（`event_type` = `hold` / `release`。期限切れの解除は `reason` = `expired`）


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://lmile.gitbook.io/lmile-docs/ingestion-api/inventory-holds.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
