API Reference
本页覆盖当前开发者文档站需要的核心接口,重点解决“收消息、回消息、切换 Webhook、处理媒体资源”这条主线。
统一鉴权
所有 Bot API 使用 Bearer Token 鉴权:
Authorization: Bearer <bot_token>
命名约定
- Bot 发送接口通常使用 snake_case,例如
msg_type、client_msg_id。
- Update 回调里可能出现 camelCase,例如
msgType。
- 接入时不要假设请求和回调字段风格完全一致。
POST /api/v1/s/bot/updates
以 Polling 方式拉取 Update 事件。
| 字段 | 类型 | 说明 |
offset | int | 从哪个 update_id 之后开始拉取。 |
limit | int | 本次最多返回的更新数。 |
timeout | int | 长轮询等待秒数。 |
{
"offset": 0,
"limit": 50,
"timeout": 30
}
{
"data": {
"updates": []
},
"meta": {
"success": true,
"code": 0,
"request_id": "rid_example_001",
"server_timestamp": 1700000000,
"polling": {
"next_offset": 0,
"backlog": {
"size": 0,
"oldest_update_id": 0,
"oldest_age_sec": 0,
"dropped": 0,
"offset_too_old": false
}
}
}
}
- 如果 Bot 已启用 Webhook,这个接口可能返回空数组。
- 消费成功后优先使用
meta.polling.next_offset 推进位点。
- 建议将
offset 落盘,否则服务重启后会重复消费。
meta.polling.backlog 用于判断是否有积压、是否发生过丢弃、你的 offset 是否过旧。
响应字段
| 字段 | 类型 | 说明 |
data.updates | array | 本次返回的 Update 列表,按 update_id 升序。 |
meta.polling.next_offset | int | 建议下次请求使用的 offset;非空列表时通常等于最大 update_id + 1。 |
meta.polling.backlog.size | int | 当前队列内保留的更新数。 |
meta.polling.backlog.oldest_update_id | int | 当前保留窗口内最老的 update_id。 |
meta.polling.backlog.oldest_age_sec | int | 最老 retained update 的年龄,便于判断积压。 |
meta.polling.backlog.dropped | int | 因保留窗口或 backlog 上限而被丢弃的累计数量。 |
meta.polling.backlog.offset_too_old | bool | 你的 offset 可能已经落后到保留窗口之外,存在缺口风险。 |
常见错误与语义
| 场景 | 应该怎么理解 |
| 返回空数组 | 可能是没有新事件,也可能是 Bot 已切到 Webhook。 |
offset_too_old=true | 说明你的消费位点过旧,平台已无法补全所有历史 Update。 |
| backlog.dropped 持续增长 | 说明接收方消费速度落后于事件产生速度,或 offset 使用不正确。 |
POST /api/v1/s/bot/send
向私聊或群聊发送消息。
| 字段 | 类型 | 说明 |
to_user_id | string | 私聊目标用户 ID,与 group_id 二选一。 |
group_id | string | 群聊目标群 ID,与 to_user_id 二选一。 |
msg_type | string | 当前主路径建议使用 text。 |
data.content | string | 消息文本。 |
data.entities | array | 富文本实体;当前推荐直接显式提供。 |
interaction | object | 结构化交互数据,例如后续键盘类能力。 |
replace_message_id | string | 可选;替换目标消息 ID,必须来自此前 s/bot/send 的返回值。 |
parse_mode | string | 设计上支持 markdownv2,但当前建议不要依赖它。 |
client_msg_id | string | 幂等键;24h 去重窗口;长度不超过 128,只允许 A-Za-z0-9._:-。 |
{
"to_user_id": "au_xxx",
"msg_type": "text",
"data": {
"content": "hello"
},
"client_msg_id": "msg_10001"
}
{
"data": {
"success": true,
"message": "私聊消息发送成功",
"chat_type": "private",
"message_id": "msg_12345",
"sent_at": 1734912000
},
"meta": {
"code": 0,
"success": true,
"request_id": "rid_xxx",
"server_timestamp": 1734912000
}
}
- 私聊通常要求 Bot 与目标用户已建立允许通信的关系。
- 群里默认是
mention_only 模式,不是所有消息都会投递给 Bot。
- 如果同时提供
entities 和 parse_mode,对外口径应以 entities 为准。
- 相同
client_msg_id 但 payload 不同,应视为幂等冲突,而不是重新发消息。
- 当前实现进展口径是:
parse_mode=markdownv2 还不应作为主路径依赖,推荐直接传 data.entities。
响应字段
| 字段 | 类型 | 说明 |
data.success | bool | 业务发送是否成功。 |
data.message | string | 人类可读的发送结果说明。 |
data.chat_type | string | private 或 group。 |
data.message_id | string | 平台生成的消息 ID;如果你后续要用 replace_message_id,应保留它。 |
data.sent_at | int | 发送时间戳,秒级 int。 |
meta.request_id | string | 排障时最重要的请求追踪 ID。 |
字段约束与建议
| 字段 | 约束 |
to_user_id | 支持 bot-scoped OpenID au_* 或 UnionID pu_*。 |
client_msg_id | 长度不超过 128,仅允许 A-Za-z0-9._:-,建议业务侧稳定生成。 |
replace_message_id | 长度不超过 64,必须来自此前发送接口返回的 message_id。 |
data.entities | 必须是数组;offset/length 不能越界;mention.userId 解析失败会报错。 |
parse_mode | 当前不要依赖;需要富文本时优先显式提供 data.entities。 |
发送接口常见错误
| 错误 | 说明 | 建议处理 |
7210 / IDEMPOTENCY_CONFLICT | 相同 client_msg_id 但 payload 不同。 | 修正幂等键生成策略,不要对不同消息复用同一个 key。 |
7211 / INVALID_CLIENT_MSG_ID | client_msg_id 格式不合法。 | 检查长度、空白字符和允许字符集。 |
7217 / INVALID_PARSE_MODE | parse_mode 非法,或当前仅支持 markdownv2。 | 当前建议直接移除 parse_mode,改为显式传 data.entities。 |
7221 / BOT_INVALID_ENTITIES | entities 非法,例如越界、类型错误或 mention 无法解析。 | 逐个检查 entity 的 type、offset、length 和附加字段。 |
Webhook 配置相关
Webhook 适合已有公网 HTTPS 服务的开发者。详细事件结构见协议页。
- Webhook 需要公网 HTTPS。
- 启用 Webhook 后,优先通过 Webhook 收事件。
- Webhook 需要验签,避免伪造请求和重放。
- 生产环境建议只允许 HTTPS 和常规端口;不要把内网地址暴露成 Webhook 目标。
POST /api/v1/s/bot/webhook/set
设置或移除当前 Bot 的 Webhook URL。设置成功后,事件主路径会切到 Webhook。
| 字段 | 类型 | 说明 |
webhook_url | string | 公网 HTTPS 地址;传空字符串或不传表示移除。 |
{
"webhook_url": "https://example.com/fly/bot/webhook"
}
{
"data": {
"success": true,
"webhook_url": "https://example.com/fly/bot/webhook",
"delivery_mode": "webhook",
"message": "Webhook设置成功"
},
"meta": {
"success": true,
"code": 0,
"request_id": "rid_example_002",
"server_timestamp": 1700000000
}
}
- 切到
delivery_mode=webhook 后,Polling 不应再作为主消费链路。
- 如果要回到 Polling,先移除
webhook_url,再重新从 s/bot/updates 拉取。
- 不要把 localhost、内网地址或无 HTTPS 的地址作为正式 Webhook。
POST /api/v1/s/bot/webhook/info
获取对外公开的 Webhook 投递格式说明。它本质上是文档 API,适合生成代码或离线验签时作为机器可读底稿。
{
"data": {
"webhook_format": {
"version": "signed_update_v1",
"content_type": "application/json",
"secret": {
"description": "验签 secret 为 Bot Token 的 SHA256 十六进制"
}
}
},
"meta": {
"code": 0,
"success": true
}
}
- 这个接口无需认证,可作为站点协议页的机器可读补充。
- 如果你在生成 SDK 或让 AI 直接按接口接入,优先读取这里的版本号和示例。
- 实际验签细节仍以协议页的 canonical JSON 与 HMAC 规则为准。
POST /api/v1/s/bot/files/upload_token
为媒体消息上传申请上传凭证。适合 image / voice / video / file 的发送链路。
| 字段 | 类型 | 说明 |
use_scenario | string | 当前冻结为 chat_image、chat_audio、chat_video、chat_file。 |
to_user_id | string | 私聊目标用户 ID;与 group_id 二选一。 |
group_id | string | 群聊目标群 ID;与 to_user_id 二选一。 |
{
"use_scenario": "chat_image",
"to_user_id": "au_xxx"
}
{
"data": {
"file_id": "abc123def45678901234567890123456",
"upload_token": "<qiniu_upload_token>",
"storage_key": "chat_image/aa/bb/abc123def45678901234567890123456.jpg",
"bucket": "flyfly-private",
"file_url": "https://img1.flycrytp.com/chat_image/aa/bb/abc123def45678901234567890123456.jpg",
"is_public": false,
"expires_at": 1734915600,
"size_limit_bytes": 20971520
},
"meta": {
"code": 0,
"success": true,
"request_id": "rid_xxx",
"server_timestamp": 1734912000
}
}
- 当前媒体消息推荐使用平台受控的
file_url,不要依赖第三方直链。
- 拿到上传凭证后,Bot 可将文件直传到对象存储,再把返回的
file_url 放进消息数据。
use_scenario 当前冻结为 chat_image、chat_audio、chat_video、chat_file。
POST /api/v1/s/bot/files/batch_access
把一个或多个平台内部 file_url 换成短期可访问地址,适合处理入站媒体下载。
{
"files": [
{"url": "https://img1.flycrytp.com/chat_image/aa/bb/file_a.jpg"},
{"url": "fly_file://source_file_id/ref_file_id"}
],
"expire_seconds": 600
}
{
"data": {
"files": {
"https://img1.flycrytp.com/chat_image/aa/bb/file_a.jpg": "https://files.example.com/signed/url/1?token=abc",
"fly_file://source_file_id/ref_file_id": null
},
"expire_at": 1734912600
},
"meta": {
"code": 0,
"success": true,
"request_id": "rid_xxx",
"server_timestamp": 1734912000
}
}
- Webhook / Polling 里通常只会拿到受控的
file_url。
- 真正下载媒体前,需要通过这个接口换取短 TTL 的
access_url。
files 最多 100 项;expire_seconds 会被服务端钳制上限。
开发阶段最常用的接口组合
| 目标 | 建议接口组合 |
| 本地验证首轮接入链路 | s/bot/updates + s/bot/send |
| 切到公网回调 | Webhook 配置接口 + 签名校验 + s/bot/send |
| 处理图片或文件 | s/bot/files/upload_token / s/bot/files/batch_access + s/bot/send |
常见错误码
| 错误码 | 错误名 | 典型场景 | 建议处理 |
7210 | IDEMPOTENCY_CONFLICT | 相同 client_msg_id 对应了不同 payload。 | 修正幂等键生成策略,不要跨消息复用同一个 key。 |
7211 | INVALID_CLIENT_MSG_ID | client_msg_id 长度或字符集不合法。 | 限制为 A-Za-z0-9._:- 且长度不超过 128。 |
7217 | INVALID_PARSE_MODE | parse_mode 不合法,或当前路径不建议依赖。 | 优先改为显式传 data.entities。 |
7221 | BOT_INVALID_ENTITIES | 富文本实体越界、类型非法或 mention 无法解析。 | 逐个检查 entity 的 type、offset、length 和附加字段。 |
EMPTY_SIGNATURE | EMPTY_SIGNATURE | Webhook 请求没有带签名。 | 检查服务端是否保留了完整请求头和请求体。 |
INVALID_SIGNATURE_FORMAT | INVALID_SIGNATURE_FORMAT | 签名不是 64 位十六进制。 | 确认没有把 base64 或截断后的字符串拿来校验。 |
SIGNATURE_MISMATCH | SIGNATURE_MISMATCH | Webhook 签名校验不通过。 | 检查 canonical JSON、payload sha256、时间戳和 secret。 |
REPLAY_WINDOW_EXCEEDED | REPLAY_WINDOW_EXCEEDED | Webhook 请求超出重放时间窗口。 | 检查服务端时间同步和请求延迟。 |
FILE_NO_PERMISSION | FILE_NO_PERMISSION | 通过 batch_access 获取媒体访问地址时无权限或文件不存在。 | 检查 file_url 来源、资源权限和资源是否已被治理屏蔽。 |
offset_too_old=true | POLLING_OFFSET_TOO_OLD | Polling offset 已落到保留窗口之外。 | 从最新 next_offset 重新接续,并评估丢失窗口内的影响。 |