Webhooks¶
当外部服务向例程的 URL 发送签名请求时,Webhook 例程就会启动。智能体例程和渠道例程都支持 Webhook。主机管理员在例程编辑器中配置它们。主机必须在线才能接收请求。
设置 Webhook 例程¶
- 打开智能体或渠道的例程编辑器。
- 将运行时机设置为Webhook。
- 可选:输入事件类型。不设置事件类型时,所有事件类型都会启动例程。
- 可选:添加过滤器。
- 保存例程。主机会生成 URL 和签名密钥。
- 将密钥复制到发送服务的密钥库中。OpenBot 只显示一次。
- 复制 URL。在账户服务注册路由之前,URL 为空。主机必须登录其 OpenBot 账户。
每个 Webhook 例程都有自己专属的 URL 和密钥。一台主机最多可以有 64 个 Webhook 例程。
你也可以在聊天中让智能体创建一个 Webhook 例程。智能体会调用 openbot.create_webhook_routine 并告诉你 URL 和请求格式,但它不会获取密钥。聊天中的卡片会显示 URL;按下卡片上的重新生成按钮即可生成密钥,然后复制它。只有能管理主机 Webhook 例程的人才能看到该卡片。当智能体为另一个智能体创建例程时,请在该智能体的例程编辑器中获取密钥。
要在聊天中更改事件类型或过滤器,请询问智能体:它会调用 openbot.update_webhook_routine。你也可以在卡片上更改它们并按下保存。URL 和密钥保持不变。只有人的消息才能启动这些工具:例程运行或其他智能体的消息都不能创建或更改 Webhook 例程。
要替换密钥,请在例程编辑器中生成一个新密钥并更新发送方。旧密钥会立即失效。密钥格式为 whsec_<43 base64url characters>。
当你将例程更改为计划或删除例程时,其 URL 将停止工作。如果你将其改回Webhook,例程会获得新的 URL 和新的密钥。
当你在主机上登录不同的 OpenBot 账户时,URL 也会改变。密钥保持不变。将新 URL 复制到发送服务中。
发送测试事件¶
在例程编辑器中,选择发送测试事件。主机会生成一个与事件类型和过滤器匹配的事件,使用例程密钥签名,并像接收中继请求一样接收它。当例程接受所有类型时,事件类型为 openbot.test。测试会显示在历史记录中,匹配会启动一次运行。暂停的例程不会运行。测试不会经过中继,因此它不能显示发送方是否能够到达该 URL。
发送请求¶
向例程 URL 发送一个 Content-Type: application/json 的 POST 请求。请求体形状如下:
{
"type": "deployment.completed",
"occurredAt": "2026-10-07T10:00:00.000Z",
"data": { "environment": "production", "revision": "abc123" }
}
| 字段 | 规则 |
|---|---|
type |
必填。1 到 256 个字符的字符串。 |
occurredAt |
可选。日期字符串。如果不发送,OpenBot 将使用接收时间。 |
data |
可选。任意 JSON 值。如果不发送,值为 null。 |
请求体必须为 UTF-8 编码且最大为 64 KiB。
事件类型和过滤器¶
在例程编辑器中,选择过滤事件以添加事件类型或过滤器。如果没有事件类型且没有过滤器,则每个签名请求都会启动例程。
如果例程有事件类型,则 type 必须等于该类型。过滤器包含一个指向 data 的 JSON Pointer 和一个值。该值是字符串、数字、布尔值或 null。指针处的值必须与过滤器值相同,且具有相同的 JSON 类型。例如,/environment 搭配 production 读取 data.environment。所有过滤器都必须匹配。在指针中,使用 ~1 表示 /,使用 ~0 表示 ~。空指针选择整个 data。一个例程最多可以有 128 个过滤器。
请求头和签名¶
| 请求头 | 值 |
|---|---|
X-OpenBot-Timestamp |
当前 Unix 时间(秒) |
X-OpenBot-Delivery-Id |
此事件的唯一 ID。使用 1 到 256 个 ASCII 字母、数字、.、_、: 或 -。第一个字符必须是字母或数字。 |
X-OpenBot-Signature |
sha256= 和小写十六进制 HMAC-SHA256 |
HMAC 密钥是例程密钥。被签名的字节为 <timestamp>.<deliveryId>.<body>。使用你发送的确切请求体字节。签名后不要再解析和序列化请求体。时间戳与主机时钟的偏差必须在 5 分钟之内。
Node.js 示例:
import { createHmac, randomUUID } from "node:crypto";
const body = JSON.stringify({ type: "deployment.completed", data: { environment: "production" } });
const deliveryId = randomUUID(); // Keep this ID and the body for a retry.
const timestamp = String(Math.floor(Date.now() / 1000));
const signature = createHmac("sha256", process.env.OPENBOT_WEBHOOK_SECRET)
.update(`${timestamp}.${deliveryId}.${body}`)
.digest("hex");
const response = await fetch(process.env.OPENBOT_WEBHOOK_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-OpenBot-Timestamp": timestamp,
"X-OpenBot-Delivery-Id": deliveryId,
"X-OpenBot-Signature": `sha256=${signature}`,
},
body,
});
if (response.status !== 200 && response.status !== 202) {
throw new Error(`OpenBot did not accept the event: ${response.status}`);
}
响应¶
| 状态 | 含义 | 重试 |
|---|---|---|
202 |
主机已保存事件。例程已启动,或者主机因事件类型或过滤器不匹配,或例程已暂停而忽略该事件。 | 否 |
200 |
例程已有此投递 ID。 | 否 |
400 |
请求体无效。 | 否。请更正请求体。 |
401 |
请求头或签名无效,或时间戳与主机时钟的偏差超过 5 分钟。 | 否。请更正密钥、请求头或时钟。 |
404 |
主机不知道此路由。 | 否 |
413 |
请求体大于 64 KiB,或者例程指令和事件合计超过 100,000 个字符。 | 否。请发送更少的数据或缩短指令。 |
415 |
Content-Type 不是 application/json。 |
否 |
429 |
来自你的地址对此路由的请求过多。 | 是,稍后重试 |
503 |
主机离线、繁忙或未在 2.5 秒内响应。中继不知道路由,主机无法读取密钥,或者代理或频道当前无法接受运行。 | 是,稍后重试 |
202 响应并不表示运行已完成。请查看例程历史以获取运行结果。当主机离线时,中继不会保留请求。如果你不重试,事件将丢失。
重试与重复¶
投递 ID 是每个例程的去重键。要重试,请发送相同的投递 ID 和相同的请求体,并附带新的时间戳和新的签名。重复的投递 ID 会收到 200,且不会启动第二次运行。这也适用于例程忽略的事件。主机保留回执 7 天。超过该时间后,相同的投递 ID 将启动新的运行。
代理收到的内容¶
运行指令是例程指令后接事件。事件被标记为外部数据,而非指令。它包含投递 ID、例程 ID、type、occurredAt、回执时间和 data。事件数据可以像所有例程输入一样发送给例程的模型提供方。该数据不能为代理提供工具,也不能跳过审批。
历史记录¶
例程历史显示主机忽略的每个请求及其原因:事件类型或过滤器不匹配,或者例程未处于活动状态。
数据保留¶
- 回执:7 天。回执包含投递 ID、事件类型、结果和运行 ID。它不包含请求体。
- 删除例程会删除其回执。
安全性¶
- 只有主机管理员可以管理 webhook 例程。
- 主机在解码请求体之前验证签名。中继不拥有该密钥。
- 密钥使用操作系统的密钥存储进行加密。它们不会出现在日志、错误或活动中。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 openbo/openbot