My user architecture is like: ONE user account can have multiple profiles Onboarding/Signup - Done by a guardian account Engagement/Post Onboarding - Done by profiles is it wiser to have distinct ID as userID or profileID? Keep in the mind the onboarding goes like this:
onboarding, sign up, "aha" moment --> user account (no profile created yet)
profile creation (immediately after aha moment), post onboarding, all events after this --> profile account actions
This is a nuanced identity management question. Based on Mixpanel's documentation, here's how to think through it:
The Core Challenge
Your flow has two distinct phases:
Pre-profile: Guardian/user account performs onboarding, signup, and hits the "aha" moment
Post-profile: A profile (child of the user account) drives all subsequent engagement events
Recommendation: Use Profile ID as the $user_id, with careful identity linking
Here's the reasoning:
Why not User Account ID alone?
Mixpanel's distinct_id is designed to track a single user's journey. Since post-onboarding events are tied to a profile, using only the user account ID would mean all profile-level events would need to be sent under the parent account ID — losing the granularity of which profile did what. [Identity Management Overview]
Recommended approach: Profile ID as $user_id, Device ID bridges the gap
Use Mixpanel's ID Merge to stitch the pre-profile and post-profile journeys:
During onboarding (no profile yet): Events are tracked anonymously with a $device_id (auto-generated by the SDK or manually set). You can also include the user account ID as an event property for reference.
At profile creation (the "aha" moment transition): Call .identify(profileID). This links the anonymous $device_id (and all prior onboarding events) to the new profileID. [Identifying Users (Simplified)]
Post-onboarding: All events fire under profileID as the canonical distinct_id.
This mirrors the New User Signup flow in Mixpanel's docs — anonymous pre-signup events get retroactively linked to the identified user once .identify() is called.
What about the User Account ID?
Since one user account can have multiple profiles, track the user account ID as a user/event property (e.g., account_id) rather than as the distinct_id. This lets you:
Filter/segment by account across profiles
Avoid merging separate profiles into one identity cluster unintentionally
Mixpanel recommends using an ID that is unique to each user and does not change as the $user_id — a database-generated profile ID fits this well. [Identifying Users (Simplified)]
Key caveat
Note that you cannot merge two $user_ids in the Simplified API, so avoid using both user account ID and profile ID as $user_id at different points for the same event stream. [Identifying Users (Simplified)]
Summary
| Phase | distinct_id / tracking | User Account ID |
| - | - | - |
| Onboarding (pre-profile) | $device_id (anonymous) | Event property |
| Profile creation | Call .identify(profileID) | Event property |
| Post-onboarding | profileID (canonical) | User/event property |
ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.
the issue with this is during the entire onboarding process, there will be multiple IDs being created and assigned as distinct id from my understanding, mixpanel treats all of these as separate users even if they actually are the same user therefore the onboarding process' funnels and flows are unreliable kapa.ai
You're right to flag this concern, but this is exactly the problem that ID Merge is designed to solve. Let me clarify how it works:
How Mixpanel Handles Multiple Anonymous IDs
During onboarding, yes — multiple $device_ids may be generated (e.g., if the user switches devices or clears cookies). However, once .identify(profileID) is called, Mixpanel retroactively links all prior anonymous events from that $device_id to the identified user. [Identifying Users (Simplified)]
From the docs, the flow looks like this:
| Event | $device_id | $user_id | distinct_id (resolved by Mixpanel) |
| - | - | - | - |
| Onboarding event 1 | D1 | — | $device:D1 → ProfileID (retroactively updated) |
| Onboarding event 2 | D1 | — | $device:D1 → ProfileID (retroactively updated) |
| Profile created → .identify(ProfileID) | D1 | ProfileID | ProfileID |
So the funnel will correctly stitch pre- and post-identification events together under one user.
The Caveat: Propagation Delay
There is one important limitation to be aware of:
It may take up to 24 hours for the ID mapping to propagate to all parts of the system. In real-time funnel analysis, some users may appear as dropped-off even though they completed the conversion event. [Identifying Users (Simplified)]
So if you're checking funnels immediately after events occur, they may look broken — but this resolves within 24 hours.
The Real Risk: Multiple Devices During Onboarding
If a user starts onboarding on one device (D1) and continues on another (D2) before .identify() is called, those two anonymous sessions will remain separate until both devices call .identify(profileID). Once both do, all events from D1 and D2 are merged into the same identity cluster. [Identifying Users (Simplified)]
Bottom Line
Your onboarding funnels will be reliable as long as:
.identify(profileID) is called at profile creation on every device/platform
You allow up to 24 hours for mappings to propagate before drawing conclusions from real-time funnel data
ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.
is the better conclusion -- keep the distinct_id as user_id and send profile details as properties. so itll be like this for a user with 2 profiles event_a distinct_id: user_id property: profile_id1 event_a distinct_id: user_id property: profile_id2 kapa.ai
That's a valid alternative approach, and it does sidestep the identity complexity. Here's how to evaluate it honestly:
Using user_id as distinct_id with profile_id as a Property
What this solves:
One stable distinct_id per user account throughout the entire journey (onboarding → post-onboarding)
No identity stitching needed between pre-profile and post-profile phases
Simpler ID management overall
What this means in practice:
| Event | distinct_id | profile_id (property) |
| - | - | - |
| Onboarding event | user_id | (none yet) |
| Profile created | user_id | profile_id_1 |
| Post-onboarding event (profile 1) | user_id | profile_id_1 |
| Post-onboarding event (profile 2) | user_id | profile_id_2 |
The Trade-off to Consider
This approach works well if your primary unit of analysis is the user account. However, if you need to analyze behavior at the profile level (e.g., funnels or retention per profile), you'd be relying entirely on filtering by the profile_id property rather than having profiles as distinct users in Mixpanel.
Mixpanel's distinct_id is what determines uniqueness in reports — unique user counts, funnel conversion, retention, etc. [Identity Management Overview] If two profiles under the same user_id both perform event_a, Mixpanel will count that as one user doing event_a (not two), since they share the same distinct_id.
Which is Better for You?
Use user_id as distinct_id if your analytics focus is on the account-level journey (onboarding → activation → retention of the account)
Use profile_id as distinct_id if your analytics focus is on profile-level behavior (each profile's engagement, retention, funnel completion independently)
Given your architecture where post-onboarding engagement is driven by profiles, the right choice depends on whether you care more about account-level or profile-level metrics. Neither approach is universally "better" — it depends on your reporting needs.
ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.
