← 所有文章
教學

UnifyPort 入站整合:先建立 Webhook 的檢查清單

如果你要把 WhatsApp、LINE、Telegram、Zalo、TikTok 或 X 訊息接入客服系統,第一步應該先建立 webhook,而不是先連接正式帳戶。UnifyPort 沒有通用 REST 訊息歷史讀取 API,也不保證錯過的 payload 一定可以重送;所以 webhook 就是你的入站記錄層。先註冊 POST /v1/webhook-endpoints、設定 signing_secret、訂閱 message.received[*],將事件安全儲存,再交給 CRM、AI 或自動化工具。

重點

  • 先註冊 webhook,再連接生產 messaging account。
  • 設定 signing_secret,投遞會帶上 X-Device-TimestampX-Device-Signature
  • 慢速下游任務開始之前,先保存標準事件 envelope。
  • 入站 inbox 可先訂閱 message.received,並檢查 data.message.direction === "inbound"
  • 事件篩選、簽名驗證、重試處理和業務 routing 要分開設計。

為何 webhook 要排第一

客戶訊息可能在 CRM、AI agent 或 shared inbox 準備好之前已經到達。如果 receiver 尚未註冊,之後不能假設一定可以用歷史 API 補回。UnifyPort 的 Quickstart 亦把 webhook 註冊放在帳戶授權之前。

這篇清單可配合兩篇現有文章:Webhook HMAC replay protection 深入講時間戳、簽名與 idempotency;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,可接受範圍是 05。實作時請對照 Create webhook endpoint

步驟 2:驗證原始 request body

啟用簽名後,UnifyPort 會送出 X-Device-Signature。它是以下內容的 HMAC-SHA256 十六進制摘要:

<X-Device-Timestamp>.<raw request body>

receiver 必須在 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);
});

正式環境應再加入 timestamp freshness、持久化去重和 retry-aware acknowledgement。詳細見 Webhook delivery and signature verification

步驟 3:先保存標準事件 envelope

message.received 在不同 provider 上維持一致頂層格式:

{
  "id": "evt_2f9c1a4b7e",
  "type": "message.received",
  "provider": "whatsapp",
  "account_id": "acc_8c21d0",
  "occurred_at": "2026-06-08T12:34:56Z",
  "data": {
    "conversation": { "id": "85261234567", "type": "user", "title": "Jordan Lee" },
    "sender": { "id": "85261234567", "name": "Jordan Lee", "type": "user" },
    "message": {
      "id": "wamid.HBgM",
      "text": "想問訂單出咗貨未?",
      "direction": "inbound",
      "sent_at": "2026-06-08T12:34:55Z"
    }
  }
}

至少保存 idtypeprovideraccount_idoccurred_atdata.conversation.iddata.sender.iddata.message.id。如果之後接 n8n,可參考 n8n WhatsApp AI agent tutorial:n8n 適合處理已驗證事件,不應成為第一道安全邊界。

限制與取捨

非官方接口適合需要從普通或既有帳戶接收入站訊息的團隊;如果你需要官方平台認證、官方商務功能或 provider 層面的政策保證,應採用對應官方 API。亦要留意 message.received 可能描述入站或出站,inbox 流程必須檢查 data.message.direction

FAQ

應該先訂閱哪個事件?

入站 inbox 先用 message.received。只有完整事件收集器才使用 [*]

HMAC 簽名是否足夠處理重複投遞?

不足。HMAC 驗證完整性與 shared secret,仍要加入時間戳新鮮度和按 event ID 去重。

CRM 未寫完可以回 200 嗎?

可以,但事件必須已經進入你的持久化 inbox 或 queue。CRM、AI 和通知可放到 worker 非同步處理。

下一步

打開 Create webhook endpoint,先註冊 receiver,再按 webhook delivery guide 完成驗證與確認策略。

Sources

官方來源核對日期:2026-08-26。

UnifyPort API

令訊息接入變成一條穩定嘅產品管線。

先用統一嘅 API 跑通發送,再用標準事件將所有入站訊息接返去業務系統。