Backfill existing users
The recipes in this section handle users going forward. To bring in the base you had before Apex, you have two paths — pick based on how complete you need coverage to be.
Passive (default, zero effort)
Wire your identity trigger to the login / post-authentication event (not just signup):
- Cognito → PostAuthentication Lambda
- Auth0 → post-login Action (fires on every login)
- Firebase / Supabase → client
onAuthStateChanged/onAuthStateChange
Now every existing user reconciles automatically the next time they log in. It's safe: the server-side trigger carries the verified internal_user_id, not a shared-device guess. Within one login cycle your active base is covered, with zero migration work.
Active (full or dormant base)
When you need everyone in immediately — including dormant users who won't log in soon — push them in bulk.
CSV import
Upload a CSV of userId,email[,name,plan,avatar_url,...] to the import endpoint. Each row becomes an identified Contact keyed by internal_user_id; later events reconcile against it via the identifier index. Any canonical attribute column (e.g. avatar_url) is captured — include avatar_url to backfill end-user photos.
curl -X POST https://app.apex.inc/api/v1/contacts/import \
-H "x-api-key: apex_sk_..." \
-H "content-type: text/csv" \
--data-binary @users.csv
userId,email,name,plan,avatar_url
u_1001,jane@acme.co,Jane Doe,pro,https://cdn.acme.co/avatars/jane.png
u_1002,sam@globex.com,Sam Lee,free,
The endpoint reports { imported, reconciled, skipped, errors }. It's idempotent — re-importing the same userId updates the existing Contact rather than duplicating it.
Per-provider backfill scripts
If your base lives in your auth provider, page through it and POST in batches of 100 via the Server Events API. Reference scripts live in scripts/identity-backfill/:
| Provider | API used |
|---|---|
| Cognito | ListUsers (paginated) |
| Auth0 | Management API GET /api/v2/users |
| Firebase | Admin SDK listUsers() |
| Supabase | auth.admin.listUsers() |
Each script reads provider credentials from env, pages through every user, and emits a user_identified event carrying the provider's stable id as data.userId. Pass the provider's profile-photo field (picture / photoURL) as data.avatar_url to backfill end-user photos in the same pass.
Tip
Run the active backfill once, then leave the passive trigger in place. New users and re-engaging dormant users keep reconciling on their own — no second migration.