WhatsApp 引用回复:reply_token 与父消息 ID 有什么区别?
通过 UnifyPort 引用回复 WhatsApp 消息时,把所选消息的 data.message.reply_token 原样放入独立发送请求的 reply_to.reply_token。不要用 data.message.reply_to_message_id 替代:它指向收到的引用回复所关联的父消息,而不是刚收到的这条消息。如果没有 token,不要根据 ID 拼造,也不要静默改成普通消息发送。
要点
- 当前消息 ID、父消息 ID 和不透明的回复 token 是三个不同值。
- 发送账号和目标会话应来自所选消息,而非聊天中的最新消息。
- 当前文档中的引用发送操作仅支持 WhatsApp。
- 历史消息不含回复 token;是否改发普通消息,应由明确的业务决策决定。
你的回复究竟引用哪条消息?
考虑这个假设场景:消息 A 提问,消息 B 引用 A 并补充更正,客服希望回复时引用 B。
A ← B ← 你的新回复
标准 webhook 文档区分了以下字段:
| B 中的字段 | 含义 | 应用用途 |
|---|---|---|
data.message.id | B 的标识 | 存储并在收件箱选中 B |
data.message.reply_to_message_id | A 的标识 | 展示 B 的父消息关系 |
data.message.reply_token | 用于引用 B 的不透明句柄 | 原样传入发送请求 |
data.conversation.id 与 type | 原始会话 | 填充发送目标 |
account_id | 已连接的消息账号 | 保持正确发送账号 |
如果界面选择的是 A,就需要 A 自己存下来的 token。B 的父消息 ID 不能替代它。如果本地没有父消息,只显示引用内容不可用,不要推测其正文。
这与“能否在 HTTP webhook 响应中发送消息”是不同的问题。Webhook 响应与独立发送请求对比解释传输方式;本文解决的是发出的引用指向哪条消息。
从所选事件构造请求
先验证入站事件,再持久化保存。配置 signing_secret,按 webhook 投递约定对时间戳、一个点号和原始请求体计算 HMAC-SHA256,验签后才能信任载荷。时间戳新鲜度检查和去重仍是独立控制,详见 HMAC 防重放教程。
下面的 JavaScript 只是请求构造器,不是完整接收器或自动发送循环。输入应是经过验证、已保存,并由有权限的客服选中的实时事件。函数名和错误文本是应用代码,不是 API 字段或服务端错误码。
function buildQuotedReply(event, text) {
const conversation = event.data?.conversation;
const message = event.data?.message;
if (event.type !== 'message.received' ||
event.provider !== 'whatsapp' ||
message?.direction !== 'inbound') {
throw new Error('Select an inbound WhatsApp message');
}
if (!event.account_id || !conversation?.id || !conversation.type) {
throw new Error('Missing destination context');
}
if (typeof message.reply_token !== 'string' || !message.reply_token) {
throw new Error('Quoted reply unavailable');
}
if (typeof text !== 'string' || !text.trim()) {
throw new Error('Reply text is required');
}
return {
account_id: event.account_id,
to: { id: conversation.id, type: conversation.type },
message: { type: 'text', text },
reply_to: { reply_token: message.reply_token }
};
}
按引用回复接口文档使用 POST /v1/messages 发送生成的请求体,以 X-Api-Key 认证,密钥只保存在后端。群聊里的 conversation 表示群;把它替换为 data.sender.id 改变的是发送目标,而不是引用选择。
真正发送前,确认操作者有权访问该消息账号和会话。在本地发送任务中保存所选消息的标识,避免新消息到达后改变引用目标。限制存储 token 的访问权限,不要将其写入通用日志或 AI 提示词。
token 缺失、无效还是渠道不支持?
| 现象 | 文档边界 | 建议处理 |
|---|---|---|
| 实时消息没有 token | WhatsApp 在配置回复 token 签名时提供它 | 核对事件来源和配置;明确提供普通消息选项 |
消息来自 conversation.history | 历史消息没有 reply_token | 不拼造 token,也不承诺能从历史记录恢复 |
400 invalid_reply_token | token 被修改、使用不同密钥加密,或无法读取 | 检查存储值和序列化过程,保留错误信息排查 |
501 unsupported_by_provider | 所选渠道未实现引用发送 | 禁用这条引用发送路径,不要无限重试 |
省略 reply_to | 会发送普通消息 | 必须明确选择这一降级行为 |
Webhook 的 signing_secret 用来验证投递,不应假定更换它能修复不可读取的加密回复句柄。错误参考说明错误含义,但没有承诺 token 修复方法或有效期。
验收用例与能力边界
测试 B 引用了 A 时,选择 B 仍应引用 B。还应测试编辑草稿时收到新消息、群聊、token 缺失、仅有历史消息及重复投递。重复接收不应生成另一条发送任务。这些是建议测试,不是已完成的测试结果。
记录真实发送结果。data.status: accepted 不是已读回执,网络超时也不证明消息没有发出。不要盲目重发,或在报错后自动移除 reply_to。
UnifyPort 提供非官方接口。统一事件结构不意味着每个渠道都支持引用发送。例如 Telegram 的官方 Bot API 文档定义了自己的 reply_parameters 和 ReplyParameters,不能把该结构直接放进这里的请求。需要原生能力时应使用对应原生 API。UnifyPort 不提供 REST 消息历史读取 API,也不保证重放遗漏载荷。
常见问题
能把 reply_to_message_id 放进 reply_to.reply_token 吗?
不能。前者指向收到消息的父消息;后者必须是所选消息原样保存的不透明 token。
有历史消息 ID 就能引用它吗?
没有对应 token,就不能通过此文档规定的 token 操作引用它。历史载荷不提供 token。普通消息只能作为明确选择的替代方案。
UnifyPort 的 Telegram、LINE、Zalo 也支持吗?
当前引用发送文档限定为 WhatsApp。不能根据入站消息包含引用关系,就推断渠道支持引用发送。
下一步与来源
先按引用回复文档落实消息选择检查,再启用收件箱里的“引用”按钮。
核对日期:2026-09-26。
让消息接入变成一条稳定的产品管线。
先用统一 API 跑通发送,再用标准事件把所有入站消息接回业务系统。