创建 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-Keyheader 鉴权;不要放进浏览器代码,也不要提交到代码仓库。 - 新建 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-Id、X-Device-Delivery-Id、X-Device-Timestamp 和 X-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:id、type、provider、account_id、occurred_at 和 data。standard 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 做幂等,保存 provider 和 account_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
让消息接入变成一条稳定的产品管线。
先用统一 API 跑通发送,再用标准事件把所有入站消息接回业务系统。