> ## Documentation Index
> Fetch the complete documentation index at: https://docs.searchcompany.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Clerk Webhook

> Handle Clerk webhook events to automatically sync organization member data

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.

<Note>
  **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.
</Note>

## Headers

<ParamField header="svix-id" type="string" required>
  Svix message ID for webhook verification
</ParamField>

<ParamField header="svix-timestamp" type="string" required>
  Svix timestamp for webhook verification
</ParamField>

<ParamField header="svix-signature" type="string" required>
  Svix signature for verifying the request authenticity
</ParamField>

## 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

<ResponseField name="status" type="string">
  `"success"` when the webhook was processed successfully
</ResponseField>

<ResponseField name="event_type" type="string">
  The type of event that was processed
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  # 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"
      }
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "status": "success",
    "event_type": "organizationMembership.created"
  }
  ```
</ResponseExample>

## Errors

| Status | Description                                       |
| ------ | ------------------------------------------------- |
| 400    | Missing webhook signature headers or invalid JSON |
| 401    | Invalid 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](https://dashboard.clerk.com/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

<Warning>
  **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.
</Warning>

## Environment Variables

| Variable               | Description                                    |
| ---------------------- | ---------------------------------------------- |
| `CLERK_SECRET_KEY`     | Clerk secret key for fetching member data      |
| `CLERK_WEBHOOK_SECRET` | Webhook signing secret from Clerk Dashboard    |
| `SUPABASE_DATA_URL`    | Supabase database URL for storing member cache |
| `SUPABASE_API_KEY`     | Supabase 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:

```sql theme={null}
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;
```
