
# エラーとページネーション

このページでは、すべての統合で処理が必要となる2つの事項、つまり失敗したリクエストがどのようなものか、およびリストを返すエンドポイントのページネーション方法について説明します。

---

## エラーエンベロープ

リクエストが失敗した場合、レスポンスは常に同じ形式のJSONとなります。つまり、`success`フラグが`false`に設定され、人間が読める`error`メッセージが含まれ、HTTPステータスと一致する数値の`error_code`が含まれます。

```json
{
  "success": false,
  "error": "Invalid cursor",
  "error_code": 400
}
```

`success`と`error_code`は常に存在するため、必要に応じて生のHTTPステータスコードを検査することなく、それらに基づいて分岐処理を行うことができます。成功したレスポンスには常に`success: true`が含まれます。

---

## ステータスコード

| Status | `error_code` | Meaning | What to do |
|---|---|---|---|
| `200` | — | Success | Read the response data. |
| `201` | — | Resource created | Save the returned ID (e.g. `campaign_id`, `contactId`). |
| `400` | `400` | Bad request | A parameter is missing or invalid. Read the `error` message and fix the request. |
| `401` | `401` | Unauthorized | Your API key is missing or invalid. Check the key and how you are sending it — see [Authentication](authentication.md). |
| `403` | `403` | Forbidden | Your plan does not include API access. See [API Access](../integrations/api-access.md) or contact [<span data-t="supportEmail">hi@youraiconnector.com</span>](mailto:hi@youraiconnector.com). |
| `404` | `404` | Not found | The resource (e.g. a contact, campaign, or task ID) does not exist on your account. |
| `409` | `409` | Conflict | The resource already exists — for example, creating a contact whose phone number is already on your account. |
| `429` | `429` | Rate limited | You have exceeded 300 requests per minute (or the wider 1,200/minute account ceiling). Back off and retry shortly. |
| `500` | `500` | Server error | Something went wrong on our side. Retry after a short wait; email [<span data-t="supportEmail">hi@youraiconnector.com</span>](mailto:hi@youraiconnector.com) if it persists. |

これらが実際にどのように表示されるかの例をいくつか示します。

```json
{
  "success": false,
  "error_code": 401,
  "error": "Invalid API key"
}
```

```json
{
  "success": false,
  "error": "A contact with this phone number already exists",
  "error_code": 409
}
```

```json
{
  "success": false,
  "error_code": 429,
  "error": "Rate limit exceeded. Please try again later."
}
```

### エラーの適切な処理

- **データを読み取る前に`success`（またはステータスコード）を確認してください。** レスポンスボディに期待するフィールドが含まれていると想定しないでください。
- **`429`と`500`は短いバックオフ（待機時間）を置いて再試行してください。** つまり、少し待ってから再度試してください。`400`、`401`、`403`、`404`、または`409`の場合は再試行しないでください。これらはリクエストを変更しない限り失敗し続けます。
- **`error`メッセージを読み取ってください。** 通常、どのフィールドが間違っているかが正確に示されます。

---

## ページネーション

リストエンドポイント（`GET /contacts`、`GET /campaigns`、`GET /tasks`など）は結果をページ単位で返すため、1回の呼び出しでアカウント全体を読み込む必要はありません。ページネーションには不透明なカーソルを使用します。

これを制御するクエリパラメータは2つあります。

| パラメータ | 説明 |
|---|---|
| `limit` | 1ページあたりに返すアイテム数。デフォルトはエンドポイントによって異なります（多くの場合50）。最大値は**100**です。 |
| `cursor` | 次のページへの不透明なポインタ。最初のページでは指定しないでください。 |

各ページには、レスポンス内に`next_cursor`フィールドが含まれています。

- `next_cursor` が文字列の場合、さらに結果が存在します。次のリクエストでそれを `cursor` として渡してください。
- `next_cursor` が `null` の場合、最後のページに到達しています。終了してください。

連絡先の1ページ分は以下のようになります：

```json
{
  "success": true,
  "contacts": [
    { "id": "abc123", "first_name": "Jane", "phone_number": "+15551234567" },
    { "id": "def456", "first_name": "John", "phone_number": "+15557654321" }
  ],
  "next_cursor": "eyJsYXN0IjoiZGVmNDU2In0"
}
```

::: note
**注意:** カーソルは不透明な値です。解析、構築、変更は行わないでください。以前のレスポンスから受け取った `next_cursor` 値をそのまま渡すようにしてください。
:::


---

## すべての連絡先をページングする

リスト全体を取得するには、カーソルなしで開始し、`next_cursor` が `null` になるまで呼び出しを続けてください。

**cURL**

この例では、最初の2ページを手動で辿ります。最初の呼び出しを実行し、そのレスポンスから `next_cursor` をコピーして `CURSOR` に設定し、2回目の呼び出しを実行します。`next_cursor` が `null` になるまで繰り返してください。

```bash
# First page
curl "https://api.youraiconnector.com/v1/contacts?apiKey=YOUR_API_KEY&limit=100"

# Next page — paste the next_cursor from the previous response
CURSOR="eyJsYXN0IjoiZGVmNDU2In0"
curl "https://api.youraiconnector.com/v1/contacts?apiKey=YOUR_API_KEY&limit=100&cursor=$CURSOR"
```

**JavaScript**

```javascript
async function getAllContacts() {
  const all = [];
  let cursor = null;

  do {
    const url = new URL("https://api.youraiconnector.com/v1/contacts");
    url.searchParams.set("limit", "100");
    if (cursor) url.searchParams.set("cursor", cursor);

    const res = await fetch(url, {
      headers: { "X-API-Key": "YOUR_API_KEY" },
    });
    const data = await res.json();

    if (!data.success) throw new Error(data.error);

    all.push(...data.contacts);
    cursor = data.next_cursor;
  } while (cursor);

  return all;
}
```

**Python**

```python
import requests

def get_all_contacts():
    all_contacts = []
    cursor = None

    while True:
        params = {"limit": 100}
        if cursor:
            params["cursor"] = cursor

        res = requests.get(
            "https://api.youraiconnector.com/v1/contacts",
            params=params,
            headers={"X-API-Key": "YOUR_API_KEY"},
        )
        data = res.json()

        if not data["success"]:
            raise Exception(data["error"])

        all_contacts.extend(data["contacts"])
        cursor = data["next_cursor"]

        if not cursor:
            break

    return all_contacts
```

同じループがすべてのページネーション対応エンドポイントで機能します。パスとレスポンスから読み取るフィールド（`campaigns`、`tasks` など）を変更するだけです。

---

## 次のステップ

- [認証](authentication.md) — キーを送信する4つの方法。
- [連絡先](contacts.md) — 上記の例で使用されている完全な連絡先エンドポイント。
- [APIキー](api-keys.md) — `429` を回避するために、現在のレート制限の使用状況を確認してください。
