<!-- Source: https://docs.infraio.xyz/ja/api-reference/errors -->
<!-- Last updated: 2026-10-04 -->

# エラー

すべての 4xx/5xx レスポンスは同じ JSON エンベロープを持ちます:

```json
{
  "code": 400,
  "message": "invalid_input",
  "details": [
    { "field": "items[0].unit_price", "message": "must be a positive decimal string" }
  ]
}
```

- `code` — **数値の HTTP ステータス** (`400`、`401`、`404`、…)。汎用的な HTTP レイヤの処理には有用ですが、分岐ロジックには `message` のほうを switch してください — `code` だけでは、例えば `invalid_input` と `payment_method_not_supported` (どちらも 400) を区別できません。
- `message` — **lower-snake-case** のセンチネル名 (センチネル名を小文字のスネークケースにしたもの。例: `INVALID_INPUT` は `"invalid_input"` になります)。リリース間で安定しています — こちらで switch してください。
- `details` — バリデーションエラー時に設定されます。`{ field, message }` の配列で、クライアントが入力にエラーをひも付けられるようになっています。それ以外では省略されます。

> **Warning:**
>
> エラーボディにはトレース ID もタイムスタンプも含まれません。問題を報告する際は、レスポンスの `Date` ヘッダー、`X-RateLimit-*` ヘッダー、マーチャント ID、エンドポイント、おおよそのリクエスト時刻をサポートにお送りください。

> **Note:**
>
> **認証失敗は別の形をしています。** 上記のエンベロープは、ほとんどのエラーで API が返すものです。処理される前に拒否されたリクエスト (`/b2b/v1/*` 呼び出しでの欠落 / 不正な `X-Signature`、未知の `X-Client-ID`、期限切れの `X-Timestamp`) は `{ "error": "...", "message": "..." }` の形で返ります。ここで `error` は粗いスラグ (`unauthorized` / `bad_request` / `service_unavailable`)、`message` が詳細を運びます。数値の `code` も `details` もありません。まず HTTP ステータスで分岐し、次に `message` を読んでください。例 (401):
>
> ```json
> { "error": "unauthorized", "message": "invalid signature" }
> ```

## HTTP ステータス → 典型的なコード

| HTTP | 典型的な `code` 値 | 意味 |
| --- | --- | --- |
| **400** | `INVALID_INPUT`、`MISSING_REQUIRED`、`INVALID_FORMAT`、`INVALID_LENGTH`、`INVALID_VALUE`、`PAYMENT_METHOD_NOT_SUPPORTED`、`AMOUNT_BELOW_MINIMUM` | 不正なリクエスト — `details` を確認 |
| **401** | `INVALID_CREDENTIALS`、`INVALID_TOKEN`、`TOKEN_EXPIRED`、`INVALID_SIGNATURE` (B2B); `SESSION_EXPIRED` (ダッシュボードのみ) | 認証失敗 — 不正なキー、期限切れタイムスタンプ、署名誤り。ダッシュボードのサインイン関連コードは B2B 統合には関係ありません。 |
| **402** | `INSUFFICIENT_CREDIT` | マーチャントのプリペイド残高が尽きました — リトライ前にチャージしてください |
| **403** | `FORBIDDEN`、`IP_BLOCKED` | キーは有効ですが、この呼び出しに必要なスコープ / IP 許可がありません |
| **404** | `NOT_FOUND`、`RECORD_NOT_FOUND` | リソースが存在しない (またはこのマーチャントには存在しない) |
| **409** | `ALREADY_EXISTS` | 異なるボディでの冪等性リプレイ、またはステートマシンが遷移を拒否 |
| **429** | `TOO_MANY_REQUESTS`、`TOO_MANY_ATTEMPTS` | レート制限到達; バックオフしてリトライ |
| **500** | `INTERNAL_SERVER_ERROR`、`EXTERNAL_SERVICE_ERROR` | 当方の問題; バックオフ付きでリトライ可能。 |
| **503** | `SERVICE_UNAVAILABLE` | 下流の依存が停止中。バックオフ付きでリトライ |

## 全コードリファレンス

目にする可能性のある `code` 値:

### 認証 (401)
- `INVALID_CREDENTIALS` — API キーの組み合わせが拒否された
- `INVALID_TOKEN` — トークンがパース不能または改ざんされている
- `TOKEN_EXPIRED` — トークンが期限切れ
- `SESSION_EXPIRED` — ダッシュボードセッションが期限切れ
- `INVALID_SIGNATURE` — B2B / Webhook 呼び出しの HMAC 署名不一致

### 認可 (403)
- `FORBIDDEN` — 認証は通ったが、このアクションを実行する権限がない
- `IP_BLOCKED` — この IP アドレスからのリクエストはブロックされている

### 未検出 (404)
- `NOT_FOUND` — 汎用
- `RECORD_NOT_FOUND` — 指定された ID の行が存在しない

### 競合 (409)
- `ALREADY_EXISTS` — 汎用

### バリデーション (400)
- `INVALID_INPUT` — 汎用; `details` を確認
- `MISSING_REQUIRED` — 必須フィールドが欠落
- `INVALID_FORMAT` — 値が期待形式と一致しない (例: UUID、URL、メール)
- `INVALID_LENGTH` — 値が短すぎる、または長すぎる
- `INVALID_VALUE` — 値が許容される enum / 範囲外
- `PAYMENT_METHOD_NOT_SUPPORTED` — プロバイダ / アセットの組み合わせがマーチャントに対して有効化されていない
- `AMOUNT_BELOW_MINIMUM` — 注文額がネットワーク別または環境レベルの下限を下回っている。レスポンスの `details.floor_usd` に設定された下限 (USD) が入っているので、そのまま表示できます; `message` テキストにも同じ内容が記載されています。
- `INSUFFICIENT_BALANCE` — バイヤーのウォレットが送金分の支払いアセットを十分に保有していない。
- `INSUFFICIENT_GAS` — バイヤーのウォレットが送金をブロードキャストするためのネイティブトークン（ネットワーク手数料用）を保有していない。

### 決済 / 課金 (402)
- `INSUFFICIENT_CREDIT` — マーチャントのプリペイド残高がネットワーク手数料（ガス）とプラットフォーム手数料を賄えない。ダッシュボードからチャージしてリトライしてください

### レート制限 (429)
- `TOO_MANY_REQUESTS` — IP のレート制限を超過
- `TOO_MANY_ATTEMPTS` — 同じリソースに対する繰り返し失敗 (例: OTP) がスロットルを発動

### サーバーエラー (500)
- `EXTERNAL_SERVICE_ERROR` — サードパーティプロバイダが失敗した
- `INTERNAL_SERVER_ERROR` — 予期しないエラー; レスポンスの `Date` ヘッダーと `X-RateLimit-*` ヘッダーを添えてサポートにご連絡ください

### 可用性 (503)
- `SERVICE_UNAVAILABLE` — サービスが一時的に利用できません; バックオフ付きでリトライ

## バリデーションエラー (400)

リクエストボディの形式不正が原因の場合、`details` は配列なので、
エラーをフィールドに紐付けてマッピングできます:

```json
{
  "code": 400,
  "message": "invalid_input",
  "details": [
    { "field": "items[0].unit_price", "message": "must be a positive decimal string" },
    { "field": "success_url", "message": "must be a valid https URL" }
  ]
}
```

## 認証エラー (401)

`INVALID_SIGNATURE` の一般的な原因は 3 つあります:

- 不正なシークレットキー (環境変数を再確認)
- タイムスタンプのずれが 5 分超 (NTP で同期)
- 不正な canonical 文字列 (最も多いのは: `\n` セパレータ漏れ、または、送信したものと異なるパース後に再シリアライズしたボディに署名している)

正確な署名アルゴリズムは [認証](https://docs.infraio.xyz/ja/api-reference/authentication) を参照してください。

## レート制限 (429)

| サーフェス | 制限 |
| --- | --- |
| 全 API ルート (`/b2b/v1/*` 含む) | IP アドレス単位 — **500 req/min**、共通バケット。マーチャント単位ではありません。 |
| 公開チェックアウト (`/checkout/*`) | より厳しいサブバケット — **IP あたり 20 req/min** |

`X-RateLimit-Limit` と `X-RateLimit-Remaining` は、レート制限がかかった
すべてのレスポンスに含まれます (成功時のみではありません)。
あなたの IP に対する現在のバジェットとして扱ってください。

レート制限のレスポンスには `Retry-After` ヘッダー (ウィンドウがリセットされる
までの秒数) が含まれます — これに従ってください。フォールバックとして、
ジッタ付きでバックオフ — ベース 1s から指数的に 30s まで。

> **Note:**
>
> レート制限は変更される可能性があります。正当なトラフィックで制限に
> 達する場合 (例: 大規模な過去範囲の整合性チェック) は、サポートにご連絡ください。

## 残高不足 (402)

`INSUFFICIENT_CREDIT` (HTTP **402 Payment Required**) は、プリペイド残高が、
実行しようとしていた操作 (典型的には暗号資産決済の精算、またはネットワーク
手数料がスポンサーされるオンチェーン処理) のネットワーク手数料（ガス）と
プラットフォーム手数料をカバーできないことを意味します。
マーチャントダッシュボードからチャージし (**Billing → Add credit**)、
操作をリトライしてください。すでに進行中の操作は、チャージ後に自動的に継続されます。

## サーバーエラー (5xx)

500 はリクエストを処理できなかったことを意味します。バックオフ付きで
リトライ — `idempotency_key` があれば、元のリクエストが部分的に成功
していても二重課金になりません。

リトライが 1 分以内に回復しない場合、バイヤーには「一時的に決済が
利用できません」と一般的なメッセージを表示し、失敗したエンドポイント、
マーチャント ID、レスポンスの `Date` ヘッダーと `X-RateLimit-*` の値、
おおよそのリクエスト時刻を添えてサポートにご連絡ください — それで
該当リクエストを特定できます。

## Webhook 配信エラー

Webhook 配信は別の失敗チャネルです — あなたのサーバーが呼び出し側
ではないため、API エラーとしては表面化しません。配信が non-2xx を
返す (またはタイムアウトする) と、0s、1min、5min、15min、1h、6h の
指数バックオフでリトライされます (合計 6 回 — [Webhook →
概要](https://docs.infraio.xyz/ja/webhooks/overview) と同じスケジュール)。配信単位の完全な
履歴は、ダッシュボードの **Developers → Webhooks → [endpoint] →
Delivery log** に表示されます。6 回目の試行後、配信はダッシュボードで
「Failed」と表示され、サーバーが正常化した後に手動で再生できます。
