UnifyPort 入站整合:先建立 Webhook 的檢查清單
做跨平台訊息整合時,第一步應該是先建立 webhook,而不是先連接生產帳號。UnifyPort 沒有通用的 REST 訊息歷史讀取 API,也不保證錯過的 payload 一定能重送;所以 webhook 是你的入站記錄層。先呼叫 POST /v1/webhook-endpoints、設定 signing_secret、訂閱 message.received 或 [*],把事件安全存下來,再交給客服、CRM、AI 或自動化流程。
重點摘要
- 先註冊 webhook,再連接正式的 messaging account。
- 使用
signing_secret,讓投遞帶上X-Device-Timestamp與X-Device-Signature。 - 在慢速的下游任務開始前,先保存標準事件 envelope。
- 入站 inbox 可先訂閱
message.received,並檢查data.message.direction === "inbound"。 - 事件篩選、簽章驗證、重試處理與業務路由應分層處理。
為什麼 webhook 要先做
客戶訊息可能在 CRM、AI agent 或共享 inbox 完成前就到達。如果接收端還沒註冊,後續不能假設一定能用歷史 API 補回來。UnifyPort 的 Quickstart 也把 webhook 放在帳號授權之前。
這篇文章可以搭配兩篇既有教學閱讀:Webhook HMAC replay protection 說明時間戳、簽章與冪等,UnifyPort webhook event filters 說明 subscribed_events 與 wildcard 的取捨。本文則把順序整理成可執行清單。
步驟 1:建立 signed endpoint
API 路由是 POST /v1/webhook-endpoints。若只是入站 inbox,可以先訂閱 message.received;若是完整事件收集器,才使用 [*]。
curl -X POST https://api.unifyport.ai/v1/webhook-endpoints \
-H "X-Api-Key: $UNIFYPORT_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"url\": \"$PUBLIC_WEBHOOK_URL\",
\"status\": \"active\",
\"subscribed_events\": [\"message.received\"],
\"signing_secret\": \"$WEBHOOK_SIGNING_SECRET\",
\"retry_policy\": { \"max_attempts\": 3 }
}"
retry_policy.max_attempts 代表初次投遞後的重試次數。文件預設值是 3,可接受範圍是 0 到 5。實作時請對照 Create webhook endpoint。
步驟 2:驗證原始 request body
啟用簽章後,UnifyPort 會送出 X-Device-Signature。它是以下字串的 HMAC-SHA256 十六進位摘要:
<X-Device-Timestamp>.<raw request body>
接收端必須在 JSON parse 或重新序列化之前驗證原始 bytes。Node.js 的 crypto.createHmac() 和 crypto.timingSafeEqual() 可用於這個模式;官方文件也提醒 timingSafeEqual() 需要長度相同的 buffer。
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const signingSecret = process.env.WEBHOOK_SIGNING_SECRET;
app.post('/webhooks/unifyport', express.raw({ type: 'application/json' }), async (req, res) => {
const timestamp = req.get('X-Device-Timestamp') ?? '';
const signature = req.get('X-Device-Signature') ?? '';
const expected = crypto.createHmac('sha256', signingSecret)
.update(timestamp + '.')
.update(req.body)
.digest('hex');
const valid = /^[0-9a-f]{64}$/i.test(signature) &&
signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
if (!valid) return res.sendStatus(401);
const event = JSON.parse(req.body.toString('utf8'));
await inbox.insertIfAbsent(event.id, event);
return res.sendStatus(202);
});
正式環境還要加入時間戳新鮮度、持久化去重與 retry-aware acknowledgement,細節見 Webhook delivery and signature verification。
步驟 3:保存標準事件 envelope
message.received 在不同 provider 上保留一致的頂層格式:
{
"id": "evt_2f9c1a4b7e",
"type": "message.received",
"provider": "line",
"account_id": "acc_8c21d0",
"occurred_at": "2026-06-08T12:34:56Z",
"data": {
"conversation": { "id": "U4af4980629", "type": "user", "title": "Jordan Lee" },
"sender": { "id": "U4af4980629", "name": "Jordan Lee", "type": "user" },
"message": {
"id": "msg_10472",
"text": "請問訂單出貨了嗎?",
"direction": "inbound",
"sent_at": "2026-06-08T12:34:55Z"
}
}
}
至少保存 id、type、provider、account_id、occurred_at、data.conversation.id、data.sender.id 和 data.message.id。若你之後接 n8n,可參考 n8n WhatsApp AI agent tutorial:n8n 適合處理已驗證事件,而不是第一道安全邊界。
限制與取捨
非官方介面適合需要從既有或一般帳號接收入站訊息的團隊;若你需要官方平台認證、官方商務功能或 provider 層級的政策保證,應採用對應官方 API。也不要把 message.received 誤解成只代表入站,inbox 流程仍要檢查 data.message.direction。
FAQ
一開始該訂閱什麼?
入站 inbox 先訂閱 message.received。只有完整事件收集器才使用 [*]。
簽章驗證是否足以防止重送造成的重複處理?
不足。HMAC 驗證完整性與共享密鑰;還需要時間戳新鮮度與事件 ID 去重。
可以在 CRM 寫入前回傳 200 嗎?
可以,但事件必須已經進入你的持久化 inbox 或 queue。CRM、AI 與通知應非同步處理。
下一步
打開 Create webhook endpoint,先註冊 receiver,再依 webhook delivery guide 完成驗證與確認策略。
Sources
官方來源核對日期:2026-08-26。
讓訊息接入變成一條穩定的產品管線。
先用統一 API 跑通傳送,再用標準事件把所有入站訊息接回業務系統。