Skip to content
语言: 中文(简体)

开发者 API

Webhook:事件、负载和签名

更新于 2026年11月1日 阅读约 6 分钟

在控制台中设置 Webhook,接收投递并验证 HMAC 签名,附有 curl、Node.js 和 PHP 的示例代码。

Webhook 会在你的工作区发生某件事时(例如页面被发布)立即向你的服务器发送一个 HTTP 请求。这样你就不必反复查询是否有变化。每次投递都带有签名,你的服务器可以据此区分真实消息和伪造消息。

前提条件

  • 工作区的所有者或管理员;只有这两种角色才会看到“Webhook”菜单项
  • Creator Pro 起提供;在 Creator Free 中显示“Webhook 自 Creator Pro 起提供。”
  • Creator Pro 最多 5 个 Webhook,Creator Agency 最多 25 个,Enterprise 最多 100 个(可调整)
  • 一台可以通过公开 https 地址访问的服务器

Webhook 只能在控制台中管理,无法通过 API 密钥创建或修改。

创建 Webhook

还没有 Webhook 的“Webhook”页面和“+ 新建 Webhook”按钮
工作区中的“Webhook”页面
  1. 在导航中打开“Webhook”
  2. 点击“+ 新建 Webhook”
  3. 输入目标网址并选择事件
  4. 可选:输入描述并选择状态
  5. 点击“创建 Webhook”
  6. 在“完成”步骤中复制签名密钥(“签名密钥——仅此一次可见”)并保存到你的服务器上,它只会在此时显示
  7. 用“测试”测试 Webhook。结果会一直显示在卡片上,直到下一次测试:“投递成功”或“未投递”,并附带 HTTP 状态码(或“无响应”)、耗时和响应的开头部分
“目标网址(https://…)”
最多 2048 个字符。仅限 https,网址中不能包含凭据,不能是 localhost 地址,也不能是 .local、.internal 或 .lan 等内部名称。该名称必须指向公网 IP 地址;LinkHarbor 在每次投递前都会再次检查。
“事件”
至少选择一个事件。每组都有“全选”和“取消全选”。
“描述”
可选,最多 255 个字符,例如写明这个 Webhook 的用途。
“状态”
“已启用”或“已停用”。已停用的 Webhook 不会收到投递。

事件

事件在控制台中触发时机data 的内容
page.published页面已发布页面被发布,无论是在控制台中、通过 API 还是由 AI 助手发布page_id、version、checksum、published_by_user_id(API 密钥和 AI 助手发布时为 null)
form.submitted表单已提交访客在你的某个页面上提交了表单form_id、form_name、submission_id、page_id、block_id、submitted_at、values(字段键 → 值)、consent(同意项键 → true/false)
domain.verified域名已验证自定义域名验证成功domain_id、hostname、verification_method、status
domain.disabled域名已停用自定义域名在控制台中被停用(降级时不会触发)domain_id、hostname、status
webhook.test–(“测试”按钮)你点击“测试”时message(使用发起测试者的账户语言的提示文本,不要据此匹配)

form.submitted 包含访客提交的原始值,但绝不包含访客的 IP 地址或浏览器信息。投递在后台进行:访客无需等待,投递失败也不会影响提交。

投递的结构

LinkHarbor 以 POST 请求发送每次投递,带有 JSON 请求体和以下请求头:

请求头内容
Content-Typeapplication/json
X-LinkHarbor-Event事件名称,例如 page.published
X-LinkHarbor-Delivery-Id本次投递的唯一 ID(UUID),与请求体中的 id 相同
X-LinkHarbor-Timestamp发送时间,以 Unix 秒表示
X-LinkHarbor-Signaturesha256= 后接十六进制的 HMAC-SHA256 签名
X-LinkHarbor-Signature-Versionv1
page.published 的示例负载
{
  "id": "3f1c2a9e-8d4b-4a51-9f0e-6b7c1d2e3f40",
  "event": "page.published",
  "created_at": "2026-11-01T09:30:12+00:00",
  "data": {
    "page_id": 42,
    "version": 8,
    "checksum": "9b1e5c…",
    "published_by_user_id": 7
  }
}

重新投递会获得新的 id,另外还有 redelivery_of(被重复的那次投递的 ID)和 delivery_root(这条链中第一次投递的 ID)两个字段。

验证签名

每个 Webhook 都有自己的签名密钥(32 个十六进制字符)。你只能看到它一次:在创建 Webhook 的最后一步,以及点击“轮换密钥 ↺”之后。请立即把它复制到你的服务器上,LinkHarbor 以后不会再显示它。LinkHarbor 用它按如下方式计算签名:

  1. 构建消息:时间戳、点、投递 ID、点、原始请求体,即 timestamp.delivery_id.body
  2. 以签名密钥为密钥,对这条消息计算 HMAC-SHA256
  3. 把结果作为带 sha256= 前缀的十六进制字符串发送

你的服务器应使用恒定时间比较来检查签名,拒绝时间戳超过 300 秒的投递,并至少在这段时间内保存投递 ID,以识别重复投递。重要:签名针对的是原始请求体,即到达时的原样内容,而不是你重新编码后的 JSON。

Node.js(Express):接收并验证 Webhook
import crypto from 'node:crypto';
import express from 'express';

const app = express();
const SECRET = process.env.LH_WEBHOOK_SECRET;
const seen = new Set(); // 生产环境中:使用带过期时间的数据库或 Redis

app.post('/webhooks/linkharbor', express.raw({ type: 'application/json' }), (req, res) => {
  const ts = req.get('X-LinkHarbor-Timestamp') || '';
  const id = req.get('X-LinkHarbor-Delivery-Id') || '';
  const sig = req.get('X-LinkHarbor-Signature') || '';

  if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > 300) {
    return res.status(400).send('timestamp');
  }

  const expected = 'sha256=' + crypto
    .createHmac('sha256', SECRET)
    .update(ts + '.' + id + '.')
    .update(req.body) // 原始请求体(Buffer)
    .digest('hex');

  const a = Buffer.from(expected);
  const b = Buffer.from(sig);
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.status(401).send('signature');
  }

  const event = JSON.parse(req.body.toString('utf8'));
  const key = event.delivery_root || event.id; // 识别同一条链中的重新投递
  if (!seen.has(key)) {
    seen.add(key);
    // 在这里处理,最好放进队列异步处理
  }
  res.sendStatus(200);
});

app.listen(3000);
PHP:接收并验证 Webhook
<?php
$secret = getenv('LH_WEBHOOK_SECRET');
$body   = file_get_contents('php://input');
$ts     = $_SERVER['HTTP_X_LINKHARBOR_TIMESTAMP'] ?? '';
$id     = $_SERVER['HTTP_X_LINKHARBOR_DELIVERY_ID'] ?? '';
$sig    = $_SERVER['HTTP_X_LINKHARBOR_SIGNATURE'] ?? '';

if (!ctype_digit($ts) || abs(time() - (int)$ts) > 300) {
    http_response_code(400);
    exit('timestamp');
}

$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $id . '.' . $body, $secret);
if (!hash_equals($expected, $sig)) {
    http_response_code(401);
    exit('signature');
}

$event = json_decode($body, true);
// 通过 $event['delivery_root'] ?? $event['id'] 识别重复投递,
// 然后再处理。
http_response_code(200);

用 curl 测试你的端点

这个脚本会向你自己的服务器发送一个正确签名的测试投递,在 LinkHarbor 中创建 Webhook 之前就能派上用场:

模拟一次带签名的测试投递
SECRET="your-signing-secret"
URL="https://example.com/webhooks/linkharbor"
TS=$(date +%s)
ID=$(uuidgen | tr 'A-Z' 'a-z')
BODY='{"id":"'"$ID"'","event":"webhook.test","created_at":"2026-11-01T09:30:12+00:00","data":{"message":"这是该 Webhook 的测试事件。"}}'
SIG="sha256=$(printf '%s' "$TS.$ID.$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')"

curl -X POST "$URL" \
  -H "Content-Type: application/json" \
  -H "X-LinkHarbor-Event: webhook.test" \
  -H "X-LinkHarbor-Delivery-Id: $ID" \
  -H "X-LinkHarbor-Timestamp: $TS" \
  -H "X-LinkHarbor-Signature: $SIG" \
  -H "X-LinkHarbor-Signature-Version: v1" \
  -d "$BODY"

响应、超时和重新投递

  • 任何 2xx 状态的响应都算成功;不会跟随重定向(3xx)
  • 你的服务器有 10 秒时间响应,建立连接最多 5 秒
  • 同一事件的所有 Webhook 总共共享 25 秒;到时还没轮到的会被标记为失败,之后可以重新投递
  • LinkHarbor 不会自动重试失败的投递
  • 你可以手动重新发送失败的投递,但之后已经成功的投递不能重发。重新投递会有新的 id 和新的 created_at
  • 每个事件最多允许 3 次尝试(包括第一次投递);之后它会进入“死信”,无法再重新发送

控制台中的投递记录

“投递记录”会打开 Webhook 的日志,其中有三个标签页:

“所有投递”
所有投递,可按事件、状态(“成功(2xx)”“客户端错误(4xx)”“服务器错误(5xx)”“失败”)和“数量上限”筛选。只要之后没有成功的投递,“↺ 重试”就会重新投递失败的那一次。点击某一行可以查看请求、响应、请求体和原始 JSON。
“重放”
重新投递的链条,带有结果“已恢复”或“仍在失败”以及尝试次数。
“死信”
所有允许的尝试都失败的投递,包括第一次投递和各次重新投递。这个标签页只能查看:其中的投递无法再重新投递。请检查你的端点;之后新的事件会照常投递。如果你需要这些数据,请通过 API 获取。

如果重新投递没有成功,控制台会给出原因:

“未重新投递:此事件已失败 … 次,无法再尝试。你可以在‘死信’中找到它。”
3 次尝试都已用完。
“未重新投递:此事件此后已成功投递。”
同一条链中之后的某次投递已经成功。

控制台中的验证

在“验证”中有两个辅助工具:

“验证工具包”
“加载工具包”会显示算法、请求头名称、签名消息的格式以及推荐的 300 秒容差。其中不包含密钥本身。
“验证签名”
输入你收到的某次投递的投递 ID、时间戳、签名和原始请求体。LinkHarbor 随后会告诉你“签名有效”或“签名无效”。时间戳和签名请从 X-LinkHarbor-Timestamp 和 X-LinkHarbor-Signature 请求头中复制。这项检查只适用于最近 5 分钟内的投递;对于更早的投递,即使签名正确,它也会报告“签名无效”。

轮换密钥、停用、删除

  • “轮换密钥 ↺”会生成新的签名密钥并显示一次;旧密钥立即失效,你的服务器必须改用新密钥
  • 在“编辑”中把状态设为“已停用”,即可暂停投递
  • 垃圾桶图标会删除 Webhook;在你关闭提示消息之前,可以用“撤销”取消删除

降级到更小的套餐时,超出新上限的 Webhook 会被停用,降级到 Creator Free 时则全部停用。它们会被保留;升级后,LinkHarbor 会自动重新启用已停用的 Webhook,直到达到新套餐的上限,其中也包括你自己停用的 Webhook。

没找到答案?

告诉我们你需要什么,我们的客服团队会亲自帮你。

联系客服