> 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/stock-transfer-api/stock-transfer.md).

# Stock Transfer — 拠点間在庫移動

ある拠点（出庫元）から別の拠点（入庫先）へ、商品を **数量ベース**で移動します。1 件の移動は **作成 →（承認）→ 出庫 → 入庫** というライフサイクルで進み、出庫元・入庫先の棚と在庫を段階的に確定します。**販売可能在庫（Shopify に見える数）が実際に増減するのは、自社在庫（MFC 自己在庫）を持つ拠点だけ**です。外部システム（Shopify / スマレジ）が在庫を管理する拠点は、移動を記録・可視化しますが実数量は外部が正のため、この API では動かしません（→ [ハイブリッド在庫](#ハイブリッド在庫-自社在庫だけが動く)）。

> **★ 返送バルク（Transfer API）とは別機能です。** 既存の [Transfer Batches](/lmile-docs/transfer-api/batches.md)（倉庫間移動バルク / 刻印返送）は、注文の出荷単位（unit）をまとめて送り返す状態機械です。本ページの Stock Transfer は、注文に紐づかない **商品 × 数量** の拠点間在庫移動で、別の仕組み（別テーブル）として併存します。

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

> 入力・出力フィールドは **camelCase**（`stockTransferId` / `originRef` / `shippedQty` / `idempotencyKey`）です。ingest 系の snake\_case とは異なる点に注意してください。

## エンドポイント

| Method | Path                                         | 用途                       |
| ------ | -------------------------------------------- | ------------------------ |
| `POST` | `/api/routing/stock-transfer/create`         | 移動を作成（承認なし・すぐ出庫できる）      |
| `POST` | `/api/routing/stock-transfer/create-request` | 承認が要る移動として作成（出庫依頼）       |
| `POST` | `/api/routing/stock-transfer/label-options`  | 配送ラベルを発行できる出庫元・入庫先を調べる   |
| `POST` | `/api/routing/stock-transfer/set-lines`      | 明細を追加・数量置換（出庫前のみ）        |
| `POST` | `/api/routing/stock-transfer/remove-lines`   | 明細を barcode 指定で削除（出庫前のみ） |
| `POST` | `/api/routing/stock-transfer/update`         | ヘッダ（伝票番号・メモ）と明細をまとめて更新   |
| `POST` | `/api/routing/stock-transfer/approve`        | 承認（既定で承認と同時に出庫まで実行）      |
| `POST` | `/api/routing/stock-transfer/reject`         | 承認を却下                    |
| `POST` | `/api/routing/stock-transfer/ship`           | 出庫を確定（自社在庫は出庫元の棚を減らす）    |
| `POST` | `/api/routing/stock-transfer/print-label`    | 発行済みの配送ラベルをもう一度印刷する      |
| `POST` | `/api/routing/stock-transfer/receive`        | 入庫検品（自社在庫は入庫先の棚を増やす）     |
| `POST` | `/api/routing/stock-transfer/cancel`         | 業務取消（出庫済みは出庫元の棚を復元）      |
| `POST` | `/api/routing/stock-transfer/delete`         | 出庫前の移動を物理削除              |
| `GET`  | `/api/routing/stock-transfer/list`           | 移動の一覧（ステータス・拠点で絞り込み）     |
| `GET`  | `/api/routing/stock-transfer/get`            | 移動 1 件（ヘッダ + 明細）         |
| `GET`  | `/api/routing/stock-transfer/lines`          | 移動 1 件の明細のみ              |

> 入庫検品中の **修正依頼**（出庫内容の見直し）は別ページ [Modification Request](/lmile-docs/stock-transfer-api/modification-request.md) を参照してください。

***

## ライフサイクル

移動のステータスは **3 つの軸**を独立に持ちます。1 つの軸だけで完結せず、組み合わせで状態を表します。

| 軸                     | 値                                                                                | 意味                                                                                           |
| --------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `progress`            | `draft` / `pending_shipment` / `shipped` / `receiving` / `received` / `canceled` | 出庫・入庫の進捗                                                                                     |
| `approval`            | `none` / `pending_request` / `pending` / `approved` / `rejected`                 | 承認の状態（`none` = 承認フローなし）                                                                      |
| `modificationRequest` | `none` / `requested` / `completed`                                               | 入庫検品中の修正依頼（→ [Modification Request](/lmile-docs/stock-transfer-api/modification-request.md)） |

レスポンスには上記 3 軸に加え、Shopify InventoryTransfer 相当の **派生フェーズ** `shopifyPhase`（`DRAFT` / `READY_TO_SHIP` / `IN_PROGRESS` / `TRANSFERRED` / `CANCELED`）も返します（3 軸から自動計算した表示用の値です）。

### 作成の 2 系統（承認の有無）

* **`create`（承認なし）** — `approval = none` で作成し、すぐ `ship`（出庫）できます。承認フローを持たない拠点間移動に使います。
* **`create-request`（承認あり）** — `approval = pending_request`（出庫依頼）で作成します。`approve` で承認するまで `ship` できません（承認前の出庫は `approval_required` で拒否）。`reject` で却下できます。

### 進捗の流れ

```
[create]  progress=pending_shipment / approval=none
    └────────────────────────────────────▶ [ship] ──▶ [receive] ──▶ [receive(完了)]
                                            shipped     receiving       received
[create-request]  approval=pending_request
    └─ [approve] ─▶ approval=approved ─▶ [ship] ──▶ [receive] ──▶ ...
    └─ [reject]  ─▶ approval=rejected  （出庫不可）

（出庫前のみ）  [delete]  ──▶ 物理削除
（各段階で）    [cancel]  ──▶ progress=canceled   ※ receiving / received からは取消不可
```

* `create` された移動は `progress = pending_shipment` から始まります（`draft` は将来用の予約値で、通常は使いません）。
* **出庫（`ship`）は 1 回で全明細を出庫**します。`shipped → receiving → received` と進み、`receiving` の間は複数回に分けて受領できます（→ [receive の挙動](#post-apiroutingstock-transferreceive)）。
* `received` は終端です（取消・巻き戻し不可）。取消は `received` になる前に行ってください。

### ハイブリッド在庫（自社在庫だけが動く）

実際の数量（棚・販売可能在庫）が動くのは、その拠点が **自社在庫（MFC 自己在庫）** の場合だけです。

* **出庫元が自社在庫**: `ship` で出庫元の棚を数量ぶん減らし、販売可能在庫を再計算します。
* **入庫先が自社在庫**: `receive` で入庫先の棚を受領数ぶん増やし、販売可能在庫を再計算します（棚コード `destinationShelfCode` の指定が必須）。
* **外部管理拠点（Shopify / スマレジ）**: 棚には触れず、移動の数量を記録するだけです。実在庫は外部システムが正のため、そちら側で反映されます。

自社在庫の販売可能在庫が変化した分は、[Source Inventory Webhook](/lmile-docs/webhooks/source-inventory.md) で外部へ通知されます（変化なしの場合は通知しません）。

***

## `POST` /api/routing/stock-transfer/create

移動を新規作成します（`approval = none`・すぐ出庫できる状態）。明細は同時に渡しても、後から `set-lines` で足しても構いません。

### リクエスト

```http
POST https://api.lmile.io/api/routing/stock-transfer/create
x-ingest-secret: {INGEST_SECRET}
Content-Type: application/json
```

```json
{
  "shop": "demo-store.myshopify.com",
  "originRef": { "locationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" },
  "destinationRef": { "smaregiStoreId": "4" },
  "identificationNo": "TR-2026-0001",
  "idempotencyKey": "smaregi-shipment:8891",
  "note": "定期補充",
  "receivingDesiredDate": "2026-08-15",
  "lines": [
    { "barcode": "1234567890123", "quantity": 5 },
    { "barcode": "1234567890456", "quantity": 2, "requestQuantity": 3 }
  ]
}
```

### ボディパラメータ

| パラメータ                  | 型      |  必須 | 説明                                                          |
| ---------------------- | ------ | :-: | ----------------------------------------------------------- |
| `shop`                 | string |  ✓  | 店舗ドメイン（API キーで呼ぶ場合は省略できます）                                  |
| `originRef`            | object |  ✓  | 出庫元拠点の参照（下記「拠点の指定」）                                         |
| `destinationRef`       | object |  ✓  | 入庫先拠点の参照。出庫元と同一だと `same_location`                           |
| `identificationNo`     | string |     | 伝票番号（運用採番・外部連携キー。任意）                                        |
| `idempotencyKey`       | string |     | 冪等キー。同じ値で再送すると同一の移動を返します（再作成しない）                            |
| `note`                 | string |     | 移動のメモ（任意）                                                   |
| `receivingDesiredDate` | string |     | 入庫希望日（`YYYY-MM-DD`・任意）。入庫先で受け取りたい日付。形式不正は `invalid_payload` |
| `lines`                | array  |     | 明細（0〜1,000 件。省略可・後から `set-lines` で追加可）                      |

#### 拠点の指定（`originRef` / `destinationRef`）

内部の拠点 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" } }
```

解決できない参照は `origin_unresolved` / `destination_unresolved` を返します。

#### `lines[]` オブジェクト

| パラメータ             | 型      |  必須 | 説明                                                         |
| ----------------- | ------ | :-: | ---------------------------------------------------------- |
| `barcode`         | string |  ✓  | 商品識別子（商品マスターとの結合キー）                                        |
| `quantity`        | number |  ✓  | 移動する計画数量（0 以上の整数）。出庫数はこれを超えられません                           |
| `requestQuantity` | number |     | 依頼数量（0 以上の整数。省略時は 0。`create-request` では省略時 `quantity` と同値） |
| `note`            | string |     | 明細ごとのメモ（任意）                                                |

> 同一伝票内で **同じ `barcode` は 1 行**です（重複させると数量が置き換わります）。

### レスポンス

```json
{
  "ok": true,
  "stockTransferId": "0f1e2d3c-4b5a-6789-0abc-def123456789",
  "progress": "pending_shipment",
  "approval": "none",
  "originKind": "mfc_self",
  "destinationKind": "smaregi_store"
}
```

| フィールド                            | 説明                                                                      |
| -------------------------------- | ----------------------------------------------------------------------- |
| `stockTransferId`                | 作成した移動の ID（以降の操作で使用）                                                    |
| `progress` / `approval`          | 3 軸ステータス（作成直後は `pending_shipment` / `none`）                             |
| `originKind` / `destinationKind` | 拠点の種別（`mfc_self` / `smaregi_store` / `shopify` / `other`）。在庫効果の分岐に使われます |
| `idempotent`                     | 冪等キー一致で既存の移動を返したときのみ `true`                                             |

### 挙動・冪等性

* **作成は冪等**: `idempotencyKey` を付けて同じ値で再送すると、新しい移動を作らず既存の移動を `idempotent: true` で返します。イベント単位で一意なキーを付けてください。
* `originRef` / `destinationRef` が同じ拠点に解決されると `same_location` で拒否します（自己移動は作れません）。
* 明細を同時に渡すと、そのまま登録します。数量は 0 未満に丸めません（負値は 0 として扱います）。

***

## `POST` /api/routing/stock-transfer/create-request

`create` と同じボディで、**承認が要る移動**（出庫依頼）として作成します。`approval = pending_request` で作成され、`approve` で承認するまで出庫できません。

* ボディパラメータは [create](#ボディパラメータ) と同一です。加えて `labelMethod`（下記）を指定できます。
* 明細の `requestQuantity` を省略した場合、`create-request` では `quantity` と同値で登録します（依頼数量＝計画数量）。
* レスポンスの `approval` は `pending_request` になります。

### 送り状の出し方（`labelMethod`）

移動する箱に付ける送り状を、作成のときに決めます。

| `labelMethod` | 出庫（`ship`）のときに起きること                                                              |
| ------------- | -------------------------------------------------------------------------------- |
| `"issue"`     | 配送ラベルを発行して出庫元のプリンターで印刷し、追跡番号を箱に記録します。発行に失敗したら出庫しません                              |
| `"manual"`    | 手書きの送り状を使います。`ship` に `trackingNumber` を渡さないと出庫できません（`tracking_number_required`） |
| 省略            | 送り状の出し方を決めない移動です。`ship` の `trackingNumber` は任意のままです                              |

* `"issue"` を指定できるのは、**出庫元に配送ラベルの発行設定があり、入庫先の拠点に住所（郵便番号・都道府県・市区町村・番地・電話番号）が入っている**ときだけです。満たさない組み合わせは作成せず `label_not_available` を返します。指定できるかは先に [label-options](#post-apiroutingstock-transferlabel-options) で調べられます。
* `"issue"` を指定するときは、`originRef` / `destinationRef` を `locationId` で渡してください。
* 冪等キーで再送したとき、最初に決めた出し方は変わりません。

***

## `POST` /api/routing/stock-transfer/label-options

配送ラベルを発行できる出庫元と、ラベルの宛先にできる入庫先を返します。`create-request` の `labelMethod: "issue"` を指定できるかを、作成の前に調べるために使います。

### リクエスト

```json
{ "shop": "demo-store.myshopify.com" }
```

### レスポンス

```json
{
  "ok": true,
  "originLocationIds": ["a1b2c3d4-…"],
  "destinationLocationIds": ["b2c3d4e5-…", "c3d4e5f6-…"]
}
```

| フィールド                      | 説明                                    |
| -------------------------- | ------------------------------------- |
| `originLocationIds[]`      | 配送ラベルの発行設定がある拠点。出庫元がここに含まれるときだけ発行できます |
| `destinationLocationIds[]` | 住所が入っている拠点。入庫先がここに含まれるときだけ発行できます      |

### 挙動・冪等性

* 読み取りだけです。何度呼んでも何も変わりません。
* 出庫元と入庫先の**両方**が含まれる組み合わせだけ、`labelMethod: "issue"` を指定できます。

***

## `POST` /api/routing/stock-transfer/set-lines

明細を追加・置換します（**出庫前のみ**）。同じ `barcode` が既にあれば数量を置き換え、無ければ新しい行として追加します。

### リクエスト

```json
{
  "shop": "demo-store.myshopify.com",
  "stockTransferId": "0f1e2d3c-4b5a-6789-0abc-def123456789",
  "lines": [
    { "barcode": "1234567890123", "quantity": 8 },
    { "barcode": "9990000000001", "quantity": 1 }
  ]
}
```

### ボディパラメータ

| パラメータ             | 型             |  必須 | 説明                                                                  |
| ----------------- | ------------- | :-: | ------------------------------------------------------------------- |
| `shop`            | string        |  ✓  | 店舗ドメイン（API キーで呼ぶ場合は省略できます）                                          |
| `stockTransferId` | string (uuid) |  ✓  | 対象の移動 ID                                                            |
| `lines`           | array         |  ✓  | 明細（1〜1,000 件。`barcode` / `quantity` / `requestQuantity?` / `note?`） |

### レスポンス

```json
{ "ok": true, "stockTransferId": "0f1e2d3c-…", "lineCount": 2 }
```

### 挙動・冪等性

* **編集できるのは出庫前**（`progress` が `draft` / `pending_shipment`・却下されていない）だけです。出庫後や却下済みは `not_editable` を返します。
* 同じ `barcode` の再送は数量を置き換えます（行を増やしません）。同じボディの再送は同値で収束します。

***

## `POST` /api/routing/stock-transfer/remove-lines

明細を `barcode` 指定で削除します（**出庫前のみ**）。

### ボディパラメータ

| パラメータ             | 型             |  必須 | 説明                         |
| ----------------- | ------------- | :-: | -------------------------- |
| `shop`            | string        |  ✓  | 店舗ドメイン（API キーで呼ぶ場合は省略できます） |
| `stockTransferId` | string (uuid) |  ✓  | 対象の移動 ID                   |
| `barcodes`        | array         |  ✓  | 削除する `barcode` の配列（1 件以上）  |

### レスポンス

```json
{ "ok": true, "stockTransferId": "0f1e2d3c-…", "removed": 1 }
```

* `removed` は実際に削除した行数です。既に無い `barcode` を指定しても数に含まれないだけでエラーにはなりません（再送に安全）。
* 出庫後や却下済みは `not_editable` を返します。

***

## `POST` /api/routing/stock-transfer/update

ヘッダ（伝票番号・メモ）と、任意で明細をまとめて更新します。

### ボディパラメータ

| パラメータ              | 型              |  必須 | 説明                                     |
| ------------------ | -------------- | :-: | -------------------------------------- |
| `shop`             | string         |  ✓  | 店舗ドメイン（API キーで呼ぶ場合は省略できます）             |
| `stockTransferId`  | string (uuid)  |  ✓  | 対象の移動 ID                               |
| `identificationNo` | string \| null |     | 伝票番号。指定したキーだけ更新します                     |
| `note`             | string \| null |     | メモ。指定したキーだけ更新します                       |
| `lines`            | array          |     | 明細（渡すと `set-lines` と同じ upsert。出庫前のみ有効） |

### 挙動・冪等性

* **更新できるのは、出庫前**、または **入庫検品中（`receiving`）かつ修正依頼中（`modificationRequest = requested`）** のときです。それ以外は `not_editable` を返します。
* `identificationNo` / `note` は **リクエストに含めたキーだけ**を更新します（含めなければ現在値を維持）。
* 明細（`lines`）は出庫前のときだけ反映します（[set-lines](#post-apiroutingstock-transferset-lines) と同じ挙動）。

***

## `POST` /api/routing/stock-transfer/approve

承認します。既定では **承認と同時に出庫（`ship`）まで実行**します（スマレジの「承認＝出庫」に合わせた挙動）。承認だけ行いたい場合は `autoShip: false` を指定します。

### ボディパラメータ

| パラメータ             | 型             |  必須 | 説明                                                             |
| ----------------- | ------------- | :-: | -------------------------------------------------------------- |
| `shop`            | string        |  ✓  | 店舗ドメイン（API キーで呼ぶ場合は省略できます）                                     |
| `stockTransferId` | string (uuid) |  ✓  | 対象の移動 ID                                                       |
| `autoShip`        | boolean       |     | 承認後に自動で出庫するか（**既定 `true`**）。`false` で承認のみ（`READY_TO_SHIP` で待機） |

> 送り状が手書き（`labelMethod: "manual"`）の移動は、自動出庫では追跡番号を渡せないため出庫まで進みません（承認だけが確定し、`ship` に `tracking_number_required` が入ります）。`autoShip: false` で承認し、[ship](#post-apiroutingstock-transfership) に `trackingNumber` を渡してください。

### レスポンス

```json
{
  "ok": true,
  "approved": true,
  "ship": {
    "ok": true,
    "stockTransferId": "0f1e2d3c-…",
    "progress": "shipped",
    "totalShipped": 7,
    "selfItems": [ { "locationId": "a1b2c3d4-…", "barcode": "1234567890123" } ],
    "changed": [ { "locationId": "a1b2c3d4-…", "barcode": "1234567890123" } ]
  }
}
```

* `autoShip: false` のときは `{ "ok": true, "approved": true, "ship": null }` を返します。
* `ship` の中身は [ship エンドポイント](#post-apiroutingstock-transfership)のレスポンスと同じです。

### 挙動・冪等性

* 承認できるのは承認待ち（`approval` が `pending_request` / `pending`）だけです。既に `approved` なら `noop: true` を返します。それ以外の状態からの承認は `illegal_transition` を返します。
* **自動出庫が失敗しても承認は確定済み**です（`approved` = `READY_TO_SHIP`）。現場が在庫を整えてから `ship` を呼び直して回復できます。

***

## `POST` /api/routing/stock-transfer/reject

承認を却下します（`approval → rejected`）。承認待ち（`pending_request` / `pending`）からのみ実行できます。進捗（`progress`）は `pending_shipment` のまま保たれます。

### ボディパラメータ

| パラメータ             | 型             |  必須 | 説明                         |
| ----------------- | ------------- | :-: | -------------------------- |
| `shop`            | string        |  ✓  | 店舗ドメイン（API キーで呼ぶ場合は省略できます） |
| `stockTransferId` | string (uuid) |  ✓  | 対象の移動 ID                   |

### 挙動・冪等性

* 既に `rejected` なら `noop: true` を返します（再送に安全）。
* 承認待ち以外からの却下は `illegal_transition` を返します。
* 却下後の移動は出庫できません。

***

## `POST` /api/routing/stock-transfer/ship

出庫を確定します（`progress → shipped`）。**出庫元が自社在庫の明細は、出庫元の棚を数量ぶん減らして**販売可能在庫を再計算します。外部管理拠点の明細は数量を記録するだけです。

### リクエスト

```json
{
  "shop": "demo-store.myshopify.com",
  "stockTransferId": "0f1e2d3c-…",
  "lines": [
    { "barcode": "1234567890123", "shippedQty": 5, "originShelfCode": "A-1" }
  ],
  "trackingNumber": "564822474731",
  "carrierCode": "sagawa_regular"
}
```

### ボディパラメータ

| パラメータ                     | 型             |  必須 | 説明                                                                                                |
| ------------------------- | ------------- | :-: | ------------------------------------------------------------------------------------------------- |
| `shop`                    | string        |  ✓  | 店舗ドメイン（API キーで呼ぶ場合は省略できます）                                                                        |
| `stockTransferId`         | string (uuid) |  ✓  | 対象の移動 ID                                                                                          |
| `lines`                   | array         |     | 明細ごとの出庫指定（省略時は各明細の `quantity` を全量出庫）                                                              |
| `lines[].barcode`         | string        |  ✓  | 対象の商品 barcode                                                                                     |
| `lines[].shippedQty`      | number        |     | 出庫数（省略時は明細の `quantity`）。**計画数量 `quantity` を超えられません**                                              |
| `lines[].originShelfCode` | string        |     | 出庫元の棚コードの指定（自社在庫のとき優先的にこの棚から引きます）                                                                 |
| `trackingNumber`          | string        |     | 箱の追跡番号。送り状が手書き（`labelMethod: "manual"`）の移動では**必須**です。ラベルを発行する移動（`"issue"`）では発行した番号を使うため、渡しても使いません |
| `carrierCode`             | string        |     | 運送会社のコード（例 `sagawa_regular`）。`trackingNumber` と一緒に渡すと配送状況を追えます                                    |

### レスポンス

```json
{
  "ok": true,
  "stockTransferId": "0f1e2d3c-…",
  "progress": "shipped",
  "totalShipped": 5,
  "selfItems": [ { "locationId": "a1b2c3d4-…", "barcode": "1234567890123" } ],
  "changed": [ { "locationId": "a1b2c3d4-…", "barcode": "1234567890123" } ]
}
```

| フィールド          | 説明                                                                                                                                                                                |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `totalShipped` | 出庫した合計数                                                                                                                                                                           |
| `selfItems[]`  | 自社在庫として棚を動かした `locationId` × `barcode`                                                                                                                                            |
| `changed[]`    | 販売可能在庫が変化した `locationId` × `barcode`（[Source Inventory Webhook](/lmile-docs/webhooks/source-inventory.md) の通知対象）                                                                  |
| `label`        | ラベルを発行する移動（`labelMethod: "issue"`）のときだけ付きます。`status`（`generated` = 今回発行 / `reused` = 前回発行したものを使った）・`trackingNumber`・`carrierCode`・`print`（印刷の結果。`printed` / `skipped` / `failed`） |

### 挙動・冪等性

* **承認が要ります**: `approval` が `pending_request` / `pending` の移動を出庫しようとすると `approval_required`、却下済みは `rejected` を返します。
* **送り状を先に用意します**: ラベルを発行する移動は、出庫の前に配送ラベルを発行して印刷します。発行に失敗すると `label_generation_failed` を返し、出庫しません（在庫は動きません）。原因を直して `ship` を呼び直してください。
* **ラベルを二重に発行しません**: ラベルの発行後に出庫が失敗した場合（在庫不足など）、呼び直すと前回のラベルをそのまま使います（`label.status: "reused"`）。印刷はやり直しません。
* **印刷の失敗では出庫を止めません**: ラベルは発行済みなので出庫は確定し、`label.print.status` が `failed` / `skipped` になります。この値を確認してください。
* 手書きの移動は `trackingNumber` が無いと `tracking_number_required` を返します。
* **売越を作りません（移動可能量ガード）**: 自社在庫の出庫元では、**移動可能量 = 出庫元の棚合計 − 引き当て済み数**を超える `shippedQty` を `insufficient_transferable_qty` で拒否します。例えば棚合計が 8・引き当て済みが 6 なら移動可能量は 2 で、`shippedQty: 3` は拒否されます（既存の受注引き当ての裏付け在庫を奪わないため）。
* **計画数量が上限**: `shippedQty` は明細の `quantity` を超えられません（超過は拒否されます）。
* **冪等**: 既に出庫済み（`shipped` 以降）の移動への `ship` は `noop: true` を返し、棚を二重に減らしません。取消済み（`canceled`）への `ship` は `illegal_transition` を返します。

***

## `POST` /api/routing/stock-transfer/print-label

出庫のときに発行した配送ラベルを、出庫元のプリンターでもう一度印刷します（紙詰まり・刷り直し用）。

### ボディパラメータ

| パラメータ             | 型             |  必須 | 説明                         |
| ----------------- | ------------- | :-: | -------------------------- |
| `shop`            | string        |  ✓  | 店舗ドメイン（API キーで呼ぶ場合は省略できます） |
| `stockTransferId` | string (uuid) |  ✓  | 対象の移動 ID                   |

### レスポンス

```json
{ "ok": true, "stockTransferId": "0f1e2d3c-…", "print": { "status": "printed" } }
```

| フィールド          | 説明                                                                                      |
| -------------- | --------------------------------------------------------------------------------------- |
| `print.status` | `printed` = 印刷した / `skipped` = 出庫元にプリンターの設定が無い / `failed` = 印刷できなかった（`print.error` に理由） |

### 挙動・冪等性

* 何回でも呼べます。ラベルを発行し直さないので、追跡番号は変わらず、発行費用もかかりません。
* ラベルをまだ発行していない移動は `label_not_issued` を返します。
* 印刷できなかったときも HTTP `200` で `print.status` が `failed` / `skipped` になります。この値を確認してください。

***

## `POST` /api/routing/stock-transfer/receive

入庫検品を記録します（`shipped → receiving → received`）。**入庫先が自社在庫の明細は、入庫先の棚を受領数ぶん増やして**販売可能在庫を再計算します。

### リクエスト

```json
{
  "shop": "demo-store.myshopify.com",
  "stockTransferId": "0f1e2d3c-…",
  "finalize": false,
  "received": [
    { "barcode": "1234567890123", "acceptedQty": 5, "rejectedQty": 0, "destinationShelfCode": "B-2" }
  ]
}
```

### ボディパラメータ

| パラメータ                             | 型             |  必須 | 説明                                                       |
| --------------------------------- | ------------- | :-: | -------------------------------------------------------- |
| `shop`                            | string        |  ✓  | 店舗ドメイン（API キーで呼ぶ場合は省略できます）                               |
| `stockTransferId`                 | string (uuid) |  ✓  | 対象の移動 ID                                                 |
| `finalize`                        | boolean       |     | `true` を指定すると、残りがあっても移動を `received`（完了）で締めます（既定 `false`） |
| `received`                        | array         |  ✓  | 受領した明細（0〜1,000 件）                                        |
| `received[].barcode`              | string        |  ✓  | 対象の商品 barcode                                            |
| `received[].acceptedQty`          | number        |  ✓  | 受け入れた数（0 以上）。**この値までを絶対値で記録**します                         |
| `received[].rejectedQty`          | number        |     | 不良などで受け入れなかった数（0 以上）                                     |
| `received[].destinationShelfCode` | string        |     | 入庫先の棚コード。**入庫先が自社在庫で `acceptedQty > 0` のときは必須**          |

### レスポンス

```json
{
  "ok": true,
  "stockTransferId": "0f1e2d3c-…",
  "progress": "received",
  "selfItems": [ { "locationId": "d5e6…", "barcode": "1234567890123" } ],
  "changed": [ { "locationId": "d5e6…", "barcode": "1234567890123" } ]
}
```

* `progress` は、まだ受領しきっていなければ `receiving`、全明細が完了（または `finalize: true`）なら `received` を返します。

### 挙動・冪等性

* **出庫後のみ**: `shipped` / `receiving` 以外からの受領は `illegal_transition` を返します。初回の受領で `shipped → receiving` に進みます。
* **絶対値で記録（冪等）**: `acceptedQty` は「受け入れた総数」として上書きします。同じ値の再送は棚を二重に増やしません（既に反映済みの分を超えた差分だけ棚を増やします）。
* **入庫先が自社在庫のとき**は `destinationShelfCode` が必須です（未指定は `destination_shelf_required`）。棚を増やせない場合（別 barcode が占有等）は、その理由の `error_kind` をそのまま返します。
* **受領数の上限**: `acceptedQty + rejectedQty` は出庫数（`shippedQty`）を超えられません（超過は `invalid_quantity`）。
* **完了判定**: 全明細で `受け入れ + 不良 ≥ 出庫数` になると `received` で締まります。分割受領の途中で締めたいときは `finalize: true` を使います。
* **修正依頼中はロック**: 修正依頼が開いている（`modificationRequest = requested`）間の受領は `modification_requested_locked` で拒否されます（→ [Modification Request](/lmile-docs/stock-transfer-api/modification-request.md)）。

***

## `POST` /api/routing/stock-transfer/cancel

業務上の取消です（`progress → canceled`）。**出庫済み（`shipped`）の移動を取り消すと、出庫元の自社在庫の棚を元に戻します**（復元）。

### ボディパラメータ

| パラメータ             | 型             |  必須 | 説明                         |
| ----------------- | ------------- | :-: | -------------------------- |
| `shop`            | string        |  ✓  | 店舗ドメイン（API キーで呼ぶ場合は省略できます） |
| `stockTransferId` | string (uuid) |  ✓  | 対象の移動 ID                   |

### 挙動・冪等性

* 出庫前（`draft` / `pending_shipment`）は即座に取消します。
* **出庫済み（`shipped`）は、出庫元の棚を復元してから取消**します。復元に失敗した場合（元の棚が別 barcode で占有されている等の稀な競合）は `restore_failed` を返し、取消を確定しません（在庫を静かに失わないため。棚を空けてから再実行してください）。
* **`receiving` / `received` からは取消できません**（`not_cancelable`）。受領が始まった移動は取り消せません。
* 既に `canceled` なら `noop: true` を返します（再送に安全）。

***

## `POST` /api/routing/stock-transfer/delete

出庫前の移動を **物理削除**します。明細・修正依頼・棚ログも一緒に削除されます。

### ボディパラメータ

| パラメータ             | 型             |  必須 | 説明                         |
| ----------------- | ------------- | :-: | -------------------------- |
| `shop`            | string        |  ✓  | 店舗ドメイン（API キーで呼ぶ場合は省略できます） |
| `stockTransferId` | string (uuid) |  ✓  | 対象の移動 ID                   |

### 挙動・冪等性

* **削除できるのは出庫前**（`draft` / `pending_shipment`）だけです。`shipped` / `receiving` / `received` は削除できません（`not_deletable`。取消は [cancel](#post-apiroutingstock-transfercancel) を使ってください）。
* 既に存在しない移動の削除は `noop: true`（HTTP `200`）を返します（再送に安全）。

***

## `GET` /api/routing/stock-transfer/list

移動の一覧を取得します。

### クエリパラメータ

| パラメータ                   |  必須 | 説明                                                                                        |
| ----------------------- | :-: | ----------------------------------------------------------------------------------------- |
| `shop`                  |  ✓  | 店舗ドメイン（API キーで呼ぶ場合は省略できます）                                                                |
| `progress`              |     | 進捗で絞り込み（`draft` / `pending_shipment` / `shipped` / `receiving` / `received` / `canceled`） |
| `approval`              |     | 承認で絞り込み（`none` / `pending_request` / `pending` / `approved` / `rejected`）                 |
| `modificationRequest`   |     | 修正依頼で絞り込み（`none` / `requested` / `completed`）                                             |
| `originLocationId`      |     | 出庫元拠点で絞り込み                                                                                |
| `destinationLocationId` |     | 入庫先拠点で絞り込み                                                                                |
| `identificationNo`      |     | 伝票番号で絞り込み                                                                                 |
| `limit`                 |     | 取得件数（1〜200・既定 50）                                                                         |
| `offset`                |     | オフセット（既定 0）                                                                               |

### レスポンス

```json
{
  "ok": true,
  "items": [
    {
      "id": "0f1e2d3c-…",
      "identificationNo": "TR-2026-0001",
      "progress": "shipped",
      "approval": "approved",
      "modificationRequest": "none",
      "shopifyPhase": "IN_PROGRESS",
      "originLocationId": "a1b2c3d4-…",
      "originLocationLabel": "東京DC",
      "originKind": "mfc_self",
      "destinationLocationId": "d5e6…",
      "destinationLocationLabel": "心斎橋店",
      "destinationKind": "smaregi_store",
      "smaregiShippingId": null,
      "shopifyInventoryTransferGid": null,
      "requestedAt": null,
      "approvedAt": "2026-07-09T02:10:00.000Z",
      "shippedAt": "2026-07-09T02:11:00.000Z",
      "receivingAt": null,
      "receivedAt": null,
      "canceledAt": null,
      "createdAt": "2026-07-09T02:00:00.000Z",
      "updatedAt": "2026-07-09T02:11:00.000Z"
    }
  ]
}
```

新しい順（`createdAt` 降順）で返します。各行に拠点のラベル（`originLocationLabel` / `destinationLocationLabel`）と派生フェーズ（`shopifyPhase`）を付与します。

## `GET` /api/routing/stock-transfer/get

移動 1 件を、明細付きで取得します。

### クエリパラメータ

| パラメータ  |  必須 | 説明                         |
| ------ | :-: | -------------------------- |
| `shop` |  ✓  | 店舗ドメイン（API キーで呼ぶ場合は省略できます） |
| `id`   |  ✓  | 移動 ID                      |

### レスポンス

```json
{
  "ok": true,
  "transfer": {
    "id": "0f1e2d3c-…",
    "progress": "shipped",
    "approval": "approved",
    "shopifyPhase": "IN_PROGRESS",
    "originLocationLabel": "東京DC",
    "destinationLocationLabel": "心斎橋店",
    "lines": [
      {
        "id": "…",
        "lineNo": 1,
        "barcode": "1234567890123",
        "sku": "SKU-001",
        "productName": "サンプル商品",
        "variantName": "M / ブラック",
        "requestQuantity": 0,
        "quantity": 5,
        "shippedQuantity": 5,
        "receivedAcceptedQuantity": 0,
        "receivedRejectedQuantity": 0,
        "originShelfCode": "A-1",
        "destinationShelfCode": null
      }
    ]
  }
}
```

* ヘッダの各フィールドは [list](#get-apiroutingstock-transferlist) と同じ + `lines[]`。
* 該当が無ければ `404` + `error_kind: not_found`。

## `GET` /api/routing/stock-transfer/lines

移動 1 件の明細だけを取得します（ヘッダなし）。

### クエリパラメータ

| パラメータ  |  必須 | 説明                         |
| ------ | :-: | -------------------------- |
| `shop` |  ✓  | 店舗ドメイン（API キーで呼ぶ場合は省略できます） |
| `id`   |  ✓  | 移動 ID                      |

### レスポンス

```json
{ "ok": true, "lines": [ { "id": "…", "lineNo": 1, "barcode": "1234567890123", "quantity": 5, "shippedQuantity": 5, "…": "…" } ] }
```

各明細に商品情報（`sku` / `productName` / `variantName`）を付与します（`barcode` で商品マスターと照合。未登録は `null`）。

***

## エラー

エンドポイント固有:

| `error_kind`                                   | Status | エンドポイント                           | 意味                                                                                                                 |
| ---------------------------------------------- | :----: | --------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `origin_unresolved` / `destination_unresolved` |  `422` | create / create-request           | 出庫元 / 入庫先の拠点参照が解決できない                                                                                              |
| `same_location`                                |  `422` | create / create-request           | 出庫元と入庫先が同一拠点                                                                                                       |
| `not_editable`                                 |  `422` | set-lines / remove-lines / update | 出庫後などで明細を編集できない状態                                                                                                  |
| `not_deletable`                                |  `422` | delete                            | 出庫後は削除できない（取消は cancel）                                                                                             |
| `illegal_transition`                           |  `422` | approve / reject / ship / receive | その状態からは実行できない遷移（承認待ちでない・取消済み・未出庫 等）                                                                                |
| `label_not_available`                          |  `422` | create-request / ship             | 配送ラベルを発行できない組み合わせ（`error` が `origin_has_no_label_config` = 出庫元に発行設定が無い / `destination_has_no_address` = 入庫先に住所が無い） |
| `tracking_number_required`                     |  `422` | ship / approve                    | 手書きの送り状の移動に `trackingNumber` が無い                                                                                   |
| `label_generation_failed`                      |  `422` | ship / approve                    | 配送ラベルの発行に失敗（`error` に理由。出庫していない）                                                                                   |
| `label_not_issued`                             |  `422` | print-label                       | 配送ラベルをまだ発行していない                                                                                                    |
| `approval_required`                            |  `422` | ship                              | 承認前（`pending_request` / `pending`）の移動は出庫できない                                                                       |
| `rejected`                                     |  `422` | ship                              | 却下済みの移動は出庫できない                                                                                                     |
| `insufficient_transferable_qty`                |  `422` | ship                              | 出庫数が移動可能量（棚合計 − 引き当て済み）を超過（売越防止。`transferable` / `shelfSum` / `activeCount` 付き）                                    |
| `invalid_quantity`                             |  `422` | receive                           | 受け入れ + 不良が出庫数を超過（`shipped` 付き）                                                                                     |
| `destination_shelf_required`                   |  `422` | receive                           | 自社在庫の受領に `destinationShelfCode` が無い                                                                                |
| `modification_requested_locked`                |  `422` | receive                           | 修正依頼が開いている間は受領できない                                                                                                 |
| `not_cancelable`                               |  `422` | cancel                            | `receiving` / `received` からは取消できない                                                                                 |
| `restore_failed`                               |  `422` | cancel                            | 出庫済みの棚復元に失敗（取消を確定しない）                                                                                              |
| `not_found`                                    |  `404` | 各エンドポイント / get                    | 指定 ID の移動が存在しない（delete は `ok:true` の no-op）                                                                        |

* 受領時に棚を増やせない場合（同一棚に別 barcode が占有等）は、棚操作側の `error_kind`（例 `shelf_occupied_by_different_barcode`）をそのまま返します（silent skip しません）。
* `noop: true` を伴う `ok: true`（既に `shipped` / `received` / `canceled` / `rejected` / `approved` への再操作）はエラーではありません（冪等な再送）。
* 共通エラー（`401` / `400` / `403` / `405` / `500` / `503`）は [Error Codes](/lmile-docs/reference/error-codes.md) を参照。ボディのバリデーション失敗は `400` + `invalid_input`（`details[]` 付き）です。

## データモデル

* `sot.lm_stock_transfers` — 移動ヘッダ。3 軸ステータス（`progress` / `approval` / `modification_request`）・出庫元/入庫先（`origin_location_id` / `destination_location_id` + 削除耐性の snapshot）・拠点種別（`origin_kind` / `destination_kind`）・各遷移時刻・`identification_no`・チャネル外部 ID（`smaregi_shipping_id` / `shopify_inventory_transfer_gid`）・送り状の出し方（`label_method`）と発行したラベル（`label_tracking_number` / `label_carrier_code` / `label_pdf_path` / `label_error`）・`note`・作成者（`created_by_staff_user_id`）。UNIQUE `shop + idempotency_key`（冪等）/ `shop + smaregi_shipping_id` / `shop + shopify_inventory_transfer_gid`。CHECK: 自己移動禁止・`modification_request='requested'` は `receiving` のみ・`shipped` は承認前（`pending_request`）不可。
* `sot.lm_stock_transfer_lines` — 移動明細（`barcode` × 数量）。`request_quantity` / `quantity`（計画）/ `shipped_quantity`（出庫）/ `received_accepted_quantity` / `received_rejected_quantity`・`origin_shelf_code` / `destination_shelf_code`・自社在庫棚 ± の冪等マーカー（`shelf_ship_applied_qty` / `shelf_receive_applied_qty`）。UNIQUE `stock_transfer_id + barcode` / `stock_transfer_id + line_no`。CHECK: `shipped_quantity <= quantity`（計画超の出庫を全 writer で構造的に禁止＝売越防止）・`received_accepted + received_rejected <= shipped_quantity`。
* `sot.lm_stock_transfer_modification_requests` — 入庫検品中の修正依頼（`status`: `requested` / `completed` / `deleted`・明細スナップショット）。1 伝票に active（`requested`）は 1 件（partial unique）。詳細は [Modification Request](/lmile-docs/stock-transfer-api/modification-request.md)。
* `sot.lm_stock_transfer_shelf_logs` — 移動が自社在庫棚を ± した追記ログ（`reason`: `ship_out` = 出庫元 − / `receive_in` = 入庫先 +）。棚在庫ログ本体とは別の attribution ログです。
* read（read view）: `agent_read.lm_stock_transfers`（ヘッダ + `shopify_phase` / `line_count` / `total_quantity` / `received_quantity`。顧客 PII・スタッフ ID・`note` は含みません）/ `agent_read.lm_stock_transfer_lines`（明細 + `sku` / `product_name` / `variant_name`）。

> 実際の数量（棚・販売可能在庫）が動くのは自社在庫（`source_system = 'mfc_inventory_ingest'`）の拠点だけで、`ship` / `receive` が棚 ± と販売可能在庫の再計算を原子的に行います。外部管理拠点（Shopify / スマレジ）は数量を記録するのみで、実在庫は外部システムが正です。返送バルク（`sot.lm_transfer_batches`）とは独立した別テーブルです。


---

# 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/stock-transfer-api/stock-transfer.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.
