> This is a page from the ElevenLabs documentation. For a complete page index, fetch https://elevenlabs.io/docs/llms.txt. For the full documentation in a single file, fetch https://elevenlabs.io/docs/llms-full.txt.

# 自定义渠道

## 概述

自定义渠道可将外部消息系统连接到 ElevenLabs 智能体。将用户消息发送到 ElevenLabs webhook，然后在自己的 HTTPS 端点接收智能体回复。

> **Warning**
>
> 自定义渠道目前处于 alpha 阶段。

> **Warning**
>
> 使用零留存模式的智能体或工作区无法使用自定义渠道。

## 功能

| 功能         | 支持情况                    |
| ---------- | ----------------------- |
| 零留存模式（ZRM） | 不支持，ZRM 工作区和 ZRM 智能体不可用 |
| 消息中的附件     | 不支持，消息仅支持文本             |

## 设置

#### 打开自定义渠道

打开智能体，选择 **渠道**，选择 **自定义渠道**，然后点击 **添加触发器**。

#### 配置触发器

选择现有连接或创建新连接，然后输入 **回复 Webhook URL**。

#### 复制凭据

点击 **添加**，然后复制 **入站 Webhook URL**、**入站密钥** 和 **出站签名密钥**。

#### 配置服务

将用户消息发送到入站 webhook URL，并在 `X-Webhook-Secret` 中提供入站密钥。使用出站签名密钥验证每条回复。

## 发送消息

向生成的 webhook URL 发送 `POST` 请求：

```text
POST /v1/convai/api-integrations/custom_channel/triggers/{trigger_connection_id}/async_message
X-Webhook-Secret: <inbound-secret>
Content-Type: application/json
```

```json
{
  "data": {
    "type": "user_message",
    "text": "Where is my order?",
    "user_identifier": "customer_8427"
  },
  "user_message_id": "msg_01k1e6z3f4t8n9c2",
  "dynamic_variables": {
    "order_id": "order_72491"
  }
}
```

| 字段                     | 必填 | 说明                       |
| ---------------------- | -- | ------------------------ |
| `data.type`            | 是  | 必须为 `user_message`。      |
| `data.text`            | 是  | 非空用户消息。                  |
| `data.user_identifier` | 否  | 外部用户的标识符。                |
| `user_message_id`      | 是  | 由系统提供的非空幂等键。             |
| `conversation_id`      | 否  | 包含返回的 ID 可继续对话；省略则开始新对话。 |
| `dynamic_variables`    | 否  | 此轮提供给智能体的动态变量。           |

ElevenLabs 会在处理此轮前返回 `202 Accepted`：

```json
{
  "conversation_id": "conv_01k1e72d4x8p6v3m",
  "status": "queued"
}
```

要继续对话，请发送另一个请求，并附上该 `conversation_id` 和新的 `user_message_id`。

## 接收回复

每轮结束后，ElevenLabs 会向回复 webhook URL 发送 `POST` 请求：

```json
{
  "version": "1",
  "conversation_id": "conv_01k1e72d4x8p6v3m",
  "user_message_ids": ["msg_01k1e6z3f4t8n9c2"],
  "status": "completed",
  "data": [
    {
      "type": "agent_response",
      "event": {
        "agent_response": "Your order is scheduled to arrive tomorrow.",
        "response_id": "9f2c1a7e-4b3d-4e8a-9c1f-2d6b8e0a5f31",
        "event_id": 4
      }
    },
    {
      "type": "agent_tool_response",
      "event": {
        "tool_name": "end_call",
        "tool_call_id": "toolu_01k1e70r4b8y",
        "tool_type": "system",
        "event_id": 4,
        "is_called": true,
        "is_error": false,
        "is_blocked": false,
        "status": "success"
      }
    }
  ],
  "error": null
}
```

如果处理失败，`status` 为 `failed`，`data` 为 `[]`，`error` 包含具体说明。

`data` 按轮次顺序列出事件。每个项目都包含 `type` 和 `event`：

* `agent_response` 包含一条智能体发言。`response_id` 唯一标识该发言，`event_id` 将其关联到某一轮。如果渠道每轮只显示一个文本气泡，请拼接 `agent_response` 的值。
* `agent_tool_response` 报告工具执行结果，并共享该轮的 `event_id`。其 `status` 可以是 `success`、`error`、`blocked` 或 `skipped`。当回复包含 `tool_type: "system"`、`tool_name: "end_call"` 和 `status: "success"` 时，表示智能体已结束对话。

多个入站消息可能会合并为一轮。`user_message_ids` 列出此回复所回答的用户消息 ID。

## 验证回复签名

每条回复均包含 `ElevenLabs-Signature` 标头：

```text
t=1753876800,v0=<hex-digest>
```

摘要是使用出站签名密钥对 `{timestamp}.{raw_request_body}` 生成的 HMAC-SHA256 签名。请在解析 JSON 前验证原始请求正文，并拒绝过期时间戳。

```python
import hashlib
import hmac
import time


def verify_signature(raw_body: bytes, header: str, secret: str) -> None:
    values = dict(part.split("=", 1) for part in header.split(","))
    timestamp = values["t"]
    if abs(time.time() - int(timestamp)) > 30 * 60:
        raise ValueError("Stale webhook signature")

    expected = hmac.new(
        secret.encode(),
        timestamp.encode() + b"." + raw_body,
        hashlib.sha256,
    ).hexdigest()
    if not hmac.compare_digest(expected, values["v0"]):
        raise ValueError("Invalid webhook signature")
```

```typescript
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifySignature(rawBody: Buffer, header: string, secret: string): void {
  const values = Object.fromEntries(header.split(",").map((part) => part.split("=", 2)));
  const timestamp = values.t;
  if (!timestamp || Math.abs(Date.now() / 1000 - Number(timestamp)) > 30 * 60) {
    throw new Error("Stale webhook signature");
  }

  const expected = createHmac("sha256", secret).update(`${timestamp}.`).update(rawBody).digest();
  const received = Buffer.from(values.v0 ?? "", "hex");
  if (received.length !== expected.length || !timingSafeEqual(expected, received)) {
    throw new Error("Invalid webhook signature");
  }
}
```

## 传送行为

ElevenLabs 会在大约 0、0.5 和 2 秒时进行 3 次进程内传送尝试。收到 `2xx` 响应即视为传送成功。

请求正文限制为 256 KiB。