用簽名入站 Webhook 建立 n8n WhatsApp AI Agent
2026 年想快速做一個 WhatsApp AI Agent,通常不要先從 Agent 開始。先把入站邊界做好。
公開社群裡的痛點很清楚。最近一則 n8n Reddit 討論裡,有開發者想把 WhatsApp 自動化接到 DeepSeek,要求能處理媒體、穩定運作,並且不想卡在 Meta Business verification 和 Cloud API 設定流程中。其他討論則集中在 WhatsApp trigger 只觸發一次、只在 test mode 生效,或 webhook 送達與 workflow 回應時機沒有對齊。
小團隊應該先修正的是順序:可靠接收訊息,快速回傳確認,先儲存,再讓 AI Agent 決定是否回覆。
UnifyPort 提供入站部分:簽名的 message.received 事件。n8n 提供工作流程畫布。中間加上一個很小的邊緣驗證器,就能得到適合正式環境使用的路徑:
WhatsApp 客戶訊息
-> UnifyPort 簽名 message.received webhook
-> 驗證 X-Device-Signature 的邊緣服務
-> n8n production Webhook URL
-> AI 分流、CRM 查詢、Slack 通知,或透過 POST /v1/messages 回覆
為什麼 n8n webhook URL 很重要
n8n 官方 Webhook node 文件明確說明,一個 Webhook node 有兩個 URL:test URL 和 production URL。test URL 用在編輯器正在監聽時的手動測試;production URL 才是 workflow 啟用後應該接入外部系統的地址。
客戶訊息管道必須以 production URL 為中心設計。客戶不會因為你的編輯器沒有正在監聽,就重新傳一次訊息。入站路徑應該保持啟用、穩定,並且能快速回傳 2xx。
n8n 的 Respond to Webhook node 適合由 workflow 自己控制 HTTP 回應。對入站訊息來說,回應應該保持簡單:接受事件、入佇列或儲存、回傳 200。長時間 AI 推理、CRM 寫入和出站回覆,都應該放在 delivery 被確認之後。
註冊 UnifyPort webhook
在 UnifyPort 建立 webhook endpoint,並訂閱 message.received。設定 signing_secret,這樣每次投遞都會帶上 X-Device-Timestamp 和 X-Device-Signature。
curl -X POST https://api.unifyport.ai/v1/webhook-endpoints \
-H "X-Api-Key: <YOUR_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"url": "https://edge.example.com/unifyport/n8n",
"status": "active",
"subscribed_events": ["message.received"],
"signing_secret": "<WEBHOOK_SIGNING_SECRET>"
}'
這裡的 URL 不是直接填 n8n URL,而是填一個很小的邊緣驗證器。這樣可以嚴格做簽名驗證,因為 UnifyPort 使用 HMAC-SHA256 對原始 request body 簽名。被簽名的字串是:
<X-Device-Timestamp>.<raw request body>
如果你的 n8n 部署能在 JSON parse 之前取得完全一致的原始請求位元組,也可以在 n8n 內完成驗證。更多團隊會選擇在小服務裡驗證,再用內部 token 把可信事件轉發到 n8n production webhook,讓邊界更清楚。
加上邊緣驗證器
下面是完整的 Node.js 驗證器。它會驗證 UnifyPort 簽名,解析事件,確認事件型別,並把可信 payload 轉發到 n8n。
import crypto from "crypto";
import express from "express";
const app = express();
const signingSecret = process.env.WEBHOOK_SIGNING_SECRET;
const n8nWebhookUrl = process.env.N8N_PRODUCTION_WEBHOOK_URL;
const internalToken = process.env.N8N_INTERNAL_TOKEN;
app.post("/unifyport/n8n", 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", signingSecret);
hmac.update(timestamp);
hmac.update(".");
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"));
if (event.type !== "message.received") {
res.status(202).end();
return;
}
await fetch(n8nWebhookUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Internal-Token": internalToken
},
body: JSON.stringify(event)
});
res.status(200).end();
});
app.listen(3000);
這個服務不保存 WhatsApp 帳號憑證,也不決定 Agent 應該如何回答。它唯一的工作,是證明事件來自你的 UnifyPort endpoint,並把可信事件交給 n8n。
建立 n8n workflow
在 n8n 中建立一個已啟用的 workflow,用 Webhook trigger 的 production URL 接收事件。傳入的 JSON 已經是 UnifyPort 標準事件:
{
"id": "evt_2f9c1a4b7e",
"type": "message.received",
"provider": "whatsapp",
"account_id": "acc_8c21d0",
"occurred_at": "2026-07-08T02:30:00Z",
"data": {
"conversation": { "id": "8613912345678", "type": "user", "title": "Jordan Lee" },
"sender": { "id": "8613912345678", "name": "Jordan Lee", "type": "user" },
"message": {
"id": "wamid.HBgM",
"type": "text",
"text": "Can I change the delivery address?",
"direction": "inbound",
"sent_at": "2026-07-08T02:29:59Z"
},
"event": { "kind": "message_received" }
}
}
一個實用的第一版 workflow 可以有五個節點:
- Webhook:接收轉發後的事件。
- IF:確認
type是message.received,且provider是whatsapp。 - Data store、Postgres、Airtable 或 CRM:保存
id、account_id、provider、data.conversation.id、data.sender.id和data.message.text。 - AI Agent 或指向模型閘道的 HTTP Request:把訊息分類為銷售、客服、帳單或人工接手。
- HTTP Request:視需要透過 UnifyPort 回覆。
先儲存。UnifyPort 的 webhook 文件把事件視為入站流量的記錄來源。錯過的 payload 沒有 message-history read API 可以補取,所以 workflow 應該在慢速 AI 步驟失敗之前先持久化事件。
只在 workflow 決定後回覆
如果 Agent 需要回覆,使用 POST /v1/messages。收件人來自入站事件,回覆動作保持明確。
curl -X POST https://api.unifyport.ai/v1/messages \
-H "X-Api-Key: <YOUR_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"account_id": "acc_8c21d0",
"to": { "id": "8613912345678", "type": "user" },
"message": {
"type": "text",
"text": "Yes. Send us the new delivery address and we will update the order note."
}
}'
在 n8n 裡,這就是一個 HTTP Request node。account_id 對應 {{$json.account_id}},收件人 id 對應 {{$json.data.sender.id}},message.text 使用 AI 節點輸出。
為什麼不要讓 Agent 做第一個入口
AI Agent 不應該成為第一個接觸客戶訊息的系統。第一個系統應該足夠單純:驗證、確認、儲存、路由。這樣即使模型、prompt 或升級規則改變,營運契約仍然穩定。
這個結構也能讓同一個 n8n workflow 擴展到 WhatsApp 之外。標準 envelope 使用 provider、account_id、occurred_at 和 data。以後接入 Telegram、LINE、Zalo、TikTok 或 X 時,workflow 可以按 provider 分支,而不需要學六套 webhook 格式。
官方 WhatsApp Business Developer Hub 仍然是學習 Meta Cloud API、webhooks、pricing 和 policy surface 的正確入口。如果你要建構官方平台能力,就應該參考它。但如果你的團隊需要把 WhatsApp 入站客服 Agent 接到 n8n,又不想把所有官方設定步驟都塞進工作流程,UnifyPort 的非官方接口會把任務收斂成一件事:接收簽名客戶訊息,並交給團隊已經在使用的工具。
先把邊緣做好。邊緣可靠之後,Agent 只是另一個節點。