跳到正文
文档 · Webhook

Webhook

Usher 可以在环境中发生事情时即时通知你的服务器:有人加入、登录、更改资料或离开。每个事件都是一个发送到你所选地址的已签名 POST 请求,如果你的服务器没有响应,还会再次尝试。

添加端点

在 Usher Console 中打开环境,然后前往 Webhook → 新建端点。填写一个属于你的 https:// 地址,并选择它应当接收的事件。Console 只会显示一次该端点的签名机密(whsec_…):请把它和服务器的其他机密存放在一起。

事件触发时机
user.created有人创建了账户
user.updated账户发生了变更:电子邮件地址、姓名、验证状态或封禁(包括你的组织目录通过 SCIM 执行的封禁)
user.deleted账户已被删除
session.created有人登录了
session.ended会话已结束:退出登录、被撤销或提前过期

发送测试只会向该端点发送一个 webhook.test 事件,无论它订阅了哪些事件。

会收到什么

请求
POST /usher/webhooks HTTP/1.1
content-type: application/json
user-agent: Usher-Webhooks/1
webhook-id: msg_2fk7x…
webhook-timestamp: 1793643116
webhook-signature: v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE=
正文
{
  "type": "user.created",
  "timestamp": "2026-11-02T18:11:56.412Z",
  "data": {
    "user": {
      "id": "Qz3m…",
      "email": "lena@acme.com",
      "emailVerified": false,
      "name": "Lena Live",
      "image": null,
      "banned": false,
      "createdAt": "2026-11-02T18:11:56.380Z",
      "updatedAt": "2026-11-02T18:11:56.380Z"
    }
  }
}

事件描述的是用户和会话,从不包含机密:没有令牌、没有密码哈希、没有角色,也不会说明某人被封禁的原因。

校验每一次投递

Usher 按照 Standard Webhooks 规范签名,因此它的任何一个库都可以替你校验签名。凡是验证不通过的,一律拒绝。这些库还会拒绝时间戳相差超过五分钟的请求,因此旧的投递无法被重放。

app/usher/webhooks/route.ts
// a route that receives Usher's webhooks (Node, the standardwebhooks package)
import { Webhook } from 'standardwebhooks'

const wh = new Webhook(process.env.USHER_WEBHOOK_SECRET!) // whsec_…, from Usher Console

export async function POST(request: Request) {
  const body = await request.text() // the raw body: the signature covers it byte for byte
  let event: { type: string; timestamp: string; data: Record<string, unknown> }
  try {
    event = wh.verify(body, Object.fromEntries(request.headers)) as typeof event
  } catch {
    return new Response('bad signature', { status: 400 })
  }
  // webhook-id is the same on every attempt: skip one you have handled already
  if (await alreadyHandled(request.headers.get('webhook-id')!)) return new Response(null, { status: 204 })
  await queue(event) // answer quickly; do the work afterwards
  return new Response(null, { status: 204 })
}
  • 在 15 秒内作出响应,返回任意 2xx 状态码即可。其他任何响应(包括重定向)都算作失败。
  • 做好收到重复消息的准备:同一条消息在每次尝试中都保留相同的 webhook-id;已经处理过的,跳过即可。
  • 不要依赖顺序:重试的消息可能在较新的事件之后才到达。

当你的服务器没有响应时

Usher 会在 5 秒、5 分钟、30 分钟、2 小时、5 小时、10 小时和 10 小时之后依次重试(总计约一天半),然后放弃。Console 的投递日志会显示每条消息的每次尝试以及你的服务器最后一次的响应,点按 重新发送 即可重新开始投递。

更换机密

Console 中的 新建机密 会生成另一个机密,并且只显示一次。在接下来的 24 小时内,每次投递都会带有两个签名,每个机密各一个,让你可以从容部署新机密而不会漏掉任何消息。此后,只有新机密会用于签名。

投递从哪里发出

Webhook 通过 Usher 的出站代理发出,该代理不会连接私有地址或内部地址。端点必须是使用公开域名的 https:// 地址:不能是 IP 地址、私有域名,也不能是 Usher 自己域名下的地址。