UTM parameter tracking via super properties is automatically handled by the JavaScript (Web) SDK, but for mobile SDKs (Android, iOS, React Native, Flutter, Unity), this is not done automatically — you'll need to implement it manually.
Here's how to approach it:
For Mobile Apps (Manual Implementation)
Since mobile SDKs don't auto-parse UTM parameters from URLs, you need to:
Extract UTM parameters from the deep link or campaign URL that brought the user to your app.
Register them as super properties so they're automatically included in all subsequent events.
Here are examples for each mobile SDK:
React Native
mixpanel.registerSuperProperties({
utm_source: 'google',
utm_medium: 'cpc',
utm_campaign: 'summer_sale',
// add other UTM params as needed
});[React Native super properties]
Android
JSONObject utmProps = new JSONObject();
utmProps.put("utm_source", "google");
utmProps.put("utm_medium", "cpc");
utmProps.put("utm_campaign", "summer_sale");
mixpanel.registerSuperProperties(utmProps);iOS (Swift)
Mixpanel.mainInstance().registerSuperProperties([
"utm_source": "google",
"utm_medium": "cpc",
"utm_campaign": "summer_sale"
])Flutter
mixpanel.registerSuperProperties({
'utm_source': 'google',
'utm_medium': 'cpc',
'utm_campaign': 'summer_sale',
});Key Notes
Use registerSuperPropertiesOnce() (available in all SDKs) if you want to capture first-touch UTM values without overwriting them on subsequent sessions. [React Native super properties]
Super properties on mobile are stored in local storage and persist across app launches until the app is uninstalled. [Android super properties]
The UTM values themselves must come from your app's deep link handling logic (e.g., from the URL that opened the app via a campaign link). [server-side best practices]
ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.
Yes, you can absolutely track page views and other events server-side without any Mixpanel client-side scripts, and you manage the distinct_id yourself. Here's how:
Tracking Events Server-Side
You can use Mixpanel's server-side SDKs (Python, Node.js, Java, Go, PHP) or the HTTP API directly — no browser scripts needed. [Choosing the Right Method]
Option 1: HTTP API (language-agnostic)
curl --request POST \
--url https://api.mixpanel.com/track \
--header 'Content-Type: application/json' \
--data '[{
"event": "page_view",
"properties": {
"token": "YOUR_PROJECT_TOKEN",
"distinct_id": "user-12345",
"time": 1740000000,
"$insert_id": "unique-dedup-key-abc123",
"page_name": "/pricing"
}
}]'Option 2: Python SDK
from mixpanel import Mixpanel
mp = Mixpanel('YOUR_PROJECT_TOKEN')
mp.track(user_id, 'page_view', {
'page_name': '/pricing',
'ip': request.remote_addr # forward client IP for geolocation
})Option 3: Node.js SDK
const Mixpanel = require('mixpanel');
const mp = Mixpanel.init('YOUR_PROJECT_TOKEN');
mp.track('page_view', {
distinct_id: 'user-12345',
page_name: '/pricing'
});Managing distinct_id Without the SDK
Since server-side SDKs don't auto-generate IDs, you manage them yourself: [Server-Side Identity]
Anonymous users: Generate a UUID and store it in a cookie. Use it as $device_id.
Authenticated users: Use your internal user ID as $user_id.
Linking them: Send both $device_id and $user_id on the same event after login — Mixpanel will merge the sessions automatically.
import uuid
def track_to_mp(request, event_name, properties):
properties["$device_id"] = request.cookies.get('SESSION_ID') # anonymous ID from cookie
if request.user.is_authenticated():
properties["$user_id"] = request.user.username # known user ID
mp.track("", event_name, properties) # leave distinct_id blank; Mixpanel resolves itPage View Best Practices
When tracking page views server-side: [Server-Side Best Practices]
Use a single event name (e.g., page_view) and differentiate pages via a property (e.g., page_name).
Fire the event only on successful responses to the client.
Parse headers for referrer and UTM parameters to capture marketing attribution.
Forward the client's IP address for correct geolocation (otherwise all events will appear to come from your server's location).
ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.
You can achieve this using the if or ifs function in a custom property formula. The idea is to return a single unified value regardless of which property (A or B) is set on the event.
Here's a simple formula using if with the defined function:
if(defined(A), A, B)This returns the value of property A if it exists, otherwise it returns property B. The resulting custom property will reflect whichever of the two is present, allowing you to filter on a single unified property while still being able to filter on A or B individually in other reports.
If both properties can be set simultaneously and you want to handle all cases explicitly, you can use ifs:
ifs(defined(A), A, defined(B), B, TRUE, "not set")This checks for A first, then B, and falls back to "not set" if neither is defined.
Once created, you can apply a filter on this new custom property (e.g., custom_property = "some_value") and it will match events where either A or B equals that value — without touching your Lexicon setup. [Custom Properties]
Note: Custom properties are local to a report by default. To reuse them across reports, click Save when creating the property (available on Growth and Enterprise plans). [Custom Properties]
ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.
This is a common discrepancy that can arise from how Mixpanel computes session duration versus page view duration. Here are the most likely reasons based on how Mixpanel sessions work:
Key Reasons for the Discrepancy
1. Sessions Reset at Midnight
Mixpanel resets all sessions at midnight (in your project timezone). This means a session spanning midnight is split into two separate sessions. If you're measuring page view duration across a full day without accounting for this reset, your calculation may differ from the session-based measurement. [Sessions Docs]
2. Non-User-Triggered Events Extending Sessions
If you haven't excluded backend or server-side events (e.g., push notifications, API events, membership payments) from your session settings, these events can restart the session timeout clock, artificially inflating session length. For example, if a user receives a notification every 25 minutes and your session timeout is 30 minutes, the session could last up to 24 hours. [Excluded Events]
3. How Session Duration Is Computed
Session duration is measured as the time between the Session Start and the last event before the timeout expires — not the time of the Session End event itself. If page view duration is being calculated differently (e.g., delta between first and last page view), the two metrics may not align. [Sessions Docs]
4. Bounce Sessions Skewing Averages
Sessions where users view only one page (bounces) have a session duration of near zero, but if you're summing page view durations without filtering these out, the aggregate can look different. Filtering for sessions longer than a threshold (e.g., 10 seconds) can help normalize this. [Use Cases]
Recommended Steps
Exclude non-user-triggered events from your Session Settings (Project Settings > Session Settings).
Use the Session Duration (Seconds) property on Session Start/End events for accurate session time measurement, rather than computing it manually from page views.
Filter out bounce sessions to get a more meaningful average.
If you share more details about how you're computing page view duration, I can help narrow down the cause further.
ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.
The knowledge sources don't directly address setting custom start dates per individual user for tracking purposes.
However, here's what the knowledge sources do cover that may be relevant to your use case:
Historical Profile Properties (via Warehouse Connectors)
If you want to track when certain user properties became active (e.g., a subscription start date), you can import historical profile data using Warehouse Connectors. This requires:
An Enterprise plan
A source table modeled as an SCD (Slowly Changing Dimensions) Type 2 table with a Start Time column
This allows Mixpanel to attribute the correct property value to each user at the right point in time. [Importing Historical Profile Values]
Custom Retention Brackets
If your goal is to measure retention starting from a specific user action (like sign-up), Mixpanel's Retention report uses the first event (e.g., "Sign Up") as each user's individual "birth" date. You can also use custom retention brackets to define flexible time windows (e.g., Days 1–7, Days 8–30) relative to that starting event. [Custom Retention Brackets]
Could you clarify what you're trying to achieve? That would help narrow down the best approach for your use case.
ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.
By default, Mixpanel's Session Replay masks most content for privacy — all text, inputs, images, and videos are masked (shown as **** or placeholder boxes). If you're seeing too many stars and want to reveal more content, here's how to customize it:
Web (JavaScript SDK)
Unmask Text
By default, all text is masked. You can selectively unmask specific elements using CSS selectors:
mixpanel.init('YOUR_PROJECT_TOKEN', {
record_sessions_percent: 100,
record_unmask_text_selector: ['.navigation', '.footer', '#public-content']
});Or, to show all text by default and only mask sensitive areas:
mixpanel.init('YOUR_PROJECT_TOKEN', {
record_sessions_percent: 100,
record_mask_all_text: false,
record_mask_text_selector: ['.user-data', '.account-info', '#billing-section']
});Unmask Inputs
By default, all inputs are masked. You can selectively unmask specific input fields:
mixpanel.init('YOUR_PROJECT_TOKEN', {
record_sessions_percent: 100,
record_unmask_input_selector: ['#search-box', '.quantity-input', '[data-public="true"]']
});⚠️ Note: Certain input types are always masked regardless of configuration and cannot be overridden: password, email, tel, hidden, and inputs with autocomplete attributes. [Always Masked Inputs]
You can also use the .mp-mask CSS class directly in your HTML to mask specific elements. [Privacy Controls]
Android SDK
On Android, all text, images, and WebViews are masked by default. You can mark specific views as safe (not masked) using mpReplaySensitive:
// Compose - mark as safe
Image(
painter = painterResource(id = R.drawable.family_photo),
contentDescription = "Family Photo",
modifier = Modifier.mpReplaySensitive(false)
)Note: EditText (input fields) cannot be unmasked. [Android Privacy]
iOS (Swift SDK)
Similarly, you can mark views as safe using mpReplaySensitive:
// SwiftUI
BackgroundImage()
.mpReplaySensitive(false)
// UIKit
let bgImage = BackgroundImage()
bgImage.mpReplaySensitive = falseUITextField inputs cannot be unmasked. [iOS Privacy]
In summary, the key is to use the unmask selectors (web) or mpReplaySensitive(false) (mobile) to reveal content you're comfortable showing, while keeping sensitive data protected.
ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.
That's correct, but with an important nuance — this limitation applies specifically to custom events appearing as intermediate steps (i.e., steps Mixpanel automatically surfaces in the flow), not to custom events you manually select as anchor steps.
Here's the breakdown from the docs:
As intermediate/automatic steps: Mixpanel only considers the 50 most frequently queried custom events in the project for automatic display in flows. If your custom event doesn't make that list, it won't appear automatically as an intermediate step. [Custom Events in Flows]
As manually selected anchor steps: You can still pick any custom event directly in the query builder as a start/end event, regardless of query frequency. [Custom Events in Flows]
So if your custom event is new and hasn't been queried much yet, it won't automatically surface as an intermediate step — but you can still manually add it as an anchor step in your Flows report.
ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.
The knowledge sources do not contain enough information to answer this specific question. They explain which custom events can appear in Flows (only shared custom events, and only the 50 most frequently queried ones) [Custom Events in Flows], but they do not specify a timeframe for how long after creating a custom event it will become available in the Hide Events section of the Flows report.
For a precise answer, I'd recommend reaching out to Mixpanel Support directly.
ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.
Based on the knowledge sources available, I can provide a partial answer, but there are some limitations to note.
Filtering in Flows
The Flows report does support global filters (applied to the entire report) and inline filters on steps you explicitly add to the query. [Flows Overview]
However, the knowledge sources do not explicitly describe the ability to apply per-step conditional filtering on intermediate/subsequent steps in Flows the way you're describing (i.e., "show conversion to Event A where property B = X OR Event C where property D = Y").
A Potential Workaround: Custom Events
The closest supported approach would be to use Custom Events. You can combine "Event A filtered by property B" and "Event C filtered by property D" into a single custom event, which could then be used as a step in your Flows report. [Custom Events]
Specifically:
Create a custom event that includes "Event A" (filtered by property B) and "Event C" (filtered by property D).
Use that custom event as an anchor step in your Flows report.
This would effectively let you track conversion to either of those two conditions as a single step.
Recommendation
If you need the granularity of seeing Event A with property B and Event C with property D as separate paths in a single report, the Funnels report (rather than Flows) may be better suited, as it supports inline filtering per step and comparison events. [Funnels Advanced]
The knowledge sources do not contain enough detail to confirm whether Flows natively supports the exact per-step OR filtering logic you're describing beyond what's outlined above.
ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.
