Skip to content

Clerk and other auth providers

Whoever creates the accounts, your site identifies and tracks where it learns that an account exists. This guide says where, for your own site’s accounts and for a provider such as Clerk, Auth0 or Supabase, then the optional webhook and two pitfalls. It builds on Track conversions, which gives Astro.locals.routingcat.identify and .track.

Choose where to record the sign-up

  • Where your site creates its own record of the account, such as its user row after sign-up, in an endpoint: identify, then track there, as Track conversions shows.
  • With no such place, as with a provider alone: from your server, at the new user’s first signed-in page load. Nothing is set up at the provider, but a user who never loads a signed-in page is missed.
  • Also in the provider’s user-created webhook, if you want every sign-up: it needs its endpoint added in the provider’s dashboard. Both paths use the id signup:<user id>, so a sign-up that both record counts once.

Whichever you choose, identify at every sign-in too, so that the person’s other devices link to the account. The calls run in server code at each request, an endpoint or a page with export const prerender = false: a prerendered page renders at build, for no one in particular, and sends nothing.

Record the sign-up at the first signed-in page load

In a page rendered at each request that knows the signed-in user, or an endpoint your signed-in pages call, your server reads the user from the provider, with the time the provider created it:

  1. At each sign-in and signed-in page load, call await Astro.locals.routingcat.identify(user.id), which links the request’s visitor to the user.
  2. The first time it shows a user created in the last seven days, also call Astro.locals.routingcat.track('signup', …) with:
    • id: `signup:${user.id}`, so that each user counts once, however many pages and browsers record them;
    • accountId: the user’s id;
    • occurredAt: when the provider created the user, as a Date. A provider that gives Unix milliseconds, as Clerk does, needs new Date(ms), since RoutingCat reads a plain number as seconds.

A user created in the week before the install is recorded at their next signed-in page load, dated when they signed up. An older user signed up before RoutingCat, and records no sign-up. Read the user on the server, never from what the browser says, so that a browser can only report its own user. Remembering in the user’s session that the sign-up was recorded saves a call at each page load; the shared id keeps the count right without it.

Add the user-created webhook

Optional. A webhook carries no visitor cookie, so the sign-up passes the visitor id to the provider, in the metadata it lets a sign-up set: with Clerk, unsafeMetadata, the only metadata Clerk lets a sign-up set; with Supabase, signUp’s options.data. The sign-up page reads the id from the rc_vid cookie, on the server or in the browser, whose code can read it. OAuth sign-ups have no such field, so they rely on the first-load recording.

Receive the provider’s user-created event in an endpoint, with export const prerender = false, and verify each delivery with the provider’s signing secret. Then identify the new user with the visitor id the sign-up carried, await locals.routingcat.identify(user.id, { visitorId }), and track signup under the same id as the first-load recording, with that visitorId, the user’s id as accountId, and when the provider created the user as occurredAt.

Add the endpoint in the provider’s dashboard, and keep its signing secret in your site’s environment. On Vercel, RoutingCat’s middleware leaves /api paths alone, and on Workers its Worker hands them to Astro undecided, so a webhook under src/pages/api/ reaches your endpoint unchanged.

Avoid two pitfalls

  • Verification on another device. When email verification finishes on another device, identify at account creation, on the first device: that browser is the visitor who saw your pages.
  • Callbacks posted from another site, such as Sign in with Apple’s form_post, arrive without RoutingCat’s cookies, which are SameSite=Lax, so an identify there has no visitor id. Identify at the next page load instead. status reports such calls as identify without a visitor id.

Next