> 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/order-line-master.md).

# Order Line Master — 受注

受注明細を Last Mile の受注マスターに取り込みます。**1 行 = 1 販売単位**（数量の概念を持ちません。5 個販売は 5 行 = `line_instance_no` 1〜5）。商品は `barcode`、販売チャネル・拠点は ID で結合します。

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

## エンドポイント

| Method | Path                                 | 用途                         |
| ------ | ------------------------------------ | -------------------------- |
| `POST` | `/api/order-line-master/bulk-upsert` | 受注明細の一括登録・更新（1 行 = 1 販売単位） |
| `POST` | `/api/order-line-master/customers`   | 取引に、注文のメールの顧客を結び付ける        |

***

## `POST` /api/order-line-master/bulk-upsert

受注明細を 1 販売単位 = 1 行で一括登録・更新します。同じ明細（下記の一意キーが同じ行）の再送は上書きになるため、安全に再送できます。内容が変わった行だけ変更ログに記録されます。

### リクエスト

```http
POST https://api.lmile.io/api/order-line-master/bulk-upsert
x-ingest-secret: {INGEST_SECRET}
Content-Type: application/json
```

```json
{
  "shop": "demo-store.myshopify.com",
  "dry_run": false,
  "change_source": "shopify_sync",
  "ingest_run_id": "run-2026-06-02-001",
  "idempotency_key": "shopify-2026-06-02-001",
  "rows": [
    {
      "source_system": "shopify",
      "transaction_id": "4567890123",
      "transaction_line_id": "1",
      "line_instance_no": 1,
      "barcode": "1234567890123",
      "ordered_at": "2026-06-02T12:34:56+09:00",
      "currency": "JPY",
      "is_test": false,
      "channel_id": "b2c3d4e5-f6a7-8901-bcde-f23456789012",
      "location_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "customer_id": "3097658523781",
      "gross_amount_taxincl": 9800,
      "discount_amount_taxincl": 800,
      "net_amount_taxincl": 9000,
      "net_amount_taxexcl": 8182,
      "tax_amount": 818,
      "tax_rate": 0.10,
      "refund_amount": 0,
      "refunded_at": null,
      "refund_reason": null
    }
  ]
}
```

### ボディパラメータ

| パラメータ                | 型             |  必須 | 説明                               |
| -------------------- | ------------- | :-: | -------------------------------- |
| `shop`               | string        |     | 店舗ドメイン（省略するとキーのアカウント）            |
| `rows`               | array         |  ✓  | 受注明細行の配列（最大 5,000 行/バッチ）         |
| `dry_run`            | boolean       |     | `true` で検証のみ（書込なし）               |
| `change_source`      | string        |     | 変更ログの出所（default: `external_app`） |
| `ingest_run_id`      | string        |     | 取込ラン ID（ログに記録）                   |
| `idempotency_key`    | string        |     | 監査用キー（ログに記録）                     |
| `changed_by_user_id` | string (uuid) |     | 変更実行ユーザー                         |

#### `rows[]` オブジェクト（1 行 = 1 販売単位）

| パラメータ                     | 型             |  必須 | 説明                                                                                                             |
| ------------------------- | ------------- | :-: | -------------------------------------------------------------------------------------------------------------- |
| `source_system`           | string        |  ✓  | 取込元（`shopify` / `smaregi` 等）                                                                                   |
| `transaction_id`          | string        |  ✓  | 取引（注文）ID                                                                                                       |
| `transaction_line_id`     | string        |  ✓  | 取引明細 ID                                                                                                        |
| `line_instance_no`        | integer (≥1)  |  ✓  | 明細内の販売単位連番（数量展開。3 個なら 1,2,3）                                                                                   |
| `barcode`                 | string        |  ✓  | 商品 barcode                                                                                                     |
| `ordered_at`              | ISO 8601      |  ✓  | 注文日時                                                                                                           |
| `currency`                | string        |  ✓  | 通貨コード                                                                                                          |
| `is_test`                 | boolean       |  ✓  | テスト注文フラグ                                                                                                       |
| `channel_id`              | string (uuid) |  ✓※ | Last Mile が発行する販売チャネル ID                                                                                       |
| `channel_code`            | string        |  ✓※ | 販売チャネルのコード（例 `store`）。`channel_id` の代わりに指定できます                                                                 |
| `location_id`             | string (uuid) |  ✓※ | Last Mile が発行する拠点 ID                                                                                           |
| `location`                | object        |  ✓※ | 取込元の識別子で拠点を指定（`{ "identifier_type": "smaregi_store_id", "identifier_value": "35" }`）。`location_id` の代わりに指定できます |
| `customer_id`             | string        |     | 顧客識別子（外部 ID、null 可）                                                                                            |
| `gross_amount_taxincl`    | number        |  ✓  | 税込総額（割引前）                                                                                                      |
| `discount_amount_taxincl` | number        |  ✓  | 税込割引額                                                                                                          |
| `net_amount_taxincl`      | number        |  ✓  | 税込正味額                                                                                                          |
| `net_amount_taxexcl`      | number        |  ✓  | 税抜正味額                                                                                                          |
| `tax_amount`              | number        |  ✓  | 税額                                                                                                             |
| `tax_rate`                | number        |  ✓  | 税率（0〜1。10% は `0.10`）                                                                                           |
| `refund_amount`           | number        |  ✓  | 返金額（≥0）                                                                                                        |
| `refunded_at`             | ISO 8601      |     | 返金日時（null 可）                                                                                                   |
| `refund_reason`           | string        |     | 返金理由（null 可）                                                                                                   |

※ 販売チャネルは `channel_id` か `channel_code` のどちらか、拠点は `location_id` か `location` のどちらかを送ります。両方ある行は ID を使います。

#### バリデーション

* **金額整合**（許容誤差 0.01）: `gross_amount_taxincl − discount_amount_taxincl ≈ net_amount_taxincl`、`net_amount_taxincl − tax_amount ≈ net_amount_taxexcl`
* `tax_rate` は `[0, 1]`、`refund_amount` は `≥ 0`
* `channel_id` / `location_id` が当該 shop に存在しない行は **reject**（`errors[]`）
* 1 回のリクエストで指定できる `location` の識別子と `channel_code` は、合わせて 200 種類までです（超えると `400`）
* `location` の識別子に当たる拠点が無い行（`location_unresolved`）、2 つ以上ある行（`location_ambiguous`）、`channel_code` に当たる有効なチャネルが無い行（`channel_not_found`）も **reject** します。近い拠点を選んで取り込むことはありません
* `barcode` が商品マスター未登録でも **投入**（`warnings[]`。`agent_read` 上で商品名/SKU が NULL になるだけ）

### レスポンス

```json
{
  "ok": true,
  "dry_run": false,
  "accepted_rows": 48,
  "inserted_rows": 20,
  "updated_rows": 28,
  "unchanged_rows": 2,
  "rejected_rows": 2,
  "warnings_count": 1,
  "errors": [
    { "row_index": 5, "field": "location_id", "code": "location_not_found", "message": "location_id not found ..." }
  ],
  "warnings": [
    { "row_index": 7, "field": "barcode", "code": "product_not_in_master_warning", "message": "barcode not found in product master ..." }
  ],
  "adapter_version": "lm463_order_line_master_v1"
}
```

| フィールド                                               | 説明                                         |
| --------------------------------------------------- | ------------------------------------------ |
| `accepted_rows`                                     | upsert 対象として受理した行数                         |
| `inserted_rows` / `updated_rows` / `unchanged_rows` | 新規 / 更新 / 内容が同じで何もしなかった件数                  |
| `rejected_rows`                                     | バリデーションで除外した行数（`errors[]`）                 |
| `warnings_count` / `warnings[]`                     | 投入したが要注意の行（商品マスター未登録 等）                    |
| `errors[]`                                          | `row_index` / `field` / `code` / `message` |

### 挙動・冪等性

* 明細は `(shop, source_system, transaction_id, transaction_line_id, line_instance_no)` の組で 1 行に定まります。**同じ組の再送は上書きされるだけなので安全です**（差分があれば更新、無ければ何も変わりません）。
* **変更ログ**: 登録（`insert`）/ 更新（`update`）/ 返金（`refund`）が、変更したフィールド名と変更前後の値つきで記録されます。内容が同じ再送は記録しません（`unchanged_rows` に数えます）。日時は表記（`+00:00` / `Z`）ではなく時刻で比べます。ログは 365 日で消えます。
* **per-unit**: 数量列は持ちません。数量 N の明細は `line_instance_no` 1〜N の N 行で表現します。
* 税・返金は受信時に `order_line_master` の列として反映されます（別途の refund allocation pipeline は持ちません）。

***

## `POST` /api/order-line-master/customers

受注明細に取り込んだ取引に、その注文のメールの顧客を結び付けます。配送の宛先を持たない取引（店頭販売など）の購入を、顧客の購入履歴に出すための窓口です。ルーティングに渡した注文は宛先のメールから顧客が自動で作られるので、この窓口は要りません。

### リクエスト

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

```json
{
  "shop": "demo-store.myshopify.com",
  "items": [
    {
      "source_system": "smaregi",
      "transaction_id": "138721",
      "email": "hanako@example.com",
      "name": "山田 花子",
      "phone": "090-0000-0000"
    }
  ]
}
```

### ボディパラメータ

| パラメータ   | 型      |  必須 | 説明                      |
| ------- | ------ | :-: | ----------------------- |
| `shop`  | string |  ✓  | 店舗ドメイン                  |
| `items` | array  |  ✓  | 取引ごとの顧客（最大 200 件/リクエスト） |

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

| パラメータ            | 型      |  必須 | 説明                        |
| ---------------- | ------ | :-: | ------------------------- |
| `source_system`  | string |  ✓  | 取込元（受注明細に送った値と同じ）         |
| `transaction_id` | string |  ✓  | 取引（注文）ID（受注明細に送った値と同じ）    |
| `email`          | string |  ✓  | 注文のメールアドレス。メールの無い取引は送りません |
| `name`           | string |     | 氏名（空ならメールアドレスを名前にします）     |
| `phone`          | string |     | 電話番号                      |

### レスポンス

```json
{
  "ok": true,
  "results": [
    { "source_system": "smaregi", "transaction_id": "138721", "status": "linked", "rows": 2 }
  ]
}
```

| フィールド              | 説明                                                                                      |
| ------------------ | --------------------------------------------------------------------------------------- |
| `ok`               | すべての取引を結び付けられたとき `true`                                                                 |
| `results[].status` | `linked`（結び付けた）/ `transaction_not_found`（その取引が受注明細に無い）/ `invalid_email`（メールアドレスとして読めない） |
| `results[].rows`   | 顧客を結び付けた受注明細の行数                                                                         |

### 挙動・冪等性

* 顧客はメールアドレスごとに 1 人です。同じメールの顧客がいなければ作り、いればその顧客に結び付けます。
* **既にいる顧客の氏名・電話は上書きしません**（コンソールで手入力した内容を守るため）。
* 同じ内容を再送しても結果は変わりません。**そのまま再送してかまいません。**
* メールアドレスとして読めない値は、その取引だけ `invalid_email` で返します。ほかの取引の処理は続きます（顧客は作りません）。
* 取引は先に [`bulk-upsert`](#post-apiorder-line-masterbulk-upsert) で取り込んでおきます。受注明細に無い取引は `transaction_not_found` で返るので、取り込んでから送り直してください（顧客だけは作られます）。

***

## エラー

`bulk-upsert` の行単位の不備は `200` のレスポンス内 `errors[]`（reject）/ `warnings[]` として返り、正常行の投入は継続します（silent skip しません）。バッチ上限超過は `400`。`customers` は取引ごとの結果を `200` の `results[]` で返します。必須の項目が欠けている・200 件を超えるなど入力の形が合わないときだけ `400`（`error_kind: invalid_input`）で、何も書きません。共通エラー（`401` / `400` / `403` / `405` / `500` / `503`）は [Error Codes](/lmile-docs/reference/error-codes.md) を参照。

## データモデル

* `order_line_master` — 受注明細 SoT（1 行 = 1 販売単位）
* `order_line_master_update_logs` — 変更監査ログ（`insert` / `update` / `refund`。内容が同じ再送は記録しない・365 日で消える）
* read は `agent_read.order_line_master`（safe view）経由
* `lm_customers` — 顧客（`customers` が作る・結び付ける。氏名・メール・電話はここにだけ持ちます）


---

# 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/order-line-master.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.
