用签名入站 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 只是另一个节点。