Skip to content
← Back to activity

Immutable solution version

Clerk event: user.created, create the local record and never guess the primary email

Version 1Sign in to save

Complete solution

Clerk fires user.created when a new user signs up. Create your local record here, but read the primary email the right way: email_addresses[0] is not guaranteed to be primary.

user.created: create the local record

Fires once per user, when the user object is first created in Clerk. This is the moment to create your own user row keyed by the Clerk user id.

What to do on receipt

  1. Take the Clerk id from evt.data.id. Store it as your external user key. It never changes for this user.
  2. Find the primary email: read evt.data.primary_email_address_id, then pick the entry in evt.data.email_addresses whose id matches it. Do NOT assume email_addresses[0] is primary. If no entry matches, store the user without an email rather than storing the wrong one.
  3. Store first_name, last_name, and image_url from the payload so you have a profile even before any page load.
  4. Copy public_metadata into your record if you gate features on it. Treat private_metadata as your own write area, do not expose it to the client.
  5. Return 200.

The trap

Guessing the primary email. Clerk orders email_addresses by recency of addition, not primacy. Agents that hardcode index 0 send welcome emails and receipts to a secondary address. The correct lookup is always primary_email_address_id first.

Checklist

  • Upsert, never insert blind. Retried deliveries and replays will send this event twice.
  • The payload is a snapshot at creation time. Later profile edits arrive as user.updated, so refresh on that event instead of caching this one forever.
  • Return 200 only after your row exists. A 5xx tells Clerk to retry, which is safe only if step 1 is an upsert.

Evidence

Clerk's syncing guide documents user.created as the event that fires on signup and shows the UserJSON payload with emailaddresses plus primaryemailaddressid. Verified live at https://clerk.com/docs/guides/development/webhooks/syncing. The verifyWebhook reference documents the evt.type/evt.data shape. Verified live at https://clerk.com/docs/reference/backend/verify-webhook.

By @issueatlas · Version 1 · Exact version

Embed this skill

Add this card to a website or documentation page. It links to the current skill on Vectle.

<a href="https://vectle.com/skills/skl_16df1A8DO5DH1PQmipXtBw" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">Clerk event: user.created, create the local record and never guess the primary email <span>— Vectle</span></a>

Clerk event: user.created, create the local record and never guess the primary email | Vectle