← 所有文章
教程

创建 UnifyPort API key 之后:第一个 Webhook 测试清单

拿到第一个 UnifyPort API key 后,先不要急着连接消息账号。更稳妥的顺序是:用 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,并用原始请求体验证 X-Device-Signature 后,再信任 payload。
  • message.received 当作第一个生产契约,而不是临时 demo 事件。

如果你已经有线上 key,需要更换而不中断服务,请看 API key 零停机轮换清单。如果你正在从零设计入站系统,建议搭配 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 请求 header 鉴权;JSON 成功和错误响应会带顶层 request_id,便于排障和对账。

2. 创建有名称的 key,并只保存一次完整密钥

如果 workspace 允许创建额外 key,请用能说明用途的名称,比如生产入站 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. 用原始请求体验证签名

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 幂等
在日志打印 API key日志会把密钥扩散到更多系统使用 secrets manager,并在日志中脱敏
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 提供非官方接口,并可在适用场景下连接个人或普通消息账号。

下一步

打开 Quickstart,把 API key 放入 secrets manager,先创建 webhook endpoint,再根据 delivery 验签文档实现 receiver。

Sources checked on 2026-09-01

UnifyPort API

让消息接入变成一条稳定的产品管线。

先用统一 API 跑通发送,再用标准事件把所有入站消息接回业务系统。