Skip to main content
Understanding how the system tracks users is fundamental to getting value from your event data. This guide explains anonymous visitors, identified users, and the “magic” that connects them into one complete customer journey.

The Two Types of Users

Every person who interacts with your product exists in one of two states in Zixflow: anonymous or identified. Understanding these states is crucial because they determine what you can do with your data—from personalization to multi-channel messaging to conversion tracking. Think of it like this: an anonymous user is someone browsing your store with a shopping basket (we can see what they’re doing), while an identified user is someone with a loyalty card (we know who they are and can engage with them directly).

What is an Anonymous User?

An anonymous user is someone using your product before you know who they are. They haven’t signed up, logged in, or given you their email address. You don’t know their name, contact information, or anything personally identifiable about them. But here’s the powerful part: Even though you don’t know who they are, you can still track what they do.

How Anonymous Tracking Works

When someone first visits your website or opens your mobile app, the Zixflow SDK automatically generates a unique identifier called an anonymousId. This is a random string (UUID) that looks like abc-123-def-456. Where this ID is stored:
  • Web browsers: First-party cookie (or localStorage as fallback)
  • Mobile apps: Device keychain (iOS) or SharedPreferences (Android)
  • Server-side: Your session management system (you generate and pass it)
Important: This ID stays the same as long as the user is on the same device/browser. If they clear cookies or switch devices, they get a new anonymousId—until they identify themselves, at which point we can connect all their devices.

What You Can Do With Anonymous Users

Even without knowing who they are, you can:
  • Track their behavior: Every page view, button click, product view
  • Measure conversion funnels: See where anonymous visitors drop off
  • Show personalized content: “You recently viewed these items”
  • Retarget with ads: Connect anonymous IDs to ad platforms
  • Calculate true conversion rates: Include pre-signup behavior in your metrics
  • Send push notifications: If a device is registered before sign-up, you can send push notifications to it immediately — no sign-up required. The device is also automatically linked to the user’s profile when they identify later.
What you CANNOT do:
  • ❌ Send them emails (you don’t have their email)
  • ❌ See their activity across different devices (each device has its own anonymousId)
  • ❌ Connect their behavior to your CRM or internal systems

Real-World Anonymous User Journey

Example: Sarah discovers your product
Day 1, Monday 2:30 PM:
  - Sarah clicks a LinkedIn ad and lands on your homepage
  - SDK generates: anonymousId = "anon_abc-123-def"
  - Zixflow tracks:
    → Viewed Homepage (anonymousId: anon_abc-123-def)
    → Clicked "Pricing" (anonymousId: anon_abc-123-def)
    → Viewed Pricing Page (anonymousId: anon_abc-123-def)
  
Day 1, Monday 4:00 PM:
  - Sarah returns via a Google search
  - Same browser, same cookie, same anonymousId
  - Zixflow tracks:
    → Viewed Product "Red Sneakers" (anonymousId: anon_abc-123-def)
    → Added to Cart (anonymousId: anon_abc-123-def)
    → Started Checkout (anonymousId: anon_abc-123-def)
    → Abandoned (closed browser)
At this point, you have 6 events for anonymous user anon_abc-123-def. You can see what they did, when they did it, and that they abandoned their cart—but you can’t email them a reminder because you don’t know who they are.

What is an Identified User?

An identified user is someone you know. They’ve given you their email, created an account, logged in, or otherwise told you who they are. Now you have a way to reach them and can track their behavior across all their devices. When a user identifies themselves, Zixflow transitions them from anonymous tracking (using anonymousId) to identified tracking (using userId from your system).

How User Identification Works

You tell Zixflow who the user is by calling the identify() method, usually triggered by:
  • User signs up: Creates a new account
  • User logs in: Existing account authentication
  • Email capture: Newsletter signup, waitlist, lead form
  • OAuth/SSO: Google, Facebook, Apple, Microsoft login
  • Guest checkout: Enters email to complete purchase
Your code calls:
Zixflow.identify({
  userId: "user_12345",  // Your internal user ID
  traits: {
    email: "sarah@example.com",
    name: "Sarah Johnson",
    plan: "free"
  }
});
What happens next:
  1. Zixflow creates or updates a user profile with userId = "user_12345"
  2. All future events from this user are linked to their profile
  3. You can now message them across multiple channels

The Magic of userId

The userId is your identifier for this person—the primary key from your database, their email address, or any other stable, unique identifier in your system. This is how Zixflow connects to the rest of your infrastructure.

What You Can Do With Identified Users

Once you know who someone is, the possibilities expand dramatically:
  • Send multi-channel messages: Email, push notifications, SMS, in-app messages
  • Track across all devices: Same userId on iPhone, iPad, and web browser
  • Enrich with external data: Sync with your CRM, customer support, billing systems
  • Build rich user profiles: Combine behavior data with demographic and firmographic data
  • Personalize deeply: “Hi Sarah, your trial expires in 3 days”
  • Calculate customer lifetime value: Aggregate all purchases, subscriptions, and activities
  • Attribute marketing campaigns: Know which campaign brought them in originally

Real-World Identified User Journey

Example: Sarah creates an account
Day 2, Tuesday 10:00 AM:
  - Sarah returns to your site (still has cookie with anonymousId: anon_abc-123-def)
  - She's impressed and decides to sign up
  - Your app calls: identify(userId: "sarah@example.com", traits: {...})
  - ✨ IDENTITY MERGE HAPPENS ✨
  - Zixflow automatically links her 6 anonymous events to her new profile
  
  - Sarah's profile now shows:
    [Anonymous History - Now Merged]
    → Day 1: Viewed Homepage
    → Day 1: Viewed Pricing Page
    → Day 1: Viewed Product "Red Sneakers"
    → Day 1: Added to Cart
    → Day 1: Started Checkout
    → Day 1: Abandoned Cart
    
    [Identified Events - Going Forward]
    → Day 2: Signed Up (userId: sarah@example.com)
    
Future events (all with userId: sarah@example.com):
  → Day 2: Completed Profile
  → Day 3: Made First Purchase
  → Day 5: Opened Mobile App
  → Day 7: Invited Team Member
Now you can:
  • Email Sarah about her abandoned cart (you have her email)
  • Send her push notifications when she installs your app
  • Show her personalized recommendations based on her full history
  • Calculate her conversion path from first touch (LinkedIn ad) to signup
  • Track her across all devices once she logs in on each one

The Power of Identification

The transition from anonymous to identified is where Zixflow becomes most powerful. You get:
  1. Complete customer journey: See everything from their first visit to their latest purchase
  2. True conversion attribution: Know which marketing channel brought them in (even if they didn’t convert immediately)
  3. Cross-device continuity: Track them seamlessly across phone, tablet, and desktop
  4. Personalization at scale: Speak to thousands of users as individuals
  5. Predictive capabilities: Use their full behavioral history to predict churn, upsell opportunities, and lifetime value

Identity Merging: How It Works

Identity merging is what makes pre-signup attribution and funnel analysis possible. When someone signs up or logs in, Zixflow automatically connects all their previous anonymous activity to their identified profile.

When Merging Happens

You call identify with both IDs:
Zixflow.identify({
  userId: "user_12345",
  anonymousId: "anon_abc-123",  // ← Including both triggers the merge
  traits: { email: "user@example.com" }
});
What Zixflow does automatically:
  1. Finds all events from that anonymous visitor
  2. Upgrades the existing anonymous profile in-place — the user’s first_seen_at and created_at timestamps are preserved from when they first arrived, not from when they signed up
  3. Links all anonymous events to the identified profile
  4. Links all devices registered before sign-up to the user’s profile (enables push notifications immediately after identify)
  5. Makes everything searchable under their user ID
Result: The user’s profile now shows their complete journey — everything they did before and after signing up, with accurate first-touch timestamps.

Profile Timeline Preservation

When a user signs up after browsing anonymously, their profile retains the original timestamps from their first visit:
Anonymous browsing starts:  Monday 14:30  ← first_seen_at stays as Monday 14:30
Signs up / calls identify(): Tuesday 10:00 ← NOT overwritten
This means your “time to convert” metrics and “first seen” reports accurately reflect when a visitor first engaged, not when they finally created an account.

Visual Timeline Example

Timeline: Anonymous → Identified

Day 1 (Anonymous):
  [anonymousId: abc-123]
  14:30 → Viewed Homepage
  14:35 → Viewed Product "Red Sneakers"  
  14:37 → Added to Cart

Day 2 (Sign Up + Merge):
  10:00 → Signed Up

     [You call identify() with both userId + anonymousId]

     ✨ MERGE HAPPENS ✨

  [userId: sarah@example.com]
  
Final Profile (sarah@example.com):
  Events:
    ✓ Viewed Homepage (Day 1, 14:30)        ← merged from anonymous
    ✓ Viewed Product (Day 1, 14:35)         ← merged from anonymous
    ✓ Added to Cart (Day 1, 14:37)          ← merged from anonymous
    ✓ Signed Up (Day 2, 10:00)              ← identify event
    → Future events linked to sarah@example.com

Why Identity Merging Matters

1. Multi-Touch Attribution

Connect first-touch (anonymous) to conversion (identified):
Anonymous phase:
  Day 1: User clicks LinkedIn ad → Lands on homepage
  Day 2: Returns via Google (direct) → Views pricing
  Day 3: Clicks email campaign link → Starts trial

Identified phase:
  Day 3: Signs up → identify() merges history
  
Result: 
  First-touch attribution = LinkedIn ad
  Last-touch attribution = Email campaign
  Full journey = LinkedIn → Direct → Email → Conversion

2. Funnel Completion Analysis

Measure conversion rates including anonymous drop-offs:
Signup funnel:
  1. Viewed homepage (1000 anonymous visitors)
  2. Viewed pricing (450 continued)
  3. Started trial form (180 proceeded)
  4. Signed up (120 converted) ← Identity merge happens here
  
Conversion rate: 12% (120/1000)
Without merging, you’d only see step 4 (missing 88% of funnel context).

3. Behavioral Enrichment for Activation

Use pre-signup behavior to optimize onboarding:
Pre-signup signals:
  - Viewed "Enterprise Features" page 3x
  - Downloaded pricing PDF
  - Spent 8 minutes on case studies
  
Onboarding personalization:
  - Skip basic intro (they're already educated)
  - Highlight enterprise features in email sequence
  - Assign to enterprise sales rep (high intent)

User IDs: What to Use

When calling identify(), you need to provide a userId — your application’s unique identifier for this user.

Choosing Your User ID

Option 1: Database Primary Key (Recommended)
identify({ userId: "user_12345" })
  • ✅ Never changes (stable identifier)
  • ✅ Unique per user
  • ✅ Works even if user changes email
  • ❌ Not human-readable in reports
Option 2: Email Address
identify({ userId: "sarah@example.com" })
  • ✅ Human-readable
  • ✅ Unique (in most systems)
  • ❌ Can change if user updates email
  • ❌ Privacy concerns (visible in logs)
Option 3: Username
identify({ userId: "sarah_johnson" })
  • ✅ Human-readable
  • ❌ Can change
  • ❌ Not always unique

What NOT to Use

Session IDs
identify({ userId: "sess_abc123" })  // Changes every login!
  • Problem: User gets a new ID every session → Can’t track across sessions
Device IDs
identify({ userId: "device_abc123" })  // Different per device!
  • Problem: Same user on multiple devices → Looks like different people
Temporary IDs
identify({ userId: "temp_123" })  // What if they reset password?
  • Problem: ID might expire or be reassigned

Rule of Thumb

Use an identifier that:
  1. Never changes for the lifetime of the user
  2. Is unique across all users
  3. Exists in your system (so you can look up the user)

User Profiles

Once a user is identified, the system maintains a profile with all their data.

Profile Structure

Profile information stored:
{
  id: "prof_xyz789",                    // System-generated UUID
  workspace_id: "ws_123",               // Your workspace
  user_id: "sarah@example.com",         // Your user ID
  email: "sarah@example.com",           // Email if provided
  anonymous_id: "abc-123-def",          // Original anonymous ID
  
  // Custom attributes (you set these)
  attributes: {
    name: "Sarah Johnson",
    plan: "pro",
    company: "Acme Corp",
    signup_date: "2026-05-04",
    total_purchases: 3,
    lifetime_value: 267.99
  },
  
  // System-generated
  created_at: "2026-05-04T10:00:00Z",   // First seen
  updated_at: "2026-05-04T14:30:00Z",   // Last attribute update
  last_seen_at: "2026-05-04T14:30:00Z"  // Last event received
}

What Profiles Contain

Core identity fields:
  • user_id: Your application’s ID for this user
  • email: Email address (if provided in traits)
  • name: Name(if provided in traits)
  • phone: Phone number (if provided in traits)
  • anonymous_id: Original anonymous ID before identification
Custom attributes:
  • Anything you pass in traits during identify()
  • Anything you set via setProfileAttributes()
  • Examples: name, plan, company, preferences, computed values
System metadata:
  • created_at: When we first saw this user
  • updated_at: When profile was last modified
  • last_seen_at: Timestamp of most recent event
Associated data:
  • Events: All events linked to this user_id
  • Devices: All devices registered for push notifications

Multiple Devices Per User

One user can interact with your product across multiple devices. The system automatically links them all to the same profile.

Scenario: Sarah’s Devices

User: sarah@example.com

Devices:
  1. iPhone (iOS)
     - Token: apns_abc123...
     - Registered: 2 days ago
     - Last seen: 1 hour ago
     
  2. iPad (iOS)
     - Token: apns_def456...
     - Registered: 5 days ago
     - Last seen: 3 hours ago
     
  3. Chrome Browser (Web)
     - Token: web_ghi789...
     - Registered: 10 days ago
     - Last seen: 30 minutes ago
How it works:
  1. Sarah logs in on iPhone → identify() called with her userId
  2. Sarah logs in on iPad → Same userId, different device token
  3. Sarah logs in on web browser → Same userId, different device token
All devices linked to the same user (user_id = "sarah@example.com"). Benefits:
  • Send push notifications to ALL her devices (or target specific platforms)
  • Track cross-device behavior (started on mobile, finished on desktop)
  • Unified view of engagement across devices

Common User Scenarios

Scenario 1: First-Time Visitor

[No cookies, no account]

User visits site

SDK generates: anonymousId = "anon_123"

Events tracked as anonymous:
  - Viewed Homepage
  - Viewed Product
  - Viewed Pricing
System view: One anonymous user with 3 events.

Scenario 2: Returning Visitor (Not Logged In)

[Has cookie with anonymousId = "anon_123"]

User returns to site

SDK reads anonymousId from cookie

Events still tracked as same anonymous user:
  - Viewed Product B
  - Added to Cart
System view: Same anonymous user (anon_123) now has 5 total events.

Scenario 3: User Logs In

[Anonymous user anon_123 with 5 events]

User creates account / logs in

App calls: identify(userId: "sarah@example.com", anonymousId: "anon_123")

System merges:
  - All 5 anonymous events → linked to sarah@example.com
  - Upgrades the existing anonymous profile in-place (same profile ID, timestamps preserved)
  - Any devices registered before sign-up → linked to sarah@example.com
  - Future events tracked as identified user
System view: Identified user (sarah@example.com) with complete 5-event history. The profile’s first_seen_at reflects when the anonymous session started, not when sign-up happened.

Scenario 4: User Logs Out

[Identified user sarah@example.com]

User logs out

SDK generates NEW anonymousId = "anon_456"

New events tracked as new anonymous user:
  - Viewed Homepage
  - Viewed Different Product
  
[Until they log back in]
System view:
  • Identified profile: sarah@example.com (previous history intact)
  • New anonymous user: anon_456 (new browsing session)
Best practice: Call clearIdentify() on logout to ensure clean separation:
// When user logs out
Zixflow.clearIdentify();

Scenario 5: User Switches Devices

Day 1: Sarah on iPhone

SDK generates: anonymousId = "anon_iphone_123"

Events: Viewed Product, Added to Cart

Creates account: identify(userId: "sarah@example.com", anonymousId: "anon_iphone_123")

Day 2: Sarah on Desktop

SDK generates NEW: anonymousId = "anon_desktop_456"

Events: Viewed Cart

Logs in: identify(userId: "sarah@example.com", anonymousId: "anon_desktop_456")

System merges desktop anonymous history too!
System view: One profile (sarah@example.com) with events from both devices, all in chronological order.

User Lifecycle States

Beyond anonymous/identified, a user profile can be in one of these server-controlled states:

User Deleted

When User Deleted is triggered:
  • Profile is soft-deleted (is_deleted = true)
  • All linked devices are deactivated (push tokens disabled)
  • Existing event history is preserved for analytics
This is triggered server-side — typically via the Track API v1 DELETE /api/v1/customers/{id} endpoint or from your backend. Cannot be triggered from the client SDK.

User Suppressed / Unsuppressed

Suppression blocks all outbound messages for a user across every channel, regardless of their individual opt-out settings:
StateProfile flagEffect
User Suppressedis_suppressed = trueNo push, email, SMS, or any channel
User Unsuppressedis_suppressed = falseNormal message delivery resumes
Use suppression when: A user requests complete communication silence, or your system detects abuse/fraud. Suppression is distinct from channel opt-out: channel opt-out is per-channel and user-controlled; suppression is workspace-controlled and blocks everything simultaneously.

Device Registration for Anonymous Users

A common pattern — especially for mobile apps — is that a user registers their device for push notifications before they create an account or log in. Zixflow handles this as a first-class use case:
  1. Anonymous device registration: The SDK fires a Device Created or Updated event with the device’s push token and the user’s anonymousId. Zixflow creates an anonymous profile and links the device to it.
  2. Push notifications work immediately: The anonymous device is fully active. You can send push notifications to it right away — no sign-up required.
  3. User signs up / logs in: The app calls identify() with the userId and anonymousId.
  4. Automatic device migration: Zixflow finds all devices registered under that anonymousId and links them to the now-identified user profile. Push notifications continue working unchanged.
Timeline:
  Monday:  User opens app → device token registered (anonymous_id: anon_123)
           ← Push notifications can be sent to this device immediately
  Tuesday: User signs up → identify(userId: "sarah", anonymousId: "anon_123")
           ← Device migrated to sarah's profile automatically
           ← Push continues working — no re-registration needed
This is handled entirely by the backend — your app only needs to call identify() at sign-up.
Next: Learn how to register devices for push notifications → Devices & Push Notifications