# Handling Clerk webhook events

![Clerk logo and graphic showing Clerk webhook events](/assets/docs/guides/clerk-webhook-events/featured-image.png)

Third party authentication providers like [Clerk](https://clerk.com/) are a fantastic way to add auth, user management, and security features to your application. They also provide drop-in components that can get your auth set up quickly. However, with an external source of truth for auth, you'll often need to:

* Sync data from Clerk with your database,
* Provision resources for new accounts, or
* Trigger other work from events (such as emails).

This page offers a guide on setting up a Clerk webhook with Inngest and using Clerk events within Inngest functions.

## Setting up the Clerk webhook

Clerk enables [sending events to a webhook endpoint](https://clerk.com/docs/integrations/webhooks/overview) when certain events occur. Inngest's [webhook endpoints](/docs/platform/webhooks) allow you to receive these events within your account just like [events that you send](/docs/events) from your own application.

To set up the Clerk webhook, open the Clerk dashboard and navigate to the 

Webhooks" page. Next, select the "Add Endpoint" button.

![The Webhooks page in the Clerk Dashboard. A red arrow points to the button for Add Endpoint.](/assets/docs/guides/clerk-webhook-events/webhook-page.webp)

On the next page, select the "Transformation" template tab and the Inngest template, then click on the "Connect to Inngest" button.

![The Webhooks page in the Clerk Dashboard showing the Inngest transformation template. Red arrows point to the Transformation Template tab, the Inngest template, and the Connect to Inngest button.](/assets/docs/guides/clerk-webhook-events/webhook-transformation-template.webp)

A popup window will appear to complete the setup. Select "Approve" to create the webhook.

![The Inngest permissions popup window showing the Approve button.](/assets/docs/guides/clerk-webhook-events/inngest-permissions-dialog.png)

After the popup window disappears, the Webhooks page will now display "Connected" with the webhook URL underneath. There is one more step to complete setup.

![The Webhooks page in the Clerk Dashboard showing a connected Inngest account. A red arrow points to the Connected button.](/assets/docs/guides/clerk-webhook-events/webhook-endpoint-connected.webp)

To complete the setup, scroll down and select "Create".

![The Webhooks page in the Clerk Dashboard showing the end of the page to create a new endpoint. A red arrow points to the Create button.](/assets/docs/guides/clerk-webhook-events/webhook-create.webp)

You'll be redirected to the new endpoint. In your Inngest dashboard, you will see a new webhook created in your account's [production environment](https://app.inngest.com/env/production/manage/webhooks).

#
Often, one key part of integrating with an auth provider like [Clerk](https://clerk.com/) is handling asynchronous updates with a webhook.

Suppose you need to write a function which will insert a new user into the database which will be triggered whenever `clerk/user.created` event occurs. You would use the `inngest.createFunction()` method, like in the example below:

```ts {{ filename: "src/inngest/sync-user.ts" }}
inngest.createFunction(
  { id: 'sync-user-from-clerk' },
  { event: 'clerk/user.created' },
  async ({ event }) => {
    // The event payload's data will be the Clerk User json object
    event.data;
    user;
    user.email_addresses.find(e =>
      e.id === user.primary_email_address_id
    ).email;
    await database.users.insert({ id, email, first_name, last_name });
  }
)
```

The `event` object contains all of the relevant data for the event. The `event.data` will match the `data` object from the standard Clerk webhook [payload structure](https://clerk.com/docs/integrations/webhooks/overview#payload-structure). With this `clerk/user.created` event, the `event.data` will be a Clerk User json object.

As you can see, you can choose which events you want to handle with each function. You might write a separate function for `clerk/user.updated` and `clerk/user.deleted` handling the entire lifecycle end to end.

Note that multiple functions can also listen to the same event. This pattern is called “[fan-out](/docs/guides/fan-out-jobs).”

## Creating a function to send a welcome email

Often, applications need to perform additional tasks when a new user is created, like send a welcome email with tips and useful information.

While it is possible to add this logic at the end of your sync function as seen in the [previous section](/docs/guides/clerk-webhook-events#creating-a-function-to-sync-a-new-user-to-a-database), it’s better to decouple unrelated tasks into different functions so issues with one task do not affect the other ones. For example, if your email fails to send, it should not affect starting a trial for that user in Stripe.

You can make use of the fact that with Inngest, each function has [automatic retries](/docs/functions/retries), so only the code that has issues is re-run.

The code below creates another function using the same `clerk/user.created` event and adds the logic to send the welcome email:

```ts {{ filename: "src/inngest/send-welcome-email.ts" }}
inngest.createFunction(
  { id: 'send-welcome-email' },
  { event: 'clerk/user.created' },
  async ({ event }) => {
    event.data;
    user;
    user.email_addresses.find(e =>
      e.id === user.primary_email_address_id
    ).email;
    await emails.sendWelcomeEmail({ email, first_name });
  }
)
```

Now, you have a function that utilizes the same Clerk webhook event for another purpose. Clerk webhook events can be used for all sorts of application lifecycle use cases. For example, adding users to a marketing email list, starting a Stripe trial, or provisioning new account resources.

### Sending a delayed follow-up email

To send a follow-up email, you can use the [`step.run()`](/docs/reference/functions/step-run). This method will encapsulate specific code that will be automatically retried ensuring that issues with one part of your function don't force the entire function to re-run. Additionally, you will extend the functionality with [`step.sleep()`](/docs/reference/functions/step-sleep).

The code below sends a welcome email, then uses `step.sleep()` to wait for three days before sending another email offering a free trial:

```ts {{ filename: "src/inngest/send-welcome-email.ts" }}
inngest.createFunction(
  { id: 'send-welcome-email' },
  { event: 'clerk/user.created' },
  async ({ event, step }) => {
    event.data;
    user;
    user.email_addresses.find(e =>
      e.id === user.primary_email_address_id
    ).email;

    // Wrapping each distinct task in step.run() ensures that each
    // will be retried automatically on error and will not be re-run
    await step.run('welcome-email', async () => {
      await emails.sendWelcomeEmail({ email, first_name })
    });

    // wait 3 days before second email
    await step.sleep('wait-3-days', '3 days');

    await step.run('trial-offer-email', async () => {
      await emails.sendTrialOfferEmail({ email, first_name })
    });
  }
)
```

## Next steps

To continue learning about how to get the most out of Clerk webhook events, check out the following:

* Platform guide: [Consuming webhooks](/docs/platform/webhooks)
* Guide: [Fan-out (one-to-many)](/docs/guides/fan-out-jobs)
* Guide: [Parallel steps](/docs/guides/step-parallelism)
* Reference: [`step.run()`](/docs/reference/functions/step-run)