---
title: "Webhook 端点"
description: "使用每个端点独立的密钥和事件筛选器管理多个 Webhook URL。"
---

基于端点的 Webhook 系统允许您为每个组织注册**多个**目标地址，每个地址都拥有各自的密钥、状态，以及对部分事件类型的独立订阅。对于所有新集成，建议使用此模型。

与[旧版单 URL Webhook](/api-reference/organizations#legacy-single-url-webhook)相比，该方式支持每个组织配置多个 URL；旧版 Webhook 仅为向后兼容而保留，并且每个组织仅支持一个 URL。

## 端点

| 方法 | 路径 | 所需角色 | 描述 |
|--------|------|---------------|-------------|
| `GET` | `/v1/developer/webhook-endpoints` | `admin+` | 列出端点 |
| `POST` | `/v1/developer/webhook-endpoints` | `admin+` | 创建端点 |
| `PATCH` | `/v1/developer/webhook-endpoints/{endpoint_id}` | `admin+` | 更新标签 / URL / 事件 / 状态 |
| `DELETE` | `/v1/developer/webhook-endpoints/{endpoint_id}` | `admin+` | 删除端点 |
| `POST` | `/v1/developer/webhook-endpoints/{endpoint_id}/rotate-secret` | `admin+` | 替换签名密钥 |
| `POST` | `/v1/developer/webhook-endpoints/{endpoint_id}/test` | `admin+` | 发送已签名的测试投递 |
| `GET` | `/v1/developer/webhook-deliveries` | `admin+` | 检查近期端点和旧版投递结果 |

## 端点对象

```json
{
  "id": "c4d5e6f7-...",
  "label": "Production — Call events",
  "url": "https://example.com/thunderphone/hook",
  "events": ["telephony.incoming", "telephony.complete"],
  "status": "active",
  "agent_id": 42,
  "agent_name": "Support Agent",
  "secret_hint": "a1b2…9f0e",
  "paused_at": null,
  "pause_last_error": "",
  "pause_undelivered_count": null,
  "created_at": "2026-04-20T18:24:10.113Z",
  "updated_at": "2026-04-20T18:24:10.113Z"
}
```

| 字段 | 类型 | 描述 |
|-------|------|-------------|
| `id` | UUID | 端点 ID |
| `label` | string | 显示名称，1–120 个字符 |
| `url` | string | HTTPS URL；开发环境允许使用 `http://localhost` |
| `events` | array of string | 订阅的事件类型（请参阅[有效值](#valid-event-types)）。空数组会订阅除仅可显式订阅的逐轮事件（`telephony.turn` / `web.turn`）之外的所有事件 |
| `status` | string | `active`、`disabled`（手动暂停）或 `failing`（当一次投递在 24 小时重试计划耗尽后仍未收到任何 2xx 响应时自动设置） |
| `agent_id` | integer \| null | 此端点限定到的智能体；`null` 表示组织范围 |
| `agent_name` | string \| null | 限定智能体的名称；组织范围端点则为 `null` |
| `secret_hint` | string | 签名密钥的前 4 个和后 4 个字符，中间以省略号分隔（`a1b2…9f0e`）——足以让您与本地保存的密钥进行核对，同时不会暴露完整值 |
| `paused_at` | timestamp \| null | ThunderPhone 将端点设为 `failing` 的时间。当端点为 `active` 或 `disabled` 时为 `null` |
| `pause_last_error` | string | 端点暂停时最后一次投递错误，例如 `HTTP 503: Service Unavailable`。除非状态为 `failing`，否则为空 |
| `pause_undelivered_count` | integer \| null | 自端点上次成功投递以来未投递的事件数，在端点暂停时统计 |
| `created_at`, `updated_at` | timestamp | |

<Note>
  端点的完整 `secret` 仅会在创建时返回**一次**，
  此后不会再次返回。请安全存储它——如果您丢失了它，
  请[轮换它](#rotate-the-signing-secret)以获取新的密钥。
</Note>

### 有效事件类型

系统会根据以下精确集合验证 `events`——列表之外的值将返回
`400`。请参阅[事件目录](/zh/webhooks/events)了解各类型的
负载结构。

- `telephony.incoming`、`telephony.complete`、`telephony.tool`、`telephony.turn`
- `web.incoming`、`web.complete`、`web.tool`、`web.turn`
- `sms.sent`、`sms.failed`
- `sms.received`（保留；尚未触发）
- `call.graded`、`call.data_extracted`、`call.transfer_completed`
- `campaign.completed`
- `issue.reported`、`issue.escalated`
- `test-call.completed`
- `alert.triggered`

`issue.escalated` 没有智能体上下文，仅会投递至
组织范围端点。

无法显式选择 `voice.ready` 和 `voice.failed`。如需接收
这些事件，请创建一个包含 `events: []` 的组织范围端点。空事件
列表会接收除 `telephony.turn` 和 `web.turn` 之外的所有受支持事件，
这两个事件必须显式选择。

### 端点状态

- `active` ——投递正常进行。
- `disabled` ——通过 `PATCH` 手动暂停。不发送任何请求。我们绝不会更改 `disabled` 端点的状态；是否将其恢复为 `active` 始终由您决定。
- `failing` ——当向该端点的投递耗尽整个重试计划（24 小时内尝试 8 次）且始终未获得 2xx 响应时自动设置。处于失败状态的端点不会再接收流量。发生这种情况时，我们会向您组织的所有者和管理员发送电子邮件，控制台会显示已暂停的端点及“重新激活”按钮。端点修复后，通过 `PATCH` 将其状态改回 `active`；重试计划尚未耗尽的投递将从中断处继续。

---

## 列出端点

<CodeGroup>
```bash cURL
curl https://api.thunderphone.com/v1/developer/webhook-endpoints \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"
```
</CodeGroup>

返回一个[端点对象](#endpoint-object)数组。
传入 `?agent_id=42` 以仅返回限定为该智能体的端点。

### 智能体限定端点

组织范围端点会接收所有匹配事件。带有
`agent_id` 的端点仅接收由该智能体处理的通话的匹配事件；
没有智能体上下文的事件（例如 `alert.triggered`）永远不会到达该端点。您
还可以通过智能体构建器的
**Webhook** 部分创建和管理这些端点。

---

## 创建端点

<CodeGroup>
```bash cURL
curl -X POST https://api.thunderphone.com/v1/developer/webhook-endpoints \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "label":  "Production — Call events",
    "url":    "https://example.com/thunderphone/hook",
    "events": ["telephony.incoming", "telephony.complete"]
  }'
```

```python Python
result = requests.post(
    "https://api.thunderphone.com/v1/developer/webhook-endpoints",
    headers={"Authorization": "Bearer sk_live_YOUR_API_KEY"},
    json={
        "label":  "Production — Call events",
        "url":    "https://example.com/thunderphone/hook",
        "events": ["telephony.incoming", "telephony.complete"],
    },
).json()
secret = result["secret"]
endpoint_id = result["id"]
```
</CodeGroup>

### 请求字段

| 字段 | 类型 | 必填 | 说明 |
|-------|------|----------|-------------|
| `label` | string | 是 | 1–120 个字符 |
| `url` | string | 是 | HTTPS URL（仅允许对 `localhost` / `127.0.0.1` 使用 `http`） |
| `events` | array | 否 | 留空或省略时订阅除 `telephony.turn` / `web.turn` 之外的所有事件，后两者需要显式订阅。必须使用[有效事件类型](#valid-event-types)中列出的值；重复项会被移除 |
| `agent_id` | integer \| null | 否 | 将投递范围限定为此组织中的某个智能体；省略或使用 `null` 则创建组织级端点 |

返回 `201 Created`，其中包含[端点对象](#endpoint-object)，以及额外的顶级 `secret` 字段，其中包含原始签名密钥——一个 48 个字符的十六进制字符串：

```json
{
  "id": "c4d5e6f7-…",
  "label": "Production — Call events",
  "url": "https://example.com/thunderphone/hook",
  "events": ["telephony.incoming", "telephony.complete"],
  "status": "active",
  "secret_hint": "a1b2…9f0e",
  "created_at": "2026-04-20T18:24:10.113Z",
  "updated_at": "2026-04-20T18:24:10.113Z",
  "secret": "a1b2c37e08d94f5b16a2c8d90e7f3a4b5c6d7e8f90a19f0e"
}
```

<Warning>
  `secret` **仅在创建时返回**。后续的 `GET` 响应仅包含 `secret_hint`。请在关闭响应前将完整值复制到您的密钥管理器。
</Warning>

---

## 更新端点

<CodeGroup>
```bash cURL
curl -X PATCH https://api.thunderphone.com/v1/developer/webhook-endpoints/c4d5e6f7-... \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "label":  "Production — Call + Grade events",
    "events": ["telephony.incoming", "telephony.complete", "call.graded"]
  }'
```
</CodeGroup>

| 字段 | 类型 | 说明 |
|-------|------|-------------|
| `label` | string | |
| `url` | string | |
| `events` | array | |
| `status` | string | `active` 或 `disabled`。将服务器标记为 `failing` 的端点重新启用时，请设置为 `active` |
| `agent_id` | integer \| null | 设置智能体 ID 以限定端点范围，或设为 `null` 以创建组织级端点 |

返回 `200 OK`，其中包含更新后的[端点对象](#endpoint-object)。

---

## 轮换签名密钥

替换端点的签名密钥，例如在密钥丢失或可能泄露时。对于智能体范围的端点，您也可以使用控制台：打开该智能体的 **Webhook** 部分，并在端点上选择 **重新生成密钥**。组织范围的端点通过 API 轮换。

<CodeGroup>
```bash cURL
curl -X POST https://api.thunderphone.com/v1/developer/webhook-endpoints/c4d5e6f7-.../rotate-secret \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```
</CodeGroup>

| 字段 | 类型 | 必填 | 描述 |
|-------|------|----------|-------------|
| `include_copies` | 布尔值 | 否 | 默认为 `true`。还会将新密钥分配给您组织中具有相同 URL 和相同当前密钥的其他端点，例如其他智能体上此端点的副本，以便同一接收方持续使用一个密钥进行验证 |

返回 `200 OK`：

```json
{
  "endpoint": { "id": "c4d5e6f7-...", "secret_hint": "7f3a…c21d", "...": "..." },
  "secret": "7f3a90b2c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5c21d",
  "rotated_endpoint_ids": ["c4d5e6f7-..."]
}
```

| 字段 | 类型 | 描述 |
|-------|------|-------------|
| `endpoint` | 对象 | 带有新 `secret_hint` 的[端点对象](#endpoint-object) |
| `secret` | 字符串 | 新的签名密钥。仅在此响应中返回 |
| `rotated_endpoint_ids` | UUID 数组 | 现在使用新密钥的每个端点 |

<Warning>
  旧密钥会立即停止使用：下一次投递（包括重试）将使用新密钥签名。在您的接收方获取新密钥之前，投递将无法通过其签名验证；如果接收方以非 2xx 状态响应，系统会按照常规的 24 小时计划重试，因此请及时更新接收方。如果您的接收方分阶段发布，请在发布期间使其同时接受两个密钥——SDK 验证辅助函数接受密钥列表（请参阅[验证 Webhook 签名](/zh/guides/verify-webhook-signatures)）。
</Warning>

---

## 发送测试投递

使用常规投递管道向一个端点发送合成的 `webhook.test` 事件，包括规范 JSON 序列化、两个签名标头、投递记录和重试记录。
无论所选端点的 `events` 过滤条件为何，测试都会以该端点为目标。

<CodeGroup>
```bash cURL
curl -X POST https://api.thunderphone.com/v1/developer/webhook-endpoints/c4d5e6f7-.../test \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"
```
</CodeGroup>

端点会收到如下信封：

```json
{
  "data": {
    "message": "ThunderPhone webhook test",
    "sent_at": "2026-07-17T20:12:34.567890+00:00"
  },
  "event_id": "2ad6507c-7d19-4498-9b2d-7e8f944ab5a1",
  "type": "webhook.test"
}
```

首次尝试后，API 会返回 `200 OK`，即使目标地址返回错误也是如此。
请检查 `success`、`status`、`response_code` 和 `error`
以了解投递结果：

```json
{
  "success": true,
  "event_id": "2ad6507c-7d19-4498-9b2d-7e8f944ab5a1",
  "event_type": "webhook.test",
  "status": "delivered",
  "response_code": 204,
  "error": ""
}
```

`webhook.test` 是合成事件，不能添加到端点的 `events`
订阅中。如果首次尝试失败，ThunderPhone 会在约一分钟后重试一次，
随后停止；该投递随后会在[调试投递](#debug-deliveries)下显示
`status: "failed"`。测试投递永远不会将端点标记为 `failing`。

要根据真实事件结构配置触发器，请传入可选的
`event_type`。投递仍然是合成的，并包含 `"sample": true`；
与通话相关的示例使用 `call_id: 0` 和 `agent_id: 0`。

```bash
curl -X POST https://api.thunderphone.com/v1/developer/webhook-endpoints/c4d5e6f7-.../test \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"event_type":"call.graded"}'
```

`event_type` 接受[有效事件类型](#valid-event-types)中的任意值。
省略该参数会保留通用的 `webhook.test` 行为。

---

## 删除端点

<CodeGroup>
```bash cURL
curl -X DELETE https://api.thunderphone.com/v1/developer/webhook-endpoints/c4d5e6f7-... \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"
```
</CodeGroup>

返回 `204 No Content`。向该 URL 的投递会立即停止；
正在进行的重试将被放弃。

---

## 调试投递

在断定 webhook 未发送之前，请检查
[`GET /v1/developer/webhook-deliveries`](/api-reference/webhook-deliveries)。
它会显示两个 webhook 系统的近期尝试，包括通话 ID、
URL 来源、HTTP 状态、尝试次数、允许列表中的失败类别以及下次重试时间。
它绝不会返回事件负载、转写文本、存储的错误文本、
响应正文或 URL 路径。

您也可以在 **智能体 → 选择一个智能体 → Webhooks →
近期投递** 中查看相同的近期历史记录。每一行会显示端点标签以及最新
尝试所使用的 URL 来源。这是运行状态，而非不可变的审计日志：删除端点
也会删除其投递行。

对于 n8n `404`，请先确认工作流处于活动状态、接受 `POST`，并且使用
生产 webhook URL 而非测试 URL。`401` 或 `403` 表示
身份验证或签名验证问题；超时表示目标地址延迟
或不可用；TLS 错误表示证书链、主机名或到期时间存在问题。

---

## 相关内容

<CardGroup cols={2}>
  <Card title="事件目录" icon="list" href="/zh/webhooks/events">
    您可以订阅的完整 `events` 值列表。
  </Card>
  <Card title="Webhook 概览" icon="bolt" href="/zh/webhooks/overview">
    签名验证和投递语义。
  </Card>
</CardGroup>
