
# Lỗi & Phân trang

Trang này đề cập đến hai điều mà mọi tích hợp cần xử lý: một yêu cầu thất bại trông như thế nào và cách phân trang qua các điểm cuối trả về danh sách.

---

## Cấu trúc lỗi

Khi một yêu cầu thất bại, phản hồi luôn là JSON với cùng một hình dạng — một cờ `success` được đặt thành `false`, một thông báo `error` dễ đọc đối với con người và một `error_code` dạng số khớp với trạng thái HTTP:

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

Vì `success` và `error_code` luôn hiện diện, bạn có thể phân nhánh dựa trên chúng mà không cần kiểm tra mã trạng thái HTTP thô nếu muốn. Một phản hồi thành công luôn có `success: true`.

---

## Mã trạng thái

| 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. |

Một vài ví dụ về cách các lỗi này xuất hiện trong thực tế:

```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."
}
```

### Xử lý lỗi hiệu quả

- **Kiểm tra `success` (hoặc mã trạng thái) trước khi đọc dữ liệu.** Đừng giả định rằng nội dung phản hồi có trường mà bạn mong đợi.
- **Thử lại `429` và `500` với thời gian chờ ngắn** — đợi một chút, sau đó thử lại. Đừng thử lại `400`, `401`, `403`, `404` hoặc `409`; những lỗi đó sẽ tiếp tục thất bại cho đến khi bạn thay đổi yêu cầu.
- **Đọc thông báo `error`.** Nó thường cho bạn biết chính xác trường nào bị sai.

---

## Phân trang

Các điểm cuối danh sách (chẳng hạn như `GET /contacts`, `GET /campaigns` và `GET /tasks`) trả về kết quả theo từng trang để một lệnh gọi duy nhất không bao giờ phải tải toàn bộ tài khoản của bạn. Phân trang sử dụng một con trỏ ẩn (opaque cursor).

Hai tham số truy vấn kiểm soát việc này:

| Tham số | Mô tả |
|---|---|
| `limit` | Số lượng mục cần trả về trên mỗi trang. Mặc định thay đổi tùy theo điểm cuối (thường là 50); tối đa là **100**. |
| `cursor` | Một con trỏ ẩn đến trang tiếp theo. Để trống cho trang đầu tiên. |

Mỗi trang bao gồm một trường `next_cursor` trong phản hồi:

- Nếu `next_cursor` là một chuỗi, nghĩa là vẫn còn kết quả — hãy truyền nó dưới dạng `cursor` trong yêu cầu tiếp theo của bạn.
- Nếu `next_cursor` là `null`, bạn đã đến trang cuối cùng. Hãy dừng lại.

Một trang danh bạ đơn lẻ trông như sau:

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

::: note
**Lưu ý:** Con trỏ (cursor) là dữ liệu mờ — đừng cố phân tích, xây dựng hoặc sửa đổi nó. Chỉ nên truyền lại giá trị `next_cursor` mà bạn đã nhận được từ phản hồi trước đó.
:::


---

## Phân trang tất cả danh bạ

Để thu thập toàn bộ danh sách, hãy bắt đầu mà không có con trỏ và tiếp tục gọi cho đến khi `next_cursor` trả về `null`.

**cURL**

Ví dụ này thực hiện thủ công việc duyệt qua hai trang đầu tiên. Chạy lệnh gọi đầu tiên, sao chép `next_cursor` từ phản hồi của nó vào `CURSOR`, sau đó chạy lệnh gọi thứ hai. Lặp lại cho đến khi `next_cursor` là `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
```

Vòng lặp tương tự hoạt động cho bất kỳ điểm cuối (endpoint) được phân trang nào — chỉ cần thay đổi đường dẫn và trường bạn đọc từ phản hồi (`campaigns`, `tasks`, v.v.).

---

## Các bước tiếp theo

- [Xác thực](authentication.md) — bốn cách để gửi khóa của bạn.
- [Danh bạ](contacts.md) — các điểm cuối danh bạ đầy đủ được sử dụng trong các ví dụ trên.
- [Khóa API](api-keys.md) — kiểm tra mức sử dụng giới hạn tốc độ trực tiếp của bạn để tránh các `429`.
