
# API Keys API

Các endpoint này cho phép bạn quản lý các khóa API của tài khoản từ mã nguồn. Tất cả chúng chỉ hoạt động trên các khóa của chính tài khoản đang thực hiện lệnh gọi.

Có hai loại khóa và chúng nằm trên các đường dẫn riêng biệt:

- **Khóa chính của bạn** — khóa có toàn quyền duy nhất nằm trong **Cài đặt → Tích hợp → Khóa API**. Tra cứu bản xem trước bị ẩn của nó, kiểm tra mức sử dụng giới hạn tốc độ, xoay vòng hoặc thu hồi khóa. Đây là các endpoint `/api-keys/current`, `/api-keys/rotate` và `/api-keys/usage` bên dưới.
- **Khóa phạm vi (Scoped keys)** — các khóa bổ sung, có tên mà bạn tạo cho một công việc cụ thể, mỗi khóa chỉ giới hạn ở các phần của API mà bạn chọn. Đây là các endpoint `/api-keys` và `/api-keys/{id}` trong phần [Khóa phạm vi](#scoped-keys). Không có gì về khóa chính của bạn thay đổi khi bạn tạo một khóa mới; các tích hợp hiện có vẫn tiếp tục hoạt động bình thường.

Tất cả các đường dẫn bên dưới đều tương đối so với URL cơ sở của API:

```
https://api.youraiconnector.com/v1
```

Mọi yêu cầu đều phải được xác thực. Xem [Xác thực](authentication.md) để biết bốn phương thức được chấp nhận. Các ví dụ ở đây sử dụng tiêu đề `X-API-Key` (và một dạng tham số truy vấn cho cURL).

> **Đọc phần này trước.** Việc xoay vòng hoặc thu hồi khóa của bạn sẽ có hiệu lực **ngay lập tức**. Ngay khi một trong hai lệnh gọi thành công, khóa cũ sẽ ngừng hoạt động — mọi tích hợp vẫn đang sử dụng khóa đó sẽ bắt đầu nhận lỗi `401`. Hãy lên kế hoạch: thực hiện xoay vòng trong thời gian bảo trì và cập nhật tất cả các tích hợp của bạn ngay lập tức.

---

## Lấy siêu dữ liệu khóa hiện tại

Trả về khóa đang hoạt động của bạn: khóa đầy đủ trong `api_key` khi có bản sao có thể truy xuất, bản xem trước bị ẩn (4 ký tự đầu và 4 ký tự cuối), và ngày tạo khóa nếu có. `api_key` là `null` đối với các khóa được tạo trước khi các bản sao có thể truy xuất được lưu giữ — hãy xoay vòng một lần và khóa mới có thể được hiển thị lại sau này.

`GET /api-keys/current`

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/api-keys/current?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/api-keys/current", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
```

**Python**

```python
import requests

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

**Phản hồi**

```json
{
  "success": true,
  "api_key": "abcdEFGH1234ijkl5678MNOP9012qrst",
  "api_key_masked": "abcd...qrst",
  "created_at": "2026-06-01T10:00:00.000Z"
}
```

Nếu tài khoản không có khóa API, phản hồi sẽ là `404` với `{ "success": false, "error": "No API key found for this account" }`.

---

## Lấy mức sử dụng giới hạn tốc độ

Trả về mức sử dụng giới hạn tốc độ của bạn trong cửa sổ hiện tại: giới hạn yêu cầu mỗi cửa sổ, số lượng yêu cầu đã được tính cho đến nay, số lượng còn lại và thời điểm cửa sổ đặt lại. Hãy sử dụng thông tin này để xây dựng cơ chế điều tiết phía máy khách (client-side throttling) để tích hợp của bạn tự giảm tải trước khi gặp phải các phản hồi `429`.

`GET /api-keys/usage`

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/api-keys/usage" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/api-keys/usage", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
```

**Python**

```python
import requests

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

**Phản hồi**

```json
{
  "success": true,
  "usage": {
    "limit": 300,
    "window_seconds": 60,
    "used": 37,
    "remaining": 263,
    "window_resets_at": "2026-06-09T12:01:00.000Z"
  }
}
```

Nếu chưa có yêu cầu nào được ghi lại trong cửa sổ hiện tại, mức sử dụng sẽ được báo cáo là bằng không và phản hồi bao gồm trường `note` giải thích lý do.

---

## Xoay vòng khóa

Tạo một khóa API mới và vô hiệu hóa khóa trước đó trong cùng một bước. Hãy sử dụng tính năng này nếu bạn nghi ngờ khóa của mình đã bị lộ, hoặc như một phần của chính sách xoay vòng thông tin xác thực định kỳ.

`POST /api-keys/rotate`

> **Khóa mới chỉ được hiển thị một lần duy nhất.** Nó được trả về trong phản hồi này và sau đó không thể truy xuất đầy đủ — hãy lưu trữ nó an toàn ngay khi bạn nhận được. Khóa cũ sẽ ngừng hoạt động ngay khi lệnh gọi này thành công, vì vậy hãy cập nhật mọi tích hợp đã sử dụng khóa đó.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/api-keys/rotate?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/api-keys/rotate", {
  method: "POST",
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
// Save data.api_key now — it will not be shown again.
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/api-keys/rotate",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
# Save data["api_key"] now — it will not be shown again.
```

**Phản hồi**

```json
{
  "success": true,
  "api_key": "abcdEFGH1234ijkl5678MNOP9012qrst",
  "message": "API key rotated. The previous key is no longer valid. Store this key now — it will not be shown again."
}
```

---

## Thu hồi khóa

Xóa vĩnh viễn khóa API của tài khoản bạn. Việc thu hồi có hiệu lực ngay lập tức: mọi yêu cầu tiếp theo sử dụng khóa đã bị thu hồi — bao gồm các tích hợp như Make, Zapier hoặc các tập lệnh tùy chỉnh — đều bị từ chối với mã `401`. Để khôi phục quyền truy cập API sau đó, hãy tạo khóa mới từ cài đặt tài khoản khi đã đăng nhập vào ứng dụng.

`DELETE /api-keys/current`

> **Không thể hoàn tác.** Không giống như việc xoay vòng, thu hồi không cung cấp cho bạn khóa thay thế. Chỉ thu hồi khi bạn có ý định dừng quyền truy cập API (ví dụ: khóa bị rò rỉ mà bạn không thể thay thế ngay lập tức).

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/api-keys/current" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/api-keys/current", {
  method: "DELETE",
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.delete(
    "https://api.youraiconnector.com/v1/api-keys/current",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Phản hồi**

```json
{
  "success": true,
  "revoked": true,
  "message": "API key revoked. All requests using it will be rejected immediately."
}
```

Nếu tài khoản không có khóa để thu hồi, phản hồi sẽ là `404`.

---

## Khóa phạm vi

Khóa phạm vi là một khóa API bổ sung mà bạn tạo cho một công việc cụ thể, chỉ mang quyền truy cập mà công việc đó cần. Trường hợp điển hình: bạn muốn trỏ một bảng điều khiển khách hàng, công cụ báo cáo hoặc tập lệnh nội bộ vào tài khoản của mình mà không cần cung cấp khóa có thể gửi tin nhắn, thay đổi tác nhân AI hoặc mua số điện thoại.

Sự hạn chế đi kèm với chính khóa đó, vì vậy bất kỳ ai giữ khóa chỉ có thể thực hiện những gì bạn đã cho phép khi tạo nó.

**Những gì bạn có thể hạn chế**

| Trường | Ý nghĩa |
|---|---|
| `read_only` | `true` (mặc định) nghĩa là chỉ cho phép các yêu cầu đọc. Mọi hành động tạo, cập nhật hoặc xóa đều bị từ chối. |
| `tags` | Danh sách các phần API mà khóa có thể sử dụng, được viết bằng cùng tên phần bạn thấy trong tài liệu này và trong [API explorer](reference.md) — `Analytics`, `Campaigns`, `Contacts`, `Messages`, `Appointments`, v.v. Danh sách trống nghĩa là tất cả các phần. |
| `sub_account_ids` | Các tài khoản được quản lý mà khóa có thể tác động. Để trống nghĩa là chỉ tài khoản của riêng bạn; `["*"]` nghĩa là bất kỳ tài khoản nào bạn thực sự quản lý. Quyền sở hữu vẫn được kiểm tra trên mọi yêu cầu. |
| `rate_limit_per_min` | Số yêu cầu mỗi phút cho khóa này, được tính trong ngân sách riêng của nó để không sử dụng hết hạn mức của các tích hợp khác của bạn. Mặc định là `60` và không thể đặt cao hơn `300`. |

Bạn cũng có thể cung cấp cho khóa một ngày `expires_at` (ISO 8601 và phải là ngày trong tương lai). Sau thời điểm đó, khóa sẽ tự động ngừng hoạt động. Nếu bỏ qua, khóa sẽ không bao giờ hết hạn cho đến khi bạn thu hồi nó.

> **Các yêu cầu bị từ chối sẽ được chặn lại.** Nếu một yêu cầu nằm ngoài phạm vi cho phép của khóa, nó sẽ bị từ chối thay vì được thông qua: một hành động ghi với khóa chỉ đọc sẽ trả về `403` với `error_code: "key_read_only"`, và bất kỳ hành động nào nằm ngoài các phần được phép của khóa sẽ trả về `403` với `error_code: "key_scope_denied"`. Nếu một khóa phạm vi nhận được `403` không mong đợi, endpoint bạn đã gọi đơn giản là không nằm trong phạm vi của nó — hãy mở rộng phạm vi khóa hoặc sử dụng khóa chính của bạn.

> **Chỉ chủ sở hữu tài khoản mới quản lý được khóa.** Bốn endpoint này yêu cầu khóa chính của bạn hoặc phiên làm việc của chủ sở hữu trong ứng dụng. Khóa phạm vi không bao giờ có thể liệt kê, tạo, chỉnh sửa hoặc thu hồi khóa — bao gồm cả chính nó — vì vậy một khóa bị hạn chế không bao giờ có thể được sử dụng để tạo ra một khóa có quyền rộng hơn. Cố gắng thực hiện sẽ trả về `403` với `error_code: "key_scope_denied"`. Vì lý do tương tự, `API Keys` không phải là một phần bạn có thể cấp quyền: yêu cầu thực hiện điều này sẽ trả về `400` với `error_code: "invalid_scopes"`.

### Liệt kê các khóa phạm vi

Trả về các khóa phạm vi của tài khoản, khóa mới nhất hiển thị trước (tối đa 200), bao gồm cả các khóa đã bị thu hồi để bạn có thể thấy những gì đã bị hủy bỏ và khi nào. Chỉ các bản xem trước bị ẩn mới được trả về — giá trị của khóa phạm vi chỉ được hiển thị một lần, tại thời điểm tạo và không bao giờ có thể truy xuất sau đó.

`GET /api-keys`

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/api-keys" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Phản hồi**

```json
{
  "success": true,
  "api_keys": [
    {
      "id": "key_9f2c1a7b4d6e8f0a1b2c3d4e5f60718a",
      "label": "Client dashboard - Acme",
      "key_preview": "abcd...qrst",
      "scopes": {
        "read_only": true,
        "tags": ["Analytics"],
        "sub_account_ids": [],
        "rate_limit_per_min": 60
      },
      "expires_at": null,
      "last_used_at": "2026-08-20T14:03:00.000Z",
      "created_at": "2026-08-14T09:12:00.000Z",
      "revoked_at": null,
      "revoked": false
    }
  ]
}
```

### Tạo một khóa phạm vi

Tạo một khóa phạm vi mới và trả về giá trị của nó **một lần duy nhất**.

`POST /api-keys`

> **Khóa chỉ được hiển thị một lần.** Nó chỉ xuất hiện trong phản hồi này và không bao giờ xuất hiện ở bất kỳ nơi nào khác — không có cách nào để tra cứu lại sau đó. Hãy lưu trữ nó ngay khi bạn nhận được. Nếu bạn làm mất, hãy thu hồi nó và tạo một khóa khác.

**Các trường trong phần thân (Body)** — tất cả đều là tùy chọn:

| Trường | Loại | Ghi chú |
|---|---|---|
| `label` | string | Tên riêng của bạn cho khóa, được hiển thị trong danh sách và trong Cài đặt. |
| `scopes` | object | Bốn trường trong bảng trên. Nếu bỏ qua toàn bộ đối tượng này, bạn sẽ nhận được mặc định an toàn: chỉ đọc, giới hạn ở `Analytics`, chỉ tài khoản của bạn, 60 yêu cầu mỗi phút. |
| `expires_at` | Ngày ISO 8601 | Ngày hết hạn tùy chọn, phải là thời điểm trong tương lai. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/api-keys" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Client dashboard - Acme",
    "scopes": {
      "read_only": true,
      "tags": ["Analytics"],
      "sub_account_ids": [],
      "rate_limit_per_min": 60
    }
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/api-keys", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    label: "Client dashboard - Acme",
    scopes: { read_only: true, tags: ["Analytics"] },
  }),
});
const data = await res.json();
// Save data.api_key now — it will not be shown again.
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/api-keys",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "label": "Client dashboard - Acme",
        "scopes": {"read_only": True, "tags": ["Analytics"]},
    },
)
data = res.json()
# Save data["api_key"] now — it will not be shown again.
```

**Phản hồi** — `201 Created`

```json
{
  "success": true,
  "api_key": "abcdEFGH1234ijkl5678MNOP9012qrst",
  "key": {
    "id": "key_9f2c1a7b4d6e8f0a1b2c3d4e5f60718a",
    "label": "Client dashboard - Acme",
    "key_preview": "abcd...qrst",
    "scopes": {
      "read_only": true,
      "tags": ["Analytics"],
      "sub_account_ids": [],
      "rate_limit_per_min": 60
    },
    "expires_at": null,
    "revoked": false
  },
  "message": "Store this key now — it is shown once and cannot be retrieved again."
}
```

Một vài chi tiết cần lưu ý khi bạn xây dựng dựa trên phần này:

- **Việc bỏ qua `scopes` không giống như việc gửi một danh sách `tags` trống.** Hãy bỏ qua `scopes` hoàn toàn để nhận mặc định an toàn (chỉ đọc, chỉ `Analytics`). Nếu bạn cố tình gửi `"tags": []`, khóa có thể sử dụng mọi phần — điều đó được hiểu là một yêu cầu có chủ đích cho một khóa không bị hạn chế.
- **`read_only` vẫn là `true` trừ khi bạn gửi rõ ràng `false`.** Một lỗi đánh máy hoặc một cờ bị thiếu sẽ không bao giờ vô tình tạo ra một khóa có quyền ghi.

### Cập nhật khóa phạm vi

Thay đổi nhãn, phạm vi và/hoặc ngày hết hạn của khóa. Gửi bất kỳ kết hợp nào trong ba yếu tố này; nếu không gửi gì cả sẽ trả về `400`.

`PATCH /api-keys/{id}`

`{id}` là `id` của khóa từ danh sách (giá trị `key_...`), không bao giờ là chính khóa đó.

> **Các phạm vi được thay thế, không phải hợp nhất.** Bất cứ thứ gì bạn gửi sẽ trở thành tập hợp quyền hoàn chỉnh của khóa. Điều này là có chủ đích: việc thu hẹp phạm vi của khóa sẽ không bao giờ vô tình để lại quyền truy cập rộng hơn trước đó. Luôn gửi toàn bộ đối tượng `scopes` mà bạn muốn, không chỉ trường bạn đang thay đổi.

Giá trị của khóa không bao giờ thay đổi. Không có tính năng xoay vòng tại chỗ (rotate-in-place) cho khóa phạm vi — để thay đổi, hãy tạo một khóa mới và thu hồi khóa cũ, để quyền truy cập của thông tin xác thực không bao giờ thay đổi đối với một tích hợp vẫn đang giữ nó.

**cURL**

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/api-keys/key_9f2c1a7b4d6e8f0a1b2c3d4e5f60718a" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Client dashboard - Acme (read-only)",
    "scopes": {
      "read_only": true,
      "tags": ["Analytics", "Campaigns"],
      "sub_account_ids": [],
      "rate_limit_per_min": 30
    }
  }'
```

**Phản hồi**

```json
{
  "success": true,
  "key": {
    "id": "key_9f2c1a7b4d6e8f0a1b2c3d4e5f60718a",
    "label": "Client dashboard - Acme (read-only)",
    "key_preview": "abcd...qrst",
    "scopes": {
      "read_only": true,
      "tags": ["Analytics", "Campaigns"],
      "sub_account_ids": [],
      "rate_limit_per_min": 30
    },
    "expires_at": null,
    "last_used_at": "2026-08-20T14:03:00.000Z",
    "created_at": "2026-08-14T09:12:00.000Z",
    "revoked_at": null,
    "revoked": false
  }
}
```

Nếu không có khóa nào với id đó trong tài khoản của bạn, phản hồi sẽ là `404`.

### Thu hồi một khóa có phạm vi (scoped key)

Việc thu hồi có hiệu lực ngay lập tức: yêu cầu tiếp theo sử dụng khóa đó sẽ bị từ chối với mã `401`. Khóa chính và mọi khóa có phạm vi khác của bạn đều không bị ảnh hưởng.

`DELETE /api-keys/{id}`

Khóa vẫn nằm trong danh sách của bạn với trạng thái `"revoked": true`, vì vậy bạn vẫn lưu giữ được hồ sơ về những gì đã tồn tại và những gì nó có thể truy cập. Việc thu hồi một khóa đã bị thu hồi sẽ thành công và không làm thay đổi bất cứ điều gì.

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/api-keys/key_9f2c1a7b4d6e8f0a1b2c3d4e5f60718a" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Phản hồi**

```json
{
  "success": true,
  "revoked": true,
  "id": "key_9f2c1a7b4d6e8f0a1b2c3d4e5f60718a",
  "message": "API key revoked. All requests using it will be rejected immediately."
}
```

---

## Các lỗi API của API Keys

Các endpoint của API-key trả về phong bì lỗi tiêu chuẩn:

```json
{
  "success": false,
  "error": "No API key found for this account"
}
```

Trên một endpoint API-key, khóa bị thiếu hoặc không hợp lệ sẽ trả về `401` và tài khoản không có khóa trong hồ sơ sẽ trả về `404`. Các mã chia sẻ mà mọi endpoint có thể trả về — `400`, `403` (gói của bạn không bao gồm quyền truy cập API), `429` (giới hạn tốc độ) và `500` — được liệt kê cùng với hướng dẫn thử lại trong [Lỗi & Phân trang](errors-and-pagination.md).

Các điểm cuối (endpoints) của khóa có phạm vi bổ sung một vài mã được đặt tên trong trường `error_code` để bạn có thể phân biệt các trường hợp:

| `error_code` | Trạng thái | Điều gì đã xảy ra |
|---|---|---|
| `key_read_only` | `403` | Một khóa chỉ đọc đã cố gắng thực hiện thao tác ghi. |
| `key_scope_denied` | `403` | Khóa không được phép sử dụng trên điểm cuối đó hoặc tài khoản được quản lý đó — hoặc một khóa có phạm vi đã cố gắng quản lý các khóa API, điều này không bao giờ được cho phép. |
| `invalid_scopes` | `400` | Các phạm vi được yêu cầu bao gồm phần `API Keys`. Các khóa không thể quản lý các khóa khác. |
| `404` | `404` | Không có khóa nào với ID đó trong tài khoản của bạn. |

---

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

- [Xác thực](authentication.md) — bốn cách để xác thực một yêu cầu và cách thực thi các phạm vi khóa.
- [Lỗi & Giới hạn tốc độ](errors-and-pagination.md) — các mã trạng thái và giới hạn 300 yêu cầu/phút.
