Clerk Integration
Receive and route Clerk webhooks for user lifecycle, authentication, organization, and session events.
Setup
1. Create a Source in Hookbase
Clerk uses Svix for webhook delivery, signing payloads with HMAC-SHA256.
curl -X POST https://api.hookbase.app/api/sources \
-H "Authorization: Bearer whr_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Clerk Production",
"slug": "clerk",
"verificationConfig": {
"type": "svix",
"secret": "whsec_..."
}
}'Save your webhook URL:
https://api.hookbase.app/ingest/{orgSlug}/clerk2. Configure Clerk Webhook
- Go to Clerk Dashboard → Webhooks
- Click Add Endpoint
- Enter your Hookbase ingest URL
- Select the events you want to subscribe to
- Click Create
- Copy the Signing Secret (starts with
whsec_) and update your Hookbase source
3. Create Routes
# Create destination for your user sync handler
curl -X POST https://api.hookbase.app/api/destinations \
-H "Authorization: Bearer whr_your_api_key" \
-d '{"name": "User Sync Service", "url": "https://api.myapp.com/webhooks/clerk"}'
# Create route
curl -X POST https://api.hookbase.app/api/routes \
-H "Authorization: Bearer whr_your_api_key" \
-d '{"name": "Clerk to User Sync", "sourceId": "src_...", "destinationIds": ["dst_..."]}'Signature Verification
Clerk uses Svix for webhook delivery. The signature is sent across three headers:
| Header | Description |
|---|---|
svix-id | Unique message ID |
svix-timestamp | Unix timestamp (seconds) |
svix-signature | v1,<base64-encoded-signature> |
Hookbase verifies Svix signatures automatically:
{
"verificationConfig": {
"type": "svix",
"secret": "whsec_..."
}
}Timestamp Tolerance
Svix signatures include a timestamp. Hookbase rejects webhooks older than 5 minutes by default to prevent replay attacks.
Common Events
user.created
Triggered when a new user signs up.
{
"type": "user.created",
"object": "event",
"data": {
"id": "user_2abc123def456",
"object": "user",
"first_name": "Jane",
"last_name": "Smith",
"email_addresses": [
{
"id": "idn_2abc123",
"email_address": "[email protected]",
"verification": {
"status": "verified",
"strategy": "email_code"
}
}
],
"primary_email_address_id": "idn_2abc123",
"username": "janesmith",
"profile_image_url": "https://img.clerk.com/...",
"created_at": 1709812800000,
"updated_at": 1709812800000,
"external_accounts": [],
"public_metadata": {},
"private_metadata": {},
"unsafe_metadata": {}
}
}user.updated
Triggered when a user profile is modified.
{
"type": "user.updated",
"object": "event",
"data": {
"id": "user_2abc123def456",
"object": "user",
"first_name": "Jane",
"last_name": "Doe",
"email_addresses": [
{
"id": "idn_2abc123",
"email_address": "[email protected]",
"verification": { "status": "verified" }
}
],
"primary_email_address_id": "idn_2abc123",
"public_metadata": {
"plan": "pro"
},
"updated_at": 1709899200000
}
}user.deleted
Triggered when a user account is deleted.
{
"type": "user.deleted",
"object": "event",
"data": {
"id": "user_2abc123def456",
"object": "user",
"deleted": true
}
}organization.created
Triggered when a new organization is created.
{
"type": "organization.created",
"object": "event",
"data": {
"id": "org_2xyz789ghi012",
"object": "organization",
"name": "Acme Corp",
"slug": "acme-corp",
"members_count": 1,
"max_allowed_memberships": 5,
"created_by": "user_2abc123def456",
"public_metadata": {},
"private_metadata": {},
"created_at": 1709812800000,
"updated_at": 1709812800000
}
}organizationMembership.created
Triggered when a user is added to an organization.
{
"type": "organizationMembership.created",
"object": "event",
"data": {
"id": "orgmem_2abc123",
"object": "organization_membership",
"role": "org:member",
"organization": {
"id": "org_2xyz789ghi012",
"name": "Acme Corp",
"slug": "acme-corp"
},
"public_user_data": {
"user_id": "user_2def456ghi789",
"first_name": "Bob",
"last_name": "Wilson",
"identifier": "[email protected]"
},
"created_at": 1709899200000,
"updated_at": 1709899200000
}
}session.created
Triggered when a user signs in and a new session is created.
{
"type": "session.created",
"object": "event",
"data": {
"id": "sess_2abc123",
"object": "session",
"user_id": "user_2abc123def456",
"status": "active",
"last_active_at": 1709812800000,
"expire_at": 1710417600000,
"created_at": 1709812800000,
"updated_at": 1709812800000
}
}Transform Examples
Sync New Users to Your Database
function transform(payload) {
const user = payload.data;
const primaryEmail = user.email_addresses.find(
e => e.id === user.primary_email_address_id
);
return {
external_id: user.id,
email: primaryEmail ? primaryEmail.email_address : null,
first_name: user.first_name,
last_name: user.last_name,
username: user.username,
avatar_url: user.profile_image_url,
metadata: user.public_metadata,
created_at: new Date(user.created_at).toISOString()
};
}Slack Alert for New Sign-Ups
function transform(payload) {
const user = payload.data;
const primaryEmail = user.email_addresses.find(
e => e.id === user.primary_email_address_id
);
return {
blocks: [
{
type: "header",
text: {
type: "plain_text",
text: "New User Sign-Up"
}
},
{
type: "section",
fields: [
{
type: "mrkdwn",
text: `*Name:*\n${user.first_name} ${user.last_name}`
},
{
type: "mrkdwn",
text: `*Email:*\n${primaryEmail ? primaryEmail.email_address : 'N/A'}`
},
{
type: "mrkdwn",
text: `*Username:*\n${user.username || 'N/A'}`
},
{
type: "mrkdwn",
text: `*Auth Method:*\n${user.external_accounts.length > 0 ? 'OAuth' : 'Email'}`
}
]
}
]
};
}User Deletion Cleanup
function transform(payload) {
return {
action: "delete_user",
clerk_user_id: payload.data.id,
deleted_at: new Date().toISOString(),
cleanup_tasks: [
"remove_from_database",
"cancel_subscriptions",
"delete_uploaded_files",
"send_confirmation_email"
]
};
}Filter Examples
User Events Only
{
"name": "User Events",
"logic": "and",
"conditions": [
{
"field": "type",
"operator": "starts_with",
"value": "user."
}
]
}Organization Events
{
"name": "Organization Events",
"logic": "or",
"conditions": [
{
"field": "type",
"operator": "starts_with",
"value": "organization."
},
{
"field": "type",
"operator": "starts_with",
"value": "organizationMembership."
}
]
}New Sign-Ups Only
{
"name": "New Sign-Ups",
"logic": "and",
"conditions": [
{
"field": "type",
"operator": "equals",
"value": "user.created"
}
]
}Specific Organization
{
"name": "Acme Corp Events",
"logic": "and",
"conditions": [
{
"field": "type",
"operator": "starts_with",
"value": "organizationMembership."
},
{
"field": "data.organization.slug",
"operator": "equals",
"value": "acme-corp"
}
]
}Headers
Clerk (Svix) webhooks include these headers:
| Header | Description |
|---|---|
svix-id | Unique message identifier |
svix-timestamp | Unix timestamp (seconds) |
svix-signature | v1,<base64-signature> |
Content-Type | application/json |
User-Agent | Svix-Webhooks |
Troubleshooting
Signature Verification Failed
- Verify you're using the webhook signing secret (starts with
whsec_) - Ensure verification type is set to
svix, nothmac - Check that the secret matches what's shown in the Clerk dashboard
- Note: Clerk rotates signing secrets when you regenerate them — update your Hookbase source immediately
Missing Events
- Check which events are selected in Clerk's webhook endpoint settings
- Verify filters aren't blocking expected events
- Check Clerk's webhook logs (Dashboard → Webhooks → your endpoint → Message Attempts)
User Data Incomplete
- Some fields are only populated based on your Clerk instance configuration
usernameis null unless you enable usernames in Clerk settingsexternal_accountsonly populated when users sign in via OAuthprivate_metadataandunsafe_metadataare not included in webhook payloads by default