clerk-webhooks

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Webhooks

Webhooks

Prerequisite: Webhooks are asynchronous. Use for background tasks (sync, notifications), not synchronous flows.
前提条件:Webhooks 是异步的。适用于后台任务(同步、通知),而非同步流程。

Documentation Reference

文档参考

Quick Start

快速开始

  1. Create endpoint at
    app/api/webhooks/route.ts
  2. Use
    verifyWebhook(req)
    from
    @clerk/nextjs/webhooks
  3. Dashboard → Webhooks → Add Endpoint
  4. Set
    CLERK_WEBHOOK_SIGNING_SECRET
    in env
  5. Make route public (not protected by middleware)
  1. app/api/webhooks/route.ts
    创建端点
  2. 使用
    @clerk/nextjs/webhooks
    中的
    verifyWebhook(req)
  3. 控制台 → Webhooks → 添加端点
  4. 在环境变量中设置
    CLERK_WEBHOOK_SIGNING_SECRET
  5. 将路由设置为公开(不被中间件保护)

Supported Events

支持的事件

User:
user.created
user.updated
user.deleted
Organization:
organization.created
organization.updated
organization.deleted
Organization Domain:
organizationDomain.created
organizationDomain.updated
organizationDomain.deleted
Organization Invitation:
organizationInvitation.created
organizationInvitation.accepted
organizationInvitation.revoked
Organization Membership:
organizationMembership.created
organizationMembership.updated
organizationMembership.deleted
Roles:
role.created
role.updated
role.deleted
Permissions:
permission.created
permission.updated
permission.deleted
Session:
session.created
session.updated
session.ended
session.removed
session.revoked
session.pending
Communication:
email.created
sms.created
Invitations:
invitation.created
invitation.accepted
invitation.revoked
Waitlist:
waitlistEntry.created
waitlistEntry.updated
Full catalog: Dashboard → Webhooks → Event Catalog
用户
user.created
user.updated
user.deleted
组织
organization.created
organization.updated
organization.deleted
组织域名
organizationDomain.created
organizationDomain.updated
organizationDomain.deleted
组织邀请
organizationInvitation.created
organizationInvitation.accepted
organizationInvitation.revoked
组织成员
organizationMembership.created
organizationMembership.updated
organizationMembership.deleted
角色
role.created
role.updated
role.deleted
权限
permission.created
permission.updated
permission.deleted
会话
session.created
session.updated
session.ended
session.removed
session.revoked
session.pending
通信
email.created
sms.created
邀请
invitation.created
invitation.accepted
invitation.revoked
等待列表
waitlistEntry.created
waitlistEntry.updated
完整目录:控制台 → Webhooks → 事件目录

When to Sync

何时进行同步

Do sync when:
  • Need other users' data (social features, profiles)
  • Storing extra custom fields (birthday, country, bio)
  • Building notifications or integrations
Don't sync when:
  • Only need current user data (use session token)
  • No custom fields (Clerk has everything)
  • Need immediate access (webhooks are eventual consistency)
建议同步的场景
  • 需要其他用户的数据(社交功能、个人资料)
  • 存储额外的自定义字段(生日、国家、个人简介)
  • 构建通知或集成功能
不建议同步的场景
  • 仅需当前用户数据(使用会话令牌)
  • 无自定义字段(Clerk 已包含所有所需数据)
  • 需要即时访问数据(Webhooks 最终一致性)

Key Patterns

核心模式

Make Route Public

设置路由为公开

Webhooks come unsigned. Route must be public:
Ensure
clerkMiddleware()
doesn't protect
/api/webhooks(.*)
path.
Webhook 请求是未签名的,路由必须设置为公开:
确保
clerkMiddleware()
不会保护
/api/webhooks(.*)
路径。

Verify Webhook

验证 Webhook

Use correct import and single parameter:
typescript
import { verifyWebhook } from '@clerk/nextjs/webhooks'
const evt = await verifyWebhook(req)  // Pass request directly
使用正确的导入方式和单个参数:
typescript
import { verifyWebhook } from '@clerk/nextjs/webhooks'
const evt = await verifyWebhook(req)  // Pass request directly

Type-Safe Events

类型安全的事件

Narrow to specific event:
typescript
if (evt.type === 'user.created') {
  // TypeScript knows evt.data structure
}
缩小到特定事件类型:
typescript
if (evt.type === 'user.created') {
  // TypeScript knows evt.data structure
}

Handle All Three Events

处理全部三类事件

Don't only listen to
user.created
. Also handle
user.updated
and
user.deleted
.
不要只监听
user.created
,同时也要处理
user.updated
user.deleted

Queue Async Work

异步任务队列

Return 200 immediately, queue long operations:
typescript
await queue.enqueue('process-webhook', evt)
return new Response('Received', { status: 200 })
立即返回200状态码,将长时间操作放入队列:
typescript
await queue.enqueue('process-webhook', evt)
return new Response('Received', { status: 200 })

Webhook Reliability

Webhook 可靠性

Retries: Svix retries failed webhooks for up to 3 days. Return 2xx to succeed, 4xx/5xx to retry.
Replay: Failed webhooks can be replayed from Dashboard.
重试机制:Svix 会对失败的Webhook进行最多3天的重试。返回2xx状态码表示成功,4xx/5xx会触发重试。
重放功能:失败的Webhook可从控制台进行重放。

Common Pitfalls

常见问题

SymptomCauseFix
Verification failsWrong import or usageUse
@clerk/nextjs/webhooks
, pass
req
directly
Route not found (404)Wrong pathUse
/api/webhooks
Not authorized (401)Route is protectedMake route public
No data in DBAsync job pendingWait/check logs
Duplicate entriesOnly handling
user.created
Also handle
user.updated
TimeoutsHandler too slowQueue async work
症状原因解决方法
验证失败导入方式或使用错误使用
@clerk/nextjs/webhooks
,直接传入
req
路由未找到(404)路径错误使用
/api/webhooks
未授权(401)路由被保护将路由设置为公开
数据库中无数据异步任务待处理等待或检查日志
重复条目仅处理了
user.created
同时处理
user.updated
超时处理程序过慢将异步工作放入队列

Testing & Deployment

测试与部署

Local: Use ngrok to tunnel
localhost:3000
to internet. Add ngrok URL to Dashboard endpoint.
Production: Update webhook endpoint URL to production domain. Copy signing secret to production env vars.
本地环境:使用ngrok将
localhost:3000
隧道到公网。将ngrok URL添加到控制台的端点中。
生产环境:将Webhook端点URL更新为生产域名。将签名密钥复制到生产环境变量中。