BOT API v1

让你的服务,
在聊天里有个入口。

部署完成、订单更新、待办确认。用一个机器人,把消息送到身边,也把回复带回你的服务。

HTTP 发送 WebSocket / Webhook 多端同一会话
部署助手机器人 · 消息示意
HTTPS
发布完成14:32

Surprising v1.2.0

新版本已就绪,一切运行顺利。

环境生产环境状态已完成
POST /api/openapi/v1/bot/messages请求体示例
JSON
{
  "requestId": "release-42",
  "type": "card",
  "title": "Deployment complete",
  "content": "**v1.2.0** is live.",
  "buttons": [{ "text": "Confirm", "action": "release.confirm" }]
}

通知,随服务抵达

HTTP 调用即可发送。只发通知,无需搭建回调服务。

卡片,让消息可操作

文字、Markdown 与交互卡片,让提醒连上下一步行动。

回复,回到你的业务

通过 WebSocket 或 Webhook 接收文字回复和按钮事件。

由用户决定接收。机器人仅向创建者或主动订阅的用户发送,用户可随时停止接收。

了解权限 →

接入文档

让服务与聊天接上话。

从创建、发送到接收回复,一份手册完成接入。

快速开始

  1. 打开应用中心的机器人,登录后点击「创建机器人」。
  2. 填写名称和简介,保留默认的 WebSocket 模式。只发通知时,无需建立事件连接。
  3. 保存创建后显示的 Token,然后在自己的服务端运行下面的命令。
sh
export BOT_BASE_URL='https://api.surprising.top:8443'
export BOT_TOKEN='替换为创建时显示的 Token'
curl "$BOT_BASE_URL/api/openapi/v1/bot/messages" \
  -H "Authorization: Bearer $BOT_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"requestId":"hello-001","type":"text","content":"你好,第一条机器人消息已送达。"}'

打开机器人详情的「打开会话」即可查看消息。你也可以先在「接入与测试」中使用「发送到我的会话」,确认消息样式和接收效果。

官方服务的根地址是 https://api.surprising.top:8443,接口前缀是 /api/openapi/v1/bot。自行部署时将 BOT_BASE_URL 换成自己的后端 HTTPS 根地址,不含 /api。机器人 Token 使用 Authorization: Bearer …,不能替代用户登录凭证调用管理接口。

接收对象与权限

每个账户最多保留 3 个机器人,停用仍占名额,删除后释放。创建者默认订阅自己的机器人;发送时不传 chatId,就发送给创建者。

其他用户在应用中心通过机器人 ID 打开详情,点击「开始使用」后,你的服务才能向其会话发消息。使用该用户回复事件中的 chatId;不要自行拼接会话 ID。用户可以随时「停止接收」。

机器人消息在独立会话中展示,并有机器人标识。当前支持机器人与已订阅用户的一对一会话,不支持向好友私聊、群聊直接发送,也不提供定时发送接口。用户回复支持文字和引用文字;机器人会话不支持附件发送、音视频通话和阅后即焚。

发送与查询

方法路径用途
GET/api/openapi/v1/bot/me查看当前机器人的基本信息
POST/api/openapi/v1/bot/messages发送消息
GET/api/openapi/v1/bot/messages/{requestId}按发送请求 ID 查询投递状态

以上接口都需要 Bearer Token。发送和状态查询合计每个机器人每秒最多 10 次。

字段必填说明
requestId是1–128 字符,同一机器人内唯一;重试时保持原值
chatId否已授权会话,不传则发送给创建者
type是text、markdown、card,不区分大小写
content是非空正文,最多 16000 字符
title否卡片标题,最多 200 字符
image否卡片图片的 HTTPS 地址,最多 2048 字符
fields否卡片信息字段,最多 20 个
buttons否卡片按钮,最多 5 个

同一个 requestId 和相同消息内容重复请求,会返回同一条投递记录,不会重复发送。将同一个 ID 用于不同内容或不同会话会返回 409。网络超时后重试应保留原 ID;新消息使用新 ID。

所有接口响应使用 { "code": 200, "resp": ... }。发送接口返回 HTTP 200 表示已写入聊天服务,202 表示已受理等待投递。以下是已发送时的响应示例,ID 和时间仅用于说明:

json
{
  "code": 200,
  "resp": {
    "id": "投递记录 UUID",
    "requestId": "hello-001",
    "chatId": "bot_机器人UUID:用户ID",
    "status": "SENT",
    "messageId": "消息ID",
    "seq": 12,
    "error": null,
    "createdAt": "2026-10-28T00:00:00Z"
  }
}

resp.status 为 PENDING、SENDING、SENT 或 FAILED。只有 SENT 后才有 messageId 和 seq;它表示消息已保存,不代表用户已读。HTTP 202 时响应体的 code 仍为 200,应检查 resp.status。

sh
curl "$BOT_BASE_URL/api/openapi/v1/bot/messages/hello-001" \
  -H "Authorization: Bearer $BOT_TOKEN"

消息格式与卡片

text 展示纯文字。markdown 支持标题、列表、粗体、代码和 HTTPS 链接;原始 HTML 只展示文字。图片、字段和按钮仅适用于 card 消息。

json
{
  "requestId": "release-42",
  "type": "card",
  "title": "发布完成",
  "content": "**v1.2.0** 已发布。\n查看结果后确认。",
  "image": "https://your-host/release.png",
  "fields": [{ "label": "环境", "value": "生产" }],
  "buttons": [
    { "text": "查看详情", "url": "https://your-host/releases/42" },
    { "text": "确认", "action": "release.confirm" }
  ]
}

字段标题 label 最多 40 字符,value 最多 500 字符。按钮 text 最多 40 字符,每个按钮必须只包含 url 或 action 中的一个。链接仅支持 HTTPS,最多 2048 字符。action 使用 1–64 位字母、数字、下划线、点、冒号或连字符,同一张卡片内不能重复。

链接按钮打开外部地址;交互按钮向你的服务发送 card.action 事件。卡片按统一组件渲染,不允许任意 HTML 或脚本。转发机器人消息时会转成普通文字摘要,不会复制原卡片的操作权限。

WebSocket 接收事件

适合不想部署公网回调地址的服务。创建机器人时保留默认 WebSocket 模式,你的后端连接下面的地址,在握手请求头中携带同一个 Bearer Token:

text
wss://api.surprising.top:8443/api/openapi/v1/bot/events/ws
Authorization: Bearer YOUR_BOT_TOKEN

连接工具必须支持自定义请求头。浏览器原生 WebSocket 不适合保管机器人 Token。连接成功后收到:

json
{"op":"ready","botId":"机器人 UUID","ackTimeoutSeconds":30}

事件帧示例:

json
{
  "op": "event",
  "event": {
    "eventId": "事件 UUID",
    "type": "message.received",
    "chatId": "bot_机器人UUID:用户ID",
    "userId": "123",
    "data": {
      "messageId": "消息ID",
      "seq": 12,
      "message": { "type": "text", "content": { "text": "你好" } }
    },
    "createdAt": "2026-10-28T00:00:00Z"
  }
}

按钮事件的 type 为 card.action,data 为 { "messageId": "…", "action": "release.confirm" }。所有用户 ID 按字符串处理。引用回复的 message.type 为 quote_text,message.content 为聊天协议定义的引用对象。

完成业务处理或可靠入队后发送确认;未确认的事件会再次投递,使用 eventId 做持久化去重。可每 25 秒发送心跳,服务返回 {"op":"pong"}。

json
{"op":"ack","eventId":"收到的事件 ID"}
{"op":"ping"}

30 秒未确认或连接中断会重新投递。每个机器人只保留一个开发者连接,后建立的连接接管;重连时重新携带 Token,无需自行维护游标。下载 Node.js 示例可用于连接、重连、心跳和自动回复。

Webhook 接收事件

适合已有公网 HTTPS 服务的开发者。在机器人编辑页面选择 Webhook,填写公网 HTTPS 回调地址。一个机器人同时选择一种事件接收方式;只发送通知时不需要配置回调。

平台 POST 上面的 event 对象,不包含 WebSocket 的 op/event 外层。请求头包含:

text
X-Surprising-Timestamp: Unix 秒
X-Surprising-Signature: sha256=十六进制 HMAC
Content-Type: application/json

在机器人详情中查看「回调签名密钥」,它与 API Token 不同。签名计算为 HMAC-SHA256(webhookSecret, timestamp + "." + 原始请求正文)。先检查时间差不超过 5 分钟,再对原始字节验签,使用恒定时间比较;通过后才解析 JSON。不要把解析后重新序列化的 JSON 用于验签。

js
import { createHmac, timingSafeEqual } from 'node:crypto';

// rawBody 必须是原始请求正文 Buffer;secret 仅保存在服务端。
function verifyWebhook(rawBody, timestamp, signature, secret) {
  if (typeof timestamp !== 'string' || !/^\d+$/.test(timestamp)) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  if (typeof signature !== 'string' || !/^sha256=[0-9a-f]{64}$/.test(signature)) return false;
  const expected = createHmac('sha256', secret)
    .update(timestamp + '.').update(rawBody).digest();
  return timingSafeEqual(expected, Buffer.from(signature.slice(7), 'hex'));
}

验签不能代替去重,业务仍需使用 eventId 的持久化唯一约束。完成处理或可靠入队后返回 2xx;失败或超时会重试。同一事件保持相同 eventId 和正文,每次重新签名。自动重试最多 12 次,失败后可在「事件记录」手动重试。

回调地址必须可通过公网访问并具有有效 TLS 证书。平台拒绝内网、回环及特殊用途地址,不跟随重定向。不要填写 localhost、自签名地址或需要跳转的 URL。

可运行示例

下载 bot-example.mjs,保存到本地目录。需要 Node.js 22 或以上。WebSocket 模式额外安装 ws;脚本会用事件 ID 构造回复消息的幂等键。

sh
npm install ws
export BOT_BASE_URL='https://api.surprising.top:8443'
export BOT_TOKEN='替换为创建时显示的 Token'
node bot-example.mjs

Webhook 模式在 127.0.0.1:8088/events 监听,应由你自己的公网 HTTPS 反向代理转发到这个端口。将代理公网 URL 填入机器人编辑页:

sh
export BOT_MODE='webhook'
export BOT_WEBHOOK_SECRET='替换为回调签名密钥'
node bot-example.mjs

脚本演示验签、确认、重连和幂等回复。生产业务需持久化事件去重记录,并针对外部业务的副作用设计幂等处理。不要在收到事件但尚未保存或处理时提前确认成功。

错误与投递重试

HTTP 状态常见原因处理方式
400参数或卡片格式错误检查必填字段、长度和按钮配置
401Token 无效或已重置,机器人停用或删除检查 Token 与机器人状态,不要无限重试
403会话尚未授权或已停止订阅请用户「开始使用」,使用真实事件中的 chatId
404发送记录不存在检查 requestId 是否属于当前机器人
409requestId 对应的内容或会话发生变化原消息重试保持内容一致,新消息使用新 ID
429超出每秒 10 次的发送或查询限额延迟后重试,避免密集轮询
5xx服务暂时不可用退避重试,保留原 requestId

发送到聊天服务的消息自动尝试最多 8 次,失败后可在「消息记录」手动重试。Webhook 事件自动尝试最多 12 次,失败后可在「事件记录」手动重试。停用机器人会暂停未完成投递,重新启用后继续。

管理与常见问题

Token 关闭后找不到了?

完整 Token 无法再次查看。在机器人详情重置 Token 后更新你的服务配置,再重新连接。重置会同时更换 Webhook 签名密钥,并关闭旧开发者连接。

只想发通知,也要开 WebSocket 吗?

不用。只需 Token 和 HTTP 发送接口。需要处理用户回复或卡片交互时,再连接 WebSocket 或配置 Webhook。

为什么返回 202,却暂时没有消息?

202 表示已受理。使用相同 requestId 查询投递状态,或打开机器人详情的「消息记录」查看结果;不要换一个 ID 再发送同一条消息。

如何让其他人使用我的机器人?

把机器人 ID 给对方。对方在应用中心查找并点击「开始使用」后即可接收;让对方发一条文字,你的服务就能从事件中取得其 chatId。

能否同时使用 WebSocket 和 Webhook?

一个机器人选择一种事件接收方式。可以在编辑页面切换;WebSocket 模式每个机器人只有一个开发者连接,多端用户聊天不受此限制。

停用和删除有什么区别?

停用可恢复,仍占创建名额,并暂停未完成投递。删除后释放名额,原 Token 失效,需要重新创建机器人。

开始接入

下一条消息,来自你的服务。

创建机器人,保存 Token,然后发一条消息给自己。

打开应用中心 ↗