← 所有文章
案例分析

一個越南客服團隊如何把 Zalo、WhatsApp 和 LINE 放進同一個入站佇列

週一早上 8:12,胡志明市的客服主管還沒打開筆電,第一則 Zalo 訊息已經進來。客戶想在物流攬收前修改收件地址。4 分鐘後,曼谷供應商透過 LINE 通知一箱貨會延遲。8:27,一位新加坡回購客戶又在 WhatsApp 詢問保固條款。

這些訊息本身都很普通。真正的問題是,它們分別落在三個入口、三支手機、三個人手上。到 9 點,團隊已經把兩張截圖貼進 Slack,把一則訊息轉給業務,又忘了到底是哪位客戶問了保固。

這是一家小型美妝品牌的五人營運客服團隊,負責跨境訂單支援。客戶主要在越南、泰國和新加坡,所以通路組合很典型:本地買家用 Zalo,泰國合作夥伴用 LINE,供應商和國際回購客戶用 WhatsApp。團隊不需要行銷活動,不需要群發範本,也不想換一套新的 CRM。他們需要的是把入站訊息變成可處理的工作項目。

三個收件匣,一個回覆要求

改造前,流程很手動,但也很常見。Zalo 在越南客服主管的手機上,LINE 在負責泰國供應商的營運同事手上,WhatsApp 在創辦人手機上,因為很多老客戶仍然存著他的號碼。每天早上,大家打開 Slack,把看起來緊急的訊息截圖貼進去。

量不大時,這套流程還能撐住。後來問題開始變得具體:物流攬收前沒看到改地址訊息,最後只能退款;供應商在 LINE 上提醒延遲,被看到時倉庫已經承諾當天出貨;創辦人出差,WhatsApp 裡的保固問題就一直沒人處理。

兩週內,團隊記錄了 31 次延遲回覆。多數不是技術故障,而是可見性故障。有人看到了訊息,但其他人沒有。或是應該處理的人不在線上,其他人根本不知道這則訊息存在。

官方平台路徑各自解的是更大的問題。Zalo Official Account 適合官方帳號營運和通知範本;LINE Official Account 適合 rich menu、群發和品牌化客戶入口;WhatsApp Cloud API 適合大規模範本式商務訊息。但這個團隊不是要主動發起大量對話。重要對話都是客戶或供應商先發來的。

所以專案目標變了。它不是「導入三套官方商務訊息系統」,而是「把每則入站訊息變成可信任的佇列任務」。

他們真正需要的 webhook

團隊在後端建立了一個入口:

POST /inbound/messages

接著透過 UnifyPort 連上一個 Zalo 帳號、一個 WhatsApp 帳號和一個 LINE 帳號。三個帳號都投遞到同一個 webhook endpoint。每次投遞都使用同一套 envelope:idtypeprovideraccount_idoccurred_atdata

歸一化後的 Zalo 訊息如下:

{
  "id": "evt_7f4c20a91d",
  "type": "message.received",
  "provider": "zalo",
  "account_id": "acc_vn_support",
  "occurred_at": "2026-07-02T01:12:16Z",
  "data": {
    "conversation": { "id": "zalo_user_1942", "type": "user", "title": "Minh Anh" },
    "sender": { "id": "zalo_user_1942", "type": "user", "name": "Minh Anh" },
    "message": {
      "id": "zalo_msg_8831",
      "type": "text",
      "text": "Em muốn đổi địa chỉ giao hàng trước 11h được không?",
      "direction": "inbound",
      "sent_at": "2026-07-02T01:12:15Z"
    },
    "event": { "kind": "message_received" }
  }
}

同一個 handler 同時處理 WhatsApp 和 LINE。改變的是 provider,不是解析邏輯。

在信任 payload 前,endpoint 會驗證投遞簽名。endpoint 設了 signing_secret,所以每個請求都會帶 X-Device-TimestampX-Device-Signature。簽名是 timestamp、一個點,以及原始 request body 的 HMAC-SHA256 十六進位摘要。

import crypto from 'crypto';
import express from 'express';

const app = express();
const secret = process.env.WEBHOOK_SIGNING_SECRET;

app.post('/inbound/messages', express.raw({ type: 'application/json' }), async (req, res) => {
  const timestamp = req.get('X-Device-Timestamp');
  const signature = req.get('X-Device-Signature');

  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(timestamp + '.');
  hmac.update(req.body);
  const expected = hmac.digest('hex');

  const valid = signature.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));

  if (!valid) {
    res.status(401).end();
    return;
  }

  const event = JSON.parse(req.body.toString('utf8'));
  await enqueueInboundMessage(event);
  res.status(200).end();
});

佇列任務只抽出團隊最在意的五個欄位:平台、客戶姓名、訊息內容、conversation ID 和 account ID。其他內容則保留為原始事件 metadata,方便之後稽核。

團隊能說清楚的路由規則

第一版路由刻意做得很單純。

Zalo 訊息進越南客服佇列。LINE 訊息進供應商佇列。WhatsApp 訊息進國際客戶佇列。任何帶訂單號的訊息都會建立 CRM note。任何包含配送關鍵字的訊息都會發到 Slack 的 #fulfillment 頻道。

第一週沒有 AI 分類器。團隊需要的是在訂單流轉時也能排查的系統。他們用 delivery_changesupplier_delaywarranty_question 這類規則名稱,每個任務都顯示命中的規則。

第二週,他們只加了一層輔助能力:為三類常見問題產生回覆草稿。改地址會產生要求新地址和訂單號的回覆;保固問題會產生包含保固期間和照片要求的範本;供應商延遲訊息會產生詢問新交接時間的回覆。草稿只貼到 Slack,不會自動發給客戶。

這個界線很重要。團隊不希望自動化直接替他們和客戶對話,只希望下一個人工動作變得明確。

四週後發生了什麼

最明顯的改變很簡單:沒有人再盯三支手機。客服主管每天早上只打開一個佇列,就能看到 Zalo、WhatsApp 和 LINE。團隊仍然能依 provider 篩選,但預設視圖是按時間排序的入站工作。

首次回覆中位數從 2 小時 18 分鐘降到 26 分鐘。超過 4 小時未處理的訊息,從兩週 31 則降到接下來兩週 3 則。更重要的是,團隊不再把截圖當作營運紀錄。每個入站事件都有穩定 event ID、provider、account ID 和時間戳。

CRM 也變得更有用。以前客戶的 Zalo 歷史在一支手機裡,供應商的 LINE 上下文在某位營運同事那裡。改造後,CRM 能看到各通路最近一則訊息。創辦人準備續約電話時,不必再請團隊翻聊天紀錄,直接打開聯絡人紀錄即可。

客戶沒有被遷移到新的入口。他們仍然傳訊息到原本的 Zalo、WhatsApp 或 LINE 帳號。改變只發生在訊息抵達之後的後端路徑。

這個模式適合誰

對小團隊來說,關鍵是把「入站客服」和「出站行銷基礎設施」分開看。官方商務產品在需要認證展示、群發、範本、rich menu 或活動營運時很有價值。但如果眼前痛點是「客戶先發訊息,而團隊漏看」,最先需要的是入站路由層。

透過 UnifyPort,這個團隊把各平台當作同一條事件流處理。message.received 成為共同契約,HMAC-SHA256 驗證讓投遞可信,provider 欄位告訴佇列訊息來自哪裡,其餘客服流程保持平台無關。

對五人團隊來說,這就夠了。一個 webhook、一個佇列,加上他們原本就在用的工具。