← 所有文章
教學

建立 UnifyPort API key 之後:第一個 Webhook 測試清單

拿到第一個 UnifyPort API key 之後,先不要直接連接 messaging account。比較穩的順序是:用 GET /v1/workspace 驗證 key,先建立帶簽章的 webhook endpoint,訂閱 message.received["*"],把事件保存下來,再授權 WhatsApp、Telegram、LINE、TikTok、Zalo 或 X。

重點整理

  • API key 透過 X-Api-Key header 驗證;不要放進瀏覽器程式碼,也不要提交到版本庫。
  • 新建 key 時,完整秘密值只會在 api_key 欄位回傳一次;之後列表回應只會顯示 key_prefix
  • webhook 應該早於帳號授權建立,因為授權進度與入站訊息都會以 webhook 事件送達。
  • 設定 signing_secret,並用原始 request body 驗證 X-Device-Signature 後,再信任 payload。
  • message.received 當作第一個正式資料契約,而不是臨時 demo 事件。

如果你已有線上 key,現在要更換,請先看 API key 零停機輪換 runbook。若你正在從零規劃入站架構,建議搭配 webhook-first 入站整合清單 一起使用。

1. 先確認 key 對應的 workspace

UnifyPort 目前提供給特定客戶使用;公開文件說明需要聯絡團隊取得 workspace 權限與第一個 API key。拿到 key 後,第一個請求應該是只讀 workspace 檢查,而不是立即發訊息。

export UNIFYPORT_API_KEY="set-this-in-your-secret-manager"

curl https://api.unifyport.ai/v1/workspace \
  -H "X-Api-Key: $UNIFYPORT_API_KEY"

請求成功代表這個 key 能解析到一個 workspace。Introduction 文件 也說明,所有 /v1 endpoint 都使用 X-Api-Key request header 驗證;JSON 成功與錯誤回應會帶頂層 request_id,方便排查與對帳。

2. 建立有名稱的 key,並只保存一次完整秘密值

如果 workspace 允許建立額外 key,請用可辨識用途的名稱,例如 production inbound worker。依照 Create API key reference,回應中的 key 是記錄資訊,完整秘密值只會在 api_key 欄位出現一次。

curl -X POST https://api.unifyport.ai/v1/api-keys \
  -H "X-Api-Key: $UNIFYPORT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production inbound worker",
    "prefix": "dk_live"
  }'

實務規則:把回傳的 api_key 直接寫入 secrets manager,不要貼到 issue、聊天紀錄或前端環境變數。OWASP 官方 Secrets Management Cheat Sheet 也把 API keys 視為 secrets,並把建立、保存、輪換、撤銷與稽核當作同一個生命週期。

3. 連接帳號前先建立 webhook

這一步要早於 QR、驗證碼或 session 授權。UnifyPort 不保證補送所有錯過的 webhook delivery,因此真正持久的入站紀錄應該由你的 receiver 和資料庫負責。

export WEBHOOK_SIGNING_SECRET="generate-a-long-random-secret"

curl -X POST https://api.unifyport.ai/v1/webhook-endpoints \
  -H "X-Api-Key: $UNIFYPORT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://inbox.example.com/unifyport/webhook",
    "status": "active",
    "subscribed_events": ["message.received"],
    "signing_secret": "'"$WEBHOOK_SIGNING_SECRET"'"
  }'

Create webhook endpoint reference 允許使用精確的公開標準事件名稱,也允許以 ["*"] 接收所有公開標準事件。若你要建立完整帳號狀態機,用 ["*"];若第一個里程碑只是接收客戶入站訊息,用 message.received 會更容易驗證。

4. 用原始 request body 驗證簽章

delivery 文件定義了幾個重要 header:X-Device-Event-IdX-Device-Delivery-IdX-Device-TimestampX-Device-Signature。簽章是下列內容的十六進位 HMAC-SHA256:

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

關鍵在 raw body。如果框架先解析 JSON 再重新序列化,位元組可能已經改變,簽章驗證就會失敗。

import crypto from 'crypto';
import express from 'express';

const app = express();
const signingSecret = process.env.WEBHOOK_SIGNING_SECRET;

app.post('/unifyport/webhook', express.raw({ type: 'application/json' }), (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 = signature.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));

  if (!valid) return res.status(401).end();

  const event = JSON.parse(req.body.toString('utf8'));
  if (event.type === 'message.received') {
    console.log(event.provider, event.data.conversation.id, event.data.message.text);
  }

  res.status(200).end();
});

更完整的說明在 Webhook delivery & signature verification。它也涵蓋 retry、idempotency、過期 timestamp 檢查,以及任意 2xx 回應都會確認 delivery 的規則。

5. 把 message.received envelope 當作第一個資料契約

一般入站訊息會帶穩定 envelope:idtypeprovideraccount_idoccurred_atdatastandard event payload reference 展示了建模時應依賴的欄位:

{
  "id": "evt_2f9c1a4b7e",
  "type": "message.received",
  "provider": "whatsapp",
  "account_id": "acc_8c21d0",
  "occurred_at": "2026-06-08T12:34:56Z",
  "data": {
    "conversation": { "id": "8613912345678", "type": "user" },
    "sender": { "id": "8613912345678", "type": "user", "name": "Jordan Lee" },
    "message": {
      "id": "wamid.HBgM",
      "text": "Hi - is my order shipped yet?",
      "direction": "inbound",
      "sent_at": "2026-06-08T12:34:55Z"
    }
  }
}

建議保存頂層事件 id 做冪等,保存 provideraccount_id 做路由,保存 data.conversation.id 做佇列分組,保存 data.sender.id 做身份關聯,保存 data.message.id 做訊息層級動作。一旦這個 shape 穩定,同一個 receiver 可以先接 WhatsApp,再加入 LINE、Telegram、Zalo、TikTok 或 X。若你想看 AI coding agent 如何協助生成實作,可參考 AI 自動回覆 bot 教學

第一天常見錯誤

錯誤影響較安全做法
先建立帳號、後建立 webhook授權事件可能在 receiver 準備好之前抵達先建立 webhook endpoint
關閉簽章知道 URL 的人可能提交相似 JSON設定 signing_secret 並驗證 raw-body HMAC
只按 delivery attempt 去重retry 可能重送同一事件X-Device-Event-Id 或事件 id 做冪等
在 log 印出 API keylog 會讓秘密值擴散到更多系統使用 secrets manager,並在 log 中遮罩
message.received 視為 WhatsApp 專用它是跨平台歸一化事件保存 provider 與 account 欄位,不寫死單一平台假設

FAQ

之後可以取回完整 API key 嗎?

不可以。建立回應只會在 api_key 回傳一次完整秘密值。列表與詳情回應只會提供 key_prefix 等安全顯示欄位。

應該訂閱 message.received 還是 ["*"]

如果第一輪只測入站訊息,用 message.received。如果系統要接收授權、runtime、訊息、回執、會話或群組事件,用 ["*"]

建立帳號前一定需要 webhook endpoint 嗎?

若要可靠地完成第一次測試,建議需要。帳號授權進度與即時入站訊息都會透過 webhook 抵達,UnifyPort 不承諾完整補送錯過的 payload。

每個平台都需要官方商業帳號嗎?

不需要。UnifyPort 為 WhatsApp、Telegram、LINE、TikTok、Zalo 和 X 提供非官方接口,並可在適合的整合模式下連接個人或一般 messaging account。

下一步

打開 Quickstart,把 API key 放到 secrets manager,先建立 webhook endpoint,再依照 delivery 驗簽文件完成 receiver。

Sources checked on 2026-09-01

UnifyPort API

讓訊息接入變成一條穩定的產品管線。

先用統一 API 跑通傳送,再用標準事件把所有入站訊息接回業務系統。