Introducing Webhook Workflows with Inngest & Svix


We are excited to announce that Clerk has teamed up with Inngest and Svix to integrate with external systems reliably.

In this post we will dive into how the collaboration between Clerk, Inngest, and Svix enhances your authentication workflows using Clerk’s new Inngest webhook transformation template.

Building with Clerk Webhooks

Clerk webhooks (powered by Svix) allow you to receive event notifications from Clerk. When a user.created event is triggered, you may want to:

  • Sync user data to your database.
  • Kick off an account provisioning workflow.
  • Start a trial in Stripe.
  • Send a welcome email or start a drip campaign.
  • Add the user to your product newsletter in Mailchimp.

Today, with Clerk’s new Inngest webhook transformation template, you can easily use Clerk webhook events to trigger Inngest functions.

Inngest is a reliability layer for your application. Using it comes with a few key benefits such as managing concurrency, automatic retries, parallel execution, complex workflows, or task scheduling. This approach eliminates concerns about operating and scaling webhooks or error handling.

See it in Action

The code below features the inngest.createFunction method, which inserts a new user into the database. It will be triggered whenever a clerk/user.created event occurs.

const syncUser = inngest.createFunction(
  { id: 'sync-user-from-clerk' },
  { event: 'clerk/user.created' }, // ← This is the function's triggering event
  async ({ event }) => {
    const user = // The event payload's data will be the Clerk User json object
    const { id, first_name, last_name } = user
    const email = user.email_addresses.find((e) => === user.primary_email_address_id).email
    await database.users.insert({ id, email, first_name, last_name })

You can also have multiple functions react to the same event. The code below uses step.sleep to send a welcome email, then wait for three days, then follow up with a message offering a free trial:

const sendOnboardingEmails = inngest.createFunction(
  { id: 'onboarding-emails' },
  { event: 'clerk/user.created' },
  async ({ event, step }) => {
    // ← step is available in the handler's arguments
    const { user } =
    const { first_name } = user
    const email = user.email_addresses.find((e) => === user.primary_email_address_id).email

    await'welcome-email', async () => {
      await emails.sendWelcomeEmail({ email, first_name })

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

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

To learn how to integrate Inngest into your workflows, check out Clerk's official integration guide or refer to Inngest documentation.

Onward and Upward

As we continue to relentlessly improve the Developer Experience of our products, we are excited to see what developers build using the Inngest integration.

Domitrius Clark