Skip to main content
POST
https://searchcompany-main.up.railway.app
/
webhooks
/
clerk
# This is typically called by Clerk, not manually
curl -X POST "https://searchcompany-main.up.railway.app/webhooks/clerk" \
  -H "Content-Type: application/json" \
  -H "svix-id: msg_abc123" \
  -H "svix-timestamp: 1234567890" \
  -H "svix-signature: v1,abc123..." \
  -d '{
    "type": "organizationMembership.created",
    "data": {
      "id": "orgmem_abc123",
      "organization": {
        "id": "org_xyz789",
        "name": "Acme Corp"
      },
      "public_user_data": {
        "user_id": "user_123",
        "first_name": "John",
        "last_name": "Doe",
        "identifier": "john@example.com"
      },
      "role": "org:admin"
    }
  }'
{
  "status": "success",
  "event_type": "organizationMembership.created"
}
This endpoint receives webhook events from Clerk and automatically updates the cached organization member data in the database. This keeps member information up-to-date in real-time without needing to constantly query the Clerk API.
No authentication required - This is a public webhook endpoint. This endpoint is called by Clerk, not by your application directly. You must configure this URL in your Clerk Dashboard under Webhooks.

Headers

svix-id
string
required
Svix message ID for webhook verification
svix-timestamp
string
required
Svix timestamp for webhook verification
svix-signature
string
required
Svix signature for verifying the request authenticity

Request Body

The request body is a raw Clerk webhook event payload. The endpoint handles the following event types:

organization.created

Triggered when a new organization is created. Actions:
  • Creates organization record in database
  • Fetches and stores all organization members
  • Sets initial members_synced_at timestamp

organization.updated

Triggered when organization details are updated (e.g., name change). Actions:
  • Updates organization name in database
  • Refreshes member list to ensure accuracy

organizationMembership.created

Triggered when a member joins an organization. Actions:
  • Fetches fresh member list from Clerk API
  • Updates cached member data in database
  • Ensures new member appears immediately in member queries

organizationMembership.deleted

Triggered when a member leaves or is removed from an organization. Actions:
  • Fetches fresh member list from Clerk API
  • Updates cached member data in database
  • Removed member no longer appears in queries

organizationMembership.updated

Triggered when a member’s role or details change. Actions:
  • Fetches fresh member list from Clerk API
  • Updates cached member data with new role/details

organization.deleted

Triggered when an organization is deleted. Actions:
  • Clears member list (sets to empty array)
  • Keeps organization record for historical/audit purposes

Response

status
string
"success" when the webhook was processed successfully
event_type
string
The type of event that was processed
# This is typically called by Clerk, not manually
curl -X POST "https://searchcompany-main.up.railway.app/webhooks/clerk" \
  -H "Content-Type: application/json" \
  -H "svix-id: msg_abc123" \
  -H "svix-timestamp: 1234567890" \
  -H "svix-signature: v1,abc123..." \
  -d '{
    "type": "organizationMembership.created",
    "data": {
      "id": "orgmem_abc123",
      "organization": {
        "id": "org_xyz789",
        "name": "Acme Corp"
      },
      "public_user_data": {
        "user_id": "user_123",
        "first_name": "John",
        "last_name": "Doe",
        "identifier": "john@example.com"
      },
      "role": "org:admin"
    }
  }'
{
  "status": "success",
  "event_type": "organizationMembership.created"
}

Errors

StatusDescription
400Missing webhook signature headers or invalid JSON
401Invalid webhook signature

Benefits

This webhook integration provides:
  • Real-time Updates: Member changes reflected immediately
  • Reduced API Calls: Cached member data reduces Clerk API usage by ~90%
  • Better Performance: Member queries respond in ~10ms vs ~200ms
  • Automatic Sync: No manual refresh needed

Clerk Dashboard Configuration

To configure this webhook in Clerk:
  1. Go to Clerk Dashboard → Webhooks
  2. Click “Add Endpoint”
  3. Enter the endpoint URL: https://searchcompany-main.up.railway.app/webhooks/clerk
  4. Select events to listen for:
    • organization.created
    • organization.updated
    • organization.deleted
    • organizationMembership.created
    • organizationMembership.deleted
    • organizationMembership.updated
  5. Copy the signing secret and set it as CLERK_WEBHOOK_SECRET environment variable
Important: Make sure to select the organization and organizationMembership events, NOT the user events. User events fire for all users across your entire Clerk instance, not just organization members.

Environment Variables

VariableDescription
CLERK_SECRET_KEYClerk secret key for fetching member data
CLERK_WEBHOOK_SECRETWebhook signing secret from Clerk Dashboard
SUPABASE_DATA_URLSupabase database URL for storing member cache
SUPABASE_API_KEYSupabase API key

How Member Caching Works

When a member-related event is received:
  1. Webhook signature is verified for security
  2. Organization members are fetched from Clerk API
  3. Member data is stored in clerk_organization_details.members (JSONB column)
  4. members_synced_at timestamp is updated
  5. Subsequent calls to GET /organization/{org_id}/members serve cached data
  6. Cache is valid for 5 minutes, but webhooks keep it fresh
This means member lists load instantly (~10ms) while staying up-to-date in real-time.

Monitoring

Check the backend logs for webhook activity:
📥 Clerk webhook: organizationMembership.created
✅ Synced 5 members for org org_abc123
Query cache status in database:
SELECT
    business_uuid,
    business_name,
    jsonb_array_length(members) as member_count,
    members_synced_at
FROM clerk_organization_details
ORDER BY members_synced_at DESC;