跳到主内容
智客 ZICQ

技能库 智客分类:Agent 工作流 clerk-webhooks

Clerk Webhooks

负责实时事件和数据同步的办事员 Webhoks. 用校验Webhook进行校验

48849 安装量

官方网址:skills.sh

技能介绍

先看中文介绍;官方 description 原文单独保留,不改写 SKILL.md。

做什么

负责实时事件和数据同步的办事员 Webhoks. 用校验Webhook进行校验

何时用

网络呼号

代理如何加载

按 Agent Skills 渐进披露:启动时只加载 name 与 description(约 100 token);任务匹配后才读入整份 SKILL.md 正文;scripts/、references/、assets/ 仅在需要时再读。 本文件正文结构:Webhooks、When to Use Webhooks、Verify Every Webhook、Keep the Webhook Route Unprotected、Complete Webhook Handler (Next.js App Router)、Full Example: Welcome Email (Resend) + Slack Notification on user.created。 其中含规范建议的小节:输入输出示例。

文件分析

文件分析:除 SKILL.md 外,正文引用了 references/frameworks.md,属于带资源的技能包,这些文件按需再读。

官方 description(原文)

Clerk webhooks for real-time events and data syncing. Verify with verifyWebhook

WebhooksWhen to Use WebhooksVerify Every WebhookKeep the Webhook Route UnprotectedComplete Webhook Handler (Next.js App Router)Full Example: Welcome Email (Resend) + Slack Notification on user.createdFull Example: Organization Membership Sync to DatabaseOther FrameworksType Narrowing for `evt.data`Payload Field ReferenceUser events (`user.created`, `user.updated`, `user.deleted`)Organization events (`organization.created`, `organization.updated`, `organization.deleted`)

兼容:Requires CLERK_WEBHOOK_SIGNING_SECRET (svix signing secret from Clerk dashboard) · 许可:MIT · allowed-tools:WebFetch

来源分类:skills.sh agent-skill

SKILL.md 与 Agent 调用

官方规范 ↗
name
clerk-webhooks
description
Clerk webhooks for real-time events and data syncing. Verify with verifyWebhook
compatibility
Requires CLERK_WEBHOOK_SIGNING_SECRET (svix signing secret from Clerk dashboard)
allowed-tools
WebFetch实验字段,支持情况取决于客户端;字段声明本身不会授予工具权限。
许可
MIT
  1. 发现技能客户端向 Agent 提供名称与描述目录。
  2. 匹配与调用用户指定或任务匹配后,载入 SKILL.md 指令。
  3. 按需加载按步骤读取参考文档、使用脚本与素材。
指令中引用的文件 · 1
  • references/frameworks.md

以下路径提取自原文;文件是否齐全请以来源仓库中的完整目录为准。

具体调用语法与可用工具以目标 Agent 客户端为准。 查看调用机制说明 ↗

安装这个技能

Skills CLI ↗

先选择目标 Agent 和安装范围,保留技能包的附属文件,安装后检查客户端能否发现该技能。

该技能引用了附属文件,请从来源获取完整目录;仅复制 SKILL.md 可能缺少依赖。

交给 Agent 安装

复制安装指令给支持 Agent Skills 的代理,确认其中的目标目录与客户端匹配。

把 Agent Skill「clerk-webhooks」安装到我的项目:SKILL.md 原文与官方 description 见 https://zicq.com/zh/skills/skl-fe21136ee27c215b-Clerk-Webhooks.html
请存为 .cursor/skills/clerk-webhooks/SKILL.md 或 .claude/skills/clerk-webhooks/SKILL.md,frontmatter 的 name 与 description 保持原样,不要改写。
该技能还带 scripts/、references/、assets/ 等文件,请从 https://github.com/clerk/skills 取完整目录,不要只建一个 SKILL.md。

GitHub 完整包 ↗

终端安装 · Skills CLI

需要 Node.js 与 npx。先查看仓库技能列表,确认实际名称。

npx skills add 'https://github.com/clerk/skills' --list

npx skills add 'https://github.com/clerk/skills' --skill 'clerk-webhooks'

CLI 会交互选择目标 Agent,默认安装到项目;用户级安装使用 -g。先通过查看命令核对仓库内容,再用 npx skills list 检查已安装技能。

阅读排版
--- name: clerk-webhooks description: Clerk webhooks for real-time events and data syncing. Verify with verifyWebhook from the framework-specific package. Handle user, session, organization, billing, and payment events. Build event-driven features like database sync, notifications, and integrations. allowed-tools: WebFetch license: MIT metadata: author: clerk version: 1.2.0 compatibility: Requires CLERK_WEBHOOK_SIGNING_SECRET (svix signing secret from Clerk dashboard) --- # Webhooks Output complete, working webhook handlers with `verifyWebhook(req)` verification in every handler. ## When to Use Webhooks Webhooks are **asynchronous and eventually consistent**. Delivery is fast but not guaranteed to be immediate, and may occasionally fail (Svix retries on a fixed schedule). Use them for: - Database sync (a separate users / orgs table that follows Clerk) - Notifications (welcome emails, Slack pings, internal alerts) - Integrations triggered by lifecycle events Do NOT rely on webhook delivery as part of a synchronous flow such as onboarding ("user signs up, then we read X from our DB"). For data the user just created, read it from the [Clerk session token](https://clerk.com/docs/guides/sessions/session-tokens) or call the Backend API directly. Webhooks fill the gap when you need data about *other* users or events the session token doesn't carry. ## Verify Every Webhook Use `verifyWebhook(req)` from the framework-specific package (`@clerk/nextjs/webhooks`, `@clerk/express/webhooks`, etc.). It reads `CLERK_WEBHOOK_SIGNING_SECRET` automatically and throws on bad signatures. Skipping verification, even for notification-only handlers, exposes the endpoint to spoofed events. ## Keep the Webhook Route Unprotected Webhook deliveries carry no user session. `verifyWebhook()` is the only check the route needs. A bare `clerkMiddleware()` protects nothing, so the route is already reachable. Don't call `auth.protect()` for `/api/webhooks(.*)`, in middleware or in the handler. A project that does returns `404` for every delivery until that call is removed. See [Ensure the webhook route is public](https://clerk.com/docs/guides/development/webhooks/syncing#ensure-the-webhook-route-is-public). ## Complete Webhook Handler (Next.js App Router) ```typescript // app/api/webhooks/route.ts import { verifyWebhook } from '@clerk/nextjs/webhooks' import { NextRequest } from 'next/server' import { db } from '@/lib/db' export async function POST(req: NextRequest) { // ALWAYS verify - never skip, even for notification-only handlers let evt try { evt = await verifyWebhook(req) // uses CLERK_WEBHOOK_SIGNING_SECRET automatically } catch (err) { console.error('Webhook verification failed:', err) return new Response('Verification failed', { status: 400 }) } if (evt.type === 'user.created') { const { id, email_addresses, first_name, last_name } = evt.data const email = email_addresses[0]?.email_address const name = `${first_name ?? ''} ${last_name ?? ''}`.trim() await db.users.create({ data: { clerkId: id, email, name } }) } if (evt.type === 'user.updated') { const { id, email_addresses, first_name, last_name } = evt.data const email = email_addresses[0]?.email_address await db.users.update({ where: { clerkId: id }, data: { email, first_name, last_name } }) } if (evt.type === 'user.deleted') { const { id } = evt.data await db.users.delete({ where: { clerkId: id } }) } if (evt.type === 'organizationMembership.created') { const { organization, public_user_data, role } = evt.data const orgId = organization.id const userId = public_user_data.user_id await db.teamMembers.create({ data: { orgId, userId, role } }) } if (evt.type === 'organizationMembership.deleted') { const { organization, public_user_data } = evt.data const orgId = organization.id const userId = public_user_data.user_id await db.teamMembers.delete({ where: { orgId_userId: { orgId, userId } } }) } return new Response('OK', { status: 200 }) } ``` ## Full Example: Welcome Email (Resend) + Slack Notification on user.created Notification-only handlers still verify the signature. Same pattern as the database-sync handler: ```typescript // app/api/webhooks/route.ts import { verifyWebhook } from '@clerk/nextjs/webhooks' import { NextRequest } from 'next/server' import { Resend } from 'resend' const resend = new Resend(process.env.RESEND_API_KEY) export async function POST(req: NextRequest) { // Step 1: ALWAYS verify the webhook signature - NEVER skip this let evt try { evt = await verifyWebhook(req) // uses CLERK_WEBHOOK_SIGNING_SECRET env var } catch (err) { console.error('Webhook verification failed:', err) return new Response('Verification failed', { status: 400 }) } // Step 2: Listen for user.created event if (evt.type === 'user.created') { // Step 3: Extract user email and name from webhook payload const { id, email_addresses, first_name, last_name } = evt.data const email = email_addresses[0]?.email_address const name = `${first_name ?? ''} ${last_name ?? ''}`.trim() // Step 4: Call Resend API to send welcome email await resend.emails.send({ from: '[email protected]', to: email, subject: 'Welcome!', html: `

Hi ${name}, welcome to our app!

`, }) // Step 5: Post notification to Slack channel await fetch(process.env.SLACK_WEBHOOK_URL!, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: `New user signed up: ${name} (${email})`, }), }) } // Always return 200 to acknowledge receipt return new Response('OK', { status: 200 }) } ``` ## Full Example: Organization Membership Sync to Database ```typescript // app/api/webhooks/route.ts import { verifyWebhook } from '@clerk/nextjs/webhooks' import { NextRequest } from 'next/server' import { db } from '@/lib/db' // your database client export async function POST(req: NextRequest) { // ALWAYS verify signature - never skip, even for simple handlers let evt try { evt = await verifyWebhook(req) // uses CLERK_WEBHOOK_SIGNING_SECRET env var } catch (err) { console.error('Webhook verification failed:', err) return new Response('Verification failed', { status: 400 }) } if (evt.type === 'organization.created') { const { id, name } = evt.data await db.workspaces.create({ data: { orgId: id, name, createdAt: new Date() }, }) } if (evt.type === 'organizationMembership.created') { // Extract organization ID, user ID, and role from payload const { organization, public_user_data, role } = evt.data const orgId = organization.id const userId = public_user_data.user_id // Add to team_members table await db.team_members.create({ data: { orgId, userId, role }, }) // Create workspace record for new member await db.workspaces.create({ data: { orgId, userId, createdAt: new Date() }, }) } if (evt.type === 'organizationMembership.deleted') { // Extract organization ID and user ID from payload const { organization, public_user_data } = evt.data const orgId = organization.id const userId = public_user_data.user_id // Remove from team_members table await db.team_members.delete({ where: { orgId, userId }, }) // Remove workspace record await db.workspaces.deleteMany({ where: { orgId, userId }, }) } // Return 200 status on success return new Response('OK', { status: 200 }) } ``` ## Other Frameworks For Express, Astro, Fastify, Nuxt, React Router, and TanStack Start, use the framework-specific `verifyWebhook` adapter. Each Clerk SDK package ships its own (`@clerk/express/webhooks`, `@clerk/astro/webhooks`, `@clerk/fastify/webhooks`, etc.). See `references/frameworks.md` for full handler examples per framework. ## Type Narrowing for `evt.data` `verifyWebhook` returns `WebhookEvent`, a discriminated union of all event types. Narrow with `evt.type` to get type-safe access to `evt.data`: ```typescript const evt = await verifyWebhook(req) if (evt.type === 'user.created') { // evt.data is now UserJSON, autocompletes id, email_addresses, etc. console.log(evt.data.id) } ``` For manual typing of nested payloads, import the JSON types from your framework's webhook subpath: `DeletedObjectJSON`, `EmailJSON`, `OrganizationInvitationJSON`, `OrganizationJSON`, `OrganizationMembershipJSON`, `SessionJSON`, `SMSMessageJSON`, `UserJSON`. ## Payload Field Reference ### User events (`user.created`, `user.updated`, `user.deleted`) ```typescript const { id, // Clerk user ID email_addresses, // array; [0].email_address is primary email first_name, last_name, image_url, public_metadata, } = evt.data ``` ### Organization events (`organization.created`, `organization.updated`, `organization.deleted`) ```typescript const { id, // org ID name, // org name slug, } = evt.data ``` ### Organization Membership events (`organizationMembership.created`, `organizationMembership.updated`, `organizationMembership.deleted`) ```typescript const { organization, // { id, name, ... } public_user_data, // { user_id, first_name, last_name, ... } role, // e.g. 'org:admin', 'org:member' } = evt.data // Access: organization.id, public_user_data.user_id, role ``` ## Supported Events (Full Catalog) **User**: `user.created` `user.updated` `user.deleted` **Session**: `session.created` `session.ended` `session.removed` `session.revoked` **Organization**: `organization.created` `organization.updated` `organization.deleted` **Organization Membership**: `organizationMembership.created` `organizationMembership.updated` `organizationMembership.deleted` **Organization Domain**: `organizationDomain.created` `organizationDomain.updated` `organizationDomain.deleted` **Organization Invitation**: `organizationInvitation.accepted` `organizationInvitation.created` `organizationInvitation.revoked` **Communication**: `email.created` `sms.created` **Waitlist**: `waitlistEntry.created` `waitlistEntry.updated` **Permission**: `permission.created` `permission.updated` `permission.deleted` **Role**: `role.created` `role.updated` `role.deleted` **Subscription**: `subscription.created` `subscription.updated` `subscription.active` `subscription.pastDue` **Subscription Item**: `subscriptionItem.created` `subscriptionItem.active` `subscriptionItem.updated` `subscriptionItem.canceled` `subscriptionItem.upcoming` `subscriptionItem.ended` `subscriptionItem.abandoned` `subscriptionItem.incomplete` `subscriptionItem.pastDue` `subscriptionItem.freeTrialEnding` **Payment**: `paymentAttempt.created` `paymentAttempt.updated` ## Webhook Reliability **Retries**: Svix retries failed webhooks on a set schedule (see [Svix Retry Schedule](https://docs.svix.com/retries)). Return 2xx to succeed, 4xx/5xx to retry. Use the `svix-id` header as an idempotency key to deduplicate retried events. **Replay**: Failed webhooks can be replayed from Dashboard. ## Common Pitfalls | Symptom | Cause | Fix | |---------|-------|-----| | Verification fails (Next.js) | Wrong import or usage | Use `@clerk/nextjs/webhooks`, pass `req` directly | | Verification fails (Express) | Using `express.json()` | Use `express.raw({ type: 'application/json' })` for webhook route | | Route not found (404) | Wrong path | Use `/api/webhooks` or preserve existing path | | 401 or 404 on every delivery | `auth.protect()` covers the webhook route (in middleware or in the handler) | Remove that check; `verifyWebhook()` is the only check the route needs | | No data in DB | Async job pending | Wait/check logs | | Duplicate entries | Only handling `user.created` | Also handle `user.updated` | | Timeouts | Handler too slow | Queue async work, return 200 first | ## Testing & Deployment **Local**: Use the Clerk CLI's first-party tunnel — no auth or linked project needed: ```sh clerk webhooks listen --token "$(clerk webhooks token)" --forward-to http://localhost:3000/api/webhooks ``` Add the printed relay URL (`https://webhooks.clerk.com/in/c_.../`) as a webhook endpoint in the Dashboard — events don't flow until you do. `svix-*` headers are preserved, so `verifyWebhook()` works against that endpoint's signing secret as usual. Flags, offline signature checks (`clerk webhooks verify`), and agent-mode behavior are in the `clerk-cli` skill. Without the CLI, tunnel `localhost:3000` yourself (`ngrok`, `localtunnel`, `Cloudflare Tunnel`) and add the public URL to the Dashboard endpoint. **Production**: Update webhook endpoint URL to production domain. Copy `CLERK_WEBHOOK_SIGNING_SECRET` to production env vars. ## References | Reference | Description | |-----------|-------------| | `references/frameworks.md` | Webhook handler examples for Express, Astro, Fastify, Nuxt, React Router, TanStack Start | ## See Also - `clerk-cli` - `clerk webhooks listen`/`verify` for local webhook testing - `clerk-setup` - Initial Clerk install - `clerk-orgs` - Org membership events - `clerk-billing` - Subscription, subscription item, and payment attempt events - `clerk-backend-api` - Sync via direct API calls

相关技能

Agent 工作流

Skill Creator

创造有效技能指南。 当用户想创造出新的技能(或更新现有的技能),以专业知识,工作流程,或工具集成来扩展克洛德的能力时,应该使用这种技能.

Agent 工作流

Clawdhub

使用ClawdHub CLI搜索,安装,更新并发布从taladhub.com的代理技能. 需要获取苍蝇上的新技能时使用,将安装的技能同步到最新版本或特定版本,或者发布 npm-instainddhub CLI 的新/更新的技能文件夹.

Agent 工作流

Agent Team Orchestration

管弦乐团多代理团队,任务设定周期,交接协议,审查工作流程. 使用时间: (1)建立2+特派员队伍,具有不同专业,(2)确定任务路线和生命周期(收录框_ spec_建设_审查_完成),(3)在特派员之间制定交接协议,(4)建立审查和质量关口,(5)管理特派员之间的交流和文物共享.

Agent 工作流

Superpowers

Spec-first,TDD,子代理驱动的软件开发工作流程. 当:(1)构建任何新功能或应用——触发脑暴_计划_子代理执行回路,(2)调试出一个bug或测试失败——触发系统性的根起过程,(3)用户说"让我们构建","帮助我计划","我想添加X",或"这个被打破",(4)完成一个功…