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 }
};
}
按照引用回覆 API 文件,使用 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 跑通發送,再用標準事件將所有入站訊息接返去業務系統。