clerk-webhooks by clerk/skills
npx skills add https://github.com/clerk/skills --skill clerk-webhooks前提条件:Webhooks 是异步的。请将其用于后台任务(同步、通知),而非同步流程。
app/api/webhooks/route.ts 创建端点@clerk/nextjs/webhooks 中的 广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
verifyWebhook(req)CLERK_WEBHOOK_SIGNING_SECRET用户: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 → 事件目录
需要同步的情况:
无需同步的情况:
Webhooks 请求是未签名的。路由必须是公开的:
确保 clerkMiddleware() 不保护 /api/webhooks(.*) 路径。
使用正确的导入和单个参数:
import { verifyWebhook } from '@clerk/nextjs/webhooks'
const evt = await verifyWebhook(req) // 直接传递请求对象
缩小到特定事件:
if (evt.type === 'user.created') {
// TypeScript 知道 evt.data 的结构
}
不要只监听 user.created。同时处理 user.updated 和 user.deleted。
立即返回 200,将长时间操作加入队列:
await queue.enqueue('process-webhook', evt)
return new Response('Received', { status: 200 })
重试:Svix 会对失败的 webhooks 重试长达 3 天。返回 2xx 表示成功,返回 4xx/5xx 会触发重试。
重放:可以从仪表板重放失败的 webhooks。
| 症状 | 原因 | 修复方法 |
|---|---|---|
| 验证失败 | 导入或使用方式错误 | 使用 @clerk/nextjs/webhooks,直接传递 req |
| 路由未找到 (404) | 路径错误 | 使用 /api/webhooks |
| 未授权 (401) | 路由受保护 | 将路由设为公开 |
| 数据库中没有数据 | 异步作业挂起 | 等待/检查日志 |
| 重复条目 | 仅处理了 user.created | 同时处理 user.updated |
| 超时 | 处理程序太慢 | 将异步工作加入队列 |
本地:使用 ngrok 将 localhost:3000 隧道到互联网。将 ngrok URL 添加到仪表板端点。
生产环境:将 webhook 端点 URL 更新为生产域名。将签名密钥复制到生产环境变量中。
每周安装量
1.8K
仓库
GitHub 星标
22
首次出现
2026年1月30日
安全审计
安装于
codex1.6K
opencode1.5K
github-copilot1.5K
gemini-cli1.5K
amp1.4K
kimi-cli1.4K
Prerequisite : Webhooks are asynchronous. Use for background tasks (sync, notifications), not synchronous flows.
| Task | Link |
|---|---|
| Overview | https://clerk.com/docs/guides/development/webhooks/overview |
| Sync to database | https://clerk.com/docs/guides/development/webhooks/syncing |
| Debugging | https://clerk.com/docs/guides/development/webhooks/debugging |
| Event catalog | https://dashboard.clerk.com/~/webhooks (Event Catalog tab) |
app/api/webhooks/route.tsverifyWebhook(req) from @clerk/nextjs/webhooksCLERK_WEBHOOK_SIGNING_SECRET in envUser : 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
Do sync when:
Don't sync when:
Webhooks come unsigned. Route must be public:
Ensure clerkMiddleware() doesn't protect /api/webhooks(.*) path.
Use correct import and single parameter:
import { verifyWebhook } from '@clerk/nextjs/webhooks'
const evt = await verifyWebhook(req) // Pass request directly
Narrow to specific event:
if (evt.type === 'user.created') {
// TypeScript knows evt.data structure
}
Don't only listen to user.created. Also handle user.updated and user.deleted.
Return 200 immediately, queue long operations:
await queue.enqueue('process-webhook', evt)
return new Response('Received', { status: 200 })
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.
| Symptom | Cause | Fix |
|---|---|---|
| Verification fails | Wrong import or usage | Use @clerk/nextjs/webhooks, pass req directly |
| Route not found (404) | Wrong path | Use /api/webhooks |
| Not authorized (401) | Route is protected | Make route public |
| No data in DB | Async job pending | Wait/check logs |
| Duplicate entries | Only handling user.created | Also handle user.updated |
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.
Weekly Installs
1.8K
Repository
GitHub Stars
22
First Seen
Jan 30, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex1.6K
opencode1.5K
github-copilot1.5K
gemini-cli1.5K
amp1.4K
kimi-cli1.4K
99,500 周安装
| Timeouts | Handler too slow | Queue async work |