Branch8

HubSpot Sales Hub 香港 WhatsApp 自動跟進流程設定教學

Matt Li
August 26, 2026
14 mins read
HubSpot Sales Hub 香港 WhatsApp 自動跟進流程設定教學

Key Takeaways

  • WhatsApp 自動跟進需先打通 Cloud API、Timeline 事件與 HubSpot Workflow 三層。
  • 24 小時客服視窗外只能發已審批 Template,category 分類錯誤是主要退件原因。
  • 電話號碼必須正規化為 E.164,跨市場時 defaultCountry 不能寫死。
  • 第三次跟進後應轉人手,持續發送會拉低 Meta Quality Rating。
  • LLM 只生成草稿、由人核准送出,避免自動承諾折扣的風險。

想在香港用 HubSpot Sales Hub 自動跟進 WhatsApp 客戶,核心是三件事:接通 WhatsApp Business Platform(Cloud API)、把訊息事件寫回 CRM 時間軸、再用 Workflow 依 Deal Stage 觸發範本訊息。以下是完整設定流程、程式碼與除錯方法。

為甚麼香港與亞太團隊一定要把 WhatsApp 接進 CRM?

在香港、新加坡、馬來西亞、印尼這些市場,銷售對話幾乎不在 Email 發生。根據 Meta 官方 WhatsApp Business Platform 文件,商業帳號的訊息分為 marketing、utility、authentication、service 四類對話,各自有不同的計費與範本審批規則 — 這代表你不能把 WhatsApp 當成「另一個 Email 欄位」,它有自己的規則引擎。

而根據 DataReportal 的《Digital 2024》系列報告,WhatsApp 在馬來西亞、印尼、香港等市場均屬使用率最高的即時通訊平台之一。對於在美國、英國或歐洲總部、但把亞太銷售營運放在香港或新加坡的公司,問題不是「要不要用 WhatsApp」,而是「銷售在私人手機上的對話,CRM 完全看不到」。

這造成三個實際問題:

  1. 交接斷層:業務離職,對話紀錄跟著手機走。
  2. 合規風險:香港《個人資料(私隱)條例》(PDPO)下,個人資料的收集目的與保留期限需可稽核;散落在個人 WhatsApp 的客戶資料難以做到。
  3. 無法自動化:跟進靠人記,Deal 卡在某階段兩星期也沒人知。

把 WhatsApp 接進 HubSpot Sales Hub,等於把對話變成可觸發、可報表、可稽核的 CRM 事件。

開始前需要準備甚麼?

帳號與授權層級

  • HubSpot Sales Hub Professional 或 Enterprise。Workflow(工作流程)是 Professional 起跳的功能,Starter 版只有有限的 Sequences,無法做 Deal-based 自動觸發。
  • HubSpot Super Admin 權限,用來建立 Private App 與自訂物件。
  • Meta Business Manager 帳號,並完成 Business Verification(企業驗證)。香港公司通常以商業登記證(BR)+ 公司註冊處文件提交。
  • WhatsApp Business Account (WABA) 與一個未被個人 WhatsApp 使用的電話號碼。香港 +852 號碼可用,但建議用一條專屬的固網或虛擬號碼,避免業務員誤用。
  • Display Name 審批:Meta 會審核顯示名稱是否與品牌一致。

兩條技術路線

路線 A:HubSpot 原生 WhatsApp 整合

HubSpot 官方在 Inbox 中提供 WhatsApp channel,可直接連接 WABA。優點是零程式碼、對話直接進 Conversations Inbox、可在 Workflow 中使用「Send WhatsApp message」動作。限制是範本管理與多號碼路由彈性較低,且部分區域功能上線時間不一致。

路線 B:BSP(Business Solution Provider)+ HubSpot API

透過 360dialog、Twilio、Respond.io 等 BSP 接 Cloud API,再用 Webhook 寫回 HubSpot。優點是可自訂路由邏輯、支援多 WABA、可接自家 LLM 做草稿生成。缺點是要自己維運中介層。

本教學以路線 B 為主軸(因為它可完整展示資料流),並在每步標註路線 A 的對應做法。

環境需求

1node --version # v20.x 或以上
2npm --version # v10.x
3# 需要一個可公開存取的 HTTPS endpoint 接收 Webhook
4# 本機開發可用 ngrok
5ngrok http 3000

Ready to Transform Your Ecommerce Operations?

Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.

第一步:在 HubSpot 建立 Private App 與自訂屬性

建立 Private App

進入 Settings → Integrations → Private Apps → Create private app

在 Scopes 分頁勾選:

  • crm.objects.contacts.read
  • crm.objects.contacts.write
  • crm.objects.deals.read
  • crm.objects.deals.write
  • crm.schemas.contacts.write
  • conversations.read
  • conversations.write
  • timeline

建立後複製 Access Token(格式為 pat-na1-...pat-eu1-...)。注意:HubSpot 帳號有 NA1 與 EU1 兩個資料區域,若你的歐洲客戶要求資料落地歐盟,建立 Portal 時就要選 EU,事後無法遷移。

新增 Contact 自訂屬性

用 API 建立三個屬性,用於後續 Workflow 判斷:

1curl -X POST \
2 'https://api.hubapi.com/crm/v3/properties/contacts' \
3 -H "Authorization: Bearer $HUBSPOT_TOKEN" \
4 -H 'Content-Type: application/json' \
5 -d '{
6 "name": "whatsapp_opt_in",
7 "label": "WhatsApp Opt-in",
8 "type": "enumeration",
9 "fieldType": "select",
10 "groupName": "contactinformation",
11 "options": [
12 {"label": "Opted in", "value": "opted_in", "displayOrder": 0},
13 {"label": "Opted out", "value": "opted_out", "displayOrder": 1},
14 {"label": "Unknown", "value": "unknown", "displayOrder": 2}
15 ]
16 }'

重複同樣呼叫建立:

  • whatsapp_last_inbound_at(type: datetime)— 記錄客戶最後一次主動回覆時間,用來判斷 24 小時客服視窗是否仍開啟。
  • whatsapp_phone_e164(type: string)— 儲存 E.164 格式號碼,例如 +85298765432

預期輸出:每次呼叫回傳 HTTP 201 與屬性 JSON。若回傳 409,代表屬性名稱已存在。

為甚麼要獨立存 E.164 號碼?

香港客戶在 CRM 裡的電話欄位五花八門:9876 5432(852)98765432852-9876-5432。WhatsApp Cloud API 只接受純數字國碼格式。與其在每次發送前做清洗,不如在寫入時就正規化。

1// normalizePhone.js
2import { parsePhoneNumberFromString } from 'libphonenumber-js';
3
4// defaultCountry 依市場切換:HK / SG / TW / MY / AU
5export function toE164(raw, defaultCountry = 'HK') {
6 if (!raw) return null;
7 const parsed = parsePhoneNumberFromString(String(raw), defaultCountry);
8 if (!parsed || !parsed.isValid()) return null;
9 return parsed.number; // e.g. "+85298765432"
10}

跨市場團隊要特別注意:同一個 HubSpot Portal 若同時服務香港、台灣、馬來西亞客戶,defaultCountry 不能寫死。建議依 Contact 的 country 或 Deal 的 market 屬性動態決定。

第二步:設定 WhatsApp Cloud API 與訊息範本

取得憑證

在 Meta for Developers 建立 App → 加入 WhatsApp 產品 → 綁定 WABA。你會需要:

  • PHONE_NUMBER_ID
  • WABA_ID
  • System User Access Token(不要用臨時 token,24 小時就過期)

建立訊息範本

超過 24 小時客服視窗後,只能發送已審批的 Template。這是最多人卡住的地方。

1curl -X POST \
2 "https://graph.facebook.com/v21.0/${WABA_ID}/message_templates" \
3 -H "Authorization: Bearer ${META_TOKEN}" \
4 -H 'Content-Type: application/json' \
5 -d '{
6 "name": "quote_followup_zh_hk",
7 "language": "zh_HK",
8 "category": "UTILITY",
9 "components": [
10 {
11 "type": "BODY",
12 "text": "{{1}} 您好,關於 {{2}} 的報價,我們已於 {{3}} 發送到您的電郵。請問還有其他資料需要補充嗎?",
13 "example": {
14 "body_text": [["陳先生", "訂製珠寶擺設", "3月12日"]]
15 }
16 },
17 {
18 "type": "BUTTONS",
19 "buttons": [
20 {"type": "QUICK_REPLY", "text": "想安排通話"},
21 {"type": "QUICK_REPLY", "text": "暫時不需要"}
22 ]
23 }
24 ]
25 }'

審批要點(依 Meta WhatsApp Business Platform 官方文件):

  • category 選錯是最常見的退件原因。純交易性通知選 UTILITY,任何帶推廣意味的內容必須選 MARKETING,兩者計費不同。
  • zh_HKzh_TWzh_CN 是三個獨立語言代碼,要分別建立。香港客戶用繁體 + 港式用語,台灣客戶用繁體 + 台式用語,不要共用同一份。
  • 變數不能放在句首或句尾,也不能兩個變數相連。

預期輸出:

1{"id":"1234567890","status":"PENDING","category":"UTILITY"}

審批通常在數分鐘到數小時內完成,狀態會變為 APPROVEDREJECTED

路線 A 對應做法

若用 HubSpot 原生整合,到 Settings → Inbox → Channels → Connect a channel → WhatsApp,登入 Meta 帳號後選擇 WABA 與號碼。範本仍需在 Meta Business Manager 建立並審批,HubSpot 會同步已審批的範本清單供 Workflow 選用。

Ready to Transform Your Ecommerce Operations?

Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.

第三步:把 WhatsApp 事件寫回 HubSpot 時間軸

沒有這一步,自動化就是盲飛 — Workflow 不知道客戶回覆了沒有。

建立 Timeline Event Template

1curl -X POST \
2 "https://api.hubapi.com/crm/v3/timeline/${APP_ID}/event-templates" \
3 -H "Authorization: Bearer $HUBSPOT_TOKEN" \
4 -H 'Content-Type: application/json' \
5 -d '{
6 "name": "WhatsApp Message",
7 "objectType": "contacts",
8 "headerTemplate": "WhatsApp {{direction}} — {{preview}}",
9 "tokens": [
10 {"name": "direction", "label": "方向", "type": "string"},
11 {"name": "preview", "label": "訊息內容", "type": "string"},
12 {"name": "template_name", "label": "範本名稱", "type": "string"}
13 ]
14 }'

Webhook 接收器

1// server.js
2import express from 'express';
3import crypto from 'crypto';
4
5const app = express();
6app.use(express.json({ verify: (req, res, buf) => { req.rawBody = buf; } }));
7
8// Meta webhook 驗證
9app.get('/webhook/whatsapp', (req, res) => {
10 if (req.query['hub.verify_token'] === process.env.VERIFY_TOKEN) {
11 return res.status(200).send(req.query['hub.challenge']);
12 }
13 res.sendStatus(403);
14});
15
16function verifySignature(req) {
17 const sig = req.get('x-hub-signature-256') || '';
18 const expected = 'sha256=' + crypto
19 .createHmac('sha256', process.env.META_APP_SECRET)
20 .update(req.rawBody)
21 .digest('hex');
22 return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
23}
24
25app.post('/webhook/whatsapp', async (req, res) => {
26 res.sendStatus(200); // Meta 要求 20 秒內回應,先 ACK 再處理
27
28 if (!verifySignature(req)) return;
29
30 const change = req.body?.entry?.[0]?.changes?.[0]?.value;
31 const msg = change?.messages?.[0];
32 if (!msg) return;
33
34 const phone = '+' + msg.from;
35 const text = msg.text?.body || `[${msg.type}]`;
36
37 const contactId = await findOrCreateContact(phone);
38
39 // 1. 寫入時間軸
40 await fetch('https://api.hubapi.com/crm/v3/timeline/events', {
41 method: 'POST',
42 headers: {
43 Authorization: `Bearer ${process.env.HUBSPOT_TOKEN}`,
44 'Content-Type': 'application/json'
45 },
46 body: JSON.stringify({
47 eventTemplateId: process.env.EVENT_TEMPLATE_ID,
48 objectId: contactId,
49 tokens: { direction: 'inbound', preview: text.slice(0, 120), template_name: '' }
50 })
51 });
52
53 // 2. 更新 24 小時視窗時間戳
54 await fetch(`https://api.hubapi.com/crm/v3/objects/contacts/${contactId}`, {
55 method: 'PATCH',
56 headers: {
57 Authorization: `Bearer ${process.env.HUBSPOT_TOKEN}`,
58 'Content-Type': 'application/json'
59 },
60 body: JSON.stringify({
61 properties: {
62 whatsapp_last_inbound_at: new Date().toISOString(),
63 whatsapp_opt_in: 'opted_in'
64 }
65 })
66 });
67});
68
69app.listen(3000);

預期輸出:在 HubSpot Contact 記錄頁的 Activity 時間軸上,應看到「WhatsApp inbound — 你好,想問下個報價...」。若沒出現,先檢查 objectId 是否為數字型 Contact ID(不是 email)。

處理 opt-out

香港 PDPO 與新加坡 PDPA 都要求直接促銷須提供退出機制。建議在 Webhook 中加入關鍵字偵測:

1const OPT_OUT = ['stop', 'unsubscribe', '取消訂閱', '唔好再send', '停止'];
2if (OPT_OUT.some(k => text.toLowerCase().includes(k))) {
3 await patchContact(contactId, { whatsapp_opt_in: 'opted_out' });
4}

所有 Marketing 類 Workflow 的 enrollment criteria 都必須排除 whatsapp_opt_in = opted_out

第四步:設定 Deal-based 自動跟進 Workflow

情境設計

以 B2B 報價流程為例,設三個跟進節點:

  1. Deal 進入「已發報價」後 2 個工作天,客戶未回覆 → 發送 quote_followup_zh_hk
  2. 再過 5 天仍無回覆 → 發送第二則,附帶案例連結。
  3. 再過 7 天 → 建立 Task 給業務,轉為人手處理,自動化停止。

第三步很重要。自動化的價值在於過濾,不在於無限發送。持續向沒有回應的號碼發送 Template,會拉低 Meta 的 Quality Rating,而根據 Meta 官方文件,Quality Rating 下降會導致訊息傳送額度(messaging limits)被下調。

Workflow 建立步驟

Automation → Workflows → Create workflow → Deal-based:

  1. Enrollment trigger:Deal stage is Quote Sent AND 關聯 Contact 的 whatsapp_opt_in is opted_in
  2. Re-enrollment:關閉。避免 Deal 階段反覆變動造成重複發送。
  3. Delay:2 business days(在 Delay 設定中選 Business days,並設定 HKT 時區與工作時段 09:00–18:00)。
  4. If/then branch:檢查關聯 Contact 的 whatsapp_last_inbound_at is after Deal 的 Quote sent date
  • Yes → 客戶已回覆,Go to action: End
  • No → 繼續。
  1. Action:路線 A 選 Send WhatsApp message 並挑選已審批範本;路線 B 選 Trigger a webhook,POST 到你的中介層。

路線 B 的 Webhook payload 處理

HubSpot Workflow 的 webhook action 會送出 Contact/Deal 物件。中介層負責組裝 Template:

1app.post('/hubspot/send-followup', async (req, res) => {
2 const { properties } = req.body;
3 const to = properties.whatsapp_phone_e164?.value;
4 if (!to) return res.status(400).json({ error: 'missing_phone' });
5
6 const r = await fetch(
7 `https://graph.facebook.com/v21.0/${process.env.PHONE_NUMBER_ID}/messages`,
8 {
9 method: 'POST',
10 headers: {
11 Authorization: `Bearer ${process.env.META_TOKEN}`,
12 'Content-Type': 'application/json'
13 },
14 body: JSON.stringify({
15 messaging_product: 'whatsapp',
16 to: to.replace('+', ''),
17 type: 'template',
18 template: {
19 name: 'quote_followup_zh_hk',
20 language: { code: 'zh_HK' },
21 components: [{
22 type: 'body',
23 parameters: [
24 { type: 'text', text: properties.lastname?.value || '您' },
25 { type: 'text', text: properties.deal_product?.value || '相關產品' },
26 { type: 'text', text: formatHKDate(properties.quote_sent_date?.value) }
27 ]
28 }]
29 }
30 })
31 }
32 );
33
34 const data = await r.json();
35 if (data.error) {
36 console.error('WA send failed', data.error);
37 return res.status(502).json(data);
38 }
39 await logOutboundToTimeline(properties.hs_object_id.value, 'quote_followup_zh_hk');
40 res.json({ ok: true, wamid: data.messages?.[0]?.id });
41});

預期輸出:

1{"ok":true,"wamid":"wamid.HBgLODUyOTg3NjU0MzIVAgARGBI..."}

Ready to Transform Your Ecommerce Operations?

Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.

如何用 LLM 生成跟進草稿而不失控?

2026 年的實務做法不是讓 AI 自動發送,而是讓它準備草稿、由人核准。Template 訊息本身是固定的,但業務在 24 小時視窗內的自由回覆,可以用 LLM 加速。

可行的架構是:當客戶 inbound 訊息進入 Webhook,同步呼叫 LLM,把 Deal 的產品、報價金額範圍、過往對話摘要作為 context,生成兩到三個回覆選項,寫入 HubSpot 的一個 suggested_reply 自訂屬性或 Note,業務在 Inbox 中一鍵取用。

1const prompt = `你是香港 B2B 銷售助理。用繁體中文(港式用語)草擬回覆。
2客戶訊息:${text}
3商機階段:${deal.dealstage}
4產品:${deal.product}
5規則:不要承諾折扣、不要報具體價格、不超過 60 字、語氣專業但親切。
6輸出 2 個版本。`;

關鍵護欄有三層:禁止在 prompt 中放入價格授權、輸出必須經人手確認才送出、所有 LLM 生成內容在時間軸標註來源。這樣做的取捨很清楚 — 你放棄了「全自動」,換取的是不會出現 AI 亂承諾折扣的公關事故。

Branch8 曾為一家在香港與東南亞多市場經營的耐用消費品分銷商,把經銷商查詢從個人 WhatsApp 遷移到 HubSpot Sales Hub + Cloud API 架構。當中最花時間的不是 API 串接,而是把六個市場、四種語言的範本結構統一,以及釐清哪些訊息屬 UTILITY、哪些屬 MARKETING。技術是一週的事,治理是三個月的事。

常見錯誤與除錯方法

錯誤 131047:Re-engagement message

1{"error":{"code":131047,"message":"Re-engagement message"}}

代表 24 小時客服視窗已關閉,你嘗試發送非 Template 的自由文字訊息。解法:改用已審批 Template,或檢查 whatsapp_last_inbound_at 邏輯是否正確。

錯誤 132000:Template param count mismatch

範本裡有三個 {{n}},但你只傳了兩個 parameter。Meta 不接受空字串補位,必須傳入非空值。在中介層加預設值(如上例的 || '您')可避免。

錯誤 131026:Message undeliverable

對方號碼沒有 WhatsApp,或號碼格式錯誤。香港號碼常見錯誤是漏了 852 國碼,或誤把 + 帶入 to 欄位。Cloud API 的 to 應為純數字 85298765432

HubSpot Workflow 沒有觸發

依序檢查:

  1. Workflow 是否已 Turn on(建立後預設是關閉的)。
  2. Enrollment criteria 中若用了關聯 Contact 的屬性,該 Deal 必須有 associated contact,否則整筆略過。
  3. 到 Workflow 的 Enrollment history 分頁,看 Deal 有沒有進入;若有進入但卡在 Delay,檢查 Business days 設定的時區是否為 Asia/Hong_Kong

API Rate limit

根據 HubSpot 官方開發者文件,Private App 的 API 呼叫有每秒與每日上限,超過會回傳 HTTP 429。在中介層加入指數退避重試:

1async function withRetry(fn, max = 4) {
2 for (let i = 0; i < max; i++) {
3 const r = await fn();
4 if (r.status !== 429) return r;
5 await new Promise(s => setTimeout(s, 2 ** i * 1000));
6 }
7 throw new Error('rate_limited');
8}

訊息品質評級下降

在 Meta Business Manager 的 WhatsApp Manager 中可查看每個號碼的 Quality Rating(Green / Yellow / Red)。轉黃通常代表被封鎖或標記為垃圾訊息的比例上升。實務對策:降低 MARKETING 類範本頻率、確保首次訊息前已有明確 opt-in、在範本中加入「回覆『停止』即可退出」。

Ready to Transform Your Ecommerce Operations?

Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.

跨市場團隊要怎樣擴展這套架構?

單一市場跑通後,擴展到台灣、新加坡、馬來西亞、澳洲,要處理三個維度:

號碼與 WABA 結構

一個 WABA 可綁多個電話號碼。建議每個市場一條號碼(客戶看到本地號碼回覆率較高),但共用同一個 WABA 以便集中管理範本與計費。

範本命名規約

{用途}_{階段}_{語言} 格式,例如 quote_followup_zh_hkquote_followup_zh_twquote_followup_en_sgquote_followup_ms_my。中介層依 Contact 的 country 屬性 map 到對應 template name 與 language code,不要在 Workflow 裡開四條分支。

資料落地與私隱

香港 PDPO、新加坡 PDPA、澳洲 Privacy Act、以及服務歐盟客戶時的 GDPR,對保留期限與跨境傳輸的要求不同。實務做法是在 HubSpot 用 data_region 屬性標記每筆 Contact,並針對歐盟資料主體設定較短的保留週期。若總部在歐盟,Portal 應建在 EU 資料區域。

時區覆蓋

把 Delay 設為 business days 並綁定當地時區,是最容易被忽略的一步。香港團隊設定的 Workflow,若時區留在 US Eastern,台灣客戶會在凌晨三點收到跟進訊息。這類問題不會報錯,只會默默拉低回覆率。

上線前檢查清單

  1. Meta Business Verification 已通過,Display Name 已審批。
  2. 所有 Template 狀態為 APPROVED,且 category 分類正確。
  3. Webhook signature 驗證已啟用(x-hub-signature-256)。
  4. Opt-out 關鍵字清單涵蓋中英文與粵語口語。
  5. 每個 Marketing Workflow 的 enrollment 已排除 opted_out
  6. Delay 步驟時區為當地時區,工作時段已設定。
  7. 第三次跟進後有人手接手節點,自動化不會無限循環。
  8. Timeline event 在測試 Contact 上正確顯示 inbound 與 outbound。
  9. 429 重試邏輯已部署。
  10. 已在 WhatsApp Manager 建立 Quality Rating 的定期檢視流程。

如果你正在把香港或亞太的銷售對話整合進 HubSpot,或需要一支同時懂 CRM 架構、WhatsApp Cloud API 與本地私隱規範的團隊,Branch8 在香港、新加坡、台灣、越南、馬來西亞與澳洲設有交付團隊,可協助設計並落地跨市場的自動跟進流程。歡迎與我們聯絡討論你的現況。

Ready to Transform Your Ecommerce Operations?

Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.

Sources

FAQ

不可以做 Deal-based 的自動跟進。Workflow 功能從 Sales Hub Professional 起才提供,Starter 只有有限的 Sequences,無法依 Deal 階段與自訂屬性設定分支邏輯。若預算有限,可先用 Inbox 手動管理 WhatsApp 對話,待流程穩定再升級。

About the Author

Matt Li

Co-Founder & CEO, Branch8 & Second Talent

Matt Li is Co-Founder and CEO of Branch8, a Y Combinator-backed (S15) Adobe Solution Partner and e-commerce consultancy headquartered in Hong Kong, and Co-Founder of Second Talent, a global tech hiring platform ranked #1 in Global Hiring on G2. With 12 years of experience in e-commerce strategy, platform implementation, and digital operations, he has led delivery of Adobe Commerce Cloud projects for enterprise clients including Chow Sang Sang, HomePlus (HKBN), Maxim's, Hong Kong International Airport, Hotai/Toyota, and Evisu. Prior to founding Branch8, Matt served as Vice President of Mid-Market Enterprises at HSBC. He serves as Vice Chairman of the Hong Kong E-Commerce Business Association (HKEBA). A self-taught software engineer, Matt graduated from the University of Toronto with a Bachelor of Commerce in Finance and Economics.