
# 营销活动 API

营销活动将 AI 机器人与联系人对话所需的一切整合在一起：指令、运行渠道、活跃时间以及后续跟进行为。通过“营销活动 API”，您可以直接在代码中列出、创建、更新、复制、启用、归档和微调营销活动，而无需使用仪表板。

以下所有端点均相对于基础 URL `https://api.youraiconnector.com/v1`。每个请求都必须经过身份验证 — 请参阅 [API 访问](../integrations/api-access.md) 和 [身份验证](authentication.md) 以了解如何获取和传递 API 密钥。API 访问是一项付费功能；如果没有权限，请求将被 `403` 拒绝。

> **注意：** 部分示例展示了简单的 `?apiKey=YOUR_API_KEY` 查询形式，其他示例则使用了 `X-API-Key` 标头。两者在任何地方均可使用 — 请根据您的设置选择最合适的一种。

---

## 营销活动类型

创建营销活动时，您必须选择以下类型之一：

| 类型 | 用途 |
|---|---|
| `Incoming from Unknown Contacts` | 机器人回复首次向您发送消息的用户。 |
| `Outgoing` | 机器人向您添加到营销活动中的联系人发起对话。 |
| `Keywords` | **惰性 - 请勿使用。** `Keywords` 营销活动是惰性的：它仍被保留以实现向后兼容，但在所有渠道上对入站路由不可见，且没有任何内容会读取其触发关键词。请改用 AI 智能体上的 **关键词 (Keyword)** 类型入口点。 |
| `Combined` | 结合了入站和出站行为。 |

**大小写不敏感。** `type`、`status`、`booking_provider`、`first_response_mode`、`bot.anthropic_model` 和 `bot.ai_speed` 均接受任何大小写形式 — `"live"`、`"Live"` 和 `"LIVE"` 表示相同含义 — 且该值将以其规范形式存储，即您读取营销活动时返回的形式。唯一的例外是暂停状态对：`"Paused"` 和 `"paused"` 是两个完全不同的状态，因此像 `"PAUSED"` 这样模棱两可的拼写会被拒绝，并返回 `400` 提示您选择其中之一。

### 两种暂停状态

| 状态 | 操作方 | 含义 |
|---|---|---|
| `Paused` | 平台自身的安全检查（低参与度、重复发送错误、达到限制）以及较新的代理和广播界面 | 营销活动被挂起。一旦原因消除，预定的扫描可以自动解除安全暂停。 |
| `paused` | 仪表板上的“暂停”按钮，配合 `resumed` 进行恢复 | 由人工手动暂停。预定发送任务在恢复时会被拆除并重建。 |

两者都会停止营销活动：入站路由仅在状态完全为 `Live` 时运行。**在 API 中，请使用 `Paused` 暂停，使用 `Live` 恢复** — 小写字母对仅用于仪表板按钮，并保留以供其使用。

这两种状态均不代表 AI 在单次对话中停止回复的情况。那是针对单个联系人的开关，即联系人对象上的 `is_bot_active` — 当人工接管、联系人选择退出或 AI 结束聊天时设置。营销活动本身的状态不受影响，其中的所有其他对话将继续运行。请参阅 [为单个联系人暂停或恢复 AI](messages.md#pause-or-resume-the-ai-for-one-contact)。

> **创建营销活动并不能决定由谁来应答渠道。** 路由由 AI 智能体上的 **入口点 (Entry Points)** 处理，而非由营销活动处理。每个渠道都有一个渠道默认入口点，用于指定应答该渠道上新联系人或未知联系人的智能体：使用 `PUT /entry-points/channel-defaults` 进行设置，使用 `GET /entry-points/routing-status` 检查该账户的梯队是否处于活动状态，使用 `DELETE /entry-points/channel-defaults` 进行清除。`POST /channels/campaign` 仍然会写入旧版的按渠道营销活动路由映射，但该映射在任何账户上都不再用于入站路由；它仅保留用于回滚。请勿基于此进行开发。请参阅 [将渠道路由至营销活动](channels.md#route-a-channel-to-a-campaign) 以了解这两种界面的对比。

---

## 列出营销活动

`GET /campaigns`

返回您的营销活动，按最新日期排序。除非您传入 `archived=true`，否则不包含已归档的营销活动。

**查询参数**

| 参数 | 必填 | 描述 |
|---|---|---|
| `limit` | 否 | 返回营销活动的最大数量。默认 `50`，最大 `100`。 |
| `cursor` | 否 | 分页游标。传入上一次响应中的 `next_cursor` 值以获取下一页。 |
| `archived` | 否 | 设置为 `true` 以包含已归档的营销活动。 |

**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"])
```

**响应**

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

当 `next_cursor` 为 `null` 时，表示您已到达最后一页。

---

## 获取营销活动

`GET /campaigns/{campaignId}`

返回完整的营销活动文档，包括实时机器人配置 (`bot`)、后续设置、启用的渠道以及任何关键字。时间戳以纪元毫秒数返回。

**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"]
```

**响应**

```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
**注意：** 如果营销活动归属于其他账户，则返回 `404 Campaign not found`（而非 `403`），因此您无法判断某个 ID 是否存在于其他账户中。
:::


---

## 创建营销活动

`POST /campaigns`

创建一个新的营销活动。`name` 和 `type` 是必填项；其他均为选填项。您可以在同一请求中包含任何其他营销活动字段（例如 `language`、`ai_mode` 或完整的 `bot` 配置对象），它们将随新营销活动一起存储。所有者和创建时间会自动设置。

**请求字段**

| 字段 | 必填 | 描述 |
|---|---|---|
| `name` | 是 | 营销活动名称。 |
| `type` | 是 | 上述四种营销活动类型之一。 |
| `language` | 否 | 机器人回复所使用的语言（例如 `"en"`）。 |
| `ai_mode` | 否 | AI 模式是否开启（`true`/`false`）。对于由 AI 代理回复的营销活动，读取操作将返回代理的 **Active** 开关状态，而非存储的值 — 请参阅下文更新部分的说明。 |
| `bot` | 否 | 机器人配置对象（请参阅 [机器人配置字段](#bot-configuration-fields)）。 |
| `list_id` | 否 | 要关联的联系人列表 ID。 |
| `event_id` | 否 | AI 可预订的事件类型 ID。 |
| `event_ids` | 否 | 同时指定多个事件类型，以事件类型 ID 数组的形式提供 — 第一个为默认值。请发送 `event_id` 或 `event_ids` 中的一个，不要同时发送两者。 |

**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"]
```

**响应**

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

---

## 更新营销活动

`PUT /campaigns/{campaignId}`

部分更新营销活动 — 仅发送您想要更改的字段。这是唯一的通用更新动词；不存在 `PATCH /campaigns/{campaignId}`（两个 `PATCH` 路由是特定的 [启用](#enable-or-disable-a-campaign) 和 [归档](#archive-or-restore-a-campaign) 开关）。

**您可以更改的字段。** 营销活动编辑器所写入的所有内容，包括 `name`、`status`、`type`、`language`、`ai_mode`、`enabled_channels`、触发器和滴灌设置、预订和跟进标记、Instagram/Facebook 监控字段，以及整个 `bot` 配置。身份和所有权在营销活动的生命周期内是锁定的：`user`、`id` 和 `created_at` 会被拒绝，端点无法识别的任何字段名称也会被拒绝。拒绝是针对整个请求的，而不是针对单个字段的 — 一个未知键会导致返回 `400`，并且该请求中的**任何内容**都不会被写入。

**`ai_mode` 在由代理支持的营销活动中反映的是代理的状态。** 当营销活动由 AI 代理回复时，读取该营销活动将返回从该代理的 **Active** 开关派生的 `ai_mode`——即真正决定 AI 是否回复的那个开关。在此类营销活动上写入 `ai_mode` 是被接受的，但不会改变您读取到的值；请改为开启或关闭代理的 Active 开关（在仪表板中，或通过 Agents API）。对于没有代理的传统营销活动，`ai_mode` 将像以前一样读取和写入存储的值。

**机器人字段是合并的，不会覆盖。** 以点号分隔的键 (`"bot.instructions": "..."`) 或嵌套对象 (`"bot": { "instructions": "..." }`) 的形式发送机器人设置 — 两者都是逐个叶节点进行写入，因此您遗漏的字段将保留其当前值。`bot.instructions`、`bot.goal`、`bot.rules` 和 `bot.personality` 都可以通过这种方式编辑，[机器人配置字段](#bot-configuration-fields) 下列出的所有其他机器人设置也是如此。这同样适用于 `test_bot`、`frequency` 和 `follow_up_config`。

若要整体替换机器人配置（删除您未发送的任何字段），请使用带有完整对象的 `bot_replace`（或 `test_bot_replace`）。您不能在同一个请求中对同一个对象同时进行替换和合并操作；这会返回 `400`。

::: note
**注意：** 通过 API 写入 `bot.*` 会**立即**对实时营销活动生效。仪表板编辑器的运作方式不同：在那里的编辑内容会保存为草稿，只有当客户点击“发布”时才会生效。因此，如果客户有未发布的仪表板更改，它们会保留在 `test_bot` 中，而对 `bot` 进行 API 读取将正确显示 AI 当前正在使用的内容。
:::


部分字段通过专用键设置，而不是直接写入：使用 `list_id` 设置联系人列表，使用 `event_id` 设置事件类型（或使用 `event_ids`，即事件类型 ID 的有序数组，以允许 AI 预订多个事件 —— 第一个为默认值；空数组将取消所有关联），并使用 `contact_ids`（联系人 ID 数组）设置营销活动的联系人。知识库条目通过 [FAQs API](faqs.md) 管理，而非此端点。

**标签是替换，而不是合并。** 发送 `tags` 作为完整的数组，它将成为该营销活动的标签集 —— 请参阅 [营销活动标签](#campaign-tags) 以了解字段以及用于添加或编辑单个标签的端点。

**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()
```

**响应**

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

---

## 删除营销活动

`DELETE /campaigns/{campaignId}`

永久删除营销活动。此操作无法撤销 — 如果您以后可能还需要该营销活动，请改为 [归档它](#archive-or-restore-a-campaign)。

**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()
```

**响应**

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

---

## 复制营销活动

`POST /campaigns/{campaignId}/duplicate`

创建活动副本，并保留其所有设置。该副本启动时处于 **禁用** 状态，其名称会添加一个 `(copy)` 后缀，因此在您明确启用它之前，它永远不会发送消息。

**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"]
```

**响应**

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

> **同一账户内**的重复副本。

---


## 启用或禁用营销活动

`PATCH /campaigns/{campaignId}/enabled`

开启或关闭营销活动。禁用的营销活动会停止吸引联系人，但会保留其所有配置。

**请求字段**

| 字段 | 必需 | 说明 |
|---|---|---|
| `enabled` | 是 | `true` 表示启用，`false` 表示禁用。必须为布尔值。 |

**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()
```

**响应**

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

---

## 归档或恢复营销活动

`PATCH /campaigns/{campaignId}/archived`

归档或恢复营销活动。归档的营销活动在默认营销活动列表中会被隐藏，但会保留其所有数据，并且可以随时恢复。

**请求字段**

| 字段 | 必需 | 说明 |
|---|---|---|
| `archived` | 是 | `true` 表示归档，`false` 表示恢复。必须为布尔值。 |

**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()
```

**响应**

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

---

## 更新机器人配置

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

这是更改单个机器人设置的安全方式。您发送的每个字段都会与现有的机器人配置**合并**，因此您遗漏的任何字段都会被保留。当您只想调整机器人的一部分时，请使用此端点，而不是 campaign-update 端点。

字段键必须仅使用字母、数字、下划线和连字符。

**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()
```

**响应**

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

### 机器人配置字段

所有机器人字段均为可选。仅发送您想要设置的字段。此处列出的字段之外的任何其他机器人字段都将被接受并按原样存储。

| 字段 | 类型 | 描述 |
|---|---|---|
| `instructions` | string | 指导机器人如何与联系人对话的主要指令。 |
| `rules` | string | 机器人必须始终遵守的硬性规则。 |
| `goal` | string | 机器人在每次对话中应努力实现的结果。 |
| `personality` | string | 机器人的语调和个性描述。 |
| `ai_speed` | string | AI 在回复前进行推理的程度。可选值：`fast`、`fast_thinker`、`balanced`、`thorough`。 |
| `anthropic_model` | string | 用于此营销活动回复的 AI 质量层级。可选值：`standard`、`economy`（已弃用）、`max`、`mini`。`max` 和 `mini` 仅在符合相应层级条件的账户上生效。 |
| `max_messages` | integer | 每次对话中机器人消息的最大数量。 |
| `alert_human_when` | string | 机器人应提醒人工团队成员的条件。 |
| `availability` | object | 机器人的活跃时间表。您可以在此处设置，或使用专门的 [活跃时间端点](#set-the-bot-active-hours)。 |
| `follow_up_config` | object | 跟进行为配置，按原样存储。 |

---

## 设置机器人工作时间

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

设置机器人的可用性时间表。在配置的时间窗口之外，机器人不会自动回复。此操作会写入机器人配置的 `availability` 字段。

**请求字段**

| 字段 | 必填 | 描述 |
|---|---|---|
| `availability` | 是 | 一个以工作日为键的对象。允许的键为 `monday` 到 `sunday`；任何其他键都会返回 `400`。您遗漏的天数将保持不变。 |

每个工作日包含单个时间窗口或窗口数组。窗口具有 24 小时 `HH:MM` 格式的 `start_time` 和 `end_time`。

**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()
```

**响应**

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

---

## 列出营销活动的自定义函数

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

返回链接到此营销活动的自定义函数，并解析为完整定义。自定义函数是机器人可以在对话期间调用的外部 HTTP 操作——例如，在您的商店中查询库存或在您的 CRM 中创建记录。

**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"]
```

**响应**

```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
    }
  ]
}
```

---

## 将自定义函数关联到营销活动

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

将现有的[自定义函数](../ai-automation/custom-functions.md)关联到此营销活动，以便机器人可以在对话期间调用它。关联一个已经关联过的函数不会产生任何操作。

| 字段 | 必填 | 描述 |
|---|---|---|
| `custom_function_id` | 是 | 要关联的自定义函数的 ID。 |

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

**响应**

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

---

## 从营销活动中取消关联自定义函数

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

取消关联一个未关联的函数不会产生任何操作。

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

**响应**

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

---

## 将知识库来源关联到营销活动

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

将知识库来源（通过 [FAQs API](faqs.md) 创建）关联到此营销活动，以便机器人在回答时可以利用它。关联一个已经关联过的来源不会产生任何操作。

| 字段 | 必填 | 描述 |
|---|---|---|
| `kb_source_id` | 是 | 要关联的知识库来源的 ID。 |

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

**响应**

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

---

## 从营销活动中取消关联知识库来源

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

取消关联一个未关联的来源不会产生任何操作。

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

**响应**

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

---

## 将 MCP 服务器关联到营销活动

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

将 MCP 服务器链接到此营销活动，使机器人能够在对话期间访问该服务器的工具。链接已链接的服务器不会执行任何操作。

| 字段 | 必填 | 说明 |
|---|---|---|
| `mcp_server_id` | 是 | 要链接的 MCP 服务器的 ID。 |

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

**响应**

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

---

## 从营销活动中取消链接 MCP 服务器

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

取消链接未链接的服务器不会执行任何操作。

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

**响应**

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

---

## 营销活动媒体库

媒体库存储机器人可以在对话期间发送的图像、视频、文档和语音备忘录。

### 列出营销活动的媒体库

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

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

**响应**

```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` 是上传时捕获的签名 URL — 当您读取它时，它可能已经过期；仪表板会根据需要重新签名。

### 上传媒体项目

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

| 字段 | 必填 | 说明 |
|---|---|---|
| `base64Data` | 是 | 文件，采用 base64 编码（无 data-URL 前缀）。 |
| `mimeType` | 是 | 文件的 MIME 类型（例如 `image/png`）。 |
| `title` | 是 | 在库和 AI 提示中显示的短标签。 |
| `description` | 是 | 指示机器人**何时**发送此项目的说明。 |
| `fileName` | 否 | 原始文件名，用于构建存储对象名称。 |
| `sendMessage` | 否 | 机器人发送此项目时应使用的首选措辞。 |
| `maxSendsPerConversation` | 否 | 机器人在对话中向一个联系人发送此项目的最大次数。默认为 `1`。 |
| `sendAsVoiceNote` | 否 | 对于音频上传，将其转码为 WhatsApp 语音备忘录。默认为 `false`（存储为普通音频文件）。 |

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

**响应**

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

### 更新媒体项目

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

仅编辑项目的元数据 — 若要替换文件本身，请删除该项目并上传新文件。

| 字段 | 描述 |
|---|---|
| `title` | 短标签。 |
| `description` | 发送时机说明。 |
| `send_message` | 建议机器人使用的措辞。 |
| `max_sends_per_conversation` | 非负整数，或使用 `null` 清除上限。 |

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

**响应**

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

### 删除媒体项目

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

删除一个已不存在的项目属于空操作。

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

**响应**

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

---

## 营销活动标签

营销活动标签是您教导机器人（bot）在对话过程中应用于联系人的标签 —— `hot-lead`、`not-interested`、`booked-a-call`。每个标签包含三个部分：

| 字段 | 类型 | 描述 |
|---|---|---|
| `name` | string, 必填 | 标签本身。这是机器人应用于联系人的内容，也是您稍后进行匹配的内容，因此请保持简短且稳定。 |
| `description` | string | 指示机器人**何时**应用此标签的说明。这是发挥作用的部分 —— “此人确认他们加入了社区”会被使用，“潜在高意向客户”则不会。 |
| `webhook` | string | 当标签应用到联系人时接收 `POST` 的 URL。如果不需要，请留空。 |
| `tag_id` | string | 可选。将此条目链接到您账户中的现有标签，而不是创建一个新标签。如果您想稍后通过下方的单标签端点来处理此特定标签，请提供此项。 |

标签名称在营销活动内必须唯一。机器人**按名称**应用标签，因此两个共享同一名称的条目没有定义的胜出者。

### 设置营销活动的所有标签

`PUT /campaigns/{campaignId}` 带有 `tags` 数组。

这会用您发送的内容完全替换营销活动的标签，这与仪表板的“标签”选项卡在保存时执行的操作相同。**每次都要发送完整的数组** —— 您遗漏的标签即表示被删除。发送 `[]` 将清除所有标签。

**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()
```

**响应**

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

使用 [`GET /campaigns/{campaignId}`](#get-a-campaign) 读取标签。

### 添加一个标签

`POST /campaigns/{campaignId}/tags`

追加单个标签而不重新发送其余部分。当您要添加到并非在此请求中构建的集合时，请使用此项。

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

发布完全相同的标签两次，第二次不会有任何效果。发布具有不同名称或描述的相同 `tag_id` 会追加**第二个**条目，而不是编辑第一个 —— 请使用下方的端点进行原地编辑。

### 更新或删除一个标签

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

这些通过 `tag_id` 寻址单个条目，因此它们仅适用于使用该条目创建的标签。如果标签没有 `tag_id`，请使用上述全数组 `PUT /campaigns/{campaignId}` 进行更改。

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

不在营销活动中的 `tagId` 将返回带有 `"Tag not found in campaign tags"` 的 `404`。

---

## 切换营销活动的渠道

`POST /campaigns/{campaignId}/channels`

向营销活动的 `enabled_channels` 数组中添加或移除渠道，而无需重新发送整个数组 — 当其他进程可能同时在编辑该营销活动时，此方法比 [`PUT /campaigns/{campaignId}`](#update-a-campaign) 更安全。

发送单个切换或批量切换 — 不要在同一请求中同时发送两者：

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

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

| 字段 | 描述 |
|---|---|
| `channel` | 要切换的一个渠道。与 `action` 配对使用。 |
| `action` | `"add"` 或 `"remove"`。与 `channel` 配对使用。 |
| `add` | 要添加的渠道数组。批量形式 — 请代替 `channel`/`action` 使用。 |
| `remove` | 要移除的渠道数组。批量形式。 |

有效渠道：`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" }'
```

**响应**

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

> 这仅更改营销活动所宣传的渠道 — 它不决定谁来响应渠道。相关信息请参阅上方的 [营销活动类型](#campaign-types) 和下方的 [将营销活动路由至入站渠道](#route-a-campaign-to-incoming-channels)。

---

## 评论转私信 (Instagram 和 Facebook)

“评论转私信”功能可将您帖子下的评论转化为私密对话：当有人发表评论时，机器人会向其发送私信，随后营销活动将接管后续对话。该功能完全通过营销活动对象进行配置，因此不存在仅限于 UI 的设置。

首先连接 Facebook 公共主页——请参阅 [渠道连接](channels.md#instagram--messenger-meta)。然后使用 [`PUT /campaigns/{campaignId}`](#update-a-campaign) 设置以下字段。

> **营销活动必须处于 `Live` 状态。** 评论监控仅会拾取 `status` 为 `Live` 的营销活动（不区分大小写——请参阅 [营销活动类型](#campaign-types)）。任何其他状态都会静默禁用此功能，而像 `"Active"` 这样虚构的状态现在会被 `400` 拒绝，而不会被存储。有效状态包括 `Draft`、`Pending Approval`、`Scheduled`、`Live`、`Paused`、`Completed`、`Sent` 和 `Failed`。

**字段**

| 字段 | 类型 | 描述 |
|---|---|---|
| `monitor_instagram_posts` | boolean | 监控已连接页面上的每条 Instagram 帖子。 |
| `instagram_post_ids` | string[] | 仅监控这些 Instagram 帖子。当 `monitor_instagram_posts` 开启时，请勿设置此项。 |
| `instagram_comment_delay_minutes` | number | 在评论后等待多少分钟再发送私信 (DM)。 |
| `monitor_facebook_posts` | boolean | 监控已连接页面上的每条 Facebook 帖子。 |
| `facebook_post_ids` | string[] | 仅监控这些 Facebook 帖子。 |
| `facebook_comment_delay_minutes` | number | 发送私信前的延迟时间（以分钟为单位）。 |
| `public_comment_reply_instructions` | string | 对留在评论下方的可见回复的指导说明。会覆盖默认的“查看您的私信”措辞。 |
| `first_response_mode` | string | `"ai"`（默认）会生成第一条私信和公开回复。`"exact_text"` 会按原样发送您的措辞，不进行 AI 生成，也不扣除额度。 |
| `first_response_exact_text` | string | 当 `first_response_mode` 为 `"exact_text"` 时使用的原样第一条私信。该模式生效所必需。 |
| `first_response_exact_text_variants` | string[] | 第一条私信的额外措辞。每次发送时随机选择一条，因此重复的私信内容不会完全相同。 |
| `public_comment_reply_exact_text` | string | `"exact_text"` 模式下的原样公开回复。留空则跳过公开回复，仅发送私信。 |
| `public_comment_reply_exact_text_variants` | string[] | 公开回复的额外措辞。 |
| `monitor_instagram_followers` | boolean | 将新关注者视为触发器并发送开场私信（Instagram 个人账户）。 |
| `follower_outreach_instructions` | string | 该新关注者开场私信的指导说明。 |
| `respond_to_instagram_story_replies` | boolean | AI 是否回答您 Instagram 快拍 (Stories) 的回复。默认为 `true`。设置 `false` 可使快拍回复进入聊天（并附带快拍内容），而不进行 AI 回复。此为实时设置，不属于草稿的一部分，因此无需发布。 |

**清除字段**

当您发送 `null` 时，这些字段会被移除而不是设置为 `null`，因此机器人会回退到其默认值：`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`。

> **一个未知键会导致整个请求被拒绝。** `PUT /campaigns/{campaignId}` 会根据允许列表验证整个正文。如果存在无法识别的键，则整个请求会返回 `400`——它不会被静默忽略，且该正文中的其他字段也不会被写入。

**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()
```

**响应**

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

> 在评论中留下的可见回复需要您套餐中的“评论回复”功能。如果没有该功能，私信仍会发送，但会跳过公开回复。

---

## 使用 AI 优化营销活动

`POST /campaigns/{campaignId}/optimize`

运行与仪表板的“优化”和“踩”反馈流程相同的 AI 重写功能：接收您的反馈，重写机器人的指令，并将结果暂存为新的草稿版本供您审阅。

| 字段 | 必填 | 描述 |
|---|---|---|
| `user_feedback` | 两者选其一 | 描述需要改进之处的自由格式反馈。 |
| `thumbs_down_feedback` | 两者选其一 | 从特定机器人回复的“踩”中捕获的反馈。 |
| `thumbs_down_message` | 否 | “踩”反馈所针对的机器人消息。 |

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

**响应** (`202` — 重写在后台运行)

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

轮询 [`GET /campaigns/{campaignId}`](#get-a-campaign) 并观察 `test_bot.status`：它会立即变为 `"Optimizing"`，一旦重写内容进入 `test_bot`，它就会变回 `"Draft"`。此后，它的行为与任何仪表板草稿一样 — 审阅它，然后在仪表板中发布以使其生效。`409` 表示该营销活动已有优化任务正在运行。

> 优化会消耗积分，与您账户上的任何其他 AI 操作相同。

---

## 将联系人分配给营销活动

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

将现有联系人放入营销活动中，如果需要，还可以立即发送该营销活动的开场消息。这是向单个联系人发送营销活动已批准的 WhatsApp 模板的方法：营销活动所使用的已批准模板属于该营销活动，因此它不会出现在 [Templates API](templates.md) 库中，也无法通过 `/whatsapp-templates/send` 发送。

| 字段 | 必填 | 描述 |
|---|---|---|
| `sendOpeningMessage` | 否 | `true` 在联系人被分配后立即发送营销活动的开场消息（WhatsApp 营销活动中已批准的 WhatsApp 模板）。默认为 `false`。 |
| `triggerAIResponse` | 否 | `true` 让 AI 改为自行编写第一条消息。默认为 `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 }'
```

**响应**

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

> **积分：** 发送 WhatsApp 营销活动的开场消息的收费方式与任何模板发送相同，价格取决于收件人所在的国家/地区和模板类别。在其他渠道上，开场消息是普通的外发消息。

---

## 将营销活动路由到传入渠道

这些端点用于管理营销活动在渠道上应答哪些新的、未知的联系人。对于新的集成，**请优先使用入口点**（参见 [营销活动类型](#campaign-types) 下的说明）—— 这些端点在处理以旧方式路由的营销活动，以及解决两个传入营销活动之间的渠道所有权冲突时仍然有用。

### 将营销活动分配给传入渠道

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

| 字段 | 必填 | 描述 |
|---|---|---|
| `channels` | 是 | 该营销活动应为新的、未知联系人应答的渠道数组。 |

```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"] }'
```

**响应**

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

`channels` 仅列出实际路由到此营销活动的渠道；`failed` 列出任何未路由的渠道。如果所有请求的渠道都失败，则请求本身失败。

### 清除营销活动的传入路由

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

| 字段 | 必填 | 描述 |
|---|---|---|
| `channelToUnassign` | 否 | 仅清除此单个渠道的路由。省略此项以清除该营销活动当前应答的所有渠道。 |

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

**响应**

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

### 重新激活休眠的营销活动

`POST /campaigns/{campaignId}/reactivate`

将活动从 `Ended`、`Completed`、`Paused` 或 `Draft` 状态恢复，并重新声明其渠道。仅适用于 `Incoming from Unknown Contacts` 或 `Combined` 活动 —— 已经 `Live` 的活动会被视为成功，无需执行任何操作。

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

**响应**

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

如果某个渠道已被其他活动的代理声明，则会显示在 `channelsBlockedByConflict` 中，而不是导致整个调用失败 —— 如果您希望此活动接管该渠道，请先使用下方的 [停止冲突的传入活动](#stop-a-conflicting-incoming-campaign) 将其释放。对于不支持重新激活的活动类型，或非上述休眠状态的活动，将返回 `400`。

### 停止冲突的传入活动

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

将此活动的渠道从当前持有它们的任何“其他”活动中释放出来，以便此活动接下来可以声明它们。这是当您将传入活动启动到其他人已经在应答的渠道时，仪表板自动执行操作的 REST 版本。

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

**响应**

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

当此活动已经拥有其声明的所有渠道时，`released_channels` 将返回为空 —— 没有什么可接管的。

---

## 成本估算

在发送活动之前，估算启动该活动的成本。

### WhatsApp 模板成本估算

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

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

**响应**

```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` 在托管的 WhatsApp 通道上是 `"credits"`。在 Meta 直接向您的 WhatsApp Business 账户收费的通道上，`costPerContact`、`subtotal` 和 `totalTemplateCost` 返回 `null` —— 绝不会是 `0`（这会被解读为免费）—— 因为没有可报告的额度数字。

### 短信成本估算

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

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

**响应**

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

短信始终通过您自己的 Twilio 账户发送（请参阅 [短信提供商](../settings/sms-provider.md)），因此这始终由 Twilio 直接计费 —— `estimatedCostUsd` 是对该 Twilio 账单的估算，而不是额度扣费。

---

## 限制检查

在启动前检查限制，而不是在发送失败后才发现问题。

### 营销活动范围内的检查

`GET /campaigns/{campaignId}/limits/ai-credit-messaging` — 启动或安排此营销活动是否会超过您账户的 AI 积分消息发送限制。

`GET /campaigns/{campaignId}/limits/messaging` — 是否会超过您账户的每日消息发送限制。

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

**响应**（未超过限制）

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

如果超过限制，则会返回 `400`，并在 `error` 中说明原因。

### 账户范围内的检查

`GET /campaigns/limits/campaigns` — 您是否已达到订阅的每月营销活动创建限制。

`GET /campaigns/limits/contacts` — 您是否已达到订阅的联系人数量限制。

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

**响应**

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

---

## 营销活动统计总计

`GET /campaigns/stats/totals`

获取您账户中每个营销活动和每个 AI 代理在滚动窗口期内的发送和回复总数——这与营销活动列表页面每行旁边显示的数据相同，通过一次调用即可获取，无需为每个营销活动单独请求。

| 查询参数 | 描述 |
|---|---|
| `days` | 滚动窗口的大小，范围为 1-365。默认为 90。 |

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

**响应**

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

`byAgent` 是其自身的汇总，而非 `byCampaign` 的总和——AI 代理原生账户的流量可能完全不包含任何营销活动，因此如果不这样处理，它们在这里将不可见。

---

## 在演练场中测试营销活动

Playground 允许您与营销活动的机器人进行对话，而无需接触真实的渠道或真实的联系人。它与仪表板的试用面板是同一个沙盒，并且可以通过 API 完全使用。

流程如下：创建一个隐藏的测试联系人，发送一条消息，然后轮询营销活动以获取机器人的回复。回复是异步生成的，因此它们会出现在营销活动的 `test_messages` 中，而不是响应正文中。

> **Playground 通过 API 消耗额度运行。** 使用 API 密钥启动的测试对话将按正常的 AI 消息费率收费，与真实回复相同，并作为常规条目出现在您的使用记录中。从仪表板进行的测试是免费的。这种差异是有意为之的：测试运行与实时运行执行相同的 AI 工作，因此如果不计费，API Playground 将成为在他人账单上运行无限 AI 的途径。

### 第 1 步 - 创建测试联系人

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

创建隐藏的测试联系人并将其链接到营销活动。所有正文字段均为可选；任何您留空的字段都将回退到内置的示例身份（John Doe）。

| 字段 | 必填 | 描述 |
|---|---|---|
| `first_name` | 否 | 测试联系人的名字。 |
| `last_name` | 否 | 测试联系人的姓氏。 |
| `email` | 否 | 测试联系人的电子邮件。 |
| `phone` | 否 | 测试联系人的电话号码。 |

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

**响应**

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

### 第 2 步 - 记录传入消息

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

将消息附加到测试会话中。首先在此处发送访客的消息，以便它出现在机器人读取的对话历史记录中。

| 字段 | 必填 | 描述 |
|---|---|---|
| `messages` | 是 | 消息对象数组，每次请求最多 200 个。 |
| `messages[].body` | 是 | 消息文本。 |
| `messages[].direction` | 是 | 访客使用 `"inbound"`，机器人使用 `"outbound"`。 |
| `messages[].timestamp` | 否 | ISO-8601 字符串或纪元毫秒数。 |
| `messages[].role` | 否 | 可选的角色标签。 |
| `messages[].name` | 否 | 可选的显示名称。 |
| `ignoreCounter` | 否 | 整数。在同一次写入中重置营销活动的忽略计数器。 |

```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"
      }
    ]
  }'
```

**响应**

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

### 第 3 步 - 请求机器人回复

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

将消息发送至 AI 流水线。此调用会实际生成机器人回复。

| 字段 | 必填 | 描述 |
|---|---|---|
| `message` | 是 | 访客的最新消息文本。 |

```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?" }'
```

**响应**

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

`"Published"` 表示消息已发送至 AI 流水线。`"Ignored"` 表示一条更新的测试消息取代了此条消息——游乐场会将快速爆发的消息合并为单次回复，大约在最后一条消息发出四秒后，就像真实的对话会等待对方打字完成一样。由于存在此合并窗口，此调用需要几秒钟才能返回。

### 第 4 步 - 读取回复

`GET /campaigns/{campaignId}`

机器人的回复会附加到营销活动的 `test_messages` 数组中。轮询营销活动，直到出现新的 `outbound` 条目。

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

### 重置游乐场

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

清除整个沙盒：删除测试联系人，擦除 `test_messages`，并释放机器人的响应锁定。请在测试运行之间使用此功能。

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

**响应**

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

### 其他游乐场端点

| 端点 | 功能 |
|---|---|
| `DELETE /campaigns/{campaignId}/try-out/contact` | 仅删除当前测试联系人并取消关联，保持 `test_messages` 不变。即使没有关联联系人也会成功。 |
| `POST /campaigns/{campaignId}/try-out/transfer` | 在一个请求中启动一个以现有对话为种子的全新游乐场：替换测试联系人并覆盖 `test_messages`。主体接收 `first_name`、`last_name`、`messages`（可为空）和 `ignoreCounter`。建议优先使用此方法，而不是“删除-创建-附加”，后者会消耗三倍的速率限制额度。 |
| `POST /campaigns/{campaignId}/try-out/messages/replace` | 全量覆盖 `test_messages` 而非附加。用于截断或回退对话线程。 |
| `POST /campaigns/{campaignId}/try-out/contact/reset-ignore-counter` | 仅重置测试联系人的忽略计数器，用于发送后的重做和重复流程。 |

---

## 营销活动 API 错误

营销活动端点返回标准的错误封装：

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

| 状态 | 在营销活动端点上发生的情况 |
|---|---|
| `400` | 缺少必填字段或字段无效（例如错误的 `type`、非布尔值的 `enabled` 或未知的星期几键）。当 [限额检查](#limit-checks) 端点检测到超出限额时，以及对不支持该操作的营销活动类型或状态执行 [重新激活](#reactivate-a-dormant-campaign) 时，也会返回此状态。 |
| `404` | 未找到该营销活动 — 它不存在或属于其他账户。 |
| `409` | 此营销活动已有正在运行的 [优化](#optimize-a-campaign-with-ai) 任务。 |

每个端点都可能返回的共享代码 — `401`, `403`（您的套餐不包含 API 访问权限）, `429`（速率限制）和 `500` — 及其重试指南列在 [错误与分页](errors-and-pagination.md) 中。

---

## 相关内容

- [将渠道路由至营销活动](channels.md#route-a-channel-to-a-campaign) — 使用入口点 (Entry Points) 将 Instagram、WhatsApp 或任何其他渠道指向应答复该渠道的 AI 代理。
- [使用 AI 生成跟进模板](templates.md#generate-follow-up-templates-with-ai) — 启动后台作业，为营销活动编写 WhatsApp 跟进模板。
- [常见问题解答 API](faqs.md) — 管理营销活动所使用的问答条目。
- [API 访问](../integrations/api-access.md) — 生成您的 API 密钥。
- [身份验证](authentication.md) — 传递密钥的所有方式。
