Auth0 auth sync
Mirror your Auth0 users into the capydb_auth.users table in your own database, fed by an Auth0 Log Stream or Action webhook.
What it does
Same idea as the Clerk auth sync: CapyDB maintains a capydb_auth.users table inside your project database, kept current from Auth0 user lifecycle events, so user data is a JOIN instead of a Management API call. The table shape is identical across all auth-sync providers.
One difference worth stating up front: the Auth0 sync is webhook-only — there is no backfill from the Auth0 Management API. Existing users land in the table when their next user.created/user.updated event arrives. If you need them sooner, trigger updates (e.g. a bulk metadata touch) or insert them yourself once; the sync upserts over whatever is there.
Setup
-
Pick a shared secret (any high-entropy string;
openssl rand -hex 32is fine). -
Configure the integration with that secret:
curl -X PUT https://capydb.dev/api/capydb/v1/projects/{projectID}/integrations/auth0 \ -H "Authorization: Bearer capy_live_..." \ -H "Content-Type: application/json" \ -d '{"credentials": {"webhook_signing_secret": "<your shared secret>"}}'This creates the
capydb_authschema anduserstable in your database and stores the secret encrypted. The response'sconfig.webhook_receivertells you where to point Auth0:https://capydb.dev/api/capydb/v1/integrations/auth0/webhook/{projectID} -
Point Auth0 at that URL with the same secret as the Authorization value. Two ways to produce the events:
- Log Stream → Custom Webhook: set the payload to send user events, and set the "Authorization Token" to your shared secret. You will need a small transform (or an Action forwarding) so the body matches the expected shape below.
- Action (e.g. post-user-registration / post-login):
fetchthe receiver with anAuthorizationheader carrying the secret.
Verification mechanics
Authentication is the shared secret carried in the Authorization header, compared in constant time against the secret you configured. Auth0 custom webhooks send the configured token verbatim; a Bearer <secret> form is also accepted. Requests with a missing or mismatched header are rejected.
Expected payload
One event object, or a JSON array of them:
{
"type": "user.created",
"user": { "user_id": "auth0|abc123", "email": "a@example.com", "...": "..." }
}type(orevent— both keys are accepted) isuser.created,user.updated, oruser.deleted.useris the Auth0 user profile. For deletions, onlyuser.user_idis required.- Any other event type is acknowledged with a 2xx and ignored — you can stream broadly without erroring.
Normalization
The Auth0 profile maps onto the shared table like this:
capydb_auth.users column | From Auth0 |
|---|---|
id | user_id (required) |
email | email |
first_name / last_name | given_name / family_name; falls back to splitting name on whitespace (first token / remainder) |
username | nickname |
image_url | picture |
created_at / updated_at | created_at / updated_at (RFC 3339) |
last_sign_in_at | last_login |
deleted_at | set on user.deleted (soft delete; rows are kept) |
raw | the full Auth0 user object, verbatim JSONB |
Same ground rules as every auth sync: treat the table as read-only, filter with WHERE deleted_at IS NULL, and reach into raw for anything the typed columns do not cover.