API Reference

本页覆盖当前开发者文档站需要的核心接口,重点解决“收消息、回消息、切换 Webhook、处理媒体资源”这条主线。

统一鉴权

所有 Bot API 使用 Bearer Token 鉴权:

Authorization: Bearer <bot_token>

命名约定

  • Bot 发送接口通常使用 snake_case,例如 msg_typeclient_msg_id
  • Update 回调里可能出现 camelCase,例如 msgType
  • 接入时不要假设请求和回调字段风格完全一致。

POST /api/v1/s/bot/updates

以 Polling 方式拉取 Update 事件。

字段类型说明
offsetint从哪个 update_id 之后开始拉取。
limitint本次最多返回的更新数。
timeoutint长轮询等待秒数。
{
  "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.updatesarray本次返回的 Update 列表,按 update_id 升序。
meta.polling.next_offsetint建议下次请求使用的 offset;非空列表时通常等于最大 update_id + 1
meta.polling.backlog.sizeint当前队列内保留的更新数。
meta.polling.backlog.oldest_update_idint当前保留窗口内最老的 update_id。
meta.polling.backlog.oldest_age_secint最老 retained update 的年龄,便于判断积压。
meta.polling.backlog.droppedint因保留窗口或 backlog 上限而被丢弃的累计数量。
meta.polling.backlog.offset_too_oldbool你的 offset 可能已经落后到保留窗口之外,存在缺口风险。

常见错误与语义

场景应该怎么理解
返回空数组可能是没有新事件,也可能是 Bot 已切到 Webhook。
offset_too_old=true说明你的消费位点过旧,平台已无法补全所有历史 Update。
backlog.dropped 持续增长说明接收方消费速度落后于事件产生速度,或 offset 使用不正确。

POST /api/v1/s/bot/send

向私聊或群聊发送消息。

字段类型说明
to_user_idstring私聊目标用户 ID,与 group_id 二选一。
group_idstring群聊目标群 ID,与 to_user_id 二选一。
msg_typestring当前主路径建议使用 text
data.contentstring消息文本。
data.entitiesarray富文本实体;当前推荐直接显式提供。
interactionobject结构化交互数据,例如后续键盘类能力。
replace_message_idstring可选;替换目标消息 ID,必须来自此前 s/bot/send 的返回值。
parse_modestring设计上支持 markdownv2,但当前建议不要依赖它。
client_msg_idstring幂等键;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。
  • 如果同时提供 entitiesparse_mode,对外口径应以 entities 为准。
  • 相同 client_msg_id 但 payload 不同,应视为幂等冲突,而不是重新发消息。
  • 当前实现进展口径是:parse_mode=markdownv2 还不应作为主路径依赖,推荐直接传 data.entities

响应字段

字段类型说明
data.successbool业务发送是否成功。
data.messagestring人类可读的发送结果说明。
data.chat_typestringprivategroup
data.message_idstring平台生成的消息 ID;如果你后续要用 replace_message_id,应保留它。
data.sent_atint发送时间戳,秒级 int。
meta.request_idstring排障时最重要的请求追踪 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_IDclient_msg_id 格式不合法。检查长度、空白字符和允许字符集。
7217 / INVALID_PARSE_MODEparse_mode 非法,或当前仅支持 markdownv2当前建议直接移除 parse_mode,改为显式传 data.entities
7221 / BOT_INVALID_ENTITIESentities 非法,例如越界、类型错误或 mention 无法解析。逐个检查 entity 的 typeoffsetlength 和附加字段。

Webhook 配置相关

Webhook 适合已有公网 HTTPS 服务的开发者。详细事件结构见协议页。

  • Webhook 需要公网 HTTPS。
  • 启用 Webhook 后,优先通过 Webhook 收事件。
  • Webhook 需要验签,避免伪造请求和重放。
  • 生产环境建议只允许 HTTPS 和常规端口;不要把内网地址暴露成 Webhook 目标。

POST /api/v1/s/bot/webhook/set

设置或移除当前 Bot 的 Webhook URL。设置成功后,事件主路径会切到 Webhook。

字段类型说明
webhook_urlstring公网 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_scenariostring当前冻结为 chat_imagechat_audiochat_videochat_file
to_user_idstring私聊目标用户 ID;与 group_id 二选一。
group_idstring群聊目标群 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_imagechat_audiochat_videochat_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

常见错误码

错误码错误名典型场景建议处理
7210IDEMPOTENCY_CONFLICT相同 client_msg_id 对应了不同 payload。修正幂等键生成策略,不要跨消息复用同一个 key。
7211INVALID_CLIENT_MSG_IDclient_msg_id 长度或字符集不合法。限制为 A-Za-z0-9._:- 且长度不超过 128。
7217INVALID_PARSE_MODEparse_mode 不合法,或当前路径不建议依赖。优先改为显式传 data.entities
7221BOT_INVALID_ENTITIES富文本实体越界、类型非法或 mention 无法解析。逐个检查 entity 的 typeoffsetlength 和附加字段。
EMPTY_SIGNATUREEMPTY_SIGNATUREWebhook 请求没有带签名。检查服务端是否保留了完整请求头和请求体。
INVALID_SIGNATURE_FORMATINVALID_SIGNATURE_FORMAT签名不是 64 位十六进制。确认没有把 base64 或截断后的字符串拿来校验。
SIGNATURE_MISMATCHSIGNATURE_MISMATCHWebhook 签名校验不通过。检查 canonical JSON、payload sha256、时间戳和 secret。
REPLAY_WINDOW_EXCEEDEDREPLAY_WINDOW_EXCEEDEDWebhook 请求超出重放时间窗口。检查服务端时间同步和请求延迟。
FILE_NO_PERMISSIONFILE_NO_PERMISSION通过 batch_access 获取媒体访问地址时无权限或文件不存在。检查 file_url 来源、资源权限和资源是否已被治理屏蔽。
offset_too_old=truePOLLING_OFFSET_TOO_OLDPolling offset 已落到保留窗口之外。从最新 next_offset 重新接续,并评估丢失窗口内的影响。