> 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/configuration/dispatch-config.md).

# Dispatch Config — WMS 連携設定

ルーティングで在庫ロケーションへ引き当てた後、その出荷を外部 WMS / 3PL へ自動 POST するための設定を、**在庫ロケーションごと**に登録します。WMS ごとにペイロード形式が異なるため、宛先 URL・認証・**ペイロードテンプレート**を設定し、`barcode` / `sku` / `product_name` / `quantity` / `properties` 等を変数として差し込みます。

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

> 引き当て確定時（`/api/routing/orders`・`/api/routing/reallocate`）に、各 shipment（引き当て先在庫ロケーション）の設定が解決され送信されます。設定の無いロケーションは送信を skip します。送信結果は `sot.lm_location_dispatch_log` に記録されます。
>
> **`action=label` / `action=print` の解決キーは出荷元**: 出荷ラベルを物理出力するのは引き当て店舗ではなく出荷元（MFC センター / 倉庫）のため、`label`（ラベル生成）と `print`（ラベル物理印刷）の設定は shipment の引き当てロケーションではなく\*\*注文の出荷元（fulfillment source location）\*\*で解決されます。これらの設定の `locationId` には出荷元 location を指定してください。

## エンドポイント

| Method | Path                           | 用途                                       |
| ------ | ------------------------------ | ---------------------------------------- |
| `POST` | `/api/routing/dispatch-config` | 在庫ロケーション単位の WMS ディスパッチ設定を登録 / 更新（upsert） |

***

## `POST` /api/routing/dispatch-config

`shop + locationId` で設定を upsert します。`authSecret` はサーバー側で暗号化保存され、レスポンスや読み取り API には返りません（`has_secret` のみ）。無効化は `isActive: false` で同じ POST を送ります。

### リクエスト

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

```json
{
  "shop": "demo-store.myshopify.com",
  "locationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "destinationUrl": "https://wms.example.com/inbound",
  "httpMethod": "POST",
  "contentType": "application/json",
  "authType": "bearer",
  "authSecret": "wms-api-token-xxxxx",
  "payloadTemplate": {
    "order_ref": "{{externalOrderRef}}",
    "ship_to": { "pref": "{{shipTo.prefecture}}", "postal": "{{shipTo.postalCode}}" },
    "from": "{{location.label}}",
    "items": {
      "$each": "lines",
      "as": { "code": "{{barcode}}", "sku": "{{sku}}", "name": "{{product_name}}", "qty": "{{quantity}}", "note": "{{properties.刻印}}" }
    }
  },
  "isActive": true
}
```

### ボディパラメータ

| パラメータ             | 型             |  必須 | 説明                                                                                                                                                                                                                                                                                                                                                             |
| ----------------- | ------------- | :-: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `shop`            | string        |     | 店舗ドメイン（省略するとキーのアカウント）                                                                                                                                                                                                                                                                                                                                          |
| `locationId`      | string (uuid) |  ✓  | 設定対象 location（shop 内に実在すること）。`create`/`update`/`cancel` は**引き当て先在庫ロケーション**、`label`/`print` は**出荷元（fulfillment source location）**                                                                                                                                                                                                                               |
| `tag`             | string        |     | tag 条件（任意）。省略＝**既定設定**。注文 `tag` がこの値に一致するとこの設定で送信                                                                                                                                                                                                                                                                                                              |
| `action`          | string        |     | アクション種別 `create`(既定) / `update` / `cancel` / `label` / `print` / `pickup` / `track`。`(shop, location, tag, action)` で一意。cancel/update は [Cancel / Update](/lmile-docs/routing-api/cancel-update.md)、label/print は [Fulfillment](/lmile-docs/fulfillment-api/fulfillment.md)、pickup は [Carrier Pickup](/lmile-docs/fulfillment-api/pickup.md)、track は配送状況の照会で使用 |
| `responseMapping` | object        |     | `action=label` + `adapterType=generic_template` 用。応答 JSON から追跡番号 / ラベル PDF を抜く dot-path マップ（下記）                                                                                                                                                                                                                                                                |
| `adapterType`     | string        |     | `generic_template`(既定) / `shipandco`。`shipandco` は Ship\&Co 専用の住所整形・応答解釈を行う（下記）                                                                                                                                                                                                                                                                                |
| `destinationUrl`  | string        |  ✓  | 送信先 WMS エンドポイント（Ship\&Co は `https://api.shipandco.com/v1/shipments`、`print` は print server の受け口）。`https://` で始まるホスト名の URL のみ（IP アドレス・`localhost`・`.internal`・`.local` は不可）                                                                                                                                                                                     |
| `payloadTemplate` | object/array  |  ✓  | 送信ボディのテンプレート（下記）。**`action=print` / `action=track` は不要**（省略可）                                                                                                                                                                                                                                                                                                  |
| `httpMethod`      | string        |     | HTTP メソッド（default `POST`）                                                                                                                                                                                                                                                                                                                                      |
| `contentType`     | string        |     | `Content-Type`（default `application/json`）                                                                                                                                                                                                                                                                                                                     |
| `authType`        | string        |     | `none` / `bearer` / `header`（default `none`）                                                                                                                                                                                                                                                                                                                   |
| `authHeaderName`  | string        |     | `authType=header` のときのヘッダー名                                                                                                                                                                                                                                                                                                                                    |
| `authSecret`      | string        |     | 認証 secret（暗号化保存。省略時は既存維持。ただし `destinationUrl` か `authHeaderName` を変えるときに省略すると、保存済みの secret は消えます。送信先を変えるときは secret も送り直してください）                                                                                                                                                                                                                                 |
| `isActive`        | boolean       |     | 有効フラグ（default `true`）                                                                                                                                                                                                                                                                                                                                          |

### ペイロードテンプレート

JSON を再帰的に描画します。

* **変数差し込み**: 文字列内の `{{path}}` を値に置換（dot-path 可）。path には `{}` と空白を除く任意の文字が使えるため、`{{properties.刻印}}` のような**日本語キー**も参照できます。文字列全体が単一 `{{path}}` の場合は値の型を保持（数値などをそのまま渡す）。値が無い path は文字列内では空文字、単一 `{{path}}` では `null` になります。
* **配列展開**: `{ "$each": "lines", "as": <itemテンプレート> }` は対象配列を反復し、各要素のキー（`barcode` / `sku` / `product_name` / `quantity` / `properties`）を差し込んで配列を生成。

利用可能な変数:

| 変数                                                                                                                   | 説明                                                                                                                                                                                                 |
| -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `shop` / `sourceSystem` / `externalOrderRef` / `idempotencyKey` / `decidedAt`                                        | 注文情報                                                                                                                                                                                               |
| `shipTo.name` / `shipTo.company` / `shipTo.phone` / `shipTo.email`                                                   | 配送先 受取人・連絡先                                                                                                                                                                                        |
| `shipTo.country` / `shipTo.postalCode` / `shipTo.prefecture` / `shipTo.city` / `shipTo.address1` / `shipTo.address2` | 配送先住所                                                                                                                                                                                              |
| `location.id` / `location.label`                                                                                     | 引き当て先在庫ロケーション                                                                                                                                                                                      |
| `action`                                                                                                             | dispatch アクション（create / update / cancel）                                                                                                                                                           |
| `lines[].barcode` / `lines[].sku` / `lines[].product_name` / `lines[].quantity`                                      | 明細（`$each` 内で参照）                                                                                                                                                                                   |
| `lines[].properties`                                                                                                 | 明細単位の任意キー/値（[Routing](/lmile-docs/routing-api/orders.md) の `lines[].properties`。刻印テキスト等）。`$each` 内で `{{properties.<キー>}}` で個別値を参照（object のまま渡す場合は単一変数 `"{{properties}}"`）。properties の無い明細は `null` |

> `sku` / `product_name` は送信時に `barcode` から `sot.products` を解決して補完します（出荷指示自体は barcode・数量・properties のみ保持）。properties 導入前に保存された出荷指示の明細は `properties: null` として扱われます。

### `responseMapping`（`action=label` 専用）

`action=label` では送信先（carrier API / 印刷 agent）の**応答 JSON**から追跡番号とラベル PDF を取り込みます。応答の dot-path を指定します。

```json
{
  "trackingNumber": "tracking_no",
  "carrierCode": "carrier",
  "labelPdfBase64": "label.pdf_base64",
  "labelPdfUrl": "label.url"
}
```

| キー               | 説明                                                |
| ---------------- | ------------------------------------------------- |
| `trackingNumber` | 追跡番号のパス                                           |
| `carrierCode`    | キャリアコードのパス（任意）                                    |
| `labelPdfBase64` | base64 エンコードされた PDF のパス（あれば優先）                    |
| `labelPdfUrl`    | PDF ダウンロード URL のパス（`labelPdfBase64` が無い場合に fetch） |

取り込んだ PDF は Supabase Storage バケット `shipping-labels`（private）に `{shop}/{routingOrderId}/{fromLocationId}.pdf`（shipment の引き当てロケーション単位）で保存し、パスと追跡番号を `sot.lm_fulfillment_shipments` に記録します。詳細は [Fulfillment](/lmile-docs/fulfillment-api/fulfillment.md)。

### Ship\&Co アダプタ（`adapter_type=shipandco`）

`action=label` + `adapterType=shipandco` のとき、汎用テンプレートの代わりに **Ship\&Co 専用アダプタ**が payload を組み立てます（県名の正規化・電話整形・市区町村の `address1` 前置など、JSON テンプレートでは表現できない整形を行うため）。`payloadTemplate` は**送信ボディそのものではなく Ship\&Co 設定**として解釈されます。設定は**出荷元 location**（MFC センター / 倉庫）に登録し、`fromAddress` にはその出荷元の差出人住所を指定します。

* **認証**: `authType: "header"` / `authHeaderName: "x-access-token"` / `authSecret: "<Ship&Co API トークン>"`（暗号化保存。ソースに直書きしません）。
* **`payloadTemplate`（= Ship\&Co 設定）**:

```json
{
  "carrierId": "sFGRGsrMfjZbkFQMD",
  "service": "sagawa_regular",
  "productOriginCountry": "IT",
  "defaultProductPrice": 0,
  "fromAddress": {
    "full_name": "ボナベンチュラ", "company": "ボナベンチュラジャパン株式会社",
    "email": "support@example.jp", "phone": "050-0000-0000",
    "country": "JP", "zip": "1570068", "province": "東京都",
    "address1": "世田谷区宇奈根1丁目", "address2": "MFC"
  }
}
```

| キー                     |  必須 | 説明                                                             |
| ---------------------- | :-: | -------------------------------------------------------------- |
| `carrierId`            |  ✓  | Ship\&Co のキャリア ID（`setup.carrier_id`）                          |
| `fromAddress`          |  ✓  | 依頼主（出荷元）住所。Ship\&Co の `from_address` 形式                        |
| `service`              |     | 配送サービス（default `sagawa_regular`）                               |
| `productOriginCountry` |     | 商品の原産国（default `IT`）                                           |
| `defaultProductPrice`  |     | `products[].price` の既定値（default `0`。routing 文脈に単価が無いため。下記注記参照） |

* **宛先住所**: [Routing](/lmile-docs/routing-api/orders.md) の `shipTo`（= `sot.lm_routing_orders.ship_to_*`）から `to_address` を組み立て。県名は日本語へ正規化、`address1` に市区町村を前置、電話は `+81`→`0`・記号除去、郵便番号はハイフン除去。
* **商品**: 出荷指示の明細（`barcode` → `sku` 解決）を `products[]` に。SKU に `GIFT` / `CUSTOM` を含む行は除外。明細の `properties` は Ship\&Co payload には送りません（`products[]` は `name` / `quantity` / `price` / `origin_country` のみ）。
* **応答**: Ship\&Co 固定形（`id` / `delivery.label`(URL) / `delivery.tracking_numbers[0]`）から追跡番号と PDF URL を取得して取り込みます（`responseMapping` は使いません）。`carrier_code` には `service` を保存。
* **失敗時**: HTTP 非 200 / `delivery.label` 欠落は `sot.lm_fulfillment_shipments.last_label_error` に記録。住所不備なら [住所修正・再発行](/lmile-docs/fulfillment-api/address-recovery.md) でリカバリします。

> **注記（要疎通確認）**: routing 文脈に商品単価が無いため `products[].price` は `defaultProductPrice`（既定 `0`）を送ります。国内・非代引きの Sagawa 便では通常問題ありませんが、実 Ship\&Co API での疎通テストで `price=0` が拒否されないことを確認してください。

### `action=print`（ラベル物理印刷）

ラベル生成（`action=label`）の**成功直後**に、保存したラベル PDF を出荷元の print server へ自動送信します。`destinationUrl` は print server の受け口、認証は通常 `authType: "header"` + `authHeaderName: "x-api-key"` です。`payloadTemplate` は不要（省略可）。

送信形式は **`multipart/form-data` 固定**:

| フィールド     | 値                                                                                                             |
| --------- | ------------------------------------------------------------------------------------------------------------- |
| `pdf`     | ラベル PDF（`application/pdf`、ファイル名は `{fromLocationId}.pdf`）                                                      |
| `paper`   | `A5`                                                                                                          |
| `tray`    | `Tray1`                                                                                                       |
| `options` | `{"MediaType":"Cardstock","print-scaling":"none","Duplex":"None","cupsPrintQuality":"High"}`（CUPS オプション JSON） |

* 印刷は **best-effort**: 失敗してもラベル生成・状態遷移は成功のまま維持し、`sot.lm_fulfillment_shipments.last_print_error` に記録します。
* **リトライしません**（5xx でも実際は印刷済みの可能性があり、配送ラベルの二重出力を避けるため）。回収は [Print Label（手動再印刷）](/lmile-docs/fulfillment-api/print-label.md)。
* `print` 設定の無い出荷元は印刷を skip します（`printStatus: "skipped"`。非印刷拠点として正常）。

### `action=track`（配送状況の照会先）

出荷した荷物の配送状況（集荷・配送中・配達完了）を、出荷元ごとに配送業者へ照会するための設定です。照会は LastMile が定期的に行い、送る本文は組まないので `payloadTemplate` は不要（省略可）です。

| `adapterType` | 照会先                                                              | 認証                                                                        |
| ------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `shipandco`   | Ship\&Co の追跡 API（他社が発行した送り状も照会できます）                              | `authType: "header"` + `authHeaderName: "x-access-token"`（Ship\&Co のトークン） |
| `dhl`         | DHL Express（MyDHL API）。自社アカウントで発行した Express の送り状だけ照会できます         | `authType: "none"` + `authSecret` に `apiKey:apiSecret`                    |
| `dhl_unified` | DHL の追跡 API（Shipment Tracking Unified）。DHL eCommerce の送り状も照会できます | `authType: "header"` + `authHeaderName: "DHL-API-Key"`                    |

* 出荷元に `track` の設定が無いときは、同じ出荷元の `label` の設定で照会します。
* 照会を止めたいときは `isActive: false` で同じ POST を送ります。

### `action=pickup`（キャリア集荷の依頼先）

[Carrier Pickup](/lmile-docs/fulfillment-api/pickup.md) が集荷（オンコールピックアップ）を依頼するときの、**集荷先の住所とキャリア接続**です。`adapterType` は `ups_pickup` を指定します。

`destinationUrl` には **UPS API のベース URL** を入れます（本番 `https://onlinetools.ups.com` / テスト `https://wwwcie.ups.com`）。テスト環境では実際の集荷は発生しません。アダプタがこの URL にパスを足して呼ぶので、**本番とテストの切り替えはこの 1 項目だけ**です。

`authSecret` にはキャリアの認証情報を `"<clientId>:<clientSecret>"` の形で入れます（`action=label` + `adapterType=dhl` と同じ形式）。

`payloadTemplate` は送信ボディそのものではなく、**集荷設定**として解釈されます:

| フィールド                                   |  必須 | 説明                                                         |
| --------------------------------------- | :-: | ---------------------------------------------------------- |
| `accountNumber`                         |  ✓  | 請求先のキャリアアカウント番号                                            |
| `accountCountryCode`                    |     | アカウントの国コード（既定 `JP`）                                        |
| `pickupAddress`                         |  ✓  | 集荷先。下記の全項目が必要です                                            |
| `serviceCode`                           |     | 引き取ってもらう配送サービスのコード（3 桁ゼロ埋め。既定 `008` = Worldwide Expedited） |
| `defaultReadyTime` / `defaultCloseTime` |     | 依頼時に時間帯を省略したときの既定値 `HHmm`（既定 `1600` / `1800`）              |

`pickupAddress` の項目:

| フィールド                                   | 説明                                                                |
| --------------------------------------- | ----------------------------------------------------------------- |
| `companyName` / `contactName` / `phone` | 集荷先の会社名・担当者名・電話番号                                                 |
| `addressLine`                           | 番地・建物。**1 本の文字列**で指定します（配列で渡した場合はカンマで連結します）                       |
| `city`                                  | 市区町村                                                              |
| `stateProvince`                         | 都道府県・州を **ISO 3166-2**（例: 大阪 = `JP-27`）で指定します。日本 / 米国 / カナダでは必須です |
| `postalCode`                            | 郵便番号（ハイフンは自動で除去します）                                               |
| `countryCode`                           | 国コード（ISO 3166-1）                                                  |
| `residentialIndicator`                  | `Y` = 住宅 / `N` = オフィス（既定 `N`）                                     |

```json
{
  "shop": "demo-store.myshopify.com",
  "locationId": "054bccb8-....",
  "action": "pickup",
  "adapterType": "ups_pickup",
  "destinationUrl": "https://onlinetools.ups.com",
  "authSecret": "{CLIENT_ID}:{CLIENT_SECRET}",
  "payloadTemplate": {
    "accountNumber": "E96979",
    "accountCountryCode": "JP",
    "serviceCode": "008",
    "defaultReadyTime": "1600",
    "defaultCloseTime": "1800",
    "pickupAddress": {
      "companyName": "Example Japan",
      "contactName": "Warehouse",
      "addressLine": "1-5-17 Dojima Kita-ku, Dojima Grand Bldg 5F",
      "city": "Osaka",
      "stateProvince": "JP-27",
      "postalCode": "5300003",
      "countryCode": "JP",
      "residentialIndicator": "N",
      "phone": "0800000000"
    }
  }
}
```

* **項目が欠けていても既定値では埋めません**。足りない項目を挙げて `422` を返します（誤った住所に集荷を呼ばないため）。
* `pickup` 設定の無い出荷元へ依頼すると、エラーではなく `requestable: false` と理由を返します。
* `serviceCode` はキャリアがアカウントの契約サービスで検証します。テスト環境では通っても本番で拒否されることがあるため、**テスト環境の成功を本番の根拠にしないでください**。
* 認証情報は保存後に読み出せません（レスポンスは `has_secret` のみ）。変更したいときは`authSecret` を送り直します。省略すれば既存の値が維持されるので、住所や時間帯だけを直せます。
* ここに登録する住所は**既定値**です。1 回の集荷だけ別の住所にしたいときは、設定を変えずに[Carrier Pickup](/lmile-docs/fulfillment-api/pickup.md) の `pickupAddress` で上書きします。

### レスポンス

```json
{ "ok": true, "id": "c0ffee00-...", "shop": "demo-store.myshopify.com",
  "location_id": "a1b2c3d4-...", "is_active": true, "has_secret": true }
```

| フィールド        | 説明                                           |
| ------------ | -------------------------------------------- |
| `id`         | 設定行 id（`sot.lm_location_dispatch_config.id`） |
| `has_secret` | 認証 secret が保存済か（**secret 値そのものは返しません**）      |

### 挙動・セキュリティ

* **upsert**: `(shop, location_id, tag, action)` で1件に収束（`tag` 省略＝既定、`action` 省略＝`create`）。`authSecret` 省略時は既存の暗号化値を維持。
* **解決**: dispatch 時に `action`（create/update/cancel/label/print/pickup）で**完全一致**、その中で注文 `tag` 一致を優先（無ければ `tag=NULL` 既定）。location キーは `create`/`update`/`cancel` ＝ shipment の引き当て先在庫ロケーション、`label`/`print`/`pickup` ＝ 注文の**出荷元**（fulfillment source location）。
* **暗号化**: `authSecret` は pgcrypto で暗号化して保存（鍵は Supabase Vault 管理）。レスポンス・読み取り view・ログのいずれにも平文 secret は出ません。
* **送信（ディスパッチ）**: 引き当て確定時に各 shipment の設定を解決し、テンプレート描画→ `authType` に応じた認証（`bearer` = `Authorization: Bearer ...` / `header` = 任意ヘッダー）で送信。5xx は指数バックオフで最大 2 回再試行。
* **observability**: 送信ごとに `sot.lm_location_dispatch_log` へ `request_payload`（描画後・secret なし）/ `http_status` / `delivery_status` / `attempts` / `last_error` を記録。

***

## エラー

エンドポイント固有:

| Status | 条件                                                                                   | レスポンス例                                                                                               |
| :----: | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- |
|  `422` | 必須欠落・不正値（`error_kind: invalid_payload`）                                              | `{ "ok": false, "error_kind": "invalid_payload", "error": "destinationUrl required" }`               |
|  `422` | `destinationUrl` が https でない・IP アドレスや内部向けのホスト（`error_kind: invalid_destination_url`） | `{ "ok": false, "error_kind": "invalid_destination_url", "error": "destinationUrl must use https" }` |
|  `422` | location が shop 内に存在しない（`error_kind: location_unresolved`）                           | `{ "ok": false, "error_kind": "location_unresolved", "error": "location not found for shop" }`       |

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

## データモデル

* `sot.lm_location_dispatch_config` — 在庫ロケーション単位の WMS 設定（`UNIQUE shop + location_id`、`auth_secret_encrypted` は pgcrypto 暗号化、`adapter_type` は将来拡張用）
* `sot.lm_location_dispatch_log` — 送信ログ（`delivery_status`: `pending` / `sent` / `failed` / `skipped`、`request_payload` は secret なし）
* 暗号鍵: Supabase Vault `lm_dispatch_enc_key`。読み取り view（secret 除外）: `public.lm_v_location_dispatch_config` / `public.lm_v_location_dispatch_log`


---

# 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/configuration/dispatch-config.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.
