# v1 API からの移行ガイド

従来の BASE API（`https://api.thebase.com` の `/1/` 系エンドポイント。以下 v1）から、新しい BASE API（`https://apiv2.thebase.com` の `/api/` 系。以下 v2）への移行に必要な変更点をまとめます。v2 は v1 の後継として設計し直した API で、URL の書き換えだけでは移行できません。認証、エラー、データ表現といった共通の変更に加えて、リソースごとにエンドポイントとフィールドの対応が変わります。このガイドが扱うのは v1 にある機能の差分だけで、v2 で新たに追加された機能は扱いません。v2 の機能一覧は[API リファレンス](/docs/reference.md)を参照してください。

## 目次

- [v2 で未対応の機能](#v2-で未対応の機能)
- [共通の変更点](#共通の変更点)
- [エンドポイント対応表](#エンドポイント対応表)
- [商品の移行](#商品の移行)
- [注文の移行](#注文の移行)
- [カテゴリの移行](#カテゴリの移行)
- [ショップ情報の移行](#ショップ情報の移行)
- [配送会社の移行](#配送会社の移行)

## v2 で未対応の機能

次の機能は v2 のβ版には含まれません。これらの機能は引き続き v1 で利用できます。

- OAuth 2.0 の認可フロー（`/1/oauth/authorize` / `/1/oauth/token`）
- 商品検索 `GET /1/items/search`
- 引き出し申請情報 `GET /1/savings`

OAuth 2.0 による認証と引き出し申請関連の API は、正式リリースに合わせて提供する予定です。
商品検索 API に相当する API の提供予定はありません。

## 共通の変更点

### URL とリクエスト形式

| 項目 | v1 | v2 |
|---|---|---|
| ホスト名 | `https://api.thebase.com` | `https://apiv2.thebase.com` |
| パス | `/1/...` | `/api/...` |
| リクエストボディ | `application/x-www-form-urlencoded` | `application/json` |

URL は `https://apiv2.thebase.com/api/items` のように、パスの prefix を /api とします。`api.thebase.com` は v1 のまま残り、v2 のエンドポイントは提供しません。各エンドポイントのパスとメソッドは [API リファレンス](/docs/reference.md)を参照してください。

### 認証とトークン

v1 は OAuth 2.0（authorization_code / refresh_token グラント）のアクセストークンを使い、トークンの有効期限は 1 時間でした。v2 の初期リリースでは OAuth フローを提供せず、**Personal Access Token** のみでアクセスします。Personal Access Token は [BASE Developers](https://developers.thebase.in/) の「API」タブから発行します。リクエストヘッダーは v1 と同じ `Authorization: Bearer <token>` です。トークンの取得と更新のためのコード（認可リダイレクト、`/1/oauth/token` の呼び出し、リフレッシュ処理）は不要になります。

```sh
curl -H "Authorization: Bearer <your-token>" \
  https://apiv2.thebase.com/api/user
```

### スコープ

スコープは Personal Access Token の発行時に選びます。v2 のスコープは v1 より細かく、リソースの読み取りと書き込みの 2 択ではなく、作成、更新、削除、公開、発送、キャンセルといった操作ごとに分かれています。v1 の `write_items` や `write_orders` で一括して許可していた操作のうち、削除や公開、発送やキャンセルのような影響の大きい操作は、それぞれ別のスコープとして選ぶ必要があります。v1 のスコープに対応する v2 のスコープは次のとおりです。一覧は[スコープ](/docs/scopes.md)を参照してください。

| v1 スコープ | v2 スコープ | 補足 |
|---|---|---|
| `read_users` | `user.read` | |
| `read_users_mail` | `user.read` | `GET /api/user` は `mail_address` を常に返します |
| `read_items` | `items.read` | |
| `write_items` | `items.create`<br>`items.update`<br>`items.delete`<br>`items.publish`<br>`item_categories.write` | 商品画像の追加には `files.write` も必要です |
| `read_orders` | `orders.read` | |
| `write_orders` | `orders.cancel`<br>`orders.dispatch` | |
| — | `files.write` | v2 追加。ファイルのアップロード先の発行 |

v1 でスコープ不要だった `GET /1/users/me` と `GET /1/delivery_companies` に相当する v2 エンドポイントは、それぞれ `user.read` と `orders.read` を要求します。トークン発行時に必要なスコープを含めてください。

### エラーレスポンス

v2 のエラーは、事象に応じたステータスコード（認証エラーは 401、スコープ不足は 403、対象なしは 404 など）と、RFC 9457 (Problem Details) に沿った `application/problem+json` のボディで返します。v1 から形式が変わっていることに注意してください。存在しない商品の詳細を取得したときの応答を比べると、次のようになります。

v1

```http
HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "error": "no_item",
  "error_description": "商品が見つかりませんでした。"
}
```

v2

```http
HTTP/1.1 404 Not Found
Content-Type: application/problem+json

{
  "type": "/errors/items/not-found",
  "title": "Item not found",
  "status": 404
}
```

`type` はエラー種別ごとに固定の識別子、`title` はその英語の短い要約で、事象によっては開発者向けの説明 `detail` や、バリデーションエラーのフィールド単位の詳細 `errors` が加わります。v1 の `error` 値で分岐しているコードは、ステータスコードと `type` による分岐に書き換えてください。構造とエラー種別の一覧は[エラーレスポンス](/docs/errors.md)を参照してください。

### データ表現

| 項目 | v1 | v2 |
|---|---|---|
| 日時 | UNIX 秒（例: `1779244328`） | RFC 3339 の秒精度 UTC。`Z` 終端（例: `"2026-05-20T02:32:08Z"`）。フィールド名は `_at` で終わります |
| 日付 | — | `YYYY-MM-DD`。フィールド名は `_date` で終わります |
| 真偽値 | `0` / `1`（例: `visible`） | `true` / `false` |
| 区分値 | 数値コード（例: `item_tax_type: 1`） | snake_case の文字列 enum（例: `"standard"`） |
| ID | 数値と文字列が混在するパスがあります | JSON number に統一 |
| 金額 | 税込の整数（円） | 同じ（変更なし） |

日時は形式だけでなく基準も変わります。v2 の値はタイムゾーンが `Z`（UTC）で明示されるため、日本時間で表示する場合は利用側で変換してください。

## エンドポイント対応表

v1 の各エンドポイントと v2 の対応です。各エンドポイントの仕様は [API リファレンス](/docs/reference.md)を参照してください。

| v1 | v2 |
|---|---|
| `GET /1/items` | [`GET /api/items`](/docs/endpoints/get-api-items.md) |
| `GET /1/items/detail/:item_id` | [`GET /api/items/{id}`](/docs/endpoints/get-api-items-id.md) |
| `POST /1/items/add` | [`POST /api/items`](/docs/endpoints/post-api-items.md) |
| `POST /1/items/edit` | [`POST /api/items/{id}`](/docs/endpoints/post-api-items-id.md)<br>[`PUT /api/items/{id}/visibility`](/docs/endpoints/put-api-items-id-visibility.md)<br>[`PUT /api/items/{id}/list_order`](/docs/endpoints/put-api-items-id-list-order.md)<br>[`POST /api/items/{id}/variations`](/docs/endpoints/post-api-items-id-variations.md)<br>[`POST /api/items/{id}/variations/{variation_id}`](/docs/endpoints/post-api-items-id-variations-variation-id.md) |
| `POST /1/items/edit_stock` | [`PUT /api/items/{id}/stock`](/docs/endpoints/put-api-items-id-stock.md) |
| `POST /1/items/delete` | [`DELETE /api/items/{id}`](/docs/endpoints/delete-api-items-id.md) |
| `POST /1/items/delete_variation` | [`DELETE /api/items/{id}/variations/{variation_id}`](/docs/endpoints/delete-api-items-id-variations-variation-id.md) |
| `POST /1/items/add_image` | [`POST /api/files`](/docs/endpoints/post-api-files.md)<br>[`POST /api/items/{id}/images`](/docs/endpoints/post-api-items-id-images.md) |
| `POST /1/items/delete_image` | [`DELETE /api/items/{id}/images/{image_id}`](/docs/endpoints/delete-api-items-id-images-image-id.md) |
| `GET /1/orders` | [`POST /api/orders/search`](/docs/endpoints/post-api-orders-search.md) |
| `GET /1/orders/detail/:unique_key` | [`GET /api/orders/{unique_key}`](/docs/endpoints/get-api-orders-unique-key.md) |
| `POST /1/orders/edit_status` | [`POST /api/orders/{unique_key}/dispatch`](/docs/endpoints/post-api-orders-unique-key-dispatch.md)<br>[`POST /api/orders/{unique_key}/cancel`](/docs/endpoints/post-api-orders-unique-key-cancel.md) |
| `GET /1/categories` | [`GET /api/item_categories`](/docs/endpoints/get-api-item-categories.md) |
| `POST /1/categories/add` | [`POST /api/item_categories`](/docs/endpoints/post-api-item-categories.md) |
| `POST /1/categories/edit` | [`POST /api/item_categories/{id}`](/docs/endpoints/post-api-item-categories-id.md)<br>[`PUT /api/item_categories/{id}/list_order`](/docs/endpoints/put-api-item-categories-id-list-order.md) |
| `POST /1/categories/delete` | [`DELETE /api/item_categories/{id}`](/docs/endpoints/delete-api-item-categories-id.md) |
| `GET /1/item_categories/detail/:item_id` | [`GET /api/items/{id}`](/docs/endpoints/get-api-items-id.md) |
| `POST /1/item_categories/add` | [`PUT /api/item_categories/{id}/items/{item_id}`](/docs/endpoints/put-api-item-categories-id-items-item-id.md) |
| `POST /1/item_categories/delete` | [`DELETE /api/item_categories/{id}/items/{item_id}`](/docs/endpoints/delete-api-item-categories-id-items-item-id.md) |
| `GET /1/users/me` | [`GET /api/user`](/docs/endpoints/get-api-user.md) |
| `GET /1/delivery_companies` | [`GET /api/delivery_companies`](/docs/endpoints/get-api-delivery-companies.md) |

## 商品の移行

### レスポンス構造の変更

v1 の `item` はフラットなオブジェクトに画像キー（`img1_origin` など）を並べた構造でした。v2 の商品詳細は、商品種別（`type`）ごとに構造が分かれ、画像、種類、カテゴリ、オプションを同じレスポンスの配列として含みます。また、v1 では別エンドポイント（`GET /1/item_categories/detail/:item_id`）だった所属カテゴリも含まれます。

商品種別は `type` フィールド（`normal` / `digital` / `takeout` / `subscription` / `lottery`）で判別します。種別固有の設定（デジタルコンテンツ、定期便、抽選販売）は該当種別のときだけ、同名のフィールドに入ります。`digital`（デジタルコンテンツ）は読み取りのみで、作成と編集の `type` には指定できません。v2 では販売状態を表す `sales_status`（販売開始前、販売中、販売終了後）が追加されています。

### フィールド対応表

| v1（`item`） | v2 | 補足 |
|---|---|---|
| `item_id` | `id` | |
| `title` | `name` | |
| `detail` | `detail` | |
| `price` | `price` | v2 では未設定時に `null` |
| `proper_price` | `proper_price` | セール未設定時は `null` |
| `item_tax_type`（`1` / `2`） | `item_tax_type`（`"standard"` / `"reduced"`） | 文字列 enum 化 |
| `stock` | `stock` | 種類がある商品では合算値 |
| `visible`（`0` / `1`） | `visible`（`true` / `false`） | 真偽値化。作成と編集では指定できません（後述） |
| `list_order` | 対応なし | 並び順の数値は返しません。`GET /api/items` は既定で表示順に並び、移動は `PUT /api/items/{id}/list_order` で行います |
| `identifier` | `identifier` | |
| `modified`（UNIX 秒） | `modified_at`（RFC 3339） | |
| `img{N}_{size}`（最大 160 キー） | `images[]` | 後述 |
| `variations[].variation_id` | `variations[].id` | |
| `variations[].variation` | `variations[].name` | 種類名を持たない行では省略 |
| `variations[].variation_stock` | `variations[].stock` | |
| `variations[].variation_identifier` | `variations[].identifier` | |
| `variations[].barcode` | `variations[].barcode` | |
| `options[].option_id` | `options[].id` | |
| `options[].option_name` | `options[].name` | |
| `options[].option_type` | `options[].option_type`（`"select"` / `"form"`） | `option_type` の値で構造が分かれます |
| `options[].required` | `options[].required`（`true` / `false`） | 真偽値化 |
| `options[].select.option_variations[]` | `options[].choices[]` | 要素は `{ id, name, extra_price }` |
| `options[].form.free_text_max_length` | `options[].max_length` | |
| `options[].form.free_text_description` | `options[].free_text_description` | |

### 画像の表現

v1 の商品詳細は、20 枠 × 8 サイズの画像キー（`img1_origin`, `img1_76`, … `img20_sp_640`）を未設定分の `null` も含めて常に返していました。v2 は登録済みの画像だけを `images` 配列で返します。

```jsonc
// v1
{
  "img1_origin": "https://.../foo.jpg",
  "img1_76": "https://.../foo_76.jpg",
  "img2_origin": null,
  ...
}

// v2
{
  "images": [
    {
      "id": 123,
      "url": "https://.../foo.jpg",
      "scaled_url": "https://.../foo.jpg?width=640",
      "square_url": "https://.../foo.jpg?width=900&height=900"
    }
  ]
}
```

固定サイズのキーを選ぶ代わりに、`scaled_url` / `square_url` の URL に含まれる寸法の値を書き換えて必要なサイズを取得します。v1 の 8 サイズに対応する固定キーはありません。

### 商品の作成と公開

v1 の `POST /1/items/add` は `visible` を受け付け、省略時は公開（`1`）で作成しました。v2 の `POST /api/items` は `visible` を受け付けず、作成した商品は常に非公開（`visible: false`）です。公開するには、作成後に `PUT /api/items/{id}/visibility` で `visible` を `true` にしてください。このエンドポイントには `items.publish` スコープが必要です。作成と同時に公開していた実装は、作成と公開の 2 回の呼び出しに書き換えてください。編集 `POST /api/items/{id}` でも `visible` は受け付けないため、公開状態の変更は常に `PUT /api/items/{id}/visibility` で行います。

### 画像の追加

v1 の `POST /1/items/add_image` は画像の URL（`image_url`）を受け取る API でした。v2 は画像の URL を受け取らず、利用者がファイル本体を送信し、その参照を商品に紐付ける 3 段階になります。

1. `POST /api/files` に用途 `item_image` と `content_type` を送り、送信先（`upload_url` と `upload_fields`）とファイルの参照（`file_id`）を取得
2. `upload_url` へ、`upload_fields` のすべての項目とファイル本体を `multipart/form-data` で送信
3. `POST /api/items/{id}/images` に `{ "file_id": "..." }` を送り、商品の末尾に画像を追加

1 段階目の `POST /api/files` には `files.write` スコープが必要です。2 段階目の送信先は BASE API ではなくファイルの保管先です。URL を渡すだけで画像を登録していた実装は、画像を自分で取得して送信する形に書き換えてください。`image_no` による位置の指定はなくなり、追加した画像は常に末尾に並びます。位置を変えるには `PUT /api/items/{id}/images` に `image_ids` を表示順で送ります。1 商品あたりの上限は v1 と同じ 20 枚です。

## 注文の移行

### 一覧から検索へ

v1 の一覧 `GET /1/orders` は、v2 では `POST /api/orders/search` になります。期間、キーワード、状態などの条件を JSON ボディで渡し、レスポンスは注文の概要（`orders`）とページ情報（`pagination`）を返します。概要に含まれない明細や金額内訳は、`unique_key` で詳細 `GET /api/orders/{unique_key}` を取得してください。

### 詳細レスポンスの構造変更

v1 の注文詳細はトップレベルに多数のキーを平らに並べていましたが、v2 は関連する情報をオブジェクトに集約されています。v2 の注文詳細の骨格と、各オブジェクトに入る v1 のキーは次のとおりです。

```jsonc
{
  "unique_key": "...",
  "status": "...",
  "ordered_at": "...",

  // 購入者。v1 の first_name / last_name / mail_address と住所の各キー
  "customer": { "name": { ... }, "mail_address": "...", "address": { ... } },

  // 配送先。v1 の order_receiver
  "shipping": { "name": { ... }, "address": { ... } },

  // 金額の内訳。v1 の shipping_fee / cod_fee / total / order_discount / order_header_coin など
  "amounts": { "subtotal": 0, "shipping_fee": 0, "cod_fee": 0, "total": 0, "coupon_discount": { ... }, ... },

  // 商品明細。v1 の order_items
  "lines": [ { ... } ],

  // 発送。v1 の delivery_company_id / tracking_number / dispatched など
  // dispatches は v1 で別エンドポイントだった部分発送履歴
  "delivery": { "delivery_company_id": 0, "tracking_number": "...", "dispatched_at": "...", "dispatches": [ ... ] },

  // 決済。v1 で決済手段ごとに分かれていたキー
  "payment": { "method": "...", "transactions": [ ... ] },

  // ショップ用メモ。v1 では別エンドポイント
  "shop_memo": { "text": "..." },
  ...
}
```

`lines` の 1 要素は、ある商品（種類）を N 個購入した 1 明細です。`amounts.total` は v1 の `total` と同じく割引相殺後の最終額です。

### フィールド対応表

| v1（注文詳細） | v2 | 補足 |
|---|---|---|
| `unique_key` | `unique_key` | |
| `ordered`（UNIX 秒） | `ordered_at`（RFC 3339） | |
| `cancelled`（UNIX 秒） | `cancelled_at` | 未キャンセルは `null` |
| `modified`（UNIX 秒） | `modified_at` | |
| `dispatch_status` | `status` | 単一の状態フィールドのまま。値は文字列 |
| `remark` / `add_comment` | `remark` / `add_comment` | |
| `first_name` / `last_name` | `customer.name.first` / `.last` | v2 では `first_kana` / `last_kana` も返します |
| `mail_address` | `customer.mail_address` | 未登録は空文字（`null` にはなりません） |
| `country` / `country_code` / `zip_code` / `prefecture` / `address` / `address2` / `tel` | `customer.address.*` | |
| `order_receiver.*` | `shipping.name.*` / `shipping.address.*` | 常に返します（後述） |
| `shipping_fee` / `cod_fee` / `total` | `amounts.shipping_fee` / `.cod_fee` / `.total` | |
| — | `amounts.subtotal` | v2 追加。明細合計の明示 |
| `order_discount.discount`<br>`order_discount.note`<br>`order_discount.is_allocate_user_balance_log` | `amounts.coupon_discount.amount`<br>`amounts.coupon_discount.note`<br>`amounts.coupon_discount.allocates_balance_log` | `allocates_balance_log` は真偽値 |
| `order_header_coin.discount`<br>`order_header_coin.note` | `amounts.coin_discount.amount`<br>`amounts.coin_discount.note` | |
| `order_amount_adjustment.adjusted_amount` | `amounts.adjustment.amount` | |
| `order_charge` + `fee_type` | `amounts.service_charge` | |
| `additional_charges[]` | `amounts.additional_charges[]` | |
| `shipping_method` | `delivery.shipping_method` | サイズ別配送時は `null` |
| `shipping_lines[]` | `shipping_lines[]` | `order_item_ids` は `order_line_ids` に改名 |
| `dispatched`（UNIX 秒） | `delivery.dispatched_at` | 未発送は `null` |
| `delivery_company_id` / `tracking_number` / `delivery_date` / `delivery_time_zone` | `delivery.*` | |
| 別エンドポイント（`dispatched_logs`） | `delivery.dispatches[]` | 詳細に統合 |
| 決済手段ごとの個別キー（`c_c_*` ほか） | `payment.method` + `payment.transactions[]` | 配列に集約 |
| `order_items[]` | `lines[]` | 明細内の `options[]` / `item_total` / `option_total` / `status` なども維持 |
| 明細の `item_tax_type`（`1` / `2`） | `lines[].tax_mode`（`"standard"` / `"reduced"`） | 後述 |
| 明細の `consumption_tax_rate` | `lines[].consumption_tax_rate` | v2 では税額 `consumption_total_tax` も返します |
| `user_balance_logs[]` | `balance_logs[]` | `price` → `amount`、`created` → `created_at` |
| `subscription` | `subscription` | 定期便以外の注文は `null` |
| `membership_rewards[]` | `membership_rewards[]` | |
| `referring_site_domain` / `referring_site_param` | `referrer.site_domain` / `.site_param` | 流入元がない注文は `referrer` 自体が `null` |
| `order_group_*` 系 | `order_group` | 通常注文は `null` |
| 別エンドポイント（ショップ用メモ） | `shop_memo.text` | 詳細に統合 |
| `terminated` | 対応なし | 廃止（`status` で判定） |

### 移行時の注意

v1 では `order_receiver` が無いとき「配送先 = 購入者」とみなすフォールバックが必要でしたが、v2 は `shipping` を常に返すので不要です。あわせて、`order_receiver` の有無で「お届け先が購入者と同じか」を判定する方法も成り立たなくなります。`customer.address` と `shipping.address` を比較して判定してください。

税区分は名前と値の両方が変わります。v1 の `item_tax_type: 1` は「税率 1%」ではなく「標準税率対象」の意味でした。v2 では名前が `tax_mode`、値が `"standard"` / `"reduced"` になります。実際の税率は `consumption_tax_rate` を参照してください。

`status` の値体系は v1 と同じです。v1 の `dispatch_status` の値を文字列のまま引き継ぎます。v1 で廃止済みの値（`shipping` / `arrived` など）は v2 でも返りません。

`customer.mail_address` の未登録は v1 と同じく空文字です。`null` チェックではなく空文字チェックを使ってください。

## カテゴリの移行

v1 ではカテゴリ本体が `/1/categories`、商品との紐付けが `/1/item_categories` と別リソースでした。v2 はカテゴリ本体を `/api/item_categories` に置き、商品との紐付けはその配下（`/api/item_categories/{id}/items/{item_id}`）で操作します。v1 の `POST /1/categories/edit` は `name` と `list_order` を同時に受け付けましたが、v2 では表示名を `POST /api/item_categories/{id}`、並び順を `PUT /api/item_categories/{id}/list_order` で個別に変更します。並び順は数値ではなく、同じ親を持つカテゴリの先頭、末尾、または指定したカテゴリの直前、直後として指定します。

| v1（`categories[]`） | v2 | 補足 |
|---|---|---|
| `category_id` | `id` | |
| `name` | `name` | |
| `list_order` | 対応なし | 並び順の数値は返しません。`GET /api/item_categories` の配列の順序が表示順です |
| `number` | `number` | |
| `parent_number` | `parent` | 番号ではなく、上位カテゴリのオブジェクトを入れ子で返します。最上位は `null` |
| `code` | 対応なし | |

商品に紐付いたカテゴリの取得（v1 の `GET /1/item_categories/detail/:item_id`）は、商品詳細 `GET /api/items/{id}` の `categories` フィールドに統合しました。紐付け ID（`item_category_id`）の取り回しは不要になり、カテゴリ ID と商品 ID の組み合わせで紐付けを操作します。

v1 の `/1/categories` 系エンドポイントは、呼び出すとショップのカテゴリ機能が有効になりました。v2 のエンドポイントを呼び出しても有効にはなりません。

## ショップ情報の移行

`GET /1/users/me` は `GET /api/user` になります。スコープ不要だった v1 と異なり、`user.read` スコープが必要です。

| v1（`user`） | v2 | 補足 |
|---|---|---|
| `shop_id` | `shop_id` | |
| `shop_name` | `shop_name` | |
| `shop_introduction` | `shop_introduction` | 未設定は空文字ではなく `null` |
| `shop_url` | `shop_url` | |
| `mail_address`（`read_users_mail` スコープ保持時のみ） | `mail_address` | 常に返します |
| `twitter_id` / `facebook_id` / `ameba_id` / `instagram_id` / `line_id` / `youtube_id` / `note_id` / `tiktok_id` | `external_service_account.twitter` など | 集約。未連携は空文字ではなく `null` |
| `background` / `logo` / `display_background` / `display_logo` / `repeat_background` | 対応なし | |
| — | `nickname` / `shop_category` / `able_to_business` / `using_only_cart` / `default_item_tax_type` | v2 追加 |

## 配送会社の移行

`GET /1/delivery_companies` は `GET /api/delivery_companies` になります。スコープ不要だった v1 と異なり `orders.read` スコープが必要です。レスポンスの `delivery_company_id` は `id` に改名しています。取得した `id` は v1 と同じく発送登録（`POST /api/orders/{unique_key}/dispatch`）で使います。
