> 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/fulfillment-api/address-recovery.md).

# Address Recovery — 宛先修正・ラベル再発行

出荷ラベル（[Fulfillment](/lmile-docs/fulfillment-api/fulfillment.md) / [Dispatch Config](/lmile-docs/configuration/dispatch-config.md) の `action=label`）の発行が、**宛先住所の不備**でキャリアに拒否されて失敗した場合のリカバリ手順です。配送先住所を**修正**し、失敗した出荷元のラベルを**再発行**します。

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

> `/api/routing/shipment/address` / `/api/routing/shipment/retry-label` は機械間 secret（`x-ingest-secret`）と倉庫スタッフの operator JWT のどちらでも呼べます（系統はヘッダの有無で明示判別。[詳細](https://lmile.gitbook.io/lmile-docs/fulfillment-api/pages/7cB70I8yeRnMpvcdLkNu#routing-write-の-2-系統認証)）。operator JWT 経由は操作スタッフを記録します。

> ラベル発行が失敗すると、その出荷元（[Fulfillment](/lmile-docs/fulfillment-api/fulfillment.md) の shipment）の `last_label_error` にキャリア応答が記録されます。これを見て住所を修正（`/shipment/address`）→ 再発行（`/shipment/retry-label`）します。宛先住所は `sot.lm_routing_orders` の `ship_to_*` 列を**単一ソース**として保持・更新するため、修正は次回のラベル生成に即反映されます（[Routing](/lmile-docs/routing-api/orders.md) 受注時の住所がベース）。

## エンドポイント

| Method | Path                                | 用途                      |
| ------ | ----------------------------------- | ----------------------- |
| `POST` | `/api/routing/shipment/address`     | 宛先住所の手修正（部分上書き + 検証）    |
| `POST` | `/api/routing/shipment/retry-label` | 失敗した出荷元のラベル再発行（二重発行ガード） |

***

## `POST` /api/routing/shipment/address

注文の宛先住所を修正します。渡した項目だけを上書きし（部分更新）、マージ後の住所を検証します。国内（JP）宛は `name` / `postalCode` / `prefecture` / `city` / `address1` / `phone` を必須とします。

### リクエスト

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

```json
{
  "shop": "demo-store.myshopify.com",
  "sourceSystem": "shopify",
  "externalOrderRef": "1001",
  "shipTo": {
    "postalCode": "157-0068",
    "address1": "宇奈根1-2-3",
    "phone": "03-1234-5678"
  },
  "correctedBy": "ops@demo-store.example"
}
```

### ボディパラメータ

| パラメータ                                                                         | 型             |  必須 | 説明                                                                                            |
| ----------------------------------------------------------------------------- | ------------- | :-: | --------------------------------------------------------------------------------------------- |
| `shop`                                                                        | string        |  ✓  | 店舗ドメイン（API キーで呼ぶ場合は省略できます）                                                                    |
| `routingOrderId`                                                              | string (uuid) |  △  | 対象注文。無ければ `sourceSystem` + `externalOrderRef` で特定                                             |
| `sourceSystem` / `externalOrderRef`                                           | string        |  △  | `routingOrderId` 未指定時に必須                                                                      |
| `shipTo`                                                                      | object        |  ✓  | 修正する住所項目。**渡したキーのみ上書き**（`null` で明示クリア、未指定は据え置き）                                               |
| `shipTo.name`                                                                 | string        |     | 受取人氏名                                                                                         |
| `shipTo.company` / `shipTo.email` / `shipTo.address2`                         | string        |     | 会社名 / メール / 建物・部屋等                                                                            |
| `shipTo.phone`                                                                | string        |     | 電話番号                                                                                          |
| `shipTo.country`                                                              | string        |     | 国（`JP` / 空 / `日本` は国内扱い）                                                                      |
| `shipTo.postalCode` / `shipTo.prefecture` / `shipTo.city` / `shipTo.address1` | string        |     | 郵便番号 / 都道府県 / 市区町村 / 町名番地                                                                     |
| `correctedBy`                                                                 | string        |     | 修正実施者の識別子（監査用）。operator JWT 経由はスタッフの検証済 ID が記録され、この値より優先されます                                  |
| `validateOnly`                                                                | boolean       |     | `true` のとき **dry-run**: マージ + 検証だけ実行し、保存・変更履歴・webhook は行いません（UI の保存前バリデーション用。default `false`） |
| `autoRetryLabel`                                                              | boolean       |     | `true` のとき、住所更新成功後に対象出荷元のラベルを自動再発行し、成功分は物理印刷まで自動実行します（下記「挙動・冪等性」。default `false`）             |

### レスポンス

```json
{
  "ok": true,
  "routingOrderId": "11111111-1111-1111-1111-111111111111",
  "shipTo": {
    "name": "山田太郎", "company": null, "phone": "03-1234-5678", "email": "taro@example.com",
    "country": "JP", "postalCode": "157-0068", "prefecture": "東京都", "city": "世田谷区",
    "address1": "宇奈根1-2-3", "address2": null
  },
  "correctedAt": "2026-06-03T09:00:00.000Z",
  "changedFields": ["postalCode", "address1", "phone"],
  "labelErrorShipments": [
    { "fromLocationId": "a1b2c3d4-...", "status": "secured", "trackingNumber": null,
      "lastLabelError": "HTTP 422 invalid to_address.zip" }
  ]
}
```

| フィールド                 | 説明                                                                                        |
| --------------------- | ----------------------------------------------------------------------------------------- |
| `shipTo`              | マージ後の宛先住所（保存値）                                                                            |
| `correctedAt`         | 修正時刻（`sot.lm_routing_orders.ship_to_corrected_at`）                                        |
| `changedFields`       | 今回の修正で**実際に値が変わった**項目名の配列（`name` / `phone` / `postalCode` / `address1` 等）。同値の再 POST では空配列 |
| `labelErrorShipments` | 現在ラベルエラー中の出荷元（= 次に `retry-label` する対象）。`lastLabelError` にキャリア応答                           |
| `retryResults`        | `autoRetryLabel: true` のときのみ。自動再発行の結果（下記 `retry-label` の `shipments[]` と同形）               |

`validateOnly: true` のレスポンスは `correctedAt` / `changedFields` の代わりに `"validateOnly": true` を含みます（`shipTo` はマージ後の検証済み住所・保存はされません。`labelErrorShipments` は同様に返します）。検証に失敗した場合は通常時と同じ `422 validation_failed` + `missing[]` です。

### 挙動・冪等性

* **解決キー**: 注文 1 件（`routingOrderId` または `shop + sourceSystem + externalOrderRef`）。住所は `sot.lm_routing_orders.ship_to_*` を更新し、`ship_to_corrected_at` / `ship_to_corrected_by` を記録します。
* **部分更新**: `shipTo` に渡したキーだけ既存値へ上書き（`null` でクリア、未指定は据え置き）。同じ住所での再 POST は同値で収束（冪等）。
* **検証**: 国内（`country` が `JP` / 空 / `日本`）はマージ後に必須 6 項目を検証し、欠落は `422 validation_failed` で `missing[]` を返します（silent に保存しません）。
* **変更履歴（append-only）**: 住所の更新と「**変更前→変更後**の履歴記録」は**同一トランザクション**で行います。変更前の値は更新直前の DB 値を行ロック下に読むため取りこぼしません。**実際に値が変わったときだけ** `sot.lm_routing_order_ship_to_changes` に 1 行 append し（同値の再 POST では追加しません）、変更前（before）/ 変更後（after）の宛先住所スナップショット・変更項目・実施者（operator の `staff_user_id` または `correctedBy`）・時刻を残します（PII のため `service_role` 専用）。
* **dry-run**: `validateOnly: true` はマージ + 検証だけ実行し、DB 更新・変更履歴・webhook を一切行いません（読み取りのみ。何度呼んでも状態は変わりません）。
* **自動再発行（opt-in）**: `autoRetryLabel: true` は住所更新成功後に、(1) ラベルエラー中の出荷元と、(2) ラベル面に影響する項目（`name` / `company` / `phone` / `country` / `postalCode` / `prefecture` / `city` / `address1` / `address2`。`email` は対象外）が**実際に変わった**場合の発行済み出荷元（`secured` / `oversell_secured`・ラベル未破棄）をまとめて再発行します。(2) は**新しい追跡番号**になります（旧ラベルは現場で破棄してください）。同値の再 POST（`changedFields` 空）では (2) は実行されません（冪等）。再発行成功時は下記 `retry-label` と同様に物理印刷まで自動実行され、結果はレスポンスの `retryResults` で返します（silent skip しません）。
* 住所は次回のラベル生成（[Fulfillment](/lmile-docs/fulfillment-api/fulfillment.md) の確保済遷移、または下記 `retry-label`）で使用されます。本 API 自体はラベルを発行しません（`autoRetryLabel: true` の再発行は `retry-label` と同じコア処理）。
* **解消の確認（UI 向け）**: 住所不備が解消したかの正は `retry-label` の結果です。`labelStatus: "generated"` で `last_label_error` がクリアされ、[Operator Queues](/lmile-docs/operator-api/queues.md) の `labelErrorOnly=true` フィルタ（住所不備キュー）から外れます。`failed` なら `error` に最新のキャリア応答が入り、再修正の対象として残ります。

***

## `POST` /api/routing/shipment/retry-label

ラベル発行に失敗した出荷元のラベルを再発行します。宛先住所は `sot.lm_routing_orders`（修正済み）を読むため、`/shipment/address` の修正が反映されます。

### リクエスト

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

```json
{
  "shop": "demo-store.myshopify.com",
  "sourceSystem": "shopify",
  "externalOrderRef": "1001"
}
```

### ボディパラメータ

| パラメータ                               | 型                |  必須 | 説明                                                    |
| ----------------------------------- | ---------------- | :-: | ----------------------------------------------------- |
| `shop`                              | string           |  ✓  | 店舗ドメイン（API キーで呼ぶ場合は省略できます）                            |
| `routingOrderId`                    | string (uuid)    |  △  | 対象注文。無ければ `sourceSystem` + `externalOrderRef` で特定     |
| `sourceSystem` / `externalOrderRef` | string           |  △  | `routingOrderId` 未指定時に必須                              |
| `fromLocationId`                    | string\[] (uuid) |     | 対象出荷元を限定。**省略時は `last_label_error` がある出荷元すべて**        |
| `force`                             | boolean          |     | 既にラベル（`tracking_number`）がある出荷元も強制再発行（default `false`） |

### レスポンス

```json
{
  "ok": true,
  "routingOrderId": "11111111-1111-1111-1111-111111111111",
  "retried": 1,
  "shipments": [
    { "fromLocationId": "a1b2c3d4-...", "action": "retried", "labelStatus": "generated",
      "trackingNumber": "SGW-1234-5678", "labelPdfPath": "demo-store.myshopify.com/1111.../a1b2c3d4.pdf" }
  ]
}
```

| フィールド                                         | 説明                                                                                                                           |
| --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `retried`                                     | 再発行を試行した出荷元数（`action: "retried"` の件数）                                                                                        |
| `shipments[].action`                          | `retried`（発行を試行）/ `skipped`（対象外）                                                                                             |
| `shipments[].reason`                          | skip 理由: `already_issued` / `not_secured` / `shipment_lines_not_found` / `fulfillment_source_unresolved` / `no_label_config` |
| `shipments[].labelStatus`                     | ラベル生成結果 `generated` / `failed` / `skipped` / `none`                                                                          |
| `shipments[].trackingNumber` / `labelPdfPath` | 成功時の追跡番号 / Storage パス（`shipping-labels` バケット）                                                                                |
| `shipments[].error`                           | `labelStatus=failed` 時のキャリア応答（`last_label_error` に保存）                                                                        |

### 挙動・冪等性

* **対象**: `fromLocationId` 指定 > 省略時は `last_label_error` がある出荷元。確保済（`secured` / `oversell_secured` / `in_transit_to_mfc` / `packing` / `shipped`）の出荷元のみ発行（未確保は `not_secured` で skip）。
* **二重発行ガード**: 既に `tracking_number` がある出荷元は `already_issued` で skip。`force: true` のときのみ再発行します。
* **成功**: `tracking_number` / `carrier_code` / `label_pdf_path` / `label_pdf_generated_at` を更新し `last_label_error` をクリア。**失敗**: `last_label_error` を更新（[Fulfillment](/lmile-docs/fulfillment-api/fulfillment.md) の確保時生成と同じ書き込み）。
* **自動印刷**: 再発行成功時に印刷イベント（`fulfillment_shipment_status_changed`）を印刷アプリへ自動配信します（[Print Label](/lmile-docs/fulfillment-api/print-label.md) の再配信と同形・fire-and-forget）。物理印刷と結果書き戻し（`label_printed_at` / `last_print_error`）は印刷アプリが非同期に行うため、本 API のレスポンスに印刷結果は含まれません。
* **印刷状態のリセット**: 再発行成功時に `label_printed_at` / `last_print_error` をクリアします（新ラベルは未印刷のため）。印刷済みだった出荷元の `force` 再発行でも、自動印刷が二重印刷ガード（`alreadyPrinted`）に塞がれません。
* **ラベル設定未設定**の出荷元（`action=label` の [Dispatch Config](/lmile-docs/configuration/dispatch-config.md) が無い）は `no_label_config` で skip（silent skip しません）。

## エラー

エンドポイント固有:

| Status | 条件                             | レスポンス例                                                                                                |
| :----: | ------------------------------ | ----------------------------------------------------------------------------------------------------- |
|  `400` | 注文の特定情報なし / バリデーション失敗          | `{ "ok": false, "error_kind": "invalid_input", "details": [{ "path": "shipTo", "message": "..." }] }` |
|  `404` | 注文が存在しない                       | `{ "ok": false, "error_kind": "order_not_found", "error": "routing order not found" }`                |
|  `422` | （`/shipment/address`）国内必須住所の欠落 | `{ "ok": false, "error_kind": "validation_failed", "missing": ["postalCode", "phone"] }`              |

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

## データモデル

* `sot.lm_routing_orders` — 宛先住所の単一ソース。`ship_to_name` / `ship_to_company` / `ship_to_phone` / `ship_to_email` / `ship_to_postal` / `ship_to_prefecture` / `ship_to_city` / `ship_to_address1` / `ship_to_address2`（PII・公開 view から除外）+ 監査列 `ship_to_corrected_at` / `ship_to_corrected_by`。
* `sot.lm_routing_order_ship_to_changes` — **宛先住所・氏名の変更履歴（append-only）**。住所修正のたびに変更前（`before`）/ 変更後（`after`）の宛先住所スナップショット・変更項目（`changed_fields`）・実施者（`changed_by_staff_user_id` / `changed_by`）・時刻を 1 行記録（PII・`service_role` 専用。公開 view / `agent_read` 非公開）。
* `sot.lm_fulfillment_shipments` — 出荷元ごとの `tracking_number` / `label_pdf_path` / `last_label_error`。再発行はこの行を更新（[Fulfillment](/lmile-docs/fulfillment-api/fulfillment.md)）。
* ラベル PDF: Supabase Storage `shipping-labels`（private）の `{shop}/{routingOrderId}/{fromLocationId}.pdf`。


---

# 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/fulfillment-api/address-recovery.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.
