
# API Chiến dịch

Một chiến dịch tập hợp mọi thứ mà bot AI cần để trò chuyện với các liên hệ của bạn: hướng dẫn, các kênh hoạt động, giờ hoạt động và hành vi theo dõi. API Chiến dịch cho phép bạn liệt kê, tạo, cập nhật, sao chép, bật, lưu trữ và tinh chỉnh các chiến dịch từ mã của riêng bạn thay vì từ bảng điều khiển.

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

> **Lưu ý:** Một số ví dụ hiển thị biểu mẫu truy vấn `?apiKey=YOUR_API_KEY` đơn giản, những ví dụ khác sử dụng tiêu đề `X-API-Key`. Cả hai đều hoạt động ở mọi nơi — hãy sử dụng bất kỳ cách nào phù hợp với thiết lập của bạn.

---

## Các loại chiến dịch

Khi tạo một chiến dịch, bạn phải chọn một trong các loại sau:

| Loại | Mục đích |
|---|---|
| `Incoming from Unknown Contacts` | Bot trả lời những người nhắn tin cho bạn lần đầu tiên. |
| `Outgoing` | Bot bắt đầu cuộc trò chuyện với các liên hệ mà bạn thêm vào chiến dịch. |
| `Keywords` | **Không hoạt động - không sử dụng.** Chiến dịch `Keywords` không hoạt động: nó vẫn được chấp nhận để tương thích ngược, nhưng không hiển thị với định tuyến đến trên mọi kênh và không có gì đọc các từ khóa kích hoạt của nó. Hãy sử dụng Điểm truy cập loại **Từ khóa** trên một AI Agent thay thế. |
| `Combined` | Kết hợp giữa hành vi đến và đi. |

**Chữ hoa và chữ thường không quan trọng.** `type`, `status`, `booking_provider`, `first_response_mode`, `bot.anthropic_model` và `bot.ai_speed` đều chấp nhận bất kỳ kiểu viết hoa nào — `"live"`, `"Live"` và `"LIVE"` đều là một — và giá trị được lưu trữ ở dạng chuẩn, đây cũng là dạng bạn nhận được khi đọc chiến dịch. Ngoại lệ duy nhất là cặp tạm dừng: `"Paused"` và `"paused"` là hai trạng thái thực sự khác biệt, vì vậy một cách viết mơ hồ như `"PAUSED"` sẽ bị từ chối kèm theo `400` yêu cầu bạn chọn một trong hai.

### Hai trạng thái tạm dừng

| Trạng thái | Ai ghi lại | Ý nghĩa |
|---|---|---|
| `Paused` | Các kiểm tra an toàn của chính nền tảng (tương tác thấp, lỗi gửi lặp lại, đạt giới hạn) và các bề mặt Đại lý và Phát sóng mới hơn | Chiến dịch đang bị giữ lại. Một đợt quét theo lịch trình có thể tự động dỡ bỏ trạng thái tạm dừng an toàn sau khi lý do được giải quyết. |
| `paused` | Nút Tạm dừng trên bảng điều khiển, kết hợp với `resumed` khi Tiếp tục | Một người đã tạm dừng thủ công. Các lượt gửi theo lịch trình sẽ bị hủy và xây dựng lại khi tiếp tục. |

Cả hai đều dừng chiến dịch: định tuyến đến chỉ chạy khi trạng thái chính xác là `Live`. **Từ API, hãy sử dụng `Paused` để tạm dừng và `Live` để tiếp tục** — cặp chữ thường tồn tại cho nút trên bảng điều khiển và vẫn được duy trì hoạt động cho nút đó.

Không có trạng thái nào trong số này là những gì xảy ra khi AI ngừng trả lời trong một cuộc trò chuyện. Đó là một công tắc theo từng liên hệ, `is_bot_active` trên liên hệ — được thiết lập khi con người tiếp quản, khi liên hệ chọn không tham gia hoặc khi AI kết thúc cuộc trò chuyện. Trạng thái của chính chiến dịch không bị ảnh hưởng và mọi cuộc trò chuyện khác trong đó vẫn tiếp tục chạy. Xem [tạm dừng hoặc tiếp tục AI cho một liên hệ](messages.md#pause-or-resume-the-ai-for-one-contact).

> **Việc tạo chiến dịch không quyết định ai sẽ trả lời một kênh.** Định tuyến được xử lý bởi **Điểm truy cập** trên một AI Agent, không phải bởi các chiến dịch. Mỗi kênh có một Điểm truy cập mặc định cho kênh, chỉ định Agent trả lời các liên hệ mới, chưa biết trên kênh đó: thiết lập nó với `PUT /entry-points/channel-defaults`, kiểm tra xem thang đo có đang hoạt động cho tài khoản hay không với `GET /entry-points/routing-status`, xóa nó với `DELETE /entry-points/channel-defaults`. `POST /channels/campaign` vẫn ghi bản đồ định tuyến chiến dịch theo kênh cũ, nhưng bản đồ đó không còn được tham chiếu cho định tuyến đến trên bất kỳ tài khoản nào; nó chỉ được giữ lại để khôi phục. Đừng xây dựng dựa trên nó. Xem [Định tuyến một kênh đến một chiến dịch](channels.md#route-a-channel-to-a-campaign) để biết cả hai giao diện cạnh nhau.

---

## Liệt kê chiến dịch

`GET /campaigns`

Trả về các chiến dịch của bạn, mới nhất trước. Các chiến dịch đã lưu trữ sẽ bị loại trừ trừ khi bạn truyền `archived=true`.

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

| Tham số | Bắt buộc | Mô tả |
|---|---|---|
| `limit` | Không | Số lượng chiến dịch tối đa cần trả về. Mặc định `50`, tối đa `100`. |
| `cursor` | Không | Con trỏ phân trang. Truyền giá trị `next_cursor` từ phản hồi trước đó để lấy trang tiếp theo. |
| `archived` | Không | Đặt thành `true` để bao gồm các chiến dịch đã lưu trữ. |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/campaigns?limit=20&apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/campaigns?limit=20", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.campaigns, data.next_cursor);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/campaigns",
    params={"limit": 20},
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
print(data["campaigns"], data["next_cursor"])
```

**Phản hồi**

```json
{
  "success": true,
  "campaigns": [
    {
      "id": "NBCXrhqGPSFsd6MV7pRo",
      "name": "Inbound WhatsApp Leads",
      "type": "Incoming from Unknown Contacts",
      "status": "Live",
      "enabled": true,
      "archived": false,
      "created_at": 1700000000000,
      "ai_mode": true,
      "language": "en",
      "enabled_channels": ["whatsapp", "instagram"]
    }
  ],
  "next_cursor": "NBCXrhqGPSFsd6MV7pRo"
}
```

Khi `next_cursor` là `null`, bạn đã đến trang cuối cùng.

---

## Lấy một chiến dịch

`GET /campaigns/{campaignId}`

Trả về tài liệu chiến dịch đầy đủ, bao gồm cấu hình bot trực tiếp (`bot`), cài đặt theo dõi, các kênh đã bật và bất kỳ từ khóa nào. Dấu thời gian được trả về dưới dạng mili giây epoch.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo?apiKey=YOUR_API_KEY"
```

**JavaScript**

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

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
campaign = res.json()["campaign"]
```

**Phản hồi**

```json
{
  "success": true,
  "campaign": {
    "id": "NBCXrhqGPSFsd6MV7pRo",
    "name": "Inbound WhatsApp Leads",
    "type": "Incoming from Unknown Contacts",
    "status": "Live",
    "language": "en",
    "ai_mode": true,
    "enabled": true,
    "archived": false,
    "created_at": 1700000000000,
    "enabled_channels": ["whatsapp", "instagram"],
    "bot": {
      "instructions": "Greet warmly and ask about their goals.",
      "goal": "Book a discovery call.",
      "ai_speed": "balanced",
      "anthropic_model": "standard",
      "max_messages": 20
    }
  }
}
```

::: note
**Lưu ý:** Một chiến dịch thuộc sở hữu của tài khoản khác sẽ trả về `404 Campaign not found` (không phải `403`), vì vậy bạn không thể biết liệu một ID có tồn tại trên tài khoản khác hay không.
:::


---

## Tạo một chiến dịch

`POST /campaigns`

Tạo một chiến dịch mới. `name` và `type` là bắt buộc; mọi thứ khác là tùy chọn. Bạn có thể bao gồm bất kỳ trường chiến dịch nào khác trong cùng một yêu cầu — ví dụ: `language`, `ai_mode` hoặc một đối tượng cấu hình `bot` đầy đủ — và nó sẽ được lưu trữ cùng với chiến dịch mới. Chủ sở hữu và thời gian tạo được thiết lập tự động.

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

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `name` | Có | Tên chiến dịch. |
| `type` | Có | Một trong bốn loại chiến dịch ở trên. |
| `language` | Không | Ngôn ngữ bot sử dụng để phản hồi (ví dụ: `"en"`). |
| `ai_mode` | Không | Trạng thái bật/tắt chế độ AI (`true`/`false`). Đối với chiến dịch được trả lời bởi AI Agent, các giá trị đọc được sẽ trả về trạng thái **Active** (Đang hoạt động) của Agent thay vì giá trị đã lưu — xem ghi chú bên dưới về việc cập nhật. |
| `bot` | Không | Đối tượng cấu hình bot (xem [Các trường cấu hình bot](#bot-configuration-fields)). |
| `list_id` | Không | ID của danh sách liên hệ cần đính kèm. |
| `event_id` | Không | ID của loại sự kiện mà AI có thể đặt lịch. |
| `event_ids` | Không | Nhiều loại sự kiện cùng lúc, dưới dạng một mảng các ID loại sự kiện — loại đầu tiên là mặc định. Gửi `event_id` hoặc `event_ids`, không gửi cả hai. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Spring Promo",
    "type": "Outgoing",
    "language": "en",
    "ai_mode": true,
    "bot": {
      "instructions": "Greet warmly and ask about their goals.",
      "goal": "Book a discovery call."
    }
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/campaigns", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Spring Promo",
    type: "Outgoing",
    language: "en",
    ai_mode: true,
    bot: {
      instructions: "Greet warmly and ask about their goals.",
      goal: "Book a discovery call.",
    },
  }),
});
const { campaign_id } = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/campaigns",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "name": "Spring Promo",
        "type": "Outgoing",
        "language": "en",
        "ai_mode": True,
        "bot": {
            "instructions": "Greet warmly and ask about their goals.",
            "goal": "Book a discovery call.",
        },
    },
)
campaign_id = res.json()["campaign_id"]
```

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

---

## Cập nhật chiến dịch

`PUT /campaigns/{campaignId}`

Cập nhật một phần chiến dịch — chỉ gửi các trường bạn muốn thay đổi. Đây là động từ cập nhật chung duy nhất; không có `PATCH /campaigns/{campaignId}` (hai lộ trình `PATCH` là các nút chuyển đổi [bật](#enable-or-disable-a-campaign) và [lưu trữ](#archive-or-restore-a-campaign) hẹp).

**Các trường bạn có thể thay đổi.** Mọi thứ mà trình chỉnh sửa chiến dịch ghi lại, bao gồm `name`, `status`, `type`, `language`, `ai_mode`, `enabled_channels`, các cài đặt kích hoạt và nhỏ giọt, các cờ đặt chỗ và theo dõi, các trường giám sát Instagram/Facebook và toàn bộ cấu hình `bot`. Danh tính và quyền sở hữu bị khóa trong suốt vòng đời của chiến dịch: `user`, `id` và `created_at` sẽ bị từ chối, cũng như bất kỳ tên trường nào mà điểm cuối không nhận dạng được. Việc từ chối được áp dụng cho mỗi yêu cầu, không phải cho mỗi trường — một khóa không xác định sẽ trả về `400` và **không có gì** trong yêu cầu đó được ghi lại.

**`ai_mode` trên một chiến dịch có hỗ trợ Agent sẽ phản ánh trạng thái của Agent đó.** Khi một chiến dịch được trả lời bởi AI Agent, việc đọc chiến dịch sẽ trả về `ai_mode` dựa trên nút gạt **Active** của Agent đó — công tắc duy nhất thực sự quyết định việc AI có phản hồi hay không. Việc ghi `ai_mode` trên chiến dịch như vậy vẫn được chấp nhận nhưng sẽ không thay đổi giá trị bạn đọc được; thay vào đó, hãy bật hoặc tắt nút gạt Active của Agent (trong bảng điều khiển hoặc thông qua Agents API). Đối với các chiến dịch cổ điển không có Agent, `ai_mode` vẫn đọc và ghi giá trị đã lưu như trước đây.

**Các trường bot được hợp nhất, chúng không ghi đè.** Gửi cài đặt bot dưới dạng các khóa có dấu chấm (`"bot.instructions": "..."`) hoặc dưới dạng một đối tượng lồng nhau (`"bot": { "instructions": "..." }`) — cả hai đều ghi theo từng lá, vì vậy các trường bạn bỏ qua sẽ giữ nguyên giá trị hiện tại của chúng. `bot.instructions`, `bot.goal`, `bot.rules` và `bot.personality` đều có thể chỉnh sửa theo cách này, cũng như mọi cài đặt bot khác được liệt kê trong [Các trường cấu hình bot](#bot-configuration-fields). Điều tương tự cũng áp dụng cho `test_bot`, `frequency` và `follow_up_config`.

Để thay thế toàn bộ cấu hình bot — xóa bất kỳ trường nào bạn không gửi — hãy sử dụng `bot_replace` (hoặc `test_bot_replace`) với đối tượng hoàn chỉnh. Bạn không thể kết hợp thay thế và hợp nhất cho cùng một đối tượng trong một yêu cầu; điều đó sẽ trả về `400`.

::: note
**Lưu ý:** Việc ghi `bot.*` thông qua API sẽ có hiệu lực **ngay lập tức** trên chiến dịch đang hoạt động. Trình chỉnh sửa bảng điều khiển hoạt động khác: các chỉnh sửa ở đó được lưu dưới dạng bản nháp và chỉ hoạt động khi khách hàng nhấp vào Xuất bản. Vì vậy, nếu khách hàng có các thay đổi chưa xuất bản trên bảng điều khiển, chúng sẽ nằm trong `test_bot` và việc đọc API của `bot` sẽ hiển thị chính xác những gì AI đang sử dụng ngay bây giờ.
:::


Một vài trường được thiết lập thông qua một khóa chuyên dụng thay vì ghi trực tiếp: sử dụng `list_id` cho danh sách liên hệ, `event_id` cho loại sự kiện (hoặc `event_ids`, một mảng có thứ tự các ID loại sự kiện, để cho phép AI đặt nhiều loại — loại đầu tiên là mặc định; một mảng trống sẽ hủy liên kết tất cả), và `contact_ids` (một mảng các ID liên hệ) cho các liên hệ của chiến dịch. Các mục trong cơ sở kiến thức được quản lý thông qua [FAQs API](faqs.md), không phải endpoint này.

**Các thẻ thay thế, chúng không hợp nhất.** Hãy gửi `tags` dưới dạng mảng hoàn chỉnh và nó sẽ trở thành bộ thẻ của chiến dịch — xem [Thẻ chiến dịch](#campaign-tags) để biết các trường và các điểm cuối (endpoint) dùng để thêm hoặc chỉnh sửa một thẻ đơn lẻ.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Spring Promo v2", "enabled_channels": ["whatsapp"] }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      name: "Spring Promo v2",
      enabled_channels: ["whatsapp"],
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"name": "Spring Promo v2", "enabled_channels": ["whatsapp"]},
)
data = res.json()
```

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

---

## Xóa chiến dịch

`DELETE /campaigns/{campaignId}`

Xóa vĩnh viễn một chiến dịch. Hành động này không thể hoàn tác — nếu bạn có thể cần lại chiến dịch này, hãy [lưu trữ nó](#archive-or-restore-a-campaign) thay thế.

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
  { 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/campaigns/NBCXrhqGPSFsd6MV7pRo",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Phản hồi**

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

---

## Sao chép chiến dịch

`POST /campaigns/{campaignId}/duplicate`

Tạo một bản sao của chiến dịch với tất cả các cài đặt được giữ nguyên. Bản sao sẽ bắt đầu ở trạng thái **đã tắt** và tên của nó sẽ có thêm hậu tố `(copy)`, vì vậy nó sẽ không bao giờ gửi tin nhắn cho đến khi bạn bật nó lên một cách rõ ràng.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/duplicate?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/duplicate",
  { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const { campaign_id } = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/duplicate",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
new_campaign_id = res.json()["campaign_id"]
```

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "aZ9plnewCopyId01234"
}
```

> Các bản sao trùng lặp **trong cùng một tài khoản**.

---


## Bật hoặc tắt chiến dịch

`PATCH /campaigns/{campaignId}/enabled`

Bật hoặc tắt một chiến dịch. Một chiến dịch bị tắt sẽ ngừng tương tác với các liên hệ nhưng vẫn giữ nguyên tất cả cấu hình của nó.

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

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `enabled` | Có | `true` để bật, `false` để tắt. Phải là giá trị boolean. |

**cURL**

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/enabled?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/enabled",
  {
    method: "PATCH",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ enabled: true }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.patch(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/enabled",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"enabled": True},
)
data = res.json()
```

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "enabled": true
}
```

---

## Lưu trữ hoặc khôi phục chiến dịch

`PATCH /campaigns/{campaignId}/archived`

Lưu trữ hoặc khôi phục một chiến dịch. Các chiến dịch đã lưu trữ sẽ bị ẩn khỏi danh sách chiến dịch mặc định nhưng vẫn giữ lại tất cả dữ liệu của chúng và có thể được khôi phục bất cứ lúc nào.

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

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `archived` | Có | `true` để lưu trữ, `false` để khôi phục. Phải là giá trị boolean. |

**cURL**

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/archived?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "archived": true }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/archived",
  {
    method: "PATCH",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ archived: true }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.patch(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/archived",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"archived": True},
)
data = res.json()
```

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "archived": true
}
```

---

## Cập nhật cấu hình bot

`PUT /campaigns/{campaignId}/bot-config`

Đây là cách an toàn để thay đổi các cài đặt riêng lẻ của bot. Mỗi trường bạn gửi sẽ được **hợp nhất** vào cấu hình bot hiện có, vì vậy bất kỳ trường nào bạn bỏ qua đều được giữ nguyên. Hãy sử dụng cách này thay vì endpoint cập nhật chiến dịch bất cứ khi nào bạn chỉ muốn tinh chỉnh một phần của bot.

Các khóa trường chỉ được sử dụng chữ cái, số, dấu gạch dưới và dấu gạch nối.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/bot-config?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "instructions": "Always answer in a friendly, concise tone.",
    "ai_speed": "balanced"
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/bot-config",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      instructions: "Always answer in a friendly, concise tone.",
      ai_speed: "balanced",
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/bot-config",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "instructions": "Always answer in a friendly, concise tone.",
        "ai_speed": "balanced",
    },
)
data = res.json()
```

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

### Các trường cấu hình bot

Tất cả các trường của bot đều là tùy chọn. Chỉ gửi những trường bạn muốn thiết lập. Bất kỳ trường bot bổ sung nào ngoài những trường được liệt kê ở đây đều được chấp nhận và lưu trữ nguyên trạng.

| Trường | Loại | Mô tả |
|---|---|---|
| `instructions` | string | Các hướng dẫn chính điều hướng cách bot trò chuyện với các liên hệ. |
| `rules` | string | Các quy tắc cứng mà bot phải luôn tuân thủ. |
| `goal` | string | Kết quả mà bot nên hướng tới trong mỗi cuộc trò chuyện. |
| `personality` | string | Mô tả giọng điệu và tính cách của bot. |
| `ai_speed` | string | Mức độ suy luận mà AI áp dụng trước khi trả lời. Một trong các giá trị `fast`, `fast_thinker`, `balanced`, `thorough`. |
| `anthropic_model` | string | Cấp độ chất lượng AI được sử dụng cho các câu trả lời của chiến dịch này. Một trong các giá trị `standard`, `economy` (đã lỗi thời), `max`, `mini`. `max` và `mini` chỉ có hiệu lực trên các tài khoản đủ điều kiện cho các cấp độ đó. |
| `max_messages` | integer | Số lượng tin nhắn tối đa của bot trong mỗi cuộc trò chuyện. |
| `alert_human_when` | string | Các điều kiện mà bot nên cảnh báo cho nhân viên con người. |
| `availability` | object | Lịch trình giờ hoạt động của bot. Bạn có thể thiết lập tại đây hoặc sử dụng [endpoint giờ hoạt động](#set-the-bot-active-hours) chuyên dụng. |
| `follow_up_config` | object | Cấu hình hành vi theo dõi, được lưu trữ như đã cung cấp. |

---

## Thiết lập giờ hoạt động của bot

`PUT /campaigns/{campaignId}/active-hours`

Thiết lập lịch trình khả dụng của bot. Ngoài các khoảng thời gian được cấu hình, bot sẽ không tự động trả lời. Thao tác này sẽ ghi vào trường `availability` của cấu hình bot.

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

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `availability` | Có | Một đối tượng được lập chỉ mục theo ngày trong tuần. Các khóa được phép là `monday` đến `sunday`; bất kỳ khóa nào khác sẽ trả về lỗi `400`. Những ngày bạn bỏ qua sẽ không bị thay đổi. |

Mỗi ngày trong tuần chứa một khoảng thời gian duy nhất hoặc một mảng các khoảng thời gian. Một khoảng thời gian có `start_time` và `end_time` theo định dạng `HH:MM` 24 giờ.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/active-hours?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "availability": {
      "monday": { "start_time": "09:00", "end_time": "17:00" },
      "tuesday": [
        { "start_time": "09:00", "end_time": "12:00" },
        { "start_time": "13:00", "end_time": "17:00" }
      ]
    }
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/active-hours",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      availability: {
        monday: { start_time: "09:00", end_time: "17:00" },
        tuesday: [
          { start_time: "09:00", end_time: "12:00" },
          { start_time: "13:00", end_time: "17:00" },
        ],
      },
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/active-hours",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "availability": {
            "monday": {"start_time": "09:00", "end_time": "17:00"},
            "tuesday": [
                {"start_time": "09:00", "end_time": "12:00"},
                {"start_time": "13:00", "end_time": "17:00"},
            ],
        }
    },
)
data = res.json()
```

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

---

## Liệt kê các hàm tùy chỉnh của chiến dịch

`GET /campaigns/{campaignId}/custom-functions`

Trả về các hàm tùy chỉnh được liên kết với chiến dịch này, được phân giải thành các định nghĩa đầy đủ. Các hàm tùy chỉnh là các hành động HTTP bên ngoài mà bot có thể gọi trong khi hội thoại — ví dụ: kiểm tra hàng tồn kho trong cửa hàng của bạn hoặc tạo bản ghi trong CRM của bạn.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/custom-functions?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/custom-functions",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const { custom_functions } = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/custom-functions",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
custom_functions = res.json()["custom_functions"]
```

**Phản hồi**

```json
{
  "success": true,
  "custom_functions": [
    {
      "id": "fn_abc123",
      "name": "check_stock",
      "description": "Looks up whether a product is in stock.",
      "url": "https://example.com/api/stock",
      "method": "POST",
      "input": [
        { "name": "sku", "type": "string" }
      ],
      "ai_action": "Tell the customer whether the item is available.",
      "created_at": 1700000000000,
      "updated_at": 1700000500000
    }
  ]
}
```

---

## Liên kết một hàm tùy chỉnh với một chiến dịch

`POST /campaigns/{campaignId}/custom-functions`

Liên kết một [hàm tùy chỉnh](../ai-automation/custom-functions.md) hiện có với chiến dịch này để bot có thể gọi hàm đó trong khi trò chuyện. Việc liên kết một hàm đã được liên kết sẽ không có tác dụng gì.

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `custom_function_id` | Có | ID của hàm tùy chỉnh cần liên kết. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/custom-functions?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "custom_function_id": "fn_abc123" }'
```

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "custom_function_id": "fn_abc123"
}
```

---

## Hủy liên kết một hàm tùy chỉnh khỏi một chiến dịch

`DELETE /campaigns/{campaignId}/custom-functions/{customFunctionId}`

Việc hủy liên kết một hàm chưa được liên kết sẽ không có tác dụng gì.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/custom-functions/fn_abc123?apiKey=YOUR_API_KEY"
```

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "custom_function_id": "fn_abc123"
}
```

---

## Liên kết một nguồn cơ sở tri thức với một chiến dịch

`POST /campaigns/{campaignId}/kb-sources`

Liên kết một nguồn cơ sở tri thức (được tạo thông qua [API FAQ](faqs.md)) với chiến dịch này để bot có thể sử dụng nguồn đó khi trả lời. Việc liên kết một nguồn đã được liên kết sẽ không có tác dụng gì.

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `kb_source_id` | Có | ID của nguồn cơ sở tri thức cần liên kết. |

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

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "kb_source_id": "kb_abc123"
}
```

---

## Hủy liên kết một nguồn cơ sở tri thức khỏi một chiến dịch

`DELETE /campaigns/{campaignId}/kb-sources/{kbSourceId}`

Việc hủy liên kết một nguồn chưa được liên kết sẽ không có tác dụng gì.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/kb-sources/kb_abc123?apiKey=YOUR_API_KEY"
```

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "kb_source_id": "kb_abc123"
}
```

---

## Liên kết một máy chủ MCP với một chiến dịch

`POST /campaigns/{campaignId}/mcp-servers`

Liên kết một máy chủ MCP với chiến dịch này, cho phép bot truy cập vào các công cụ của máy chủ đó trong cuộc trò chuyện. Việc liên kết một máy chủ đã được liên kết sẽ không có tác dụng gì.

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `mcp_server_id` | Có | ID của máy chủ MCP cần liên kết. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/mcp-servers?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "mcp_server_id": "mcp_abc123" }'
```

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "mcp_server_id": "mcp_abc123"
}
```

---

## Hủy liên kết máy chủ MCP khỏi chiến dịch

`DELETE /campaigns/{campaignId}/mcp-servers/{mcpServerId}`

Việc hủy liên kết một máy chủ chưa được liên kết sẽ không có tác dụng gì.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/mcp-servers/mcp_abc123?apiKey=YOUR_API_KEY"
```

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "mcp_server_id": "mcp_abc123"
}
```

---

## Thư viện phương tiện của chiến dịch

Thư viện phương tiện lưu trữ hình ảnh, video, tài liệu và ghi chú thoại mà bot có thể gửi trong cuộc trò chuyện.

### Liệt kê thư viện phương tiện của chiến dịch

`GET /campaigns/{campaignId}/media-library`

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/media-library?apiKey=YOUR_API_KEY"
```

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "media_items": [
    {
      "id": "media_abc123",
      "item_id": "media_abc123",
      "title": "Pricing sheet",
      "description": "Send when the contact asks about pricing.",
      "media_url": "https://example.com/pricing.pdf",
      "media_content_type": "application/pdf",
      "type": "document",
      "agent_id": "",
      "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
      "media_home": "campaign"
    }
  ]
}
```

`media_url` là một URL đã ký được ghi lại tại thời điểm tải lên — nó có thể đã hết hạn vào thời điểm bạn đọc lại; bảng điều khiển sẽ ký lại URL đó theo yêu cầu.

### Tải lên một mục phương tiện

`POST /campaigns/{campaignId}/media-library`

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `base64Data` | Có | Tệp tin, được mã hóa base64 (không có tiền tố data-URL). |
| `mimeType` | Có | Loại MIME của tệp (ví dụ: `image/png`). |
| `title` | Có | Nhãn ngắn hiển thị trong thư viện và trong lời nhắc AI. |
| `description` | Có | Hướng dẫn cho bot biết **khi nào** nên gửi mục này. |
| `fileName` | Không | Tên tệp gốc, được sử dụng để tạo tên đối tượng lưu trữ. |
| `sendMessage` | Không | Cách diễn đạt ưu tiên mà bot nên sử dụng khi gửi mục này. |
| `maxSendsPerConversation` | Không | Số lần tối đa bot có thể gửi mục này cho một liên hệ trong một cuộc trò chuyện. Mặc định là `1`. |
| `sendAsVoiceNote` | Không | Đối với tệp âm thanh tải lên, hãy chuyển mã nó thành ghi chú thoại WhatsApp. Mặc định là `false` (được lưu trữ dưới dạng tệp âm thanh thông thường). |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/media-library?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "base64Data": "iVBORw0KGgoAAAANSUhEUgAA...",
    "mimeType": "image/png",
    "title": "Product photo",
    "description": "Send when the contact asks what the product looks like."
  }'
```

**Phản hồi**

```json
{
  "success": true,
  "itemId": "media_abc123",
  "mediaUrl": "https://example.com/product.png",
  "storagePath": "ai_media/campaigns/NBCXrhqGPSFsd6MV7pRo/media_abc123.png",
  "mediaContentType": "image/png",
  "type": "image",
  "isVoiceNote": false
}
```

### Cập nhật mục phương tiện

`PATCH /campaigns/{campaignId}/media-library/{itemId}`

Chỉ chỉnh sửa siêu dữ liệu của mục — để thay thế tệp, hãy xóa mục đó và tải lên tệp mới.

| Trường | Mô tả |
|---|---|
| `title` | Nhãn ngắn. |
| `description` | Hướng dẫn về thời điểm gửi. |
| `send_message` | Cách diễn đạt ưu tiên để bot sử dụng. |
| `max_sends_per_conversation` | Số nguyên không âm, hoặc `null` để xóa giới hạn. |

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/media-library/media_abc123?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "title": "Updated pricing sheet" }'
```

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "item_id": "media_abc123"
}
```

### Xóa mục phương tiện

`DELETE /campaigns/{campaignId}/media-library/{itemId}`

Việc xóa một mục đã không còn tồn tại sẽ không thực hiện hành động nào.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/media-library/media_abc123?apiKey=YOUR_API_KEY"
```

**Phản hồi**

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

---

## Thẻ chiến dịch

Thẻ chiến dịch là một nhãn mà bạn dạy bot áp dụng cho liên hệ trong cuộc trò chuyện — `hot-lead`, `not-interested`, `booked-a-call`. Mỗi thẻ có ba phần:

| Trường | Loại | Mô tả |
|---|---|---|
| `name` | chuỗi, bắt buộc | Bản thân nhãn đó. Đây là nội dung bot áp dụng cho liên hệ và là nội dung bạn sẽ khớp sau này, vì vậy hãy giữ cho nó ngắn gọn và ổn định. |
| `description` | chuỗi | Hướng dẫn cho bot biết **khi nào** cần áp dụng thẻ này. Đây là phần thực hiện công việc — "người đó xác nhận họ đã tham gia cộng đồng" sẽ được sử dụng, "khách hàng tiềm năng" thì không. |
| `webhook` | chuỗi | Một URL nhận `POST` ngay khi thẻ được gắn vào một liên hệ. Hãy bỏ trống nếu bạn không cần. |
| `tag_id` | chuỗi | Tùy chọn. Liên kết mục nhập này với một thẻ hiện có trong tài khoản của bạn thay vì tạo thẻ mới. Hãy cung cấp nếu bạn muốn xử lý thẻ cụ thể này sau đó bằng các điểm cuối thẻ đơn lẻ bên dưới. |

Tên thẻ phải là duy nhất trong một chiến dịch. Bot áp dụng thẻ **theo tên**, vì vậy hai mục nhập có cùng một tên sẽ không có kết quả xác định.

### Thiết lập tất cả các thẻ của chiến dịch

`PUT /campaigns/{campaignId}` với một mảng `tags`.

Thao tác này thay thế các thẻ của chiến dịch bằng chính xác những gì bạn gửi, giống như những gì tab Thẻ trên bảng điều khiển thực hiện khi bạn lưu. **Hãy gửi mảng hoàn chỉnh mỗi lần** — một thẻ bạn bỏ qua là một thẻ bạn đã xóa. Gửi `[]` sẽ xóa tất cả các thẻ.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tags": [
      {
        "name": "hot-lead",
        "description": "The person confirms they want to buy, or asks how to get started right away.",
        "webhook": "https://example.com/hooks/campaign-events"
      },
      {
        "name": "not-interested",
        "description": "The person declines the offer or says they are not a fit."
      }
    ]
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      tags: [
        {
          name: "hot-lead",
          description:
            "The person confirms they want to buy, or asks how to get started right away.",
          webhook: "https://example.com/hooks/campaign-events",
        },
        {
          name: "not-interested",
          description: "The person declines the offer or says they are not a fit.",
        },
      ],
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "tags": [
            {
                "name": "hot-lead",
                "description": "The person confirms they want to buy, or asks how to get started right away.",
                "webhook": "https://example.com/hooks/campaign-events",
            },
            {
                "name": "not-interested",
                "description": "The person declines the offer or says they are not a fit.",
            },
        ]
    },
)
data = res.json()
```

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

Đọc lại các thẻ bằng [`GET /campaigns/{campaignId}`](#get-a-campaign).

### Thêm một thẻ

`POST /campaigns/{campaignId}/tags`

Thêm một thẻ đơn lẻ mà không cần gửi lại phần còn lại. Sử dụng cách này khi bạn đang thêm vào một tập hợp mà bạn không xây dựng trong yêu cầu này.

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/tags?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "tag": { "name": "booked-a-call", "description": "The person confirms a booked time." } }'
```

Việc đăng cùng một thẻ hai lần sẽ không có tác dụng gì ở lần thứ hai. Đăng cùng một `tag_id` với tên hoặc mô tả khác sẽ thêm một mục nhập **thứ hai** thay vì chỉnh sửa mục nhập đầu tiên — hãy sử dụng điểm cuối bên dưới để chỉnh sửa tại chỗ.

### Cập nhật hoặc xóa một thẻ

`PUT /campaigns/{campaignId}/tags/{tagId}`
`DELETE /campaigns/{campaignId}/tags/{tagId}`

Các địa chỉ này nhắm đến một mục nhập bằng `tag_id` của nó, vì vậy chúng chỉ hoạt động trên các thẻ được tạo bằng một mục nhập. Nếu một thẻ không có `tag_id`, hãy thay đổi nó bằng `PUT /campaigns/{campaignId}` cho toàn bộ mảng ở trên.

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/tags/tag_abc123?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "tag": { "name": "hot-lead", "description": "Updated instruction." } }'
```

Một `tagId` không nằm trong chiến dịch sẽ trả về `404` với `"Tag not found in campaign tags"`.

---

## Chuyển đổi các kênh của chiến dịch

`POST /campaigns/{campaignId}/channels`

Thêm hoặc xóa các kênh khỏi mảng `enabled_channels` của chiến dịch mà không cần gửi lại toàn bộ mảng — an toàn hơn so với [`PUT /campaigns/{campaignId}`](#update-a-campaign) khi có thứ khác có thể đang chỉnh sửa chiến dịch cùng lúc.

Gửi một lần chuyển đổi đơn lẻ hoặc một loạt — không gửi cả hai trong cùng một yêu cầu:

```json
{ "channel": "whatsapp", "action": "add" }
```

```json
{ "add": ["whatsapp", "instagram"], "remove": ["sms"] }
```

| Trường | Mô tả |
|---|---|
| `channel` | Một kênh để chuyển đổi. Ghép cặp với `action`. |
| `action` | `"add"` hoặc `"remove"`. Ghép cặp với `channel`. |
| `add` | Mảng các kênh cần thêm. Dạng hàng loạt — sử dụng thay cho `channel`/`action`. |
| `remove` | Mảng các kênh cần xóa. Dạng hàng loạt. |

Các kênh hợp lệ: `whatsapp`, `whatsapp_web`, `sms`, `instagram`, `messenger`, `facebook`, `chat_widget`, `custom_channel`, `imessage`, `telegram`, `instagram_private`, `line`, `viber`, `tiktok`, `email`, `linkedin`, `skool`.

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/channels?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "channel": "whatsapp", "action": "add" }'
```

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "added": ["whatsapp"],
  "removed": []
}
```

> Điều này chỉ thay đổi các kênh mà chiến dịch quảng bá — nó không quyết định ai sẽ trả lời một kênh. Xem [Các loại chiến dịch](#campaign-types) ở trên và [Định tuyến chiến dịch đến các kênh đến](#route-a-campaign-to-incoming-channels) ở bên dưới để biết thêm chi tiết.

---

## Comment-to-DM (Instagram và Facebook)

Comment-to-DM biến bình luận trên bài đăng của bạn thành một cuộc trò chuyện riêng tư: ai đó bình luận, bot sẽ gửi cho họ một tin nhắn trực tiếp (DM), và chiến dịch sẽ tiếp tục cuộc trò chuyện từ đó. Tính năng này được cấu hình hoàn toàn thông qua đối tượng chiến dịch, vì vậy không có phần nào chỉ dành riêng cho giao diện người dùng.

Trước tiên hãy kết nối Trang Facebook — xem [Kết nối Kênh](channels.md#instagram--messenger-meta). Sau đó, thiết lập các trường bên dưới bằng [`PUT /campaigns/{campaignId}`](#update-a-campaign).

> **Chiến dịch phải ở trạng thái `Live`.** Việc theo dõi bình luận chỉ chọn các chiến dịch có `status` là `Live` (không phân biệt chữ hoa chữ thường — xem [Các loại chiến dịch](#campaign-types)). Bất kỳ trạng thái nào khác sẽ âm thầm vô hiệu hóa nó, và một trạng thái tự tạo như `"Active"` hiện sẽ bị từ chối kèm theo `400` thay vì được lưu trữ. Các trạng thái hợp lệ bao gồm `Draft`, `Pending Approval`, `Scheduled`, `Live`, `Paused`, `Completed`, `Sent` và `Failed`.

**Các trường**

| Trường | Kiểu dữ liệu | Mô tả |
|---|---|---|
| `monitor_instagram_posts` | boolean | Theo dõi mọi bài đăng Instagram trên trang được kết nối. |
| `instagram_post_ids` | string[] | Chỉ theo dõi các bài đăng Instagram này. Để trống khi `monitor_instagram_posts` đang bật. |
| `instagram_comment_delay_minutes` | number | Đợi số phút này sau khi có bình luận trước khi gửi tin nhắn trực tiếp (DM). |
| `monitor_facebook_posts` | boolean | Theo dõi mọi bài đăng Facebook trên trang được kết nối. |
| `facebook_post_ids` | string[] | Chỉ theo dõi các bài đăng Facebook này. |
| `facebook_comment_delay_minutes` | number | Độ trễ trước khi gửi DM, tính bằng phút. |
| `public_comment_reply_instructions` | string | Hướng dẫn cho phản hồi công khai để lại trên chính bình luận đó. Ghi đè lên cách diễn đạt mặc định "kiểm tra DM của bạn". |
| `first_response_mode` | string | `"ai"` (mặc định) tạo DM đầu tiên và phản hồi công khai. `"exact_text"` gửi chính xác nội dung bạn đã viết, không sử dụng AI tạo và không tính phí tín dụng. |
| `first_response_exact_text` | string | DM đầu tiên nguyên văn, được sử dụng khi `first_response_mode` là `"exact_text"`. Bắt buộc để chế độ đó có hiệu lực. |
| `first_response_exact_text_variants` | string[] | Các cách diễn đạt bổ sung cho DM đầu tiên. Một cách sẽ được chọn ngẫu nhiên cho mỗi lần gửi, vì vậy các DM lặp lại sẽ không giống hệt nhau về byte. |
| `public_comment_reply_exact_text` | string | Phản hồi công khai nguyên văn ở chế độ `"exact_text"`. Để trống để bỏ qua phản hồi công khai và chỉ gửi DM. |
| `public_comment_reply_exact_text_variants` | string[] | Các cách diễn đạt bổ sung cho phản hồi công khai. |
| `monitor_instagram_followers` | boolean | Coi người theo dõi mới là một trình kích hoạt và gửi DM chào mừng (tài khoản cá nhân Instagram). |
| `follower_outreach_instructions` | string | Hướng dẫn cho DM chào mừng người theo dõi mới đó. |
| `respond_to_instagram_story_replies` | boolean | Liệu AI có trả lời các phản hồi cho Tin (Story) Instagram của bạn hay không. Mặc định là `true`. Đặt `false` để các phản hồi Tin (Story) đi vào cuộc trò chuyện (kèm theo Tin) mà không có phản hồi từ AI. Cài đặt trực tiếp — không phải là một phần của bản nháp, vì vậy không cần xuất bản. |

**Xóa một trường**

Các trường này bị xóa thay vì được đặt thành `null` khi bạn gửi `null`, vì vậy bot sẽ quay lại các giá trị mặc định của nó: `instagram_post_ids`, `facebook_post_ids`, `instagram_comment_delay_minutes`, `facebook_comment_delay_minutes`, `public_comment_reply_instructions`, `follower_outreach_instructions`, `first_response_exact_text`, `first_response_exact_text_variants`, `public_comment_reply_exact_text`, `public_comment_reply_exact_text_variants`.

> **Một khóa không xác định sẽ từ chối toàn bộ yêu cầu.** `PUT /campaigns/{campaignId}` xác thực toàn bộ nội dung dựa trên danh sách cho phép. Một khóa không được nhận dạng sẽ trả về `400` cho toàn bộ yêu cầu — nó không bị âm thầm bỏ qua, và không có trường nào khác trong nội dung đó được ghi lại.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "Live",
    "monitor_instagram_posts": true,
    "instagram_comment_delay_minutes": 2,
    "first_response_mode": "exact_text",
    "first_response_exact_text": "Hey! Sending the details over now.",
    "first_response_exact_text_variants": [
      "Hi there, here are the details you asked for.",
      "Thanks for commenting, here is what you need."
    ],
    "public_comment_reply_exact_text": "Just sent you a DM."
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      status: "Live",
      monitor_instagram_posts: true,
      instagram_comment_delay_minutes: 2,
      first_response_mode: "ai",
      public_comment_reply_instructions:
        "Tell them to check their message requests folder too.",
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "status": "Live",
        "monitor_facebook_posts": True,
        "facebook_post_ids": None,
        "facebook_comment_delay_minutes": 5,
    },
)
data = res.json()
```

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

> Phản hồi hiển thị trên bình luận yêu cầu tính năng trả lời bình luận trong gói của bạn. Nếu không có tính năng này, DM vẫn được gửi và phản hồi công khai sẽ bị bỏ qua.

---

## Tối ưu hóa chiến dịch bằng AI

`POST /campaigns/{campaignId}/optimize`

Chạy cùng một quy trình viết lại AI như các luồng Tối ưu hóa (Optimize) và phản hồi không thích (thumbs-down) trên bảng điều khiển: nhận phản hồi của bạn, viết lại các hướng dẫn của bot và chuẩn bị kết quả dưới dạng một bản nháp mới để bạn xem xét.

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `user_feedback` | Bắt buộc chọn một trong hai | Phản hồi tự do mô tả những gì cần cải thiện. |
| `thumbs_down_feedback` | Bắt buộc chọn một trong hai | Phản hồi được ghi lại từ việc nhấn không thích (thumbs-down) trên một phản hồi cụ thể của bot. |
| `thumbs_down_message` | Không | Tin nhắn của bot mà phản hồi không thích đề cập đến. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/optimize?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "user_feedback": "Make the tone more casual and mention the free trial earlier." }'
```

**Phản hồi** (`202` — quá trình viết lại chạy trong nền)

```json
{ "success": true, "campaign_id": "NBCXrhqGPSFsd6MV7pRo" }
```

Hãy thăm dò [`GET /campaigns/{campaignId}`](#get-a-campaign) và theo dõi `test_bot.status`: nó sẽ chuyển sang `"Optimizing"` ngay lập tức, sau đó quay lại `"Draft"` khi quá trình viết lại hoàn tất trong `test_bot`. Từ đó, nó hoạt động giống như bất kỳ bản nháp nào trên bảng điều khiển — hãy xem xét nó, sau đó xuất bản trên bảng điều khiển để đưa vào hoạt động. Một `409` có nghĩa là một quá trình tối ưu hóa đang chạy cho chiến dịch này.

> Việc tối ưu hóa sẽ tốn tín dụng, giống như bất kỳ hoạt động AI nào khác trên tài khoản của bạn.

---

## Gán một liên hệ vào chiến dịch

`POST /campaigns/{campaignId}/contacts/{contactId}/assign`

Đưa một liên hệ hiện có vào chiến dịch và, nếu bạn yêu cầu, sẽ gửi ngay tin nhắn mở đầu của chiến dịch đó. Đây là cách để gửi mẫu WhatsApp đã được phê duyệt của chiến dịch cho một liên hệ: mẫu mà chiến dịch được phê duyệt cùng thuộc về chiến dịch đó, vì vậy nó không xuất hiện trong thư viện [Templates API](templates.md) và không thể được gửi thông qua `/whatsapp-templates/send`.

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `sendOpeningMessage` | Không | `true` gửi tin nhắn mở đầu của chiến dịch (mẫu WhatsApp đã được phê duyệt trên chiến dịch WhatsApp) ngay khi liên hệ được gán. Mặc định là `false`. |
| `triggerAIResponse` | Không | `true` cho phép AI tự viết tin nhắn đầu tiên thay thế. Mặc định là `false`. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/contacts/contact_abc123/assign?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "sendOpeningMessage": true }'
```

**Phản hồi**

```json
{
  "success": true,
  "data": { "contactId": "contact_abc123", "campaignId": "NBCXrhqGPSFsd6MV7pRo" }
}
```

> **Tín dụng:** Việc gửi tin nhắn mở đầu trong chiến dịch WhatsApp được tính phí như bất kỳ lượt gửi mẫu nào, dựa trên quốc gia của người nhận và danh mục của mẫu. Trên các kênh khác, tin nhắn mở đầu là một tin nhắn gửi đi thông thường.

---

## Định tuyến chiến dịch đến các kênh tiếp nhận

Các điểm cuối này quản lý chiến dịch nào sẽ trả lời các liên hệ mới, chưa xác định trên một kênh. **Ưu tiên sử dụng Entry Points** cho các tích hợp mới (xem ghi chú trong phần [Các loại chiến dịch](#campaign-types)) — các điểm cuối này vẫn hữu ích khi làm việc với các chiến dịch định tuyến theo cách cũ và để giải quyết xung đột quyền sở hữu kênh giữa hai chiến dịch tiếp nhận.

### Gán chiến dịch cho các kênh tiếp nhận

`POST /campaigns/{campaignId}/incoming-routing`

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `channels` | Có | Mảng các kênh mà chiến dịch này sẽ trả lời cho các liên hệ mới, chưa xác định. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/incoming-routing?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "channels": ["whatsapp", "instagram"] }'
```

**Phản hồi**

```json
{
  "success": true,
  "uid": "abc123",
  "campaignId": "NBCXrhqGPSFsd6MV7pRo",
  "channels": ["whatsapp", "instagram"],
  "failed": []
}
```

`channels` chỉ liệt kê các kênh đã được định tuyến thực tế đến chiến dịch này; `failed` liệt kê bất kỳ kênh nào không được định tuyến. Nếu mọi kênh được yêu cầu đều thất bại, bản thân yêu cầu đó cũng sẽ thất bại.

### Xóa định tuyến tiếp nhận của chiến dịch

`DELETE /campaigns/{campaignId}/incoming-routing`

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `channelToUnassign` | Không | Xóa định tuyến chỉ cho kênh này. Bỏ qua để xóa mọi kênh mà chiến dịch này hiện đang trả lời. |

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/incoming-routing?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "channelToUnassign": "instagram" }'
```

**Phản hồi**

```json
{
  "success": true,
  "uid": "abc123",
  "campaignId": "NBCXrhqGPSFsd6MV7pRo",
  "channelsRemoved": ["instagram"]
}
```

### Kích hoạt lại một chiến dịch không hoạt động

`POST /campaigns/{campaignId}/reactivate`

Khôi phục chiến dịch từ `Ended`, `Completed`, `Paused` hoặc `Draft` và yêu cầu lại các kênh của chiến dịch đó. Chỉ hoạt động trên các chiến dịch `Incoming from Unknown Contacts` hoặc `Combined` — một chiến dịch đã `Live` sẽ được coi là thành công và không cần thực hiện thêm thao tác nào.

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/reactivate?apiKey=YOUR_API_KEY"
```

**Phản hồi**

```json
{
  "success": true,
  "data": {
    "success": true,
    "channelsReactivated": ["whatsapp"],
    "channelsBlockedByConflict": [],
    "campaignType": "Incoming from Unknown Contacts"
  }
}
```

Một kênh đã được yêu cầu bởi tác nhân của một chiến dịch khác sẽ hiển thị trong `channelsBlockedByConflict` thay vì làm thất bại toàn bộ cuộc gọi — hãy sử dụng [dừng chiến dịch đến đang xung đột](#stop-a-conflicting-incoming-campaign) bên dưới để giải phóng kênh trước nếu bạn muốn chiến dịch này tiếp quản kênh đó. Một `400` sẽ được trả về cho loại chiến dịch không hỗ trợ kích hoạt lại, hoặc trạng thái không nằm trong các trạng thái không hoạt động ở trên.

### Dừng chiến dịch đến đang xung đột

`POST /campaigns/{campaignId}/stop-incoming`

Giải phóng các kênh của chiến dịch này khỏi bất kỳ chiến dịch KHÁC nào hiện đang nắm giữ chúng, để chiến dịch này có thể yêu cầu chúng tiếp theo. Đây là phiên bản REST của những gì bảng điều khiển thực hiện tự động khi bạn khởi chạy một chiến dịch đến vào một kênh mà người khác đã trả lời.

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/stop-incoming?apiKey=YOUR_API_KEY"
```

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "ended_campaign_ids": [],
  "released_channels": ["whatsapp"],
  "cleared_entire_field": false
}
```

`released_channels` trả về trống khi chiến dịch này đã sở hữu mọi kênh mà nó quảng cáo — không có gì để tiếp quản.

---

## Ước tính chi phí

Ước tính chi phí khởi chạy một chiến dịch trước khi bạn gửi nó.

### Ước tính chi phí mẫu WhatsApp

`GET /campaigns/{campaignId}/template-cost-estimate`

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/template-cost-estimate?apiKey=YOUR_API_KEY"
```

**Phản hồi**

```json
{
  "success": true,
  "billing_mode": "credits",
  "data": {
    "countries": [
      {
        "countryCode": "1",
        "name": "United States",
        "iso": "US",
        "flag": "🇺🇸",
        "contactCount": 120,
        "costPerContact": 2,
        "subtotal": 240
      }
    ],
    "totalContacts": 120,
    "totalTemplateCost": 240,
    "templateCategory": "marketing",
    "billing_mode": "credits",
    "service_messages_billable_soon": false
  }
}
```

`billing_mode` là `"credits"` trên làn đường WhatsApp được quản lý. Trên làn đường mà Meta tính phí trực tiếp cho Tài khoản WhatsApp Business của riêng bạn, `costPerContact`, `subtotal` và `totalTemplateCost` sẽ trả về `null` — không bao giờ là `0`, điều này sẽ được hiểu là miễn phí — vì không có số liệu tín dụng nào để báo cáo.

### Ước tính chi phí SMS

`GET /campaigns/{campaignId}/sms-cost-estimate`

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/sms-cost-estimate?apiKey=YOUR_API_KEY"
```

**Phản hồi**

```json
{
  "success": true,
  "billing_mode": "twilio_direct",
  "data": {
    "totalContacts": 120,
    "messageLength": 87,
    "segmentsPerMessage": 1,
    "totalSegments": 120,
    "estimatedCostUsd": 0.96,
    "priceUnit": "USD per segment",
    "billedByTwilio": true
  }
}
```

SMS luôn được gửi qua tài khoản Twilio của riêng bạn (xem [nhà cung cấp SMS](../settings/sms-provider.md)), vì vậy khoản này luôn được Twilio tính phí trực tiếp — `estimatedCostUsd` là ước tính về hóa đơn Twilio đó, không phải là khoản phí tín dụng.

---

## Kiểm tra giới hạn

Kiểm tra giới hạn trước khi bạn khởi chạy, thay vì phát hiện ra sau khi gửi thất bại.

### Kiểm tra theo phạm vi chiến dịch

`GET /campaigns/{campaignId}/limits/ai-credit-messaging` — liệu việc khởi chạy hoặc lên lịch cho chiến dịch này có vượt quá giới hạn nhắn tin bằng AI-credit của tài khoản của bạn hay không.

`GET /campaigns/{campaignId}/limits/messaging` — liệu việc đó có vượt quá giới hạn nhắn tin hàng ngày của tài khoản của bạn hay không.

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/limits/messaging?apiKey=YOUR_API_KEY"
```

**Phản hồi** (không vượt quá giới hạn)

```json
{
  "success": true,
  "data": "Campaign is within the daily messaging limit."
}
```

Một `400` sẽ được trả về thay thế khi vượt quá giới hạn, với lý do nằm trong `error`.

### Các kiểm tra theo phạm vi tài khoản

`GET /campaigns/limits/campaigns` — liệu bạn đã đạt đến giới hạn tạo chiến dịch hàng tháng của gói đăng ký hay chưa.

`GET /campaigns/limits/contacts` — liệu bạn đã đạt đến giới hạn liên hệ của gói đăng ký hay chưa.

```bash
curl "https://api.youraiconnector.com/v1/campaigns/limits/campaigns?apiKey=YOUR_API_KEY"
```

**Phản hồi**

```json
{
  "success": true,
  "data": "You can create 3 more campaigns this month."
}
```

---

## Tổng số liệu thống kê chiến dịch

`GET /campaigns/stats/totals`

Tổng số tin nhắn đã gửi và phản hồi cho mọi chiến dịch VÀ mọi tác nhân AI trên tài khoản của bạn, trong một khoảng thời gian trượt — cùng những con số mà trang danh sách chiến dịch hiển thị bên cạnh mỗi hàng, trong một lệnh gọi thay vì một yêu cầu cho mỗi chiến dịch.

| Tham số truy vấn | Mô tả |
|---|---|
| `days` | Kích thước của khoảng thời gian trượt, từ 1-365. Mặc định là 90. |

```bash
curl "https://api.youraiconnector.com/v1/campaigns/stats/totals?days=30&apiKey=YOUR_API_KEY"
```

**Phản hồi**

```json
{
  "success": true,
  "byCampaign": {
    "NBCXrhqGPSFsd6MV7pRo": { "sent": 1204, "replied": 318 }
  },
  "byAgent": {
    "agent_abc123": { "sent": 1204, "replied": 318 }
  },
  "windowDays": 30
}
```

`byAgent` là một bản tổng hợp riêng, không phải là tổng của `byCampaign` — lưu lượng truy cập của một tài khoản sử dụng AI-Agent có thể không bao gồm bất kỳ chiến dịch nào, vì vậy nếu không có nó, dữ liệu này sẽ không hiển thị ở đây.

---

## Kiểm tra chiến dịch trong môi trường thử nghiệm (playground)

Môi trường thử nghiệm cho phép bạn thực hiện cuộc trò chuyện với bot của chiến dịch mà không cần chạm vào kênh thực hoặc liên hệ thực. Đây là cùng một môi trường sandbox như bảng điều khiển thử nghiệm trên dashboard, và nó hoàn toàn khả dụng thông qua API.

Quy trình là: tạo một liên hệ thử nghiệm ẩn, gửi tin nhắn, sau đó thăm dò chiến dịch để lấy phản hồi của bot. Các phản hồi được tạo không đồng bộ, vì vậy chúng sẽ xuất hiện trong `test_messages` trên chiến dịch thay vì trong nội dung phản hồi.

> **Playground chạy dựa trên các khoản tín dụng chi phí API.** Một cuộc trò chuyện thử nghiệm bắt đầu bằng khóa API sẽ bị tính phí theo mức giá tin nhắn AI thông thường, giống như một phản hồi thực tế, và xuất hiện trong lịch sử sử dụng của bạn như một mục thông thường. Việc thử nghiệm từ bảng điều khiển vẫn được miễn phí. Sự khác biệt này là có chủ đích: một lần chạy thử nghiệm thực hiện công việc AI tương tự như một lần chạy trực tiếp, vì vậy một playground API không giới hạn sẽ là cách để chạy AI không giới hạn bằng chi phí của người khác.

### Bước 1 - Tạo liên hệ thử nghiệm

`POST /campaigns/{campaignId}/try-out/contact`

Tạo liên hệ kiểm thử ẩn và liên kết nó với chiến dịch. Tất cả các trường nội dung đều là tùy chọn; bất kỳ trường nào bạn bỏ trống sẽ sử dụng danh tính mẫu tích hợp sẵn (John Doe).

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `first_name` | Không | Tên của liên hệ kiểm thử. |
| `last_name` | Không | Họ của liên hệ kiểm thử. |
| `email` | Không | Email của liên hệ kiểm thử. |
| `phone` | Không | Số điện thoại của liên hệ kiểm thử. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/try-out/contact?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "first_name": "Maria", "last_name": "Lopez" }'
```

**Phản hồi**

```json
{
  "success": true,
  "contactId": "8kQx1vNbA2fLpR7d"
}
```

### Bước 2 - Ghi lại tin nhắn đến

`POST /campaigns/{campaignId}/try-out/messages`

Thêm tin nhắn vào luồng kiểm thử. Hãy gửi tin nhắn của khách truy cập vào đây trước để nó xuất hiện trong lịch sử hội thoại mà bot sẽ đọc.

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `messages` | Có | Mảng các đối tượng tin nhắn, tối đa 200 tin nhắn mỗi yêu cầu. |
| `messages[].body` | Có | Nội dung văn bản của tin nhắn. |
| `messages[].direction` | Có | `"inbound"` cho khách truy cập, `"outbound"` cho bot. |
| `messages[].timestamp` | Không | Chuỗi ISO-8601 hoặc thời gian epoch tính bằng mili giây. |
| `messages[].role` | Không | Nhãn vai trò tùy chọn. |
| `messages[].name` | Không | Tên hiển thị tùy chọn. |
| `ignoreCounter` | Không | Số nguyên. Đặt lại bộ đếm bỏ qua của chiến dịch trong cùng một lần ghi. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/try-out/messages?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "body": "Do you ship to Belgium?",
        "direction": "inbound",
        "timestamp": "2026-07-22T09:30:00Z"
      }
    ]
  }'
```

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "appended": 1
}
```

### Bước 3 - Yêu cầu bot phản hồi

`POST /campaigns/{campaignId}/try-out/test-message`

Gửi tin nhắn đến quy trình AI. Đây là lệnh gọi thực sự tạo ra phản hồi từ bot.

| Trường | Bắt buộc | Mô tả |
|---|---|---|
| `message` | Có | Nội dung tin nhắn mới nhất của khách truy cập. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/try-out/test-message?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "message": "Do you ship to Belgium?" }'
```

**Phản hồi**

```json
{
  "success": true,
  "data": "Published"
}
```

`"Published"` nghĩa là tin nhắn đã được gửi đến quy trình AI. `"Ignored"` nghĩa là một tin nhắn kiểm thử mới hơn đã thay thế tin nhắn này — trình mô phỏng (playground) sẽ gộp một loạt tin nhắn nhanh thành một phản hồi duy nhất, khoảng bốn giây sau tin nhắn cuối cùng, tương tự như cách một cuộc hội thoại thực tế chờ đợi ai đó hoàn tất việc nhập liệu. Do cửa sổ gộp này, lệnh gọi này mất vài giây để trả về kết quả.

### Bước 4 - Đọc phản hồi

`GET /campaigns/{campaignId}`

Phản hồi của bot được thêm vào mảng `test_messages` của chiến dịch. Hãy thăm dò chiến dịch cho đến khi một mục `outbound` mới xuất hiện.

```json
{
  "success": true,
  "campaign": {
    "id": "NBCXrhqGPSFsd6MV7pRo",
    "test_messages": [
      { "body": "Do you ship to Belgium?", "direction": "inbound" },
      { "body": "Yes, we ship across the EU.", "direction": "outbound" }
    ]
  }
}
```

### Đặt lại trình mô phỏng

`POST /campaigns/{campaignId}/try-out/reset`

Xóa toàn bộ sandbox: xóa liên hệ kiểm thử, xóa sạch `test_messages`, và giải phóng các khóa phản hồi của bot. Hãy sử dụng lệnh này giữa các lần chạy kiểm thử.

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/try-out/reset?apiKey=YOUR_API_KEY"
```

**Phản hồi**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

### Các endpoint playground khác

| Endpoint | Chức năng |
|---|---|
| `DELETE /campaigns/{campaignId}/try-out/contact` | Chỉ xóa liên hệ kiểm thử hiện tại và hủy liên kết, giữ nguyên `test_messages`. Lệnh này thành công ngay cả khi không có liên hệ nào được liên kết. |
| `POST /campaigns/{campaignId}/try-out/transfer` | Khởi tạo một playground mới với một cuộc hội thoại có sẵn trong một yêu cầu duy nhất: thay thế liên hệ kiểm thử và ghi đè `test_messages`. Phần thân yêu cầu nhận `first_name`, `last_name`, `messages` (có thể để trống) và `ignoreCounter`. Hãy ưu tiên cách này thay vì xóa-rồi-tạo-rồi-thêm, vì cách đó sẽ làm tốn hạn mức tốc độ (rate-limit) của bạn gấp ba lần. |
| `POST /campaigns/{campaignId}/try-out/messages/replace` | Ghi đè toàn bộ `test_messages` thay vì thêm vào. Sử dụng để cắt bớt hoặc quay lại một luồng hội thoại. |
| `POST /campaigns/{campaignId}/try-out/contact/reset-ignore-counter` | Chỉ đặt lại bộ đếm bỏ qua của liên hệ kiểm thử, dùng cho các luồng làm lại và lặp lại sau khi gửi. |

---

## Lỗi API chiến dịch

Các endpoint chiến dịch trả về phong bì lỗi tiêu chuẩn:

```json
{
  "success": false,
  "error": "Campaign not found"
}
```

| Trạng thái | Khi nào nó xảy ra trên một endpoint chiến dịch |
|---|---|
| `400` | Một trường bắt buộc bị thiếu hoặc không hợp lệ (ví dụ: `type` sai, `enabled` không phải kiểu boolean, hoặc khóa ngày trong tuần không xác định). Cũng được trả về bởi endpoint [kiểm tra giới hạn](#limit-checks) khi vượt quá giới hạn, và bởi [kích hoạt lại](#reactivate-a-dormant-campaign) cho một loại hoặc trạng thái chiến dịch không hỗ trợ thao tác này. |
| `404` | Không tìm thấy chiến dịch — hoặc là nó không tồn tại hoặc nó thuộc về một tài khoản khác. |
| `409` | Một [tối ưu hóa](#optimize-a-campaign-with-ai) đang chạy cho chiến dịch này. |

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

- [Định tuyến kênh đến một chiến dịch](channels.md#route-a-channel-to-a-campaign) — trỏ Instagram, WhatsApp hoặc bất kỳ kênh nào khác đến AI Agent chịu trách nhiệm phản hồi, sử dụng Điểm truy cập (Entry Points).
- [Tạo mẫu theo dõi bằng AI](templates.md#generate-follow-up-templates-with-ai) — bắt đầu một tác vụ nền để viết các mẫu theo dõi WhatsApp cho chiến dịch.
- [API Câu hỏi thường gặp (FAQs)](faqs.md) — quản lý các mục hỏi đáp mà chiến dịch của bạn sử dụng.
- [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.
