
# 代理机构 API

作为代理机构，您可以使用与客户相同的 REST API，但可以将单个请求指向您管理的某个**子账户**，而不是您自己的账户。这使您可以构建端到端引导客户的工具——创建他们的营销活动、在知识库上训练他们的 AI、导入他们的联系人、连接他们的消息渠道以及购买电话号码——所有这些都无需手动登录每个子账户。

本页面仅涵盖代理机构特有的行为：如何使用 `sub_account_id` 参数代表子账户执行操作。有关基础知识（生成密钥、身份验证、基础 URL、错误格式、速率限制），请从 [API 访问](../integrations/api-access.md) 指南开始。其中的所有内容也适用于此处——您使用**您的代理机构账户**的 API 密钥进行身份验证。

::: note
**注意：** 本页面内容偏向技术性。如果您不是开发人员，请将其分享给负责构建集成的人员。
:::


***

## “代表执行”的工作原理

默认情况下，每个 API 请求都作用于拥有该 API 密钥的账户，即您的代理机构账户。要改为作用于受管理的客户账户，请在请求中添加可选的 `sub_account_id` 参数，并将其设置为该客户的账户 ID。

- **省略 `sub_account_id`** → 请求作用于您自己的代理机构账户。
- **包含 `sub_account_id`** → 请求作用于该子账户，但前提是平台确认该子账户确实属于您。

您始终使用**代理机构账户**的 API 密钥进行身份验证。您永远不需要子账户自己的密钥，也无需处理子账户的凭据。

### 如何使用

- **GET / DELETE 端点** → 将其作为查询参数传递：`?sub_account_id=THE_SUB_ACCOUNT_ID`（如果通过查询进行身份验证，则与您的 `apiKey` 一起）。
- **POST / PUT / PATCH 端点** → 将其包含在 JSON 请求体中，作为 `"sub_account_id": "THE_SUB_ACCOUNT_ID"`。
- **AI 助手** → 无需配置。[MCP 服务器](../integrations/connect-ai-clients.md)在其读取工具上携带相同的设置，因此使用您的代理密钥建立的一个连接即可报告所有客户：只需在请求中指明客户名称（“Bella's Bistro 有多少个联系人？”）。写入操作也可用：每个接受 `sub_account_id` 的端点都会作为工具公开，因此您可以从同一个连接代表客户进行创建、更改和发送。

### 查找子账户的 ID

`sub_account_id` 是客户账户的唯一 ID。您可以从 **SubAccounts** API 端点（请参阅 [子账户](sub-accounts.md) 指南）或侧边栏的 **Sub Accounts** 页面获取您的子账户列表及其 ID。

***

## 所有权始终会经过验证

当您传递 `sub_account_id` 时，平台会检查该账户是否为真实的子账户**且**是否属于您的代理机构。只有这样，请求才会通过。

如果 ID 未知、不是子账户或属于其他代理机构，请求将失败并返回 **`404`** 响应：

```json
{
  "success": false,
  "error_code": 404,
  "error": "Sub-account not found."
}
```

> **为什么返回 404 而不是 403？** “禁止访问”（forbidden）响应会告诉外部人员该 ID 存在但并不属于他们。对“不存在”和“不属于你”的情况返回相同的 `404`，意味着该端点无法被用于探测哪些账户 ID 属于其他代理机构。在此处将 `404` 视为“这不是你管理的子账户”。

***

## 支持 `sub_account_id` 的位置

`sub_account_id` 基本上适用于所有的**资源**端点——即任何创建、读取、更新或删除账户自身数据的调用。实际上，你可以使用你的代理机构密钥来配置和运行整个子账户的设置：

- **AI 设置** — 营销活动、智能体、常见问题解答、知识库来源（网站抓取**和**文档上传）、知识库组、广播、自定义函数、MCP 服务器
- **联系人与 CRM** — 联系人（包括导入）、列表、标签、任务、交易、预约、事件
- **渠道与号码** — 连接 WhatsApp / WhatsApp Web / Telegram / Instagram & Messenger / LINE，搜索 / 购买 / 管理电话号码，WhatsApp 模板，渠道路由
- **消息与内容** — 发送消息、聊天会话、聊天导出、每日摘要
- **设置与集成** — Webhook、聊天小部件配置、白标配置、BYOK 短信及其他账户设置、分析

在上述每一个端点中，该参数都是**可选的**——如果省略它，调用将作用于你自己的代理机构账户，因此一个集成可以同时服务两者。额度和使用量始终来自你所针对的账户：子账户的营销活动、消息、标签和号码产生的费用会扣除**子账户的**余额。

### 不适用的情况

少数端点属于代理机构级别或自引用端点，会忽略 `sub_account_id`：

- **管理子账户本身** — SubAccounts 端点（创建/列出/更新子账户）和 BYOK 消费限额端点已经在其 URL 路径中指定了子账户名称。[定价和策略端点](#set-per-client-ai-pricing-and-policy)以及[聊天监控端点](#read-a-sub-accounts-conversations)遵循相同的模式。
- **在账户之间复制智能体 (Agent)** — `POST /v1/subaccounts/agents/copy` 同时指定了两个账户，并将目标账户作为 `targetUserId`。请参阅下方的[工作示例](#worked-example-ship-a-template-agent-into-every-new-client)。（旧版 `POST /v1/subaccounts/campaigns/copy` 的工作方式相同，但已与[营销活动 API](../api/campaigns.md) 的其余部分一起弃用。）
- **调整额度以及两个代理商范围的汇总** — [`POST /v1/subaccounts/credits`](#grant-or-deduct-credits-directly) 改为通过 `email` 标识子账户；[`GET /v1/subaccounts/credit-usage`](#read-credit-usage-and-campaign-health-across-your-book) 和 `GET /v1/subaccounts/campaign-status` 会一次性报告所有子账户的情况，因此没有单一的账户可供定位。
- **您代理商自己的账户** — API 密钥管理、代理商使用情况报告、团队管理以及您的[定价层级](#manage-your-pricing-tiers-over-the-api)始终作用于您的代理商账户。
- **入站消息 Webhook** — 外部系统向其发送数据的端点绑定到配置它们的凭据所属的账户，因此无需重定向。

> 每个端点接受哪些参数的实时、机器可读列表位于你的仪表板 API 参考（**设置 → 集成 → API 密钥**）以及 `GET /v1/docs/openapi.yaml` 处的 OpenAPI 规范中。我们经常发布 API 变更——请以这些内容为准。

::: master-only
<figure><img src="../.gitbook/assets/v2-api-access-key-section.png" alt="API 密钥设置页面，包含掩码密钥和重新生成控件"><figcaption><p>设置 → 集成 → API 密钥 — 您代理机构的密钥位于此处，旁边还有指向完整 API 参考文档的链接。</p></figcaption></figure>
:::

***

## 示例：为子账户连接 Instagram 和 Messenger

连接 Instagram 和 Messenger 是一个基于浏览器的流程。你通过 API 启动它，将返回的授权 URL 交给客户（或为他们打开），等待他们在浏览器中进行授权，然后选择要连接的页面——所有这些操作都在使用 `sub_account_id` 针对其子账户的情况下完成。

### 第 1 步 — 启动连接

在请求体中使用客户的 `sub_account_id` 调用连接端点。此处不发送凭据；平台会返回一个客户必须在浏览器中打开的授权 URL，以及一个一次性的关联令牌。

**cURL**

```bash
curl -X POST "https://api.dmchamp.com/v1/channels/meta/connect?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sub_account_id": "abc123def456"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.dmchamp.com/v1/channels/meta/connect", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    sub_account_id: "abc123def456",
  }),
});

const data = await res.json();
// data.oauth_url -> open this in the client's browser
```

**Python**

```python
import requests

res = requests.post(
    "https://api.dmchamp.com/v1/channels/meta/connect",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "sub_account_id": "abc123def456",
    },
)

data = res.json()
# data["oauth_url"] -> open this in the client's browser
```

**响应：**

```json
{
  "success": true,
  "oauth_url": "https://www.facebook.com/v21.0/dialog/oauth?client_id=...&state=...",
  "state_token": "8sFq2yV0kQ7m4n1pZr3tWb6cXe9hJl2aD5gK7uN0oI",
  "expires_at": "2026-06-10T12:30:00.000Z"
}
```

将客户端引导至浏览器中的 `oauth_url` 进行授权。`state_token` 用于关联此尝试，且是一个短效密钥，请勿记录它。该尝试将于 `expires_at` 过期；如果过期，请重新开始。

### 第 2 步 — 轮询直到页面加载

在客户端授权后，轮询状态端点（使用相同的 `sub_account_id`，此次作为查询参数），直到出现可连接的页面。

**cURL**

```bash
curl "https://api.dmchamp.com/v1/channels/meta/status?apiKey=YOUR_API_KEY&sub_account_id=abc123def456"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.dmchamp.com/v1/channels/meta/status?sub_account_id=abc123def456",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);

const data = await res.json();
// Wait until data.status === "pages_loaded", then read data.pages
```

**Python**

```python
import requests

res = requests.get(
    "https://api.dmchamp.com/v1/channels/meta/status",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"sub_account_id": "abc123def456"},
)

data = res.json()
# Wait until data["status"] == "pages_loaded", then read data["pages"]
```

**响应：**

```json
{
  "success": true,
  "status": "pages_loaded",
  "pages": [
    {
      "id": "1098765432101234",
      "name": "Acme Studio",
      "category": "Hair Salon",
      "instagram_business_account": {
        "id": "17841400000000000",
        "username": "acme.studio"
      }
    }
  ],
  "selected_page": null
}
```

`status` 字段会经历 `pending` → `token_received` → `pages_loaded` → `connected` 的状态变化。请等待 `pages_loaded` 状态出现后再选择页面。除了正常流程，还可能出现两种终止错误状态：`failed` 和 `expired`（客户端拒绝了授权，或状态令牌的约 30 分钟有效期已过）——当发生这些情况时，会包含一个 `reason` 字段。如果看到这些状态，请停止轮询并从第 1 步重新开始；不要无限期地等待 `pending`。页面访问令牌永远不会被返回。

### 第 3 步 — 选择要连接的页面

从第 2 步获取的页面 ID 中选择一个。选择页面将同时连接该页面的 Instagram 和 Messenger。请再次在正文中包含 `sub_account_id`。

**cURL**

```bash
curl -X POST "https://api.dmchamp.com/v1/channels/meta/select-page?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "page_id": "1098765432101234",
    "sub_account_id": "abc123def456"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.dmchamp.com/v1/channels/meta/select-page", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    page_id: "1098765432101234",
    sub_account_id: "abc123def456",
  }),
});

const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.dmchamp.com/v1/channels/meta/select-page",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "page_id": "1098765432101234",
        "sub_account_id": "abc123def456",
    },
)

data = res.json()
```

**响应：**

```json
{
  "success": true,
  "page_id": "1098765432101234",
  "instagram_business_account_id": "17841400000000000"
}
```

完成 — Instagram 和 Messenger 现已连接到客户端的子账户。您只需提供 `page_id`；底层凭据会在服务器端解析，绝不会通过您的集成进行传递。

***

## 示例：为子账户购买号码

购买号码的方式相同：在查询中使用 `sub_account_id` 进行搜索，然后在正文中使用它进行购买。积分将从**子账户**的余额中扣除，号码也会配置在该子账户下。

**搜索 (cURL)：**

```bash
curl "https://api.dmchamp.com/v1/phone-numbers/available?apiKey=YOUR_API_KEY&country_code=US&sub_account_id=abc123def456"
```

**购买 (JavaScript)：**

```javascript
const res = await fetch("https://api.dmchamp.com/v1/phone-numbers", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    phone_number: "+14155551234",
    country_code: "US",
    display_name: "Support line",
    sub_account_id: "abc123def456",
  }),
});

const data = await res.json();
```

**购买 (Python)：**

```python
import requests

res = requests.post(
    "https://api.dmchamp.com/v1/phone-numbers",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "phone_number": "+14155551234",
        "country_code": "US",
        "display_name": "Support line",
        "sub_account_id": "abc123def456",
    },
)

data = res.json()
```

**响应：**

```json
{
  "success": true,
  "phone_number": "+14155551234",
  "channel": "whatsapp",
  "whatsapp_status": "PURCHASED",
  "outgoing_status": "PURCHASED",
  "status": "PURCHASED",
  "purchase_credits": 11.5,
  "monthly_credits": 11.5
}
```

号码将处于 `PURCHASED` 状态，WhatsApp 发送方注册会在后台继续进行。请轮询 `GET /v1/phone-numbers/{phoneNumber}/status?sub_account_id=abc123def456`，直到状态变为 `ONLINE` 后再发送消息。

***

## 工作示例：将模板智能体部署到每个新客户

常见的代理商模式是在您的代理商账户中保留一个主智能体，将其调整为您希望每个客户开始时使用的状态，并在配置时将副本分发到每个新子账户中。这只需三次调用，之后无需重复操作：副本会保留其设置，直到您更改它们为止。

### 第 1 步 — 复制智能体

`POST /v1/subaccounts/agents/copy`

```bash
curl -X POST "https://api.dmchamp.com/v1/subaccounts/agents/copy?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agentId": "YOUR_TEMPLATE_AGENT_ID",
    "targetUserId": "abc123def456",
    "newName": "Inbound Instagram Leads",
    "copyFaqs": true
  }'
```

响应中包含新智能体的 ID，位于 `data.agent_id`。常见问题解答、知识库和媒体库会一并复制；源账户的 WhatsApp 模板、已连接的社交帖子和联系人则不会被复制。完整字段列表请参阅[人工智能智能体 API](../api/agents.md#copy-an-agent-into-a-sub-account-agencies)。

请注意，此端点采用 `targetUserId` 而非 `sub_account_id` — 它本身指定了两个账户。下方的两个调用使用常规的 `sub_account_id` 参数。

### 第 2 步 — 开启智能体

副本到达时始终处于暂停状态，因此在您确认之前它无法向任何人发送消息。这也是锁定您希望客户使用的 AI 层级的时刻；它会保持在该层级，因此无需按计划重新应用。

```bash
curl -X PATCH "https://api.dmchamp.com/v1/agents/NEW_AGENT_ID/active?apiKey=YOUR_API_KEY&sub_account_id=abc123def456" \
  -H "Content-Type: application/json" \
  -d '{ "active": true }'

curl -X PUT "https://api.dmchamp.com/v1/agents/NEW_AGENT_ID?apiKey=YOUR_API_KEY&sub_account_id=abc123def456" \
  -H "Content-Type: application/json" \
  -d '{ "anthropic_model": "max" }'
```

若要防止客户之后更改层级，请[锁定允许的层级](#set-per-client-ai-pricing-and-policy)在子账户上，而不是重新发送该值。

### 第 3 步 — 将客户的渠道指向它

副本到达时也没有路由设置，因此在您将其设置为客户已连接渠道的应答者之前，没有任何消息会到达它。每个渠道调用一次：

```bash
curl -X PUT "https://api.dmchamp.com/v1/entry-points/channel-defaults?apiKey=YOUR_API_KEY&sub_account_id=abc123def456" \
  -H "Content-Type: application/json" \
  -d '{ "channel": "instagram", "agent_id": "NEW_AGENT_ID" }'
```

从这里开始，该渠道上来自未知联系人的首次消息将自动由复制的智能体接收。请参阅[将渠道指向智能体](../api/entry-points.md#point-a-channel-at-an-agent)以了解其他渠道和按号码路由的设置。

> **在创建子账户时设置客户的时区。** 在 `POST /v1/subaccounts` 上传递 `time_zone_id`。营销活动的活跃时间将根据子账户自身的时区进行评估，因此如果创建客户时未设置时区，其时间表将按照 UTC 读取——这会悄悄改变助手允许回复的时间。

***

## 为自行配置的客户跳过设置向导

`POST /v1/subaccounts`

默认情况下，新子账户所有者首次登录时，系统会引导他们完成设置向导。对于“代办”类客户（即在客户登录前，您已构建好营销活动并连接了渠道），请在创建账户时传入 `guided_onboarding: false`。这样他们会直接进入仪表板，且侧边栏中的**设置向导**入口将被隐藏。

```bash
curl -X POST "https://api.dmchamp.com/v1/subaccounts" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "client@example.com",
    "first_name": "Alex",
    "last_name": "Client",
    "business_name": "Client Co",
    "guided_onboarding": false,
    "usage_limits": { "monthly_credits": 500 }
  }'
```

省略该字段（或发送 `true`），向导的行为将与以往完全一致，因此现有的集成无需更改。若要稍后将向导重新提供给客户，请使用 `PUT /v1/subaccounts/{subAccountUid}/menu-visibility`（见下文）重新显示 `guided_onboarding` 项目——菜单可见性控制向导是否可访问，`guided_onboarding` 仅控制首次登录时的重定向。

***

## 为客户关闭任务、每日摘要或媒体库

`POST /v1/subaccounts`

除非您另有说明，否则这三项功能对每个新客户都是开启的，它们的行为方式与本指南中的所有其他功能不同：它们是**默认开启（opt-out）**，而非默认关闭（opt-in）。仅在 `features` 中省略它们是不够的，因为旧集成的 `features` 列表可能根本没有提及它们——我们无法区分“代理商关闭了此功能”和“此列表编写于该选项存在之前”。

因此，请使用 `feature_settings` 直接说明：

```bash
curl -X POST "https://api.dmchamp.com/v1/subaccounts" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "client@example.com",
    "first_name": "Alex",
    "last_name": "Client",
    "business_name": "Client Co",
    "feature_settings": {
      "tasks": false,
      "daily_summaries": false,
      "ai_media_library": true
    }
  }'
```

每个键都是可选的；您省略的任何内容都将保持开启状态。使用 `tasks: false`，AI 将停止为该客户创建任务，也不会发送“已创建新任务”的电子邮件；使用 `daily_summaries: false`，将永远不会生成或发送每日摘要邮件。

`feature_settings` 是在创建时关闭这三项功能的唯一方法。无论您的列表其余部分看起来如何，仅在 `features` 中省略它们不会产生任何作用——这是刻意设计的，以防止旧集成在静默状态下丢失所有这三项功能。

若要在此后更改任何设置，请将完整的 `features` 列表发送至 `PUT /v1/subaccounts/{subAccountUid}/features` ——在那里，列表中存在即表示开启该功能，缺失则表示关闭。

***

## 让您的客户自动登录其子账户 (SSO)

`POST /v1/subaccounts/{subAccountUid}/sso-link`

使用您的代理 API 密钥进行一次调用，即可返回一个可直接打开的 URL，让客户直接登录其自己的子账户——无需登录屏幕，无需密码步骤，无需在之上进行任何构建。您可以在新标签页、重定向页面或您自己产品内的 iframe 中打开它。

| 字段 | 必填 | 说明 |
|---|---|---|
| `redirect` | 否 | 客户端最终跳转的应用内页面，例如 `"/chats"` 或 `"/agents"`。在响应中作为 `deep_link_url` 返回。 |
| `app_base_url` | 否 | 链接的仪表板主机。默认为您的白标应用域名（如果没有，则为平台域名）。必须是 `https`。 |

**cURL**

```bash
curl -X POST "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/sso-link" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "redirect": "/chats" }'
```

**响应**

```json
{
  "success": true,
  "url": "https://app.yourdomain.com/auth?redirect=%2Fchats#token=eyJhbGciOi…",
  "deep_link_url": "https://app.yourdomain.com/chats",
  "expires_at": "2026-07-22T15:04:05.000Z",
  "sub_account_uid": "SUB_ACCOUNT_UID"
}
```

如何有效使用：

- **一步到位。** 打开 `url` 可让客户登录并直接进入仪表板的 `redirect` 页面——没有登录屏幕，也没有中间页面。`deep_link_url` 指定了相同的目标，适用于希望在登录后显式导航框架的集成商；一旦会话存在，任何仪表板路径在该浏览器上下文中均有效。
- **按需生成，立即打开。** 该链接包含登录凭据，并在一小时左右后过期。请在客户点击时在服务器端请求它，切勿存储或通过电子邮件发送。
- 登录令牌在 URL 片段 (`#…`) 中传输，浏览器从不将其发送到服务器，并且在被消耗的瞬间它会从地址栏中移除。
- **仅限您自己的子账户。** 该端点拒绝任何非您代理机构拥有的账户。
- 过期的链接会显示清晰的错误提示及重试路径——请重新生成一个。

***

## 隐藏子账户上的导航项

`PUT /v1/subaccounts/{subAccountUid}/menu-visibility`

控制子账户可见的侧边栏和设置项——当您嵌入仪表板并只想显示您的产品尚未涵盖的功能时非常有用。未列出的任何内容都将保持可见；将 `null` 作为整个 `menuVisibility` 值发送以将所有内容重置为可见。隐藏某项会隐藏菜单条目——将其与您授予子账户的功能配对以进行严格限制。

**cURL**

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/menu-visibility" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "menuVisibility": {
      "side_nav": { "Dashboard": false, "Campaigns": false, "Automations": false },
      "settings_nav": { "team": false }
    }
  }'
```

**响应**

```json
{
  "success": true,
  "data": {
    "subAccountUid": "SUB_ACCOUNT_UID",
    "menuVisibility": {
      "side_nav": { "Dashboard": false, "Campaigns": false, "Automations": false },
      "settings_nav": { "team": false }
    }
  }
}
```

`side_nav` 接受这 13 个键，它们与侧边栏项目名称相匹配：`Dashboard`、`DailySummaries`、`Chats`、`Contacts`、`Deals`、`Tasks`、`Automations`、`Campaigns`、`Appointments`、`Settings`、`Help`、`CreditsCounter`（侧边栏中显示的信用余额）以及 `guided_onboarding`（设置向导）。另外三个键 —— `AiInsights`、`Sub Accounts` 和 `Agency Reselling` —— 虽然可以接受但不起作用：它们仅适用于已停用的经典仪表板，因此设置它们对您的子账户没有任何影响。缺失的键意味着可见；当您亲自登录子账户时，隐藏的项目会暂时显示，以便您随时可以改回设置。

从菜单中隐藏页面并不会授予对其的访问权限。`Automations` 需要在子账户上授予 `automations` 功能——如果未授予该功能却将键设置为 `true`，页面仍然不会显示。`Tasks` 和 `DailySummaries` 的工作方式则相反：除非您将其关闭，否则它们对每个客户都是开启的（请参阅 [为客户关闭任务、每日摘要或媒体库](#turn-tasks-daily-summaries-or-the-media-library-off-for-a-client)）。

***

## 选择客户可以连接的渠道类型

`PUT /v1/subaccounts/{subAccountUid}/features`

您在套餐层级中看到的**渠道类型**开关是普通的功能 ID，因此您可以直接通过 API 而非仪表板为每个客户进行设置。这是在 URL 中直接指定子账户名称的端点之一，因此不需要 `sub_account_id`。

| 功能 ID | 渠道 |
|---|---|
| `channel_chat_widget` | 网站聊天小部件 |
| `channel_whatsapp_api` | WhatsApp Business API |
| `channel_whatsapp_web` | WhatsApp Web（二维码关联号码） |
| `channel_instagram` | Instagram |
| `channel_messenger` | Facebook Messenger |
| `channel_telegram` | Telegram |
| `channel_line` | LINE |
| `channel_viber` | Viber |
| `channel_email` | 电子邮件邮箱 |
| `channel_sms` | 短信 (SMS) |
| `channel_imessage` | iMessage |

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/features" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "features": [
      "channels_3",
      "channel_chat_widget",
      "channel_whatsapp_web",
      "channel_instagram",
      "image_understanding",
      "contact_tagging",
      "incoming_campaigns",
      "webhooks"
    ]
  }'
```

需要注意的三点：

- **此调用会替换整个功能列表。** 请发送客户端应保留的所有功能，而不仅仅是你正在更改的功能。这些 ID 的作用与你在创建账户时在 `features` 上使用的 `POST /v1/subaccounts` 相同。
- **渠道类型和渠道数量是独立的限制，且两者同时适用。** `channels_1` / `channels_3` / `channels_unlimited` 控制连接的*数量*；`channel_*` ID 控制*哪些类型*。上面的示例意味着“最多 3 个连接，且仅限聊天小部件 (Chat Widget)、WhatsApp Web 或 Instagram”。
- **不发送任何 `channel_*` ID 意味着没有渠道限制。** 这是最初的行为，这就是为什么现有客户端在发布此功能时未受影响的原因。发送一个或多个 ID，其他所有内容都会在客户端的“渠道”页面上显示为锁定状态，并显示升级说明而不是“连接”按钮。客户端已经连接的渠道将继续工作。

> 在**套餐层级**上设置渠道列表（以便购买该层级的每个客户端都能继承它）是在你的代理商套餐设置下的仪表板中完成的。此端点仅针对一个特定的子账户进行设置。

***

## 为客户设置精确的团队成员上限

`PUT /v1/subaccounts/{subAccountUid}/limits`

`team_seats_*` 功能仅提供预设的阶梯选项（3 / 5 / 10 / 不限）。若要为客户提供**精确**的团队席位数量（例如 2、7、15 或任何数字），请设置 `usage_limits.team_seats_limit`。该设置的优先级高于预设选项，平台会在每次邀请、直接添加和接受邀请时强制执行：一旦达到上限，服务器端将拒绝后续的邀请。

- 正整数即为精确上限。
- `0` 表示**不包含**团队成员——客户无法邀请任何人。
- `-1` 表示不限。
- `null` 将清除自定义上限，并回退到功能列表中所选的 `team_seats_*` 预设值。

降低上限不会移除现有的团队成员；它只会阻止添加新的成员。

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/limits" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "usageLimits": { "team_seats_limit": 7 }
  }'
```

您也可以在创建时进行设置：`POST /v1/subaccounts` 接受具有相同语义的 `usage_limits.team_seats_limit`。要读取当前值，请使用 `GET /v1/subaccounts?email=...` 获取子账户并查看 `usage_limits.team_seats_limit`（缺失/`null` = 由预设决定）。同一个端点还可以更新 `credits`、`monthly_credits`、`roll_over_to_next_month`、`rollover_cap_months`、`rollover_expiry_days` 和 `byok_monthly_limit_usd` —— 只需发送您想要更改的键即可。

**这如何与 SaaS 计划的席位限制交互。** 您的 SaaS 计划可以携带其自身的席位配额（在计划编辑器中设置 — 请参阅 [计划中的团队席位](agency-accounts.md#step-3--set-up-pricing-tiers)），当客户订阅时会自动应用该配额。您通过此端点设置的限制被视为**手动**授予：购买计划会将其替换为计划自身的席位配额（该购买是明确的计划选择），但无人值守的每月**续订绝不会覆盖手动限制** — 因此，您授予客户的一次性例外情况会在其计费周期内持续有效。使用 `null` 清除手动限制后，该字段将在下一次续订时交还给计划管理。

**限制客户在续订期间携带的额度。** 另外两个 `usage_limits` 键位于 `roll_over_to_next_month` 旁边。两者在创建时也都被 `POST /v1/subaccounts` 接受，并且 `null` 可以清除其中任何一个。

| 键 | 作用 |
|---|---|
| `rollover_cap_months` | 客户可以保留的额度月份数。0 到 120 之间的数字，允许小数（`0.5` = 半个月）。在每次续订时，未使用的余额在添加新额度之前会被修剪为最多为该续订授予额度的倍数；`0` 不会结转任何内容。 |
| `rollover_expiry_days` | 1 到 3650 之间的整数天数。在此期限内未使用的额度将在达到该期限后的第一次续订时被删除。消费总是优先扣除最旧的额度，因此每月用完额度的客户永远不会丢失任何额度。 |

如果未设置，两者都将回退到客户的套餐；此处发送的值将覆盖套餐的值。只有循环额度（每月津贴和套餐额度）受其限制：充值、自动充值和一次性添加的额度永远不会被限制或过期。每次修剪都会作为“结转上限额度调整”或“过期额度额度调整”写入客户的额度历史记录，并且永远不会计为使用量。套餐级别的等效项是定价层上的 `rollover_cap_months` 和 `rollover_expiry_days` —— 请参阅 [定价层上的字段](#the-fields-on-a-tier) 和 [限制结转内容](sub-accounts.md#capping-what-rolls-over)。

***

## 设置每个客户的 AI 定价和策略

`PUT /v1/subaccounts/{subAccountUid}/max-tier` · `/ai-tiers` · `/max-rate` · `/action-pricing` · `/insider-rate` · `/locked-bot-fields` · `/notifications` · `/zero-credit-reply`

除了上述 `/limits`、`/features` 和 `/menu-visibility` 之外，还有八个针对每个客户的开关。每个开关都在 URL 中使用子账户的 uid（没有 `sub_account_id` 正文/查询参数 —— 目标已在路径中命名），并且作用域相同：您的代理密钥，且子账户必须属于您的代理。

**客户可以使用哪些 AI 模型**

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/max-tier" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true }'
```

`{ "enabled": boolean }` 可以让客户选择加入（或退出）Max AI 层级 — 即我们平台标价的基础设施。为 BYOK 客户开启此功能会将他们的 AI 成本从“使用我自己的密钥免费”更改为“从我的额度池中扣除”，因此这是一个针对每个客户的慎重决定，而不是代理机构范围内的默认设置。

要限制客户的营销活动和代理可以从哪些层级中进行选择（而不仅仅是限制 Max 层级），请使用 `ai-tiers`：

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/ai-tiers" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "allowed_ai_tiers": ["standard", "economy"] }'
```

`allowed_ai_tiers` 是一个从 `standard`、`economy`、`max`、`mini` 中提取的数组 — 它会替换客户的允许列表。发送 `null`（或 `[]`）以清除限制并允许他们选择任何层级。这一点很重要，因为选择自己 AI 层级的子账户会消耗**您**的额度池，因此这是控制经销商客户可能在您的账单上产生多少费用的杠杆。

**当客户额度用完时的保持回复**

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/zero-credit-reply" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true, "message": "Thanks for your message, we will get back to you shortly." }'
```

当客户的余额（或您的资源池）为空时，AI 无法回答，联系人也听不到任何回复。使用 `enabled: true`，每个在停机期间写入的联系人都会收到一次 `message`（最多 500 个字符，在每个频道上按原样发送），一旦额度恢复，AI 就会真正回答这些对话。`enabled: false` 会保留保存的文本以供稍后使用；`enabled: false` 且不带 `message` 会删除该设置。与子账户编辑模态中的“额度用完时的保持回复”开关相同 —— 请参阅 [当客户额度用完时的保持回复](sub-accounts.md#a-holding-reply-while-a-client-is-out-of-credits)。

**客户每次 AI 操作的支付费用以及 WhatsApp 费用加价**

设置面向客户费率的两种方法，从最简单到最精细：

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/max-rate" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "rate": 0.35 }'
```

`rate` 是子账户自身余额在每次 Max 模型 AI 操作时消耗的额度价格 — 这是您在平台实际支付成本之上的面向客户的加价。`null` 会清除覆盖设置，恢复为平台标价。费率必须至少等于 Max 操作对您自身额度池的成本（因此您永远不能将客户定价低于您的成本），且不超过 10 个额度；超出该范围的请求将被拒绝，并在错误消息中返回计算出的最低限额。

如果需要按操作类型定价而不是统一的 Max 费率，请使用 `action-pricing`：

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/action-pricing" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "actionPricing": {
      "AI_MESSAGE": 0.6,
      "CHAT_SUMMARY": 0.15,
      "wa_carrier_multiplier": null
    }
  }'
```

`actionPricing` 是对客户现有映射的合并 — 您未提及的键将保持原样，而 `null` 会将该键重置回默认值。已识别的键：

| 键 | 价格 |
|---|---|
| `AI_MESSAGE` | AI 回复 |
| `AI_TOOL_USE` | AI 工具调用 |
| `EVALUATION_CALL` | 聊天评估通过 |
| `INTERRUPTION_HANDLING` | 处理回复中断 |
| `CONTACT_TAG` | AI 分配的联系人标签 |
| `CHAT_SUMMARY` | 聊天摘要 |
| `wa_carrier_multiplier` | 应用于客户支付的每笔非 AI WhatsApp 费用的加价乘数：月度号码租金、托管通道投递费以及 Meta/Twilio 模板转嫁成本。 |

每个操作的费率必须是大于 0 且不超过 10 的数字；`wa_carrier_multiplier` 必须至少为 `1`（低于成本价不打折）且不超过 10。发送无法识别的键或超出范围的值会导致整个请求被拒绝，并列出所有违规的键，因此拼写错误绝不会导致未实际应用的定价被静默保存。

如果您是 [Champions Circle](https://skool.com/dm-champions) 会员，`insider-rate` 会将您的 20% 折扣 Max/Lead Finder 费率仅应用于一个客户，而不是在整个代理机构范围内应用：

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/insider-rate" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true }'
```

开启此功能需要您自己的代理机构账户实际拥有 Circle 会员资格；关闭此功能则无此限制，因此会员资格过期的用户随时可以将客户的费率调回。

**锁定客户手册的部分内容**

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/locked-bot-fields" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "locked_bot_fields": ["instructions", "rules"] }'
```

`locked_bot_fields` 是一个从 `instructions`、`goal`、`rules`、`personality`、`conclude_unless` 中提取的数组 —— 它会替换客户的锁定列表。如果子账户本身（直接或通过 API 密钥）尝试更改已锁定的部分，服务器端会拒绝该请求，而您（通过 `sub_account_id`）和客户自己的仪表板管理员视图仍然可以编辑任何内容。发送 `null`（或 `[]`）即可解锁所有内容。这对于您拥有手册并根据结果进行评估的“代运营”客户非常有用。

**代表客户设置其通知偏好**

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/notifications" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "notifications": {
      "settings": {
        "credit_alerts": { "enabled": true, "channels": ["email", "in_app"] },
        "new_contacts": { "enabled": false }
      }
    }
  }'
```

`notifications` 会替换客户的整套通知偏好设置（不是按键合并 —— 请发送您希望保留的每个类别，这与子账户自身“设置”页面的保存方式一致）。`settings` 下的每个类别都接受 `enabled`（布尔值）以及来自 `email`、`in_app`、`webhook` 的最多三个 `channels`。发送 `null` 可重置为平台默认值。

所有七个端点都会响应 `{ "success": true, "data": { "subAccountUid": "...", ...the field(s) you set... } }`，并记录修改前后的审计日志。常见错误：如果您的账户不是代理机构/开发者账户，或者该子账户不归您管理，则返回 `403`；如果它不是代理机构子账户或值超出范围，则返回 `400`。

***

## 暂停已中止订阅的客户

`POST /v1/subaccounts/{subAccountUid}/pause` · `POST /v1/subaccounts/{subAccountUid}/unpause`

当客户中止与您的订阅时，请暂停其账户而不是删除它：他们发送的所有内容都会立即停止——包括出站消息、广播、所有渠道上的 AI 回复——并且当他们登录时，他们会看到全屏的 **账户已暂停** 锁定界面（带有您的可选消息），而不是应用程序界面。没有任何内容会被删除或断开连接：坐席、营销活动、已连接的渠道、联系人和聊天记录都保持原样，因此取消暂停会将客户带回到他们离开时的确切状态——无需重新设置。

| 字段 | 必填 | 描述 |
|---|---|---|
| `message` | 否 | 在锁定屏幕上向客户显示。留空则使用默认措辞。 |
| `reason` | 否 | 存储在暂停记录和审计日志中的代理内部备注——绝不会向客户显示。 |

**cURL**

```bash
curl -X POST "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/pause" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "message": "Your account is on hold — contact us to reactivate it.", "reason": "Subscription suspended per client email" }'
```

**响应**

```json
{
  "success": true,
  "data": {
    "subAccountUid": "SUB_ACCOUNT_UID",
    "paused": true,
    "level": "hard_blocked"
  }
}
```

当客户回来时，`POST /v1/subaccounts/SUB_ACCOUNT_UID/unpause`（无正文）会解除锁定——发送功能和 AI 回复会立即恢复。

值得了解：

- **这与仪表板的“硬拦截”开关状态相同**（[拦截/暂停子账户](sub-accounts.md#blocking-pausing-a-sub-account)）——通过 API 暂停的客户在仪表板中显示为已拦截，反之亦然，取消暂停会清除从任一侧设置的拦截。当前状态可从 `GET /v1/subaccounts` 上的 `agency_block` 字段读取（`"none"` 的 `level`，`"soft_blocked"` 或 `"hard_blocked"`）。
- **两个调用都是幂等的。** 暂停一个已经暂停的客户只会刷新消息、原因和时间戳；取消暂停一个活跃的客户不会改变任何内容。
- **不会自动向客户发送电子邮件**——许多代理机构使用白标服务，因此是否告知客户由您决定。
- **您自己的 DM Champ 账单不受影响。** 暂停客户只会影响您与他们的关系。
- **AI 助手也可以执行此操作**：[MCP 服务器](../integrations/connect-ai-clients.md) 将这些端点公开为 `pause_subaccount` 和 `unpause_subaccount` 工具。

***

## 直接授予或扣除额度

`POST /v1/subaccounts/credits`

从一个子账户的余额中添加或删除确切数量的额度 —— 这是仪表板手动额度调整的 API 等效项。这是一次性的余额更改，与 [`PUT /v1/subaccounts/{subAccountUid}/limits`](#set-an-exact-team-member-limit-for-a-client) 上循环的 `monthly_credits`、`roll_over_to_next_month`、`rollover_cap_months` 和 `rollover_expiry_days` 设置不同。

这是本页面上唯一一个通过 **电子邮件** 而非 `sub_account_id` 来识别子账户的端点。

| 字段 | 必填 | 说明 |
|---|---|---|
| `email` | 是 | 子账户在您代理机构下的电子邮件地址。 |
| `amount` | 是 | 非零的额度数值。正数表示增加，负数表示扣除。 |
| `description` | 否 | 在客户的额度历史记录中显示的调整说明。默认为通用的“由代理机构通过 API 调整”。 |

**cURL**

```bash
curl -X POST "https://api.dmchamp.com/v1/subaccounts/credits" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "email": "client@example.com", "amount": 500, "description": "Q3 bonus credits" }'
```

**响应**

```json
{
  "success": true,
  "data": {
    "email": "client@example.com",
    "previous_balance": 1200,
    "adjustment": 500,
    "new_balance": 1700
  }
}
```

会导致余额低于零的负 `amount` 将被拒绝，并返回 `400`，告知您可用余额以及您尝试扣除的金额。如果客户使用自己的 Stripe 计费（转售模式），添加的金额也算作他们购买的额度，因此它会像真正的充值一样在下一次月度重置后保留；对于标准分配的客户，它被视为其循环津贴的一部分。无论哪种方式，它们都是一次性添加的，因此账户（或其套餐）上设置的结转上限或过期时间永远不会修剪它们 —— 只有循环津贴和套餐额度受这些限制。

***

## 读取子账户的对话

`GET /v1/subaccounts/{subAccountUid}/chats` · `GET /v1/subaccounts/{subAccountUid}/chats/{contactId}/messages`

无需登录客户账户，即可构建客户对话的监控或支持视图。首先列出他们的联系人以及最新消息的预览，然后读取某个联系人的完整消息历史记录。

**列出联系人**

```bash
curl "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/chats?apiKey=YOUR_AGENCY_API_KEY&pageSize=25"
```

| 查询参数 | 必需 | 描述 |
|---|---|---|
| `pageSize` | 否 | 每页显示的联系人数量。默认 25，最大 50。 |
| `lastActivityAt` | 否 | 分页游标 — 传入上一页的 `lastActivityAt` 以继续。 |
| `searchQuery` | 否 | 按联系人姓名或电话号码进行筛选。 |

**响应**

```json
{
  "success": true,
  "data": {
    "contacts": [
      {
        "contactId": "contact456",
        "firstName": "Jamie",
        "lastName": "Lee",
        "phoneNumber": "+14155551234",
        "email": "jamie@example.com",
        "channel": "whatsapp",
        "lastActivityAt": "2026-08-30T14:22:00.000Z",
        "lastMessage": { "body": "Thanks, that fixed it!", "direction": "inbound", "timestamp": "2026-08-30T14:22:00.000Z" },
        "isBotActive": true,
        "markChatClosed": false
      }
    ],
    "subAccountName": "Client Co",
    "subAccountEmail": "client@example.com",
    "hasMore": true,
    "lastActivityAt": "2026-08-30T14:22:00.000Z"
  }
}
```

联系人按最近活动时间排序。当 `hasMore` 为 `true` 时，请继续使用 `lastActivityAt` 进行分页。

**读取单个联系人的消息**

```bash
curl "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/chats/contact456/messages?apiKey=YOUR_AGENCY_API_KEY&pageSize=30"
```

| 查询参数 | 必需 | 描述 |
|---|---|---|
| `pageSize` | 否 | 每页显示的消息数量。默认 30，最大 100。 |
| `beforeTimestamp` | 否 | 分页游标 — 获取早于此 ISO 时间戳的消息。 |

**响应**

```json
{
  "success": true,
  "data": {
    "messages": [
      {
        "messageId": "msg789",
        "body": "Thanks, that fixed it!",
        "direction": "inbound",
        "timestamp": "2026-08-30T14:22:00.000Z",
        "status": "received",
        "channel": "whatsapp",
        "botReply": false,
        "mediaUrl": null,
        "mediaContentType": null,
        "name": "Jamie Lee",
        "role": null
      }
    ],
    "contactInfo": { "firstName": "Jamie", "lastName": "Lee", "phoneNumber": "+14155551234", "channel": "whatsapp" },
    "hasMore": false,
    "oldestTimestamp": "2026-08-30T14:22:00.000Z"
  }
}
```

消息按最新时间优先返回；使用 `beforeTimestamp` 向后翻页查看历史记录。

***

## 读取您名下所有账户的额度使用情况和营销活动健康状况

`GET /v1/subaccounts/credit-usage` · `GET /v1/subaccounts/campaign-status`

针对您管理的所有子账户提供两种仪表板式的汇总，方便您构建自己的代理商报告，无需逐个点击进入每个客户的账户。

**额度使用情况**

```bash
curl "https://api.dmchamp.com/v1/subaccounts/credit-usage?apiKey=YOUR_AGENCY_API_KEY&from=2026-08-01&to=2026-08-31"
```

| 查询参数 | 必需 | 描述 |
|---|---|---|
| `from` / `to` | 是 | ISO 日期范围。 |
| `subAccountId` | 否 | 省略此项可获取代理商范围内的汇总，每个子账户一行。包含此项可切换至详细模式：显示该子账户的汇总及其原始的分页使用记录。 |
| `limitCount` | 否 | 仅限详细模式。默认 500，最大 2000。 |
| `startAfterTimestamp` | 否 | 仅限详细模式 — 分页游标。 |

```json
{
  "success": true,
  "data": {
    "subAccounts": [
      {
        "subAccountId": "abc123def456",
        "subAccountName": "Client Co",
        "subAccountEmail": "client@example.com",
        "totalCreditsUsed": 842,
        "totalCostUsd": 3.15,
        "byReason": { "AI reply": 620, "Chat summary": 80 },
        "topCampaigns": [{ "campaignName": "Inbound Leads", "creditsUsed": 500 }]
      }
    ],
    "totals": { "totalCreditsUsed": 842, "totalCostUsd": 3.15, "totalRecords": 214 },
    "dateRange": { "from": "2026-08-01", "to": "2026-08-31" },
    "hasMore": false,
    "lastTimestamp": null
  }
}
```

传入 `subAccountId`，相同的响应中还会包含 `records`：包含 `amount`、`reason`、`campaignName`、`contactName` 和 `timestamp` 的单笔费用。如果客户使用自己的 BYOK 密钥而非您的额度进行消费，则不会显示成本/代币数据（`costsRedacted: true`）——这是平台成本遥测数据，不应向经销商查看者展示。

**营销活动状态**

```bash
curl "https://api.dmchamp.com/v1/subaccounts/campaign-status?apiKey=YOUR_AGENCY_API_KEY&pageSize=20"
```

| 查询参数 | 必需 | 描述 |
|---|---|---|
| `pageSize` | 否 | 每页显示的子账户数量。默认 10，最大 50。 |
| `lastDocumentId` | 否 | 分页游标。 |
| `searchQuery` | 否 | 按子账户名称或电子邮件进行筛选。 |

```json
{
  "success": true,
  "data": {
    "totalSubAccounts": 34,
    "subAccountsWithIssues": 3,
    "totalLiveCampaigns": 51,
    "totalPausedCampaigns": 6,
    "subAccounts": [
      {
        "userId": "abc123def456",
        "email": "client@example.com",
        "displayName": "Jamie Lee",
        "businessName": "Client Co",
        "totalCampaigns": 2,
        "liveCampaigns": 1,
        "pausedCampaigns": 1,
        "hasIssues": true,
        "issueDetails": ["1 campaign paused"],
        "lastCampaignActivity": "2026-08-29T09:00:00.000Z"
      }
    ],
    "hasMore": true,
    "lastDocumentId": "abc123def456",
    "pageSize": 20
  }
}
```

`hasIssues` / `issueDetails` 标记那些值得关注的子账户——例如已暂停的营销活动，或未路由任何渠道的营销活动。使用此功能可以构建跨整个客户群的健康检查仪表板，而无需逐个打开客户页面来查看是否有停滞的营销活动。

> 若要获取所有客户的时间序列消息和信用活动（即适合图表展示的序列，而非特定时间点的快照），请参阅 Analytics API 指南中的 `GET /analytics/agency-rollup`。

***

## 交付一个已设置好的客户端账户

`PUT /v1/snapshots/default` · `POST /v1/snapshots/{snapshotId}/apply`

[快照](snapshots.md) 是一个可重用的模板：一个或多个 AI 智能体及其知识库、工具和媒体，均从你自己的账户中捕获。有两个端点可将其放入你的配置流程中。

**自动 —— 每个新客户端天生就拥有它。** 将某个快照标记为默认值一次，此后你创建的每个账户都会在创建时自动安装它。这涵盖了通过 `POST /v1/subaccounts` 创建的账户、你在仪表板中创建的账户，以及当客户端通过你的结账链接付款时自动创建的账户。

首先，找到快照的 id：

```bash
curl "https://api.dmchamp.com/v1/snapshots" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY"
```

然后将其设置为默认值：

```bash
curl -X PUT "https://api.dmchamp.com/v1/snapshots/default" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "snapshot_id": "SNAPSHOT_ID" }'
```

这就是整个集成过程。发送 `{"snapshot_id": null}` 即可再次将其关闭。你也可以通过点击“**快照**”页面上的星号，在仪表板中执行相同的操作。

要读取当前已加星标的内容（例如，在配置脚本决定是否设置一个之前），`GET /v1/snapshots/default` 会返回 `{ "success": true, "data": { "default_snapshot_id": "SNAPSHOT_ID" } }` ——当没有加星标的内容时返回 `null`。`GET /v1/snapshots`（用于查找上述 id）会返回相同的 `default_snapshot_id` 以及完整的 `snapshots` 数组，因此大多数集成只需要调用一次即可。完整快照对象的字段请参阅 [快照](snapshots.md) 指南。

**按需 —— 安装到单个账户中。** 这对于引导现有客户端，或稍后为客户端提供第二个模板非常有用。

```bash
curl -X POST "https://api.dmchamp.com/v1/snapshots/SNAPSHOT_ID/apply" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "sub_account_id": "SUB_ACCOUNT_UID" }'
```

省略 `sub_account_id`，它将改为安装到你自己的代理商账户中。与 `/subaccounts` 端点一样，这些端点在路径或正文中指定目标账户，而不是通过环境 `sub_account_id` 参数指定。

在构建之前值得了解的内容：

- **安装的智能体默认处于暂停状态。** 请先连接客户端的渠道，然后再激活智能体。这对于自动路径和按需路径都适用。
- **配置绝不会因为快照而失败。** 如果安装无法完成，客户端账户仍然会被创建并可以使用 —— 它只是会以空状态到达，你可以稍后再应用快照。
- **渠道、日历和 OAuth 连接永远不会被复制。** 每个账户都连接其自己的渠道。使用普通 API 密钥的工具会立即继续工作。
- **应用两次会创建第二个副本。** 不会覆盖任何内容。

***

## 通过 API 构建模板本身

`POST /v1/snapshots` · 通过 API 管理智能体、自定义函数和媒体

上一节介绍了如何分发在仪表板中构建的快照。创作部分也同样开放，因此整个闭环——即一次性组装好主设置、捕获它，然后将其分发给每个客户端——都可以通过代码运行。

以下是配置脚本所使用的组件，按使用顺序排列：

1. **创建自定义函数。** `POST /v1/custom-functions` 用于创建一个函数；`GET /v1/custom-functions` 列出您拥有的函数，而 `GET`、`PUT` 和 `DELETE` 在 `/v1/custom-functions/{customFunctionId}` 上用于读取、更新和删除函数。`POST /v1/custom-functions/test` 在保存定义之前对其进行试运行。
2. **创建并配置智能体。** `POST /v1/agents` 用于创建智能体，`PUT /v1/agents/{agentId}` 用于更新它，`PATCH /v1/agents/{agentId}/active` 配合 `{ "active": false }` 可在您工作时将其保持在暂停状态（使用 `true` 进行相同的调用即可使其上线）。`GET /v1/agents` 用于列出所有智能体。
3. **赋予智能体能力。** `POST /v1/agents/{agentId}/custom-functions` 配合 `{ "custom_function_id": "..." }` 将函数附加到智能体；对应的 `DELETE /v1/agents/{agentId}/custom-functions/{customFunctionId}` 则将其分离。
4. **填充媒体库。** `POST /v1/agents/{agentId}/media-library` 用于上传项目（JSON 包含 `base64Data`、`mimeType`、`title`、`description`）；`GET` 列出智能体的项目，`PATCH`/`DELETE` 在 `/{itemId}` 上用于更新或删除项目。
5. **将其捕获为快照。**

```bash
curl -X POST "https://api.dmchamp.com/v1/snapshots" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Master setup v1",
    "agent_ids": ["AGENT_ID"],
    "include_knowledge": true,
    "include_tools": true,
    "include_media": true
  }'
```

此后便回到了上一节的内容：将其设为默认值，以便每个新客户端在创建时都具备该配置，或者按需应用它。管理工作也同步进行：`PATCH /v1/snapshots/{snapshotId}` 配合 `{ "name": "..." }` 用于重命名快照，`DELETE /v1/snapshots/{snapshotId}` 用于删除快照（如果它是默认值，则会取消其默认状态），`GET /v1/snapshots/apply-targets` 列出您可以安装到的所有账户。

智能体、自定义函数和媒体端点都接受 `sub_account_id`，因此相同的调用也可以直接维护某个客户端账户内的智能体。快照调用始终作用于您的代理账户——模板存储在您这里。所有这些接口的完整请求和响应模式均可在 [API 参考](../api/reference.md) 中找到。

***

## 通过 API 管理您的定价层级

`GET /v1/agency/pricing-tiers` · `POST /v1/agency/pricing-tiers` · `PATCH /v1/agency/pricing-tiers/{tierIndex}` · `DELETE /v1/agency/pricing-tiers/{tierIndex}`

您在 **SaaS 模式 → 定价层级** 中销售的套餐可以通过代码进行读取和更改，因此您自己的管理面板或配置脚本可以在无需任何人打开仪表板的情况下添加套餐、调整价格或分发结账链接。像本页面上的其他所有调用一样，使用您的代理商 API 密钥进行身份验证；这些端点属于代理商级别，因此不需要 `sub_account_id`。每次写入操作都会运行与仪表板保存相同的验证和 Stripe 产品与价格同步，因此在此处创建的套餐与您手动设置的套餐没有区别。

### 列出您的层级

```bash
curl "https://api.dmchamp.com/v1/agency/pricing-tiers" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY"
```

**响应**

```json
{
  "success": true,
  "data": {
    "tiers": [
      {
        "tierIndex": 0,
        "credits": 1000,
        "price_cents": 2900,
        "currency": "usd",
        "label": "Starter",
        "billing_interval": "month",
        "trial_days": 14,
        "trial_credits": 250,
        "trial_card_required": false,
        "trial_hard_expiry": true,
        "stripe_price_id": "price_1PxAbC…",
        "stripe_product_id": "prod_QxAbC…",
        "checkout_url": "https://app.yourdomain.com/v1/checkout?id=YOUR_AGENCY_UID&tierIndex=0"
      }
    ],
    "count": 1,
    "max_tiers": 20
  }
}
```

每个层级返回时都会包含其 **`tierIndex`**（即它在您的套餐列表中的位置，这也是其他三个调用引用它的方式）以及一个可直接分享的 **`checkout_url`**，这与 **付款** 选项卡中提供的链接相同，且已指向销售该套餐的[白标域名](white-labeling.md)。

### 添加层级

请求主体是一个层级对象；它会被追加到您的列表末尾。

```bash
curl -X POST "https://api.dmchamp.com/v1/agency/pricing-tiers" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Starter",
    "credits": 1000,
    "price_cents": 2900,
    "currency": "usd",
    "billing_interval": "month",
    "trial_days": 14,
    "trial_credits": 250,
    "trial_card_required": false,
    "trial_hard_expiry": true,
    "features": ["channels_3", "channel_whatsapp_web", "webhooks"]
  }'
```

响应包含已创建的层级，包括它所处的 `tierIndex` 及其 `checkout_url`。

### 编辑层级

仅发送您想要更改的字段；套餐上的其他所有内容将保持不变。

```bash
curl -X PATCH "https://api.dmchamp.com/v1/agency/pricing-tiers/0" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "price_cents": 3900, "trial_hard_expiry": true }'
```

API 无法识别的字段会被拒绝而不是被忽略，并且错误信息会指出该字段名称——因此拼写错误永远不会悄悄写入一个看起来生效但实际上无效的设置。更改价格、额度、货币或计费周期会在您的 Stripe 中创建一个新价格；已经订阅的客户将保留在他们注册时的套餐中。

### 删除层级

```bash
curl -X DELETE "https://api.dmchamp.com/v1/agency/pricing-tiers/2" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY"
```

与仪表板规则相同：仍有活跃订阅者的套餐无法删除。请求会被拒绝，并告知您该套餐上有多少订阅者——请先取消或迁移他们。删除成功后，响应将返回您剩余的层级，并已重新编号。

### 层级上的字段

| 字段 | 说明 |
|---|---|
| `label` / `description` | 套餐名称，以及在结账页面上显示的可选行。 |
| `credits` | 客户在月度或年度套餐中**每月**获得的额度，以及在周度套餐中**每个计费周期**获得的额度。 |
| `price_cents` | 每个计费周期的价格，以最小货币单位表示（`2900` = $29.00）。在年度套餐中，这是全年的价格。 |
| `currency` | 小写 ISO 代码 —— `usd`、`eur`、`gbp` 等。 |
| `billing_interval` / `billing_interval_count` | `month`（默认值）、`year` 或 `week`（计数为 1–52，表示“每 N 周”）。 |
| `trial_days` | 免费试用时长，0 到 90 天。`0`（或省略）表示没有试用。 |
| `trial_credits` | 客户开始试用时获得的额度。默认为套餐的 `credits`。 |
| `trial_card_required` | `false` 允许客户在不输入银行卡的情况下开始试用。默认为 `true`。 |
| `trial_hard_expiry` | `true` 会在试用结束且未升级时将未使用的试用额度退还到您的资源池并锁定客户账户。默认为 `false` —— 请参阅 [试用后的硬过期](agency-accounts.md#step-3--set-up-pricing-tiers)。 |
| `rollover_cap_months` | 此套餐的客户在续订期间可以携带的津贴月份数 —— 0 到 120 之间的数字，允许小数。`0` 表示不结转任何内容；`null`（默认值）表示没有上限。请参阅 [限制结转内容](sub-accounts.md#capping-what-rolls-over)。 |
| `rollover_expiry_days` | 在下一次续订时删除未使用额度的天数 —— 1 到 3650 之间的整数。`null`（默认值）表示它们永不过期。 |
| `features` / `feature_settings` | 此套餐的客户获得的内容 —— 与 [选择客户可以连接的频道类型](#choose-which-channel-types-a-client-can-connect) 相同的特征 ID。 |
| `team_seats_limit` | 套餐授予的团队席位：确切数字，`0` 表示无，`-1` 表示无限。 |
| `white_label_config` | 该套餐在您的哪个 [白标域名](white-labeling.md#up-to-three-white-labels) 上销售。 |

试用相关字段仅在包含试用的方案中有效：如果保存方案时 `trial_days: 0` 为空，这些字段将被丢弃。方案的 Stripe 产品 ID 和价格 ID 由系统为您管理，无法手动设置。

需要注意的三点：

- **层级索引是位置，而非永久 ID。** 删除一个计划会将该计划之后的所有计划向前移动一位，因此在进行任何更改后请重新获取列表——并重新复制您已发布的结账链接，这与您在仪表板中删除计划后的操作完全相同。
- **必须先设置 SaaS 模式。** 这些端点需要一个已保存白标设置和 Stripe 密钥的代理账户；如果没有这些，计划的产品和价格将无法关联到任何 Stripe 账户。
- **上限为二十个计划**，与仪表板中的限制相同。列表响应中的 `max_tiers` 字段会告知您当前的限制。

完整的请求和响应模式位于 [API 参考](../api/reference.md) 的 **Agency** 部分下。

***

## 通过 API 设置您的每积分价格

`GET /v1/agency/credit-price` · `PATCH /v1/agency/credit-price`

客户为即时充值支付的价格（**SaaS 模式 → 每积分定价**）也可以通过代码进行读取和更改。此功能专为价格需要自动变动的情况而设计：例如，一家以一种货币销售积分但以另一种货币收费的代理机构，可以让定时任务根据汇率变化来调整价格，而不是每周由人工手动编辑。

### 读取当前价格

```bash
curl "https://api.dmchamp.com/v1/agency/credit-price" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY"
```

**响应**

```json
{
  "success": true,
  "data": {
    "price_per_credit_cents": 125,
    "price_per_credit_currency": "brl",
    "note": "USD 0.25 per credit at our reference rate",
    "minimum_cents": 60
  }
}
```

`minimum_cents` 是平台在该货币下允许的最低价格，因此任务可以在发送新价格之前对其进行检查。在设置价格之前，所有三个值均为 `null`。

### 更改价格

```bash
curl -X PATCH "https://api.dmchamp.com/v1/agency/credit-price" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "price_per_credit_cents": 130, "currency": "brl" }'
```

仅发送您想要更改的内容。`price_per_credit_cents` 是以最小货币单位表示的价格（`130` = 1.30 雷亚尔）；`currency` 是小写的 ISO 代码；`note` 是一个可选的文本行，最多 200 个字符，会直接显示在客户“账单”页面上每积分价格的下方——这对于显示另一种货币的参考价格非常方便，例如 *“按我们的参考汇率，每积分 0.25 美元”*。发送 `"note": ""` 即可将其移除。响应格式与上述读取操作相同，因此任务可以进行比较，并在没有变化时跳过写入操作。

适用规则与仪表板中的相同：价格不得低于该货币的平台最低限额，且账户需要启用白标功能。与定价层级端点不同，读取或更改此值不需要 Stripe 密钥。

### 为任务分配一个仅具备特定权限的密钥

将您的完整代理密钥放入调度程序中，其权限超出了价格更新所需的范围。相反，请创建一个仅限于 **Agency Credit Price**（代理额度价格）区域的**作用域密钥**：该密钥只能读取和更改每额度价格，不能执行其他任何操作——它无法访问子账户、计划、额度或您的 Stripe 连接。

```bash
curl -X POST "https://api.dmchamp.com/v1/api-keys" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "label": "FX price updater", "scopes": { "read_only": false, "tags": ["Agency Credit Price"] } }'
```

响应会在 `api_key` 中**仅一次性**携带新密钥——它之后不会再显示，因此请立即保存。如果密钥仅需读取价格，请设置 `"read_only": true`；如果您希望它在特定日期自动失效，请添加 `"expires_at"`（一个 ISO 日期）。只有账户所有者的密钥才能创建作用域密钥；请使用 `GET /v1/api-keys` 和 `DELETE /v1/api-keys/{id}` 来列出或撤销它们。

***

## 让子账户读取您的定价

`GET /v1/subaccounts/agency-pricing`

本页上的其他所有端点均使用**您的代理密钥**进行调用，并可通过 `sub_account_id` 选择性地指定目标客户。此端点则相反：它使用**子账户自己的 API 密钥**进行调用，且无需 `sub_account_id`，因此客户自己的充值页面（或您为他们构建的集成）可以显示您向他们收取的费用，而无需查看您的代理账户。

```bash
curl "https://api.dmchamp.com/v1/subaccounts/agency-pricing" \
  -H "X-API-Key: THE_SUB_ACCOUNTS_OWN_API_KEY"
```

**响应**

```json
{
  "success": true,
  "data": {
    "tiers": [{ "credits": 1000, "price_cents": 2900, "currency": "usd" }],
    "price_per_credit_cents": 125,
    "price_per_credit_currency": "brl",
    "price_per_credit_note": "USD 0.25 per credit at our reference rate",
    "agency_display_name": "Client Co's Growth Partner"
  }
}
```

这完全镜像了 [`GET /v1/agency/pricing-tiers`](#list-your-tiers) 和 [`GET /v1/agency/credit-price`](#read-the-current-price) 作为代理为您返回的内容，去除了客户无需查看的信息（Stripe ID、`max_tiers` 等）。它仅适用于实际上是带有关联代理的子账户的账户——从您自己的代理账户调用此接口会返回权限错误。

***

## 需要注意的事项

- **使用你的代理商密钥。** 使用你代理商账户的 API 密钥对每个调用进行身份验证，而不是使用子账户的密钥。`sub_account_id` 参数用于重定向操作。
- **积分来自子账户。** 购买和循环扣费会扣除目标子账户的积分余额，而不是你的余额。
- **`404` 意味着“不是你的子账户”。** 请仔细检查 id，并确认该账户是你管理的账户。
- **该参数在所有接受它的地方都是可选的。** 省略它，同一个端点就会作用于你的代理商账户，因此你可以将一个集成用于两者。

***

## 相关内容

- [API 访问](../integrations/api-access.md) — 身份验证、基础 URL、错误、速率限制。
- [子账户](sub-accounts.md) — 列出并管理您可以定位的账户。
- [子账户自动充值](sub-account-auto-recharge.md) — 通过 Webhook + API 为子账户授予信用额度。
- [营销活动 API](../api/campaigns.md) — 创建、更新和复制营销活动，包括完整的字段参考。
- [渠道连接 API](../api/channels.md) — 连接客户的渠道并将其路由到营销活动。
- Analytics API 指南（在 API 部分） — 代理子账户汇总及其他所有报告端点。
- [快照](snapshots.md) — 快照捕获的内容以及如何在仪表板中构建快照。
