> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mixpanel.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Identifying Users (Simplified)

<div className="idManagementBanner">
  <Warning>
    The information on this page is for projects on the **Simplified ID Merge API**. [Learn more here](/docs/tracking-methods/id-management/identifying-users-simplified#overview).
  </Warning>
</div>

## Overview

<Warning>
  **Check your project's ID management version**

  The information on this page is for projects using the **Simplified ID Merge API**. You can check your Identity Merge API by navigating to [your project settings](https://mixpanel.com/settings/project/id-management).

  For projects using Original ID Merge API, please refer to this documentation [here](/docs/tracking-methods/id-management/identifying-users-original).

  Learn more about the different ID Merge APIs [here](/docs/tracking-methods/id-management#identity-merge-apis).
</Warning>

Mixpanel supports stitching user behavior pre-login (eg: traffic from your website, docs, blog) and post-login (once the user has signed up). This helps answer questions like:

* What % of site visitors end up signing up?
* How much of my Purchase revenue can I attribute to a particular campaign?
* What is the conversion rate of reading a particular blog post -> signing up?

This system is called ID Merge. In this guide, we walk through how to identify users in projects using the **Simplified ID Merge API** and exactly how it works under the hood.

## Mechanism

The **Simplified ID Merge API** uses the `$device_id` and `$user_id` event properties to determine the `distinct_id` that is set for your events.

### distinct\_id

The `distinct_id` property (learn more [here](/docs/tracking-methods/id-management#distinct-id) ) is the identifier property that Mixpanel uses to determine the uniqueness of your users.

### \$device\_id

The `$device_id` event property is automatically generated by our client-side SDK and then attached to all of the user's events. All client-side events should contain a `$device_id`.

If an event contains a `$device_id` without a `$user_id`, the value of the `$device_id` will be set as the `distinct_id` for that event.

We can consider the `$device_id` as the anonymous ID for the user.

### \$user\_id

Calling `identify(<user_id>)` sets the `user_id` value passed to the identify function as the `$user_id` event property for the user's events moving forward.

If an event contains a `$user_id`, the value of the `$user_id` will be set as the `distinct_id` for that event.

**When a `$user_id` and `$device_id` are present in the same event for the first time, a mapping is created to merge the `$user_id` and `$device_id` values together, forming an identity cluster.** Any data sent with a `distinct_id` set to any of the values in an ID cluster will be attributed back to the same user in Mixpanel.

## Client-side Identity Management

If using our Web/Mobile SDKs or a CDP like Segment or Rudderstack, there are only 2 steps to identity management:

1. Call `.identify(<user_id>)` when a user signs up or logs in, passing in the user's known identifier (eg: their ID from your database).
2. Send at least one event after the `.identify()` call. This is necessary to get the `$user_id` and `$device_id` to merge. Learn more about [the merge mechanism above](/docs/tracking-methods/id-management/identifying-users-simplified#mechanism).
3. Call `.reset()` when a user logs out.

* Any events prior to calling `.identify()` are considered anonymous events. Mixpanel's SDKs will generate a `$device_id` to associate these events to the same anonymous user. By calling `.identify(<user_id>)` when a user signs up or logs in, you're telling Mixpanel that `$device_id` belongs to a known user with ID `user_id`.

* Under the hood, Mixpanel will stitch the event streams of those users together. This works even if a user has multiple anonymous sessions (eg: on desktop and mobile). As long as you always call `.identify` when the user logs in, all of that activity will be stitched together.

* Calling `.reset` will clear the local storage (which contains the `$user_id` and `$device_id`), and generate a new `$device_id` for the session. It is recommended to call `.reset` when a user logout or times out of an authenticated session to prevent the unintentional merging of multiple users sharing one device.

## Canonical Distinct ID

Once a user has been identified via the `.identify(user_id)` method, their `$device_id` prior to identification will be combined with the `$user_id` to form an identity cluster containing both IDs, as well as any other IDs previously merged with the `$user_id`. For projects using the Simplified ID Merge API, the canonical `distinct_id` is always set to the `$user_id`.

You can use any of the IDs in the cluster for ingestion, but only the canonical `distinct_id` can be used in queries and exports.

## Example User Flows

Let's walk through a few user flows where ID Merge is useful, and when to call `.identify()` and `.reset()` to use ID Merge properly.

### New User Signup

1. A user lands in your product on a new device and interacts with your product before signing up. Our SDK will assign the user a random `$device_id` and persist it. All events tracked at this point will send only a `$device_id`.

   | Event | \$device\_id | \$user\_id | distinct\_id (set by Mixpanel) | Notes |
   | ----- | ------------ | ---------- | ------------------------------ | ----- |
   | 1     | D1           |            | \$device:D1                    |       |
   | 2     | D1           |            | \$device:D1                    |       |

2. The user returns later and signs up for your product. You call `.identify(U1)`. All events sent after this point are tracked with both the original `$device_id` and the new `$user_id`. Mixpanel will retroactively set the `$user_id` on any prior events with the user’s `$device_id` so that both event streams are joined.

   | Event | \$device\_id | \$user\_id | distinct\_id (set by Mixpanel) | Notes                                              |
   | ----- | ------------ | ---------- | ------------------------------ | -------------------------------------------------- |
   | 1     | D1           |            | ~~*\$device:D1*~~ ⇒ **U1**     | Retroactively updated                              |
   | 2     | D1           |            | ~~*\$device:D1*~~ ⇒ **U1**     | Retroactively updated                              |
   | 3     | D1           | U1         | U1                             | Links D1 ⇒ U1, D1 and U1 are inside one ID cluster |

### Returning User

1. The user from the previous flow returns, but is on a new device and has not logged in yet.

   | Event | \$device\_id | \$user\_id | distinct\_id (set by Mixpanel) | Notes         |
   | ----- | ------------ | ---------- | ------------------------------ | ------------- |
   | 1     | D1           |            | ~~*\$device:D1*~~ ⇒ **U1**     |               |
   | 2     | D1           |            | ~~*\$device:D1*~~ ⇒ **U1**     |               |
   | 3     | D1           | U1         | U1                             | Links D1 ⇒ U1 |
   | 4     | D2           |            | \$device:D2                    | New device D2 |
   | 5     | D2           |            | \$device:D2                    |               |

2. The user logs in. Call `.identify(U1)` to tell us that the user on this device is the same `$user_id` we have seen before.

   | Event | \$device\_id | \$user\_id | distinct\_id (set by Mixpanel) | Notes                                                   |
   | ----- | ------------ | ---------- | ------------------------------ | ------------------------------------------------------- |
   | 1     | D1           |            | ~~*\$device:D1*~~ ⇒ **U1**     |                                                         |
   | 2     | D1           |            | ~~*\$device:D1*~~ ⇒ **U1**     |                                                         |
   | 3     | D1           | U1         | U1                             | Links D1 ⇒ U1                                           |
   | 4     | D2           |            | ~~*\$device:D2*~~ ⇒ **U1**     | Retroactively updated                                   |
   | 5     | D2           |            | ~~*\$device:D2*~~ ⇒ **U1**     | Retroactively updated                                   |
   | 6     | D2           | U1         | U1                             | Links D2 ⇒ U1, D1, D2, and U1 are inside one ID cluster |

### Multiple Users, One Device

1. A first user begins using a new device.

   | Event | \$device\_id | \$user\_id | distinct\_id (set by Mixpanel) | Notes |
   | ----- | ------------ | ---------- | ------------------------------ | ----- |
   | 1     | D1           |            | \$device:D1                    |       |

2. The user logs in. Call `.identify(U1)`, which links the `$device_id` to their `$user_id`.

   | Event | \$device\_id | \$user\_id | distinct\_id (set by Mixpanel) | Notes                                              |
   | ----- | ------------ | ---------- | ------------------------------ | -------------------------------------------------- |
   | 1     | D1           |            | ~~*\$device:D1*~~ ⇒ **U1**     | Retroactively updated                              |
   | 2     | D1           | U1         | U1                             | Links D1 ⇒ U1, D1 and U1 are inside one ID cluster |

3. The user logs out. At this point, you should call `.reset()`, or manually generate a new `$device_id` if you are managing it yourself. A new user shows up and tracks events using this new `$device_id`.

   | Event | \$device\_id | \$user\_id | distinct\_id (set by Mixpanel) | Notes                      |
   | ----- | ------------ | ---------- | ------------------------------ | -------------------------- |
   | 1     | D1           |            | ~~*\$device:D1*~~ ⇒ **U1**     |                            |
   | 2     | D1           | U1         | U1                             | Links D1 ⇒ U1              |
   | 3     | D2           |            | \$device:D2                    | Reset generated new ID: D2 |
   | 4     | D2           |            | \$device:D2                    |                            |

4. This new user (U2) now logs in. Call `.identify(U2)`.

   | Event | \$device\_id | \$user\_id | distinct\_id (set by Mixpanel) | Notes                                                   |
   | ----- | ------------ | ---------- | ------------------------------ | ------------------------------------------------------- |
   | 1     | D1           |            | ~~*\$device:D1*~~ ⇒ **U1**     |                                                         |
   | 2     | D1           | U1         | U1                             | Links D1 ⇒ U1                                           |
   | 3     | D2           |            | ~~*\$device:D2*~~ ⇒ **U2**     | Retroactively updated                                   |
   | 4     | D2           |            | ~~*\$device:D2*~~ ⇒ **U2**     | Retroactively updated                                   |
   | 5     | D2           | U2         | U2                             | Links D2 ⇒ U2, D1, D2, and U1 are inside one ID cluster |

## Server-side Identity Management

Our server libraries normally require that you specify the `distinct_id` value for each event. If you don't know the user's identity at the time the event is tracked, then they're an anonymous user.

When using our Web or Mobile SDKs, Mixpanel will automatically generate an `$device_id` that's local to that user's device, and set that value as the `distinct_id` for the event. This ID will persist on all events tracked by that user on that device, until you call `identify()` or `reset()`.

If you're tracking from servers, you'll need to generate and manage the IDs yourself. We recommend not setting the `distinct_id` directly; instead you should set the `$device_id` and `$user_id` and have our API determine the `distinct_id` for you.

### Step 1 - Generate an Anonymous ID

The key is to have an ID that is unique to each user and persists during that user's session. We recommend generating a UUID and storing that value in a cookie. All common server frameworks provide a simple way to set and retrieve cookies per request.

### Step 2 - Leverage Anonymous ID for anonymous events

Set the `$device_id` event property to the anonymous ID you generated. You do not need to set the `distinct_id` property since it will be assumed to `$device_id` if there is no `$user_id` present in the event.

### Step 3 - Set the Authenticated ID once users log in

Once the user logs in, you know their true ID, you should leverage the new ID for the user.

Set the `$user_id` property to the Authenticated ID and continue setting `$device_id` to the Anonymous ID generated in step 1, including both the `$user_id` and `$device_id` in your events moving forward.

If Mixpanel receives an event with both `$device_id` and `$user_id` set, it will merge the two IDs together. This is essential to track pre-login and post-login behavior accurately. The `distinct_id` will be assumed to the `$user_id`.

If you choose to manually define the `distinct_id` property, it should be the same value as the `$user_id`.

#### Example Python

Here's a pseudocode example using Django's cookies and authentication. It assumes the client is setting and sending cookies:

```python theme={"system"}
import uuid

def track_to_mp(request, event_name, properties):
  # This assumes you've previously set a cookie called "SESSION_ID" that is local to the user's session
  # Set `$device_id` to that cookie's value
  properties["$device_id"] = request.cookies.get('SESSION_ID')

  # Set $user_id if the user is authenticated (logged in).
  if request.user.is_authenticated():
    properties["$user_id"] = request.user.username

  # Note: leave the first argument blank here, since we're passing $device_id and $user_id as properties.
  mp.track("", event_name, properties)
```

## Best Practices

**Call `.identify` upon a registration/login or when an app is re-opened in a logged-in state**

By calling `.identify()` at these specific points in user journeys, you would be able to link the pre and post-login events to the same user on Mixpanel. Besides, calling `.identify` when the users re-open the app in a logged-in state ensures that all events in the session are tracked with the user's identifier such as user id.

**Call `.reset` upon logout or when an app leaves an authenticated-state**

By calling `.reset()` at logout or when a user enters an unauthenticated-state (such as timing out), you generate a new `$device_id` for your user, ensuring that multiple users that are sharing a single device would not be incorrect considered as a single user tracking events with the same IDs.

**Track the unique identifier as a super property and user property to assist in troubleshooting**

You can track the user's unique identifier as a super property via `.register()` and user property via `.people.set()` as soon as it is available in the app i.e. on a successful sign-up / login or when an app is re-opened in a logged in state. In the cases when ID Merge is not implemented properly, you can rely on these properties for troubleshooting purposes.

See the SDK reference on [registering super properties](/docs/tracking-methods/sdks/javascript#super-properties) and [setting profile properties](/docs/tracking-methods/sdks/javascript#setting-profile-properties).

**Avoid creating profiles for anonymous users**

Avoid creating profiles for anonymous users. If possible, cache user profile properties update in cookie or local storage and only send them to Mixpanel after the user is identified (ie logged-in state).

**QA your ID management implementation during the development phase**

Here are a few things to look out for:

* Ensure that cross-platform, pre and post-registration events are linked to the same user on Mixpanel.
* Ensure that no duplicate profiles are created as the users go through the onboarding, registration, login, and cross-platform user journey.
* Ensure that all the user’s identifiers are stored in the same Identity Cluster and that all their events are displayed on a single profile on Mixpanel.

**Keep a record of your ID management implementation**

We encourage you to document your implementation (or create a diagram of the implementation). This will come in handy when you need to re-implement this on a new platform or troubleshoot ID management issue.

## FAQ

**What does Mixpanel recommend using as the `$user_id`?**

We recommend using an ID that is unique to each user and does not change, for example, a database ID. While using an identifier like email may be more convenient, keep in mind that you cannot merge 2 `$user_ids` or change a `$user_id`, so if the user changes their email, they will count as a separate user.

**How long does it take for the `$device_id` -> `$user_id` mapping to take effect?**

For debugging purposes, the Activity Feed view of a single user is updated in real-time (less than 1 minute delay). You can get to the Activity Feed by navigating to [Users](/docs/users) and selecting a given user.

It may take up to 24 hours for this mapping to propagate to all other parts of the system. This means that, in some cases, when analyzing a funnel that spans pre-login and post-login behavior in real-time, some may be shown as dropped-off, even though they've performed the conversion event.

**How does this relate to User Profiles?**

[User Profiles](/docs/data-structure/user-profiles) are set directly on `$distinct_id`s, not on `$user_id`s or `$device_id`s. We recommend waiting until after a user is identified before setting user profile properties.

It is possible to set user profile properties for unidentified users by sending the profile updates to `$distinct_id=$device:<device-id>`. However, user profile properties are not preserved when `$device_id`s are linked to `$user_id`s, so any properties set before the IDs became linked will need to be set again using `$distinct_id=<user-id>` once the user is identified.

**Is it possible to merge two `$user_id`s?**

It is not possible to merge 2 `$user_id`s together using the Simplified API. We don't recommend doing this in general, as it adds complexity to your identity resolution strategy. Instead, we recommend having a single, unchanging `$user_id` for each user and pointing all other IDs for that user to that single `$user_id`.

**How should I link identified IDs from 3rd-party systems?**

Attribution providers (like Appsflyer, Adjust, and Branch) use Mixpanel's SDK properly to set `$device_id` to whichever ID they use for attribution.

For cohort syncs out to 3rd-party systems, we recommend designating a user property with the identifier of the user in that third-party system. More details are in our [integrations docs](/docs/cohort-sync/integrations); for example, see our [doc on exporting cohorts to Braze](/docs/cohort-sync/integrations/braze#matching-mixpanel-and-braze-users). If those integrations are bidirectional (eg: they send events back to Mixpanel), it's best to ensure that the user ID in both Mixpanel and the 3rd-party system is the same so that those events are sent to the correct user.

**What is the status of Mixpanel's legacy `alias` method?**

Prior to March 2020, the only way to connect users together was the `.alias()` method. This was very limited and was not retroactive; this meant that if a user used two devices and then logged in, you would lose activity for the user from one of the devices.

If you set up Mixpanel prior to 2020, you may have implemented using the `.alias()` method. Alias is still supported in its original state and we have preserved its documentation [here](https://github.com/mixpanel/docs/blob/main/legacy/aliases.md), but if you want to revisit your identity management strategy, we recommend setting up a new Mixpanel project and using the best practices outlined in this guide.
