消息与素材
阅读指引:本篇汇总应用与用户交互的三条主线:上传素材、发送消息、通过 Webhook 接收事件。字段级细节见《素材上传与下载》《发送应用消息》《Webhook 事件回调》。
素材上传与下载
应用素材属于当前应用私有资源,只接受具备 material:manage 的应用级 Token(不接受用户 Token)。上传返回的 media_id 是后续引用和下载的唯一标识,不能跨应用使用。
type |
大小 | 格式与处理 |
|---|---|---|
file |
5 B~20 MiB | 普通文件,下载时使用规范化安全附件名 |
voice |
5 B~2 MiB | 仅 M4A、MP3、WAV,时长不超过 60 秒,不支持 AMR |
video |
5 B~10 MiB | 仅 MP4;当前消息发送不支持视频消息 |
image |
5 B~10 MiB | 仅 JPG/JPEG、PNG |
要点:
- 上传使用
multipart/form-data,整个请求最大 20 MiB + 64 KiB,且只能有一个名为media的文件字段;不接受调用方提交的大小、MD5、时长和宽高。 - 文件扩展名必须与实际格式一致,改后缀绕不过校验;图片视频宽高不超过 20,000 像素,总像素不超过 1 亿。
- 下载原始素材
GET /materials/content只能提交一个media_id,不支持 Range。 - 素材上传专用限流返回 HTTP 429、
170507。
发送应用消息
POST /openapi/v1/messages/send,只接受应用级 Bearer Token 和严格 JSON(最大 64 KiB)。每次请求只能选择一种接收目标类型和一种消息类型:
{
"request_id": "notice_20260826_000001",
"recipient": { "type": "user", "ids": ["20001"] },
"message": {
"type": "text",
"payload": { "content": "系统维护将在今晚 22:00 开始。" }
}
}
接收目标与 Scope
recipient.type |
ids |
所需 Scope | 说明 |
|---|---|---|---|
user |
1~100 个 | message:send:user |
用户须存在、在发布范围内;默认还须保持有效 OAuth 授权 |
org_node |
1~100 个 | message:send:org_node |
固定覆盖节点自身及全部后代;不能写 department |
all |
必须省略 | message:send:all |
仅当前租户全部有效用户,且发布范围须显式含 user:0 |
group |
且只能 1 个 | group:app |
应用须已被群主/管理员添加进该群且关系 active |
消息类型
| 类型 | 说明 |
|---|---|
text |
纯文本,1~2048 字节,不能纯空白 |
file / image / voice |
素材型,media_id 的素材类型必须与消息类型完全一致 |
text_card |
文本卡片:标题、描述(仅支持 div.gray/normal/highlight 与 br 受限标记)、跳转 URL、按钮名 |
article_card |
图文卡片,1~8 条,封面只支持远程 imageUrl |
template_card |
客户端原生渲染的模板卡片,当前开放 textNotice(文本通知型)与 newsNotice(图文展示型) |
要点:
request_id是幂等标识(1~64 个 ASCII 字符):同一次重试必须复用原值和完全相同的目标与内容;不同消息不得复用。- 卡片中的
url/imageUrl由应用后端从允许名单生成;text_card描述中的特殊字符须 HTML 实体转义,用户输入建议按纯文本提交。 - 模板卡片只有
actionMenu操作菜单的点击会回传 Webhook(message.template_card.action事件);群目标可发模板卡片但当前不支持操作菜单回调。 - 代发能力
POST /messages/delegate_send(message:delegate:send,用户 Token):应用可代替真实用户向用户或群发消息,目标仅user/group,不能向发送者本人代发,向群代发时该用户须是群成员。 - 发送接口限流为每应用 + 来源 IP 每分钟 60 次,命中返回
170108。
通过 Webhook 接收事件
平台会向管理员配置的 webhook_url 异步投递用户上行消息、模板卡片操作、组织目录失效通知和群应用变化通知。应用后端必须完成四件事:
- 部署一个公网 HTTPS 的 POST 接口;
- 使用
sign_key对原始请求体验签; - 正确响应 URL 验证 challenge;
- 按
event_id分流处理消息、卡片操作与同步任务。
验签
请求头 X-Matrix-* 携带事件与签名信息,签名算法:
canonical = UTF8(X-Matrix-Timestamp) || 0x0A
|| UTF8(X-Matrix-Nonce) || 0x0A
|| raw_request_body
signature = lowercase_hex(HMAC_SHA256(key = UTF8(sign_key), data = canonical))
必须先读取未经解析的原始 body 再计算;用恒定时间比较签名,校验 X-Matrix-Event-ID == body.event_id,并拒绝与本机时间相差超过 5 分钟的请求。不要对 JSON 重新序列化后验签。
URL 验证
event_type=webhook.url.verify、message.type=webhook_verify_text 的验证消息与普通消息分离:不要写入用户会话,也不要触发业务逻辑。必须在 HTTP 2xx 响应中原样返回 challenge,并使用 sign_key 对响应签名(code 必须是数字 0,响应 body 不超过 1024 字节),仅返回 200 不算验证成功。
事件处理
| 事件 | 触发条件 | 处理要点 |
|---|---|---|
message.created |
用户给应用发消息 | 按 message_id 幂等落库;媒体引用见下 |
message.template_card.action |
用户点击模板卡片操作菜单 | 校验 task_id、operator_id、action_key 后执行业务 |
organization.department.changed / organization.member.changed |
组织目录变化 | 触发异步目录同步,以 sync-state 为准(见《目录与群数据同步》) |
group.* |
群绑定关系与群数据变化 | 触发异步群同步,以 groups/changes 为准 |
- 消息中的图片、文件、语音、视频通过
GET /openapi/v1/messages/media/download用 Webhook 返回的resource_id下载;票据绑定应用与原消息且短期有效。 - 平台对失败投递会按策略重试;应用按
event_id做幂等(同一事件的 HTTP 重试期间event_id不变),并在 5 秒内返回 2xx,耗时业务异步执行。 - 验签通过后还应使用共享存储原子记录 nonce,重放请求不得再次产生业务副作用。