> 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/products.md).

# Products — 商品マスター

商品マスターを外部システム（Shopify / Smaregi / Amazon / 手動取込 等）から Last Mile に取り込みます。商品は `barcode` 単位で管理され、**同じ `barcode` を送ると上書き、未登録なら新規作成**されます。`barcode` は受注・在庫との結合キーです。

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

## エンドポイント

| Method | Path                             | 用途                                                          |
| ------ | -------------------------------- | ----------------------------------------------------------- |
| `POST` | `/api/ingest/products`           | 商品マスターの一括登録・更新（`barcode` 単位で上書き）                            |
| `GET`  | `/api/ingest/products`           | 商品マスターを `barcode` または `sku` で引く（登録済みかの確認・SKU / barcode の取得） |
| `GET`  | `/api/ingest/accessory-services` | 付帯サービスにした商品の一覧                                              |

***

## `POST` /api/ingest/products

商品の一括登録・更新。同じ `barcode` は上書き、未登録なら新規作成されます。省略したフィールドは既存値を維持し、新規行のみ `currency=JPY` / `is_active=true` / `lifecycle_status=active` を既定適用します。

### リクエスト

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

```json
{
  "shop": "demo-store.myshopify.com",
  "source_system": "shopify",
  "idempotency_key": "import-2026-06-02-001",
  "dry_run": false,
  "products": [
    {
      "barcode": "1234567890123",
      "product_name": "スマートフォンケース ブラック",
      "sku": "DEMO-CASE-BLK-001",
      "variant_name": "iPhone 16",
      "brand": "Demo Brand",
      "category_l1": "ケース",
      "category_l2": "スマホケース",
      "current_price": 9800,
      "current_cost": 3500,
      "currency": "JPY",
      "is_active": true,
      "released_at": "2026-07-15T10:00:00+09:00",
      "preorder_delivery_dates": { "b179515a-6ca9-4ffa-a40b-0664ccc74366": "2026-10-11" },
      "product_type": "Leather Goods",
      "class_metafields": { "variant": { "global.preorder": "10月末お届け予定" } }
    }
  ]
}
```

### ボディパラメータ

| パラメータ                | 型             |  必須 | 説明                                                                                          |
| -------------------- | ------------- | :-: | ------------------------------------------------------------------------------------------- |
| `shop`               | string        |     | 店舗ドメイン（省略するとキーのアカウント）                                                                       |
| `products`           | array         |  ✓  | 商品オブジェクトの配列（最大 10,000 件）                                                                    |
| `source_system`      | string        |     | 取込元（`shopify` / `smaregi` / `manual_api` 等。default: `manual_api`）。変更ログの `change_source` に記録 |
| `idempotency_key`    | string        |     | 監査用キー（変更ログに記録）                                                                              |
| `changed_by_user_id` | string (uuid) |     | 変更実行ユーザー                                                                                    |
| `dry_run`            | boolean       |     | `true` で検証のみ（書込なし。default: `false`）                                                         |

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

| パラメータ                                     | 型         |  必須 | 説明                                                                                                                                                                                                                                                                                                 |
| ----------------------------------------- | --------- | :-: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `barcode`                                 | string    |  ✓  | 商品識別子（JAN/EAN/UPC）。受注・在庫との結合キー                                                                                                                                                                                                                                                                     |
| `product_name`                            | string    |  ✓  | 商品名                                                                                                                                                                                                                                                                                                |
| `sku`                                     | string    |     | SKU コード                                                                                                                                                                                                                                                                                            |
| `variant_name`                            | string    |     | バリエーション名（サイズ/色 等）                                                                                                                                                                                                                                                                                  |
| `brand`                                   | string    |     | ブランド名                                                                                                                                                                                                                                                                                              |
| `category_l1` `category_l2` `category_l3` | string    |     | カテゴリ（大/中/小）                                                                                                                                                                                                                                                                                        |
| `current_price`                           | number    |     | 現在販売価格                                                                                                                                                                                                                                                                                             |
| `current_compare_at_price`                | number    |     | 参考価格（定価）                                                                                                                                                                                                                                                                                           |
| `current_cost`                            | number    |     | 原価                                                                                                                                                                                                                                                                                                 |
| `currency`                                | string    |     | 通貨コード（新規 default: `JPY`）                                                                                                                                                                                                                                                                           |
| `is_active`                               | boolean   |     | 有効フラグ（新規 default: `true`）                                                                                                                                                                                                                                                                          |
| `lifecycle_status`                        | string    |     | ライフサイクル（新規 default: `active`）                                                                                                                                                                                                                                                                      |
| `primary_image_url`                       | string    |     | 代表画像 URL                                                                                                                                                                                                                                                                                           |
| `released_at`                             | string    |     | 発売日時（ISO 8601）。**タイムゾーンオフセット付きを推奨**（例 `2026-07-15T10:00:00+09:00`。オフセット無しは UTC として解釈されます）。省略すると既存値を維持します                                                                                                                                                                                           |
| `preorder_delivery_dates`                 | object    |     | 予約納期。出荷元の拠点 ID → その出荷元から出荷できるようになる日（`YYYY-MM-DD`）の対応表です（例 `{ "b179515a-…": "2026-10-11" }`）。行のある出荷元だけがこの商品を出荷でき、行の無い出荷元は候補から外れます。チェックアウトと購入者ページの到着予定日は、この日から出荷元の作業日数と配送日数を積んで出します。日付が過ぎた行は無視され、通常の在庫の計算に戻ります。省略すると既存値を維持し、`{}` を送ると予約納期を外します。拠点 ID 以外のキー（`"*"`・国コード）を送ると `400 invalid_input` になります |
| `product_type`                            | string    |     | 商品タイプ（Shopify の Product type などの生値）。到着予定日の商品区分の判定に使います。`category_l1` とは別の項目です（あちらはカテゴリの体系名が入ります）。省略すると既存値を維持します                                                                                                                                                                                    |
| `class_metafields`                        | object    |     | 到着予定日の商品区分が参照するメタフィールドの値。`{ "product": { "名前空間.キー": "値" }, "variant": { … } }` の形で、商品側とバリエーション側を分けて送ります（区分の判定が「バリエーションの値だけを見る」場合があるためです）。送る対象は区分の設定で参照しているキーだけで構いません。省略すると既存値を維持します                                                                                                                |
| `is_accessory_service`                    | boolean   |     | 付帯サービス。当日出荷チケットや刻印サービスのように、それだけでは出荷する荷物にならない明細に付けます。`true` にした商品は在庫の引き当て対象から外れ、出荷指示の `serviceLines` に入ります。`false` を送ると付帯サービスを外し、通常の商品に戻ります。省略すると既存値を維持します                                                                                                                                          |
| `inventory_push_disabled_source_ids`      | string\[] |     | この商品の在庫連携を止める出荷元（拠点 id の配列）。ここに入れた出荷元については、[出荷元在庫の通知](/lmile-docs/webhooks/source-inventory.md) の `availableTotal` が実際の合算ではなく `0` になります（販売チャネルでその出荷元から売らないため）。送ると丸ごと置き換えます（`[]` ですべての出荷元を再開）。変えた出荷元には、在庫の変動を待たずに 1 回通知します（止めた出荷元は `0`、再開した出荷元は最新の合算）。省略すると既存値を維持します                                |

### レスポンス

```json
{
  "ok": true,
  "shop": "demo-store.myshopify.com",
  "source_system": "shopify",
  "dry_run": false,
  "created": 5,
  "updated": 12,
  "unchanged": 3,
  "errors_count": 1,
  "errors": [
    { "barcode": "INVALID", "error": "barcode and product_name required" }
  ]
}
```

| フィールド                               | 説明                                                                   |
| ----------------------------------- | -------------------------------------------------------------------- |
| `created` / `updated` / `unchanged` | 新規 / 変更あり / 差分なし の件数                                                 |
| `errors[]`                          | `barcode` と `error`（`barcode and product_name required` 等）。該当行は skip |

### 挙動・冪等性

* 商品は**店舗 × `barcode` ごとに 1 行**で管理されます。送ると該当商品が更新され、未登録なら新規作成されます。
* **部分更新**: 送信しなかったフィールドは既存値のまま残ります（`null` で上書きされません）。
* **変更ログ**: 登録（`created`）と更新（`updated`）が記録されます。更新時は変更したフィールド名と変更前後の値が残ります。差分が無ければ `unchanged`（ログは増えません）。
* **同じ内容の再送は何度でも安全です（冪等）**。`unchanged` として数えられるだけで、何も変わりません。

***

## `GET` /api/ingest/products

商品マスターを `barcode` または `sku` で引きます。取込元（EC モール等）から取れた `barcode` や商品コードが Last Mile に登録されているか、その商品の `sku` / `barcode` は何かを、受注を送る前に確かめるときに使います。`shop` と `barcodes` / `skus` はクエリパラメータで指定します。

### リクエスト

```http
GET https://api.lmile.io/api/ingest/products?shop=demo-store.myshopify.com&barcodes=1234567890123,1234567890456
x-ingest-secret: {INGEST_SECRET}
```

### クエリパラメータ

| パラメータ      | 型      |  必須 | 説明                                                                               |
| ---------- | ------ | :-: | -------------------------------------------------------------------------------- |
| `shop`     | string |  ✓  | 店舗                                                                               |
| `barcodes` | string |     | 引きたい `barcode` をカンマ区切りで（1 回 200 件まで。前後の空白は無視）                                    |
| `skus`     | string |     | 引きたい `sku` をカンマ区切りで（1 回 200 件まで）。`barcodes` と `skus` のどちらかは必須。両方送るとどちらかに一致した行を返す |

### レスポンス

```json
{
  "ok": true,
  "shop": "demo-store.myshopify.com",
  "count": 1,
  "products": [
    { "barcode": "1234567890123", "sku": "BBGT1-ET" }
  ]
}
```

| フィールド        | 説明                                                            |
| ------------ | ------------------------------------------------------------- |
| `count`      | 見つかった商品の数                                                     |
| `products[]` | 商品マスターにあった行の `barcode` と `sku`（SKU が無い商品は `null`）。`barcode` 順 |

### 挙動・冪等性

* 商品マスターにある行だけを返します。送った `barcode` / `sku` が応答に無ければ、その商品は未登録です。例: 2 件送って `count` が 1 なら、応答に無い方が未登録です。
* `sku` は商品マスターで一意とは限りません。同じ `sku` の行が複数あればすべて返すので、「ちょうど 1 件か」は受け取った側で確かめてください（1 件でなければ商品を特定できないものとして止めるのが安全です）。
* 読み取り専用です。何回呼んでも安全です。
* `barcodes` / `skus` それぞれ 200 件を超えて送るとエラー（`invalid_input`）になります。分けて送ってください。

***

## `GET` /api/ingest/accessory-services

付帯サービス（`is_accessory_service = true`）にした商品の一覧を返します。出荷指示を送る側が「付帯サービスだけの荷物は送らない」を判断するときに使います。`shop` はクエリパラメータで指定します。

### リクエスト

```http
GET https://api.lmile.io/api/ingest/accessory-services?shop=demo-store.myshopify.com
x-ingest-secret: {INGEST_SECRET}
```

### クエリパラメータ

| パラメータ  | 型      |  必須 | 説明                |
| ------ | ------ | :-: | ----------------- |
| `shop` | string |     | 店舗（省略するとキーのアカウント） |

### レスポンス

```json
{
  "ok": true,
  "shop": "demo-store.myshopify.com",
  "count": 2,
  "products": [
    { "barcode": "1234567890456", "sku": "SAME-DAY-TICKET" },
    { "barcode": "1234567890789", "sku": null }
  ]
}
```

| フィールド        | 説明                                                |
| ------------ | ------------------------------------------------- |
| `products[]` | 付帯サービスにした商品の `barcode` と `sku`（SKU が無い商品は `null`） |

### 挙動・冪等性

* 付帯サービスにした商品を全件返します（`barcode` 順）。
* 読み取り専用です。何回呼んでも安全です。
* 500 件を超えるときは、一部だけ返さずにエラー（`too_many_rows`）を返します。一部だけ返すと、付帯サービスの判定が漏れるためです。

***

## エラー

エンドポイント固有:

| Status | 条件                                                                                    | レスポンス例                                                                                                                                            |
| :----: | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|  `422` | `ok:false`（`error_kind` 付き）                                                           | `{ "ok": false, "error_kind": "invalid_payload", "error": "products[] required and non-empty" }`                                                  |
|  `400` | `preorder_delivery_dates` のキーが出荷元の拠点 ID でない（`"*"`・国コードを含む）、または値が実在する `YYYY-MM-DD` でない | `{ "ok": false, "error_kind": "invalid_input", "error": "products[0].preorder_delivery_dates: key must be a fulfillment source location id: *" }` |
|  `400` | `barcodes` と `skus` が両方空、またはどちらかが 200 件を超える（`GET /api/ingest/products`）               | `{ "ok": false, "error_kind": "invalid_input", "error": "barcodes / skus: at most 200 each" }`                                                    |
|  `500` | 付帯サービスが 500 件を超える（`GET /api/ingest/accessory-services`）                               | `{ "ok": false, "error_kind": "too_many_rows", "error": "more than 500 accessory services" }`                                                     |

行単位の不備（`barcode` / `product_name` 欠落）はバッチ全体を止めず `errors[]` に記録して skip します。共通エラー（`401` / `400` / `403` / `405` / `500` / `503`）は [Error Codes](/lmile-docs/reference/error-codes.md) を参照。

## データモデル

* `sot.products` — 商品 SoT（`shop + barcode` で一意）
* `sot.product_update_logs` — 変更監査ログ（`created` / `updated` ＋ `changed_fields`）
* read は `agent_read.products` / `agent_read.product_update_logs`（safe view）経由


---

# 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/products.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.
