
# API Cơ sở kiến thức

Cơ sở kiến thức là nơi AI đọc thông tin. Nó bao gồm hai phần và trang này sẽ đề cập đến cả hai:

- **Nguồn kiến thức** (`/kb-sources`) — các trang web và tài liệu đã tải lên mà bạn cung cấp cho nền tảng. Mỗi nguồn sẽ được đọc, chia thành các phần và chuyển đổi thành các câu hỏi thường gặp (FAQ) để AI của bạn có thể trả lời.
- **Nhóm kiến thức** (`/kb-groups`) — các gói FAQ được đặt tên mà bạn có thể áp dụng cho một Tác nhân (Agent) hoặc một chiến dịch trong một lệnh gọi duy nhất, nhờ đó khối kiến thức bạn đã biên soạn có thể được tái sử dụng cho Tác nhân tiếp theo mà bạn tạo.

Các FAQ được tạo ra từ một nguồn sẽ nằm trong cùng thư viện với các FAQ bạn tự viết, vì vậy sau khi quá trình nhập hoàn tất, bạn có thể đọc, chỉnh sửa và liên kết chúng bằng [API FAQ](faqs.md).

Tất cả các endpoint bên dưới đều tương đối so với URL cơ sở `https://api.youraiconnector.com/v1`. Mọi yêu cầu đều phải được xác thực — xem [Truy cập API](../integrations/api-access.md) và [Xác thực](authentication.md). Truy cập API là một tính năng trả phí; nếu không có quyền truy cập, các yêu cầu sẽ bị từ chối với mã `403`.


> **Việc nhập dữ liệu sẽ tiêu tốn tín dụng.** Việc đọc một trang hoặc tài liệu và tạo FAQ từ đó sẽ tiêu tốn tín dụng, tỷ lệ thuận với lượng nội dung. Hãy sử dụng [Ước tính chi phí nhập](#estimate-what-an-import-will-cost) trước khi thực hiện một quá trình thu thập dữ liệu lớn.

---

## Cách thức hoạt động của việc nhập dữ liệu

Nhập dữ liệu là một tác vụ chạy ngầm, không phải là tác vụ hoàn tất ngay lập tức. Mọi endpoint nhập dữ liệu sẽ phản hồi ngay lập tức với một `source_id`, và bạn cần thăm dò (poll) nguồn đó cho đến khi hoàn tất:

1. **Bắt đầu nhập** — `POST /kb-sources/url` (một trang), `POST /kb-sources/file` (một tài liệu đã tải lên), hoặc `POST /kb-sources/bulk-import` (tối đa 100 trang). Bạn sẽ nhận lại ID nguồn và `status: "queued"`.
2. **Thăm dò (Poll)** — `GET /kb-sources/{sourceId}` cho đến khi `status` không còn là `queued` hoặc `processing`.
3. **Đọc các FAQ** — khi trạng thái là `ready`, các mục được tạo ra sẽ nằm trong thư viện FAQ của bạn: `GET /faqs`.

Mỗi nguồn sẽ báo cáo một trong các trạng thái sau:

| Trạng thái | Ý nghĩa |
|---|---|
| `queued` | Đang chờ đọc. Chưa có phí nào được tính. |
| `processing` | Đang được đọc và chuyển đổi thành FAQ. |
| `ready` | Đã hoàn tất. Các FAQ của nó đã nằm trong thư viện của bạn. |
| `failed` | Không thể nhập. `error_message` cho biết lý do. |
| `cancelled` | Đã dừng trước khi được đọc (xem [Dừng nhập](#stop-an-import)). |
| `paused` | Đã dừng do khóa AI của bạn gặp lỗi trong quá trình nhập (xem [Tiếp tục nhập bị tạm dừng](#resume-a-paused-import)). |
| `deleting` | Một quá trình xóa hàng loạt đang thực hiện trên đó. |
| `unknown` | Bản ghi không có trạng thái. Hãy coi như chưa sẵn sàng. |

> **Đính kèm khi nhập.** Truyền `autoLinkToAgentId` vào bất kỳ endpoint nhập nào và nguồn đó — cùng với mọi FAQ mà nó tạo ra — sẽ được thêm vào kiến thức của Tác nhân đó trong cùng một lệnh gọi, mà không cần bước liên kết bổ sung. `autoLinkToCampaignId` cũng thực hiện tương tự cho một chiến dịch cổ điển. Việc liên kết là nỗ lực tốt nhất: một ID không tồn tại hoặc thuộc về tài khoản khác sẽ bị bỏ qua một cách âm thầm và quá trình nhập vẫn tiếp tục, vì vậy hãy xác nhận liên kết bằng cách đọc lại Tác nhân.

---

## Nhập một trang web

`POST /kb-sources/url`

Thêm một trang web vào cơ sở kiến thức của bạn.

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `url` | Có | Địa chỉ `http` hoặc `https` đầy đủ của trang. |
| `autoLinkToAgentId` | Không | ID của AI Agent để đính kèm nguồn đã nhập. |
| `autoLinkToCampaignId` | Không | Kế thừa. ID của chiến dịch để đính kèm nguồn đã nhập. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/kb-sources/url?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/pricing",
    "autoLinkToAgentId": "ag7HkQ2ZpLxR3mNb"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/kb-sources/url", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://example.com/pricing",
    autoLinkToAgentId: "ag7HkQ2ZpLxR3mNb",
  }),
});
const { source_id } = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/kb-sources/url",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "url": "https://example.com/pricing",
        "autoLinkToAgentId": "ag7HkQ2ZpLxR3mNb",
    },
)
source_id = res.json().get("source_id")
```

**Phản hồi** — `202 Accepted`

```json
{
  "success": true,
  "source_id": "kb_src_abc123",
  "status": "queued",
  "batch_id": "batch_9f2a"
}
```

Thăm dò `source_id` bằng [Kiểm tra nguồn](#check-a-source) cho đến khi trạng thái là `ready` hoặc `failed`.

Nếu cùng một trang đã có trong cơ sở kiến thức của bạn, sẽ không có nội dung mới nào được xếp hàng và bạn sẽ nhận được `200` thay thế — và nếu bạn đã yêu cầu tự động liên kết, nguồn hiện có sẽ được liên kết cho bạn:

```json
{
  "success": true,
  "status": "exists",
  "skipped_duplicate": 1
}
```

Thiếu `url`, hoặc địa chỉ không phải là `http`/`https` hợp lệ, sẽ trả về `400`.

---

## Nhập tài liệu đã tải lên

`POST /kb-sources/file`

Thêm một tài liệu **đã có trong bộ nhớ tệp của tài khoản** làm nguồn kiến thức. Các định dạng được hỗ trợ: PDF, DOCX, TXT, MD, CSV và XLSX.

> **Endpoint này không chứa tệp.** Không có tải lên multipart, không có body base64 và không có tải xuống từ URL: bạn gửi vị trí lưu trữ của một tệp đã tồn tại, và nó phải nằm trong thư mục tải lên của riêng bạn (`storage_path` phải bắt đầu bằng `users/{your user id}/uploads/`) nếu không yêu cầu sẽ bị từ chối với `403`. Bảng điều khiển sẽ đặt các tệp ở đó khi bạn kéo chúng vào. Nếu bạn không có cách nào để đặt tệp ở đó, hãy nhập một trang web bằng [Nhập trang web](#import-a-web-page) thay thế.

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `storage_path` | Có | Nơi tệp đã tải lên nằm. Phải bắt đầu bằng `users/{your user id}/uploads/`. |
| `filename` | Có | Tên tệp gốc bao gồm phần mở rộng — đây là cách loại tệp được phát hiện. |
| `mime_type` | Có | Loại MIME của tệp, ví dụ `application/pdf`. |
| `autoLinkToAgentId` | Không | ID của AI Agent để đính kèm tài liệu vào. |
| `autoLinkToCampaignId` | Không | Kế thừa. ID của chiến dịch để đính kèm tài liệu vào. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/kb-sources/file?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "storage_path": "users/abc123uid/uploads/handbook.pdf",
    "filename": "handbook.pdf",
    "mime_type": "application/pdf",
    "autoLinkToAgentId": "ag7HkQ2ZpLxR3mNb"
  }'
```

**Phản hồi** — `202 Accepted`

```json
{
  "success": true,
  "source_id": "kb_src_abc123",
  "status": "queued"
}
```

| Trạng thái | Khi nào |
|---|---|
| `400` | Thiếu trường bắt buộc, hoặc tệp không thuộc loại chúng tôi có thể đọc. |
| `403` | `storage_path` nằm ngoài thư mục tải lên của riêng bạn. |

---

## Kiểm tra nguồn

`GET /kb-sources/{sourceId}`

Thăm dò theo sau mỗi lần nhập và làm mới. Lặp lại cho đến khi trạng thái là `ready` hoặc `failed`.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/kb-sources/kb_src_abc123?apiKey=YOUR_API_KEY"
```

**JavaScript**

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

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/kb-sources/kb_src_abc123",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
source = res.json()
```

**Phản hồi**

```json
{
  "success": true,
  "source_id": "kb_src_abc123",
  "status": "ready",
  "faq_count": 24,
  "section_count": 31,
  "error_message": null
}
```

| Trường | Kiểu | Mô tả |
|---|---|---|
| `status` | string | Vị trí của nguồn trong quy trình (xem [bảng trạng thái](#how-an-import-works)). |
| `faq_count` | integer | Số lượng FAQ đã được tạo từ nguồn này cho đến nay. |
| `section_count` | integer | Số lượng phần nội dung mà nguồn đã được chia thành. |
| `error_message` | string \| null | Lý do nhập thất bại, khi trạng thái là `failed`. `null` trong các trường hợp khác. |

---

## Xóa một nguồn

`DELETE /kb-sources/{sourceId}`

Xóa một nguồn kiến thức. **Theo mặc định, các FAQ mà nguồn đó tạo ra sẽ được giữ lại** — hãy thêm `delete_faqs=true` để xóa cả những FAQ đó.

**Các tham số truy vấn**

| Tham số | Bắt buộc | Mô tả |
|---|---|---|
| `delete_faqs` | Không | Đặt thành `true` để xóa cả mọi FAQ mà nguồn này đã tạo ra. Mặc định là `false`. |

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/kb-sources/kb_src_abc123?delete_faqs=true&apiKey=YOUR_API_KEY"
```

**Phản hồi**

```json
{
  "success": true,
  "faqs_deleted": 24
}
```

`faqs_deleted` là `0` trừ khi bạn yêu cầu `delete_faqs=true`.

---

## Nhập nhiều trang cùng lúc

`POST /kb-sources/bulk-import`

Thêm tối đa 100 trang web trong một lần gọi — đây là bước tiếp theo thông thường sau khi [Khám phá các trang trên một trang web](#discover-pages-on-a-website) hoặc [Tìm các trang mới trên một trang web](#find-new-pages-on-a-website). Các trang đã có trong cơ sở kiến thức của bạn sẽ được bỏ qua thay vì bị trùng lặp (và vẫn được liên kết với Tác nhân khi bạn yêu cầu điều đó).

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `urls` | Có | Các địa chỉ cần nhập. Tối thiểu 1, tối đa 100 cho mỗi lần gọi. |
| `autoLinkToAgentId` | Không | ID của một Tác nhân AI để đính kèm mọi trang đã nhập vào. |
| `autoLinkToCampaignId` | Không | Kế thừa. ID của một chiến dịch để đính kèm mọi trang đã nhập vào. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/kb-sources/bulk-import?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "urls": ["https://example.com/pricing", "https://example.com/faq"],
    "autoLinkToAgentId": "ag7HkQ2ZpLxR3mNb"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/kb-sources/bulk-import", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    urls: ["https://example.com/pricing", "https://example.com/faq"],
    autoLinkToAgentId: "ag7HkQ2ZpLxR3mNb",
  }),
});
const { queued_source_ids } = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/kb-sources/bulk-import",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "urls": ["https://example.com/pricing", "https://example.com/faq"],
        "autoLinkToAgentId": "ag7HkQ2ZpLxR3mNb",
    },
)
queued_source_ids = res.json()["queued_source_ids"]
```

**Phản hồi** — `202 Accepted`

```json
{
  "success": true,
  "batch_id": "batch_9f2a",
  "queued": 2,
  "skipped_duplicate": 0,
  "queued_source_ids": ["kb_src_abc123", "kb_src_def456"]
}
```

Truy vấn từng ID trong `queued_source_ids` bằng [Kiểm tra nguồn](#check-a-source). Việc gửi một mảng `urls` trống, một mục không phải là chuỗi hoặc hơn 100 mục sẽ trả về `400`.

---

## Xóa nhiều nguồn cùng lúc

`POST /kb-sources/bulk-delete`

Xóa tối đa 2.000 nguồn kiến thức trong một lần gọi. Việc xóa diễn ra trong nền và bạn sẽ nhận được email khi quá trình hoàn tất.

> **Xóa hàng loạt cũng sẽ xóa luôn các FAQ.** Không giống như [Xóa nguồn](#delete-a-source), vốn giữ lại các FAQ trừ khi bạn yêu cầu khác, điểm cuối này xóa từng nguồn cùng với các FAQ mà nó tạo ra. Không có tùy chọn nào để giữ lại chúng.

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `sourceIds` | Có | ID của các nguồn cần xóa. Tối thiểu 1, tối đa 2.000 mỗi lệnh gọi. |
| `domainLabel` | Không | Tên thân thiện cho quá trình dọn dẹp này. Chỉ được sử dụng trong email thông báo hoàn tất. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/kb-sources/bulk-delete?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sourceIds": ["kb_src_abc123", "kb_src_def456"],
    "domainLabel": "example.com"
  }'
```

**Phản hồi** — `202 Accepted`

```json
{
  "success": true,
  "batch_id": "del_batch_31a",
  "queued": 2
}
```

---

## Khám phá các trang trên một trang web

`POST /kb-sources/discover-pages`

Khám phá một trang web từ một địa chỉ bắt đầu và liệt kê các trang được tìm thấy trên cùng tên miền, mỗi trang đều kèm theo đánh giá về việc liệu nó có đáng để nhập hay không. **Không có gì được nhập và không có gì được chọn cho bạn** — đây là bước "có gì trên trang web này" mà bạn thực hiện trước khi quyết định những gì cần gửi đến [Nhập nhiều trang cùng lúc](#import-many-pages-at-once).

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `url` | Có | Địa chỉ bắt đầu khám phá, thường là trang chủ của trang web. |
| `maxPages` | Không | Giới hạn trên về số lượng trang cần trả về. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/kb-sources/discover-pages?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com", "maxPages": 100 }'
```

**Phản hồi**

```json
{
  "success": true,
  "source_type": "sitemap",
  "pages": [
    {
      "url": "https://example.com/pricing",
      "title": "Pricing",
      "depth": 1,
      "score": 95,
      "recommendation": "add",
      "reason_key": "core_page"
    }
  ]
}
```

| Trường | Loại | Mô tả |
|---|---|---|
| `source_type` | string | Cách các trang được tìm thấy — `sitemap` (sơ đồ trang web của chính trang đó) hoặc `link_discovery` (bằng cách theo các liên kết). |
| `url` | string | Địa chỉ đầy đủ của trang. |
| `title` | string \| null | Tiêu đề trang, khi có thể đọc được. |
| `depth` | integer | Trang này được tìm thấy cách trang bắt đầu bao nhiêu liên kết. |
| `score` | integer | Trang trông hữu ích như thế nào dưới dạng kiến thức, từ `0` đến `100`. |
| `recommendation` | string | `add` (rõ ràng đáng để nhập, điểm 90 trở lên), `maybe` (ở mức trung bình), hoặc `skip` (nội dung hiếm khi giúp ích cho trợ lý — nhật ký thay đổi, trang pháp lý, bản dịch trùng lặp). |
| `reason_key` | string | Một lý do ổn định, máy có thể đọc được đằng sau đề xuất, ví dụ `core_page`, `changelog_history`, `legal_page` hoặc `locale_duplicate`. |

> **Việc khám phá là nỗ lực tốt nhất.** Nếu trang web không thể đọc được, phản hồi vẫn là `200`, với `success: false`, danh sách `pages` trống và thông báo `error`. Kiểm tra `success` trước khi đọc `pages`.

Thiếu `url` sẽ trả về `400`.

---

## Ước tính chi phí nhập

`POST /kb-sources/estimate-cost`

Tính toán số lượng tín dụng mà một lần nhập đề xuất sẽ tiêu tốn trước khi bạn thực hiện. Các trang được tìm nạp và tài liệu được đọc để đo kích thước của chúng, nhưng không có gì được nhập và bản thân việc ước tính không tiêu tốn tín dụng.

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `urls` | Không | Các địa chỉ trang bạn đang cân nhắc nhập. |
| `files` | Không | Các tệp đã tải lên mà bạn đang cân nhắc. Mỗi mục cần `storage_path`, `filename` và `mime_type`. |
| `tier` | Không | Cấp độ chất lượng AI mà quá trình nhập sẽ chạy, để ước tính khớp với những gì bạn thực sự bị tính phí. Để trống nếu sử dụng mức phí tiêu chuẩn. |

Gửi `urls`, `files`, hoặc cả hai.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/kb-sources/estimate-cost?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "urls": ["https://example.com/pricing"] }'
```

**Phản hồi**

```json
{
  "success": true,
  "estimates": [
    { "ref": "https://example.com/pricing", "chunks": 7, "credits": 7 }
  ],
  "total_chunks": 7,
  "total_credits": 7
}
```

Mỗi hàng phản hồi lại URL hoặc đường dẫn lưu trữ trong `ref` để bạn có thể khớp nó với dữ liệu đầu vào của mình. Một trang hoặc tệp không thể đọc được vẫn sẽ có một hàng, được tính là một đoạn, với một `error` trên đó.

---

## Dừng nhập dữ liệu

`POST /kb-sources/cancel-import`

Dừng các trang vẫn đang chờ trong hàng đợi nhập dữ liệu — nút "dừng nhập" dành cho một quá trình thu thập dữ liệu (crawl) hóa ra lại lớn hơn bạn mong đợi. Việc hủy một trang đang chờ sẽ không tốn phí, vì nó chưa được đọc.

Các trang đã được xử lý sẽ **không** bị dừng: công việc của chúng đang được thực hiện và vẫn bị tính phí, vì vậy chúng sẽ hoàn tất. Phản hồi sẽ báo cáo số lượng các trang đó.

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `host` | Không | Chỉ dừng các trang đang chờ trên trang web này (ví dụ: `docs.example.com`). Để trống để dừng mọi quá trình nhập đang chờ trên tài khoản. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/kb-sources/cancel-import?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "host": "docs.example.com" }'
```

**Phản hồi**

```json
{
  "success": true,
  "cancelled": 412,
  "in_flight": 3
}
```

---

## Tiếp tục nhập dữ liệu đã tạm dừng

`POST /kb-sources/resume-import`

Khởi động lại quá trình nhập dữ liệu đã bị tạm dừng do khóa AI của riêng bạn ngừng hoạt động.

> Việc gọi lệnh này **đồng nghĩa với** sự đồng ý của bạn để hoàn tất quá trình nhập dữ liệu trên bất kỳ khóa nào đang hoạt động — điều này có thể đồng nghĩa với việc tiêu tốn tín dụng nền tảng nếu khóa của riêng bạn vẫn chưa hoạt động trở lại.

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `host` | Không | Chỉ tiếp tục các trang đã tạm dừng trên trang web này. Để trống để tiếp tục mọi thứ đã tạm dừng. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/kb-sources/resume-import?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

**Phản hồi**

```json
{
  "success": true,
  "resumed": 58
}
```

---

## Tìm các trang mới trên một trang web

`POST /kb-sources/refresh-domain`

Khám phá một trang web mà bạn đã nhập dữ liệu trước đó và chỉ báo cáo các trang **chưa** có trong cơ sở tri thức của bạn, mỗi trang đều có cùng đề xuất như khám phá trang. Không có gì được nhập và không có gì bị thay đổi.

Hai bước tiếp theo là các lệnh gọi riêng biệt, vì vậy việc rời khỏi bước này sẽ không tốn phí:

- nhập các trang mới bạn muốn bằng [Nhập nhiều trang cùng lúc](#import-many-pages-at-once);
- đọc lại các trang bạn đã có bằng [Làm mới mọi trang trên một trang web](#refresh-every-page-on-a-website).

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `baseUrl` | Có | Bất kỳ địa chỉ nào trên trang web, hoặc chỉ cần tên máy chủ. |
| `maxPages` | Không | Giới hạn trên về số lượng trang cần khám phá. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/kb-sources/refresh-domain?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "baseUrl": "https://example.com" }'
```

**Phản hồi**

```json
{
  "success": true,
  "source_type": "sitemap",
  "discovered": 249,
  "new_pages": [
    {
      "url": "https://example.com/new-guide",
      "score": 95,
      "recommendation": "add",
      "reason_key": "core_page"
    }
  ],
  "new_urls_queued": 0,
  "existing_refresh_queued": 249
}
```

| Trường | Kiểu | Mô tả |
|---|---|---|
| `discovered` | số nguyên | Tổng số trang được tìm thấy trên trang web. |
| `new_pages` | mảng | Các trang chưa có trong cơ sở kiến thức của bạn. Không có gì được xếp hàng cho bạn — hãy nhập những trang bạn muốn. |
| `new_urls_queued` | số nguyên | Luôn là `0`. Được giữ lại để tương thích ngược; điểm cuối này không bao giờ xếp hàng bất kỳ thứ gì. |
| `existing_refresh_queued` | số nguyên | Số lượng trang bạn đã nhập từ trang web này được tìm thấy và sẵn sàng để đọc lại. Không có gì được xếp hàng bởi lệnh gọi này. |
| `batch_id` | chuỗi | Chỉ xuất hiện khi một lô được tạo. |

Giống như khám phá, lệnh này sẽ thất bại nhẹ nhàng: một trang web không thể đọc được vẫn trả về `200`, với `success: false`, một `new_pages` trống và một `error`. Một `baseUrl` bị thiếu hoặc trống sẽ trả về `400`.

---

## Làm mới mọi trang trên một trang web

`POST /kb-sources/trigger-domain-refresh`

Đọc lại mọi trang bạn đã nhập từ một trang web, để các câu hỏi thường gặp (FAQ) của trang đó tuân theo nội dung hiện tại của trang web: các phần đã thay đổi sẽ được cập nhật, các phần mới được thêm vào và các phần đã xóa sẽ bị loại bỏ.

Lệnh này xếp hàng công việc và trả về ngay lập tức. Hãy theo dõi bằng [Theo dõi quá trình làm mới trang web](#track-a-website-refresh) và dừng nó bằng [Dừng quá trình làm mới trang web](#stop-a-website-refresh).

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `baseUrl` | Có | Bất kỳ địa chỉ nào trên trang web, hoặc chỉ cần tên máy chủ. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/kb-sources/trigger-domain-refresh?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "baseUrl": "https://example.com" }'
```

**Phản hồi**

```json
{
  "success": true,
  "queued": 249
}
```

---

## Theo dõi quá trình làm mới trang web

`GET /kb-sources/domain-refresh-status`

Mức độ hoàn thành của quá trình làm mới trang web, để bạn có thể hiển thị tiến trình như "221 trên 249".

**Các tham số truy vấn**

| Tham số | Bắt buộc | Mô tả |
|---|---|---|
| `baseUrl` | Có | Bất kỳ địa chỉ nào trên trang web, hoặc chỉ cần tên máy chủ. |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/kb-sources/domain-refresh-status?baseUrl=https://example.com&apiKey=YOUR_API_KEY"
```

**Phản hồi**

```json
{
  "success": true,
  "job": {
    "domainBatchId": "job_7c1e",
    "host": "example.com",
    "total": 249,
    "pending": 28,
    "succeeded": 219,
    "failed": 2,
    "skippedDuplicate": 0,
    "status": "refreshing",
    "startedAtIso": "2026-06-15T09:00:00.000Z"
  }
}
```

`job` là `null` khi không có quá trình làm mới nào đang chạy cho trang web đó. Số trang đã hoàn thành cho đến nay là `total` trừ đi `pending`. Công việc `status` là một trong các trạng thái `refreshing` (vẫn đang xử lý các trang), `deduplicating` (bước dọn dẹp cuối cùng), hoặc trạng thái cuối cùng là `completed`, `failed` và `cancelled`. Hãy giữ lại `domainBatchId` — đó là thứ bạn truyền vào điểm cuối hủy bỏ.

Một `baseUrl` bị thiếu hoặc trống sẽ trả về `400`.

---

## Dừng làm mới trang web

`POST /kb-sources/refresh-domain/cancel`

Dừng việc làm mới trang web vẫn đang trong quá trình xử lý các trang. Các trang đã hoàn tất sẽ giữ lại nội dung đã cập nhật; các trang chưa bắt đầu sẽ bị loại bỏ, và các trang đang được đọc lại sẽ quay về trạng thái trước đó.

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `jobId` | Có | `domainBatchId` được trả về bởi [Theo dõi làm mới trang web](#track-a-website-refresh). |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/kb-sources/refresh-domain/cancel?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "jobId": "job_7c1e" }'
```

**Phản hồi**

```json
{
  "success": true,
  "status": "cancelled",
  "cancelled_units": 28,
  "sources_reset": 3,
  "sources_cancelled": 25
}
```

| Trường | Loại | Mô tả |
|---|---|---|
| `status` | string | Trạng thái của việc làm mới sau lệnh gọi này: `cancelled`, `deduplicating`, `completed` hoặc `failed`. |
| `cancelled_units` | integer | Khối lượng công việc còn tồn đọng khi lệnh hủy được thực hiện. `0` nếu hủy lặp lại. |
| `sources_reset` | integer | Số trang được đưa ra khỏi quá trình xử lý và quay lại trạng thái `ready`. |
| `sources_cancelled` | integer | Các trang hoàn toàn mới của lần làm mới này vẫn đang trong hàng đợi và hiện đã bị hủy. |

Việc hủy hai lần không gây hại gì — lệnh gọi thứ hai sẽ báo cáo cùng một trạng thái cuối cùng. Khi quá trình làm mới đã chuyển sang bước dọn dẹp, nó không thể bị dừng lại nữa và phản hồi sẽ trả về `success: false` và `reason: "already_finalizing"`. Thiếu `jobId` sẽ trả về `400`, và một công việc không có trong tài khoản của bạn sẽ trả về `404`.

---

## Làm mới một nguồn duy nhất

`POST /kb-sources/{sourceId}/refresh`

Đọc lại một trang web bạn đã nhập và đưa các câu hỏi thường gặp (FAQ) của trang đó về đúng với nội dung hiện tại: các phần thay đổi được cập nhật, phần mới được thêm vào, phần bị xóa sẽ bị loại bỏ.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/kb-sources/kb_src_abc123/refresh?apiKey=YOUR_API_KEY"
```

**Phản hồi** — `202 Accepted`

```json
{
  "success": true,
  "source_id": "kb_src_abc123",
  "status": "queued"
}
```

Thăm dò nguồn cho đến khi trạng thái của nó không còn là `queued` và `processing`. ID nguồn không có trong tài khoản của bạn sẽ trả về `404`.

---

## Chọn các trang phù hợp nhất

`POST /kb-sources/select-relevant-pages`

Yêu cầu AI chọn năm trang từ danh sách các ứng viên mô tả tốt nhất về một doanh nghiệp — được sử dụng khi tạo sổ tay chiến dịch từ một trang web. Việc này sẽ tiêu tốn tín dụng.

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `urls` | Có | Các địa chỉ trang ứng viên để lựa chọn, thường là từ quá trình khám phá trang. |
| `homeUrl` | Có | Trang chủ của trang web, được sử dụng làm ngữ cảnh cho việc lựa chọn. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/kb-sources/select-relevant-pages?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "homeUrl": "https://example.com",
    "urls": ["https://example.com/about", "https://example.com/pricing"]
  }'
```

**Phản hồi**

```json
{
  "success": true,
  "pages": [
    { "url": "https://example.com/pricing", "title": "Pricing", "type": "pricing" }
  ]
}
```

Đây là một trình trợ giúp, không phải là một tài nguyên: khi thất bại, nó vẫn phản hồi `200`, với `success: false`, một danh sách `pages` trống và một thông báo `error`.

---

## Các nhóm kiến thức

Một **nhóm kiến thức** là một tập hợp các câu hỏi thường gặp (FAQ) được đặt tên — ví dụ: "Vận chuyển và đổi trả", "Giới thiệu" — mà bạn có thể áp dụng cho một Tác nhân hoặc một chiến dịch chỉ trong một lần gọi. Nhóm này chứa các tham chiếu chứ không phải bản sao: bản thân các FAQ vẫn nằm trong thư viện duy nhất của bạn, vì vậy việc chỉnh sửa một FAQ bằng [FAQs API](faqs.md) sẽ cập nhật nó ở mọi nơi mà nó được sử dụng.

Việc áp dụng một nhóm chỉ **thêm** những gì còn thiếu, vì vậy việc áp dụng cùng một nhóm hai lần là vô hại và `added_count` sẽ trả về là `0` trong lần thứ hai.

---

## Tạo một nhóm kiến thức

`POST /kb-groups`

Tạo một nhóm. Nhóm này ban đầu sẽ trống — hãy thêm các FAQ vào nhóm bằng cách sử dụng [Thêm FAQ vào nhóm](#add-a-faq-to-a-group).

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `name` | Có | Tên của nhóm. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/kb-groups?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Shipping and returns" }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/kb-groups", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ name: "Shipping and returns" }),
});
const { group_id } = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/kb-groups",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"name": "Shipping and returns"},
)
group_id = res.json()["group_id"]
```

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

```json
{
  "success": true,
  "group_id": "kbg_abc123"
}
```

---

## Đổi tên một nhóm kiến thức

`PUT /kb-groups/{groupId}`

Thay đổi tên của một nhóm. Các FAQ trong nhóm vẫn không bị ảnh hưởng.

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `name` | Có | Tên mới của nhóm. |

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/kb-groups/kbg_abc123?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Shipping, returns and refunds" }'
```

**Phản hồi**

```json
{
  "success": true,
  "group_id": "kbg_abc123",
  "name": "Shipping, returns and refunds"
}
```

---

## Xóa một nhóm kiến thức

`DELETE /kb-groups/{groupId}`

Xóa nhóm. Chỉ có tập hợp này bị xóa — các FAQ bên trong nó vẫn nằm trong thư viện của bạn, và bất kỳ đối tượng nào đã được áp dụng nhóm này trước đó vẫn giữ lại các FAQ đó.

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/kb-groups/kbg_abc123?apiKey=YOUR_API_KEY"
```

**Phản hồi**

```json
{
  "success": true
}
```

---

## Thêm FAQ vào một nhóm

`POST /kb-groups/{groupId}/faqs`

Đưa một FAQ hiện có vào một nhóm. Thao tác này chỉ thay đổi gói — nó không tự gắn FAQ vào bất kỳ Tác nhân (Agent) nào; hãy áp dụng nhóm cho việc đó.

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `faq_id` | Có | ID của FAQ cần thêm. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/kb-groups/kbg_abc123/faqs?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "faq_id": "aBcD1234eFgH5678" }'
```

**Phản hồi**

```json
{
  "success": true,
  "group_id": "kbg_abc123",
  "faq_id": "aBcD1234eFgH5678"
}
```

---

## Xóa FAQ khỏi một nhóm

`DELETE /kb-groups/{groupId}/faqs/{faqId}`

Lấy một FAQ ra khỏi nhóm. Bản thân FAQ không bị xóa, và các Tác nhân đã được áp dụng nhóm này trước đó vẫn giữ lại FAQ đó.

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/kb-groups/kbg_abc123/faqs/aBcD1234eFgH5678?apiKey=YOUR_API_KEY"
```

**Phản hồi**

```json
{
  "success": true,
  "group_id": "kbg_abc123",
  "faq_id": "aBcD1234eFgH5678"
}
```

---

## Áp dụng một nhóm cho một Tác nhân

`POST /kb-groups/{groupId}/apply-to-agent`

Thêm mọi FAQ trong nhóm vào kiến thức của một Tác nhân AI trong một lần gọi — cách nhanh nhất để cung cấp cho một Tác nhân mới một kho kiến thức mà bạn đã biên soạn sẵn.

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `agent_id` | Có | ID của Tác nhân AI cần áp dụng nhóm. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/kb-groups/kbg_abc123/apply-to-agent?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "agent_id": "ag7HkQ2ZpLxR3mNb" }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/kb-groups/kbg_abc123/apply-to-agent",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ agent_id: "ag7HkQ2ZpLxR3mNb" }),
  }
);
const { added_count } = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/kb-groups/kbg_abc123/apply-to-agent",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"agent_id": "ag7HkQ2ZpLxR3mNb"},
)
added_count = res.json()["added_count"]
```

**Phản hồi**

```json
{
  "success": true,
  "group_id": "kbg_abc123",
  "agent_id": "ag7HkQ2ZpLxR3mNb",
  "added_count": 12
}
```

`added_count` là số lượng FAQ thực sự đã được thêm — `0` khi nhóm trống hoặc đã được áp dụng.

---

## Áp dụng một nhóm cho một chiến dịch

`POST /kb-groups/{groupId}/apply-to-campaign`

Phiên bản chiến dịch cổ điển của lệnh gọi trên. Trên tài khoản dựa trên Tác nhân, hãy sử dụng [Áp dụng một nhóm cho một Tác nhân](#apply-a-group-to-an-agent) thay thế.

**Các trường yêu cầu**

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `campaign_id` | Có | ID của chiến dịch để áp dụng nhóm vào. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/kb-groups/kbg_abc123/apply-to-campaign?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "campaign_id": "campaign123" }'
```

**Phản hồi**

```json
{
  "success": true,
  "group_id": "kbg_abc123",
  "campaign_id": "campaign123",
  "added_count": 12
}
```

---

## Các lỗi API Cơ sở Kiến thức

Các endpoint này trả về cấu trúc lỗi tiêu chuẩn:

```json
{
  "success": false,
  "error": "Knowledge base source not found."
}
```

| Trạng thái | Khi nào nó xảy ra trên một endpoint cơ sở kiến thức |
|---|---|
| `400` | Một trường bắt buộc bị thiếu hoặc không hợp lệ — một `url` trống, thiếu `baseUrl` hoặc `jobId`, hơn 100 URL trong một lần nhập hàng loạt, hơn 2.000 ID trong một lần xóa hàng loạt, hoặc một loại tệp mà chúng tôi không thể đọc. |
| `402` | Không đủ tín dụng để thực hiện nhập. Hãy nạp thêm và thử lại. |
| `403` | Một `storage_path` nằm ngoài thư mục tải lên của riêng bạn — hoặc gói của bạn không bao gồm quyền truy cập API. |
| `404` | Nguồn, nhóm, FAQ, Tác nhân, chiến dịch hoặc tác vụ làm mới không được tìm thấy — hoặc là nó không tồn tại hoặc nó thuộc về một tài khoản khác. |

> **Lỗi nhẹ không phải là lỗi hệ thống.** Discovery (`discover-pages`, `refresh-domain`) và trình trợ giúp chọn trang trả về `200` với `success: false` và thông báo `error` khi không thể đọc trang web, thay vì làm thất bại yêu cầu. Luôn kiểm tra `success` trước khi đọc dữ liệu.

Các mã chung mà mọi điểm cuối có thể trả về — `401`, `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).

---

## Liên quan

- [API FAQ](faqs.md) — đọc, chỉnh sửa và liên kết các FAQ mà các nguồn của bạn tạo ra.
- [Quản lý FAQ](../ai-automation/faq-management.md) — cùng một cơ sở kiến thức trong bảng điều khiển.
- [Tác nhân AI](../ai-agents/ai-agents.md) — các Tác nhân mà bạn đính kèm nguồn và nhóm vào.
- [Truy cập API](../integrations/api-access.md) — tạo khóa API của bạn.
- [Xác thực](authentication.md) — tất cả các cách để truyền khóa của bạn.