如何在 WhatsApp 共用收件箱同步已讀與未讀狀態
WhatsApp 共用收件箱不應在 Webhook 一到達就把對話標為已讀,而應等團隊真正接手或完成處理後才更新。透過 UnifyPort,可以用 conversation_id 呼叫對話已讀端點;如要把 WhatsApp 回條準確推進至某則訊息,還要同時提交訊息 ID 及發送者 ID。需要再跟進時,則把對話標為未讀。
重點
- 對話已讀及未讀動作目前只支援 WhatsApp;不支援該動作的渠道組合會回傳
501 unsupported_by_provider。 POST /v1/accounts/{account_id}/conversations/read必須接收conversation_id,亦可選擇推進至指定訊息。up_to_message_id及up_to_message_sender_id必須成對提交;只提交其中一個會回傳400 invalid_request。- 標記未讀只需要
conversation_id。 - 分配、跟進及完成狀態仍應保存在自己的系統。渠道端已讀狀態只是共用收件箱的投影,並非完整工單資料庫。
先分清三種「已讀」
可靠的共用收件箱要分開處理以下概念:
- 團隊佇列狀態:例如新訊息、已分配、等待中、已完成,由你的應用程式定義。
- 已連接 WhatsApp 帳戶的聊天清單狀態:已讀或未讀。透過標記對話已讀端點及標記對話未讀端點更新。
- 收件人回條:
message.readWebhook 表示收件人讀過帳戶發出的一則或多則訊息,不等於客服開啟了入站工單。
本機對話設定改變時,UnifyPort 亦可以映射 conversation.updated 事件。data.conversation.id 識別聊天,已讀狀態變化可出現在 data.read。應把它視為核對訊號;至於誰接手、何時完成、為何重新開啟,仍要記錄在自己的資料庫。
處理任何事件前,先驗證原始請求內容的簽署,並讓重試具備冪等性。接收端安全邊界可參考Webhook HMAC、重播防護及重試指南。如果端點只需要收件箱事件,也可按Webhook 事件篩選教學明確訂閱所需事件。
決定何時更新渠道狀態
不要在每個入站 Webhook 抵達時自動標為已讀,否則無人處理的佇列看起來也像已清空。可採用以下明確規則:
| 團隊動作 | 本機佇列狀態 | WhatsApp 動作 |
|---|---|---|
| 入站訊息已儲存 | new | 不操作 |
| 客服接手對話 | assigned | 按需要推進至已接手的訊息 |
| 客服完成對話 | resolved | 將整段對話標為已讀 |
| 客服要求後續跟進 | waiting | 將對話標為未讀 |
| 自動化在分配前失敗 | new | 不操作 |
這樣亦可避免瀏覽器重新整理、Webhook 重試或背景預覽意外清掉待辦。
將 WhatsApp 對話標為已讀
conversation_id 放在 JSON 請求內容,而不是 URL 路徑,因為渠道識別碼可能包含 @、: 等字元。
將整段對話標為已讀:
curl -X POST "https://api.unifyport.ai/v1/accounts/$UNIFYPORT_ACCOUNT_ID/conversations/read" \
-H "X-Api-Key: $UNIFYPORT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"conversation_id": "8613912345678@s.whatsapp.net"
}'
如要把 WhatsApp 回條推進至明確的入站訊息,請從同一個 message.received 事件複製三個識別碼:
{
"conversation_id": "120363041234567890@g.us",
"up_to_message_id": "CURRENT-MESSAGE-ID",
"up_to_message_sender_id": "8613912345678@lid"
}
群組訊息的 up_to_message_sender_id 必須使用相符的 data.sender.id,不可由對話 ID 推算。不需要訊息層級回條時,請同時省略兩個 up_to_message_* 欄位。
以下 Node.js helper 會先確認欄位成對,再發出請求:
const apiBase = 'https://api.unifyport.ai/v1';
async function setWhatsAppReadState({ accountId, conversationId, unread, message }) {
const action = unread ? 'unread' : 'read';
const body = { conversation_id: conversationId };
if (!unread && message) {
if (!message.id || !message.senderId) {
throw new Error('message.id and message.senderId must be supplied together');
}
body.up_to_message_id = message.id;
body.up_to_message_sender_id = message.senderId;
}
const response = await fetch(
`${apiBase}/accounts/${encodeURIComponent(accountId)}/conversations/${action}`,
{
method: 'POST',
headers: {
'X-Api-Key': process.env.UNIFYPORT_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
}
);
if (!response.ok) {
const failure = await response.json();
throw new Error(`${response.status} ${failure.error?.code ?? 'unknown_error'}`);
}
return response.json();
}
在已完成簽署驗證的事件處理器中,直接使用文件定義的欄位:
await setWhatsAppReadState({
accountId: event.account_id,
conversationId: event.data.conversation.id,
unread: false,
message: {
id: event.data.message.id,
senderId: event.data.sender.id
}
});
將待跟進對話標為未讀
未讀動作更簡單:
await setWhatsAppReadState({
accountId: event.account_id,
conversationId: event.data.conversation.id,
unread: true
});
只應在團隊明確重新開啟工作時使用。渠道端未讀狀態不能取代自己的提醒機制;跟進負責人、到期狀態及原因都應保存在本機佇列。
核對狀態時避免形成循環
應用程式呼叫對話動作後,Webhook 可能收到對應的 conversation.updated 事件。為本機發起的操作保存內部操作紀錄,讓後續事件用來確認狀態,而不是再次觸發同一動作。
安全流程如下:
- 以冪等方式儲存
message.received。 - 在交易中更新本機工單狀態。
- 呼叫渠道狀態動作。
- API 回傳
{ "data": { "ok": true } }後才記錄成功。 - 將
conversation.updated視為確認,或視為來自已連接帳戶的外部變更。 - 狀態不明時,使用取得指定對話端點讀取該對話,再比較
unread_count。
為其他渠道啟用控制項前,請查看目前的各渠道統一動作支援矩陣。統一路由不表示每個渠道都實作每一種動作。
限制與取捨
如果流程依賴渠道認證的企業功能、官方範本訊息或原生管治,官方方案可能更合適。UnifyPort 的非官方介面適用於一般帳戶訊息流程,但不會令所有渠道具備完全相同的功能。
就本文流程而言,對話已讀及未讀目前只支援 WhatsApp。其他渠道的共用收件箱應隱藏或停用這些控制項,不要把 501 unsupported_by_provider 當成可持續重試的暫時錯誤。
常見問題
收到 message.received 會自動將聊天標為已讀嗎?
不會。接收及儲存事件不應清空待辦;只有到達團隊定義的工作流程節點時,才呼叫已讀端點。
message.read 與標記對話已讀有何不同?
message.read 是收件人閱讀訊息後的回條事件;對話已讀動作改變的是已連接帳戶本機聊天清單狀態。
可以只提交 up_to_message_id 嗎?
不可以。必須連同 up_to_message_sender_id 一起提交;或兩者都省略,將整段對話標為已讀。
Telegram、LINE、TikTok、Zalo 及 X 能使用相同控制嗎?
目前不能。支援矩陣將對話已讀及未讀列為 WhatsApp 專屬動作;不支援的組合會回傳 501 unsupported_by_provider。
下一步
先閱讀標記對話已讀 API Reference,並在定義本機重新開啟規則後,再加入未讀動作。
第一手來源
截至 2026 年 8 月 21 日核對: