← 所有文章
教學

用簽名入站 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-TimestampX-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 可以有五個節點:

  1. Webhook:接收轉發後的事件。
  2. IF:確認 typemessage.received,且 providerwhatsapp
  3. Data store、Postgres、Airtable 或 CRM:保存 idaccount_idproviderdata.conversation.iddata.sender.iddata.message.text
  4. AI Agent 或指向模型閘道的 HTTP Request:把訊息分類為銷售、客服、帳單或人工接手。
  5. 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 使用 provideraccount_idoccurred_atdata。以後接入 Telegram、LINE、Zalo、TikTok 或 X 時,workflow 可以按 provider 分支,而不需要學六套 webhook 格式。

官方 WhatsApp Business Developer Hub 仍然是學習 Meta Cloud API、webhooks、pricing 和 policy surface 的正確入口。如果你要建構官方平台能力,就應該參考它。但如果你的團隊需要把 WhatsApp 入站客服 Agent 接到 n8n,又不想把所有官方設定步驟都塞進工作流程,UnifyPort 的非官方接口會把任務收斂成一件事:接收簽名客戶訊息,並交給團隊已經在使用的工具。

先把邊緣做好。邊緣可靠之後,Agent 只是另一個節點。