Creating a trusted single view of the customer means recognizing the same person across devices, sessions, and channels, and deciding which values “win” when profiles collide. NotifyVisitors uses deterministic rules based on first-party identifiers (UserID, Email, Mobile) with full auditability. This aligns with industry guidance on identity resolution and “anonymous → known” stitching.
User Identification (anonymous → known)
Most visitors begin anonymous. Our SDK assigns an anonymous ID (nv_uid) and tracks events under it. When a visitor signs up or logs in, we create/attach durable identifiers—UserID and/or Email/Mobile—and link them to the same person. We also retroactively attach pre-signup events so analytics and messaging operate on a complete history.
Key behaviors
- Anonymous session → SDK issues nv_uid and (optionally) a platform device_id.
- Login/Signup → We associate provided UserID, Email, and/or Mobile to the same person.
- Backfill → All pre-signup events tied to nv_uid are moved under the now-known person, preserving event occurrence time.
Deterministic Identity Graph
The identity graph represents “who is who” across devices and identifiers. It’s a deterministic graph built only from first-party identifiers.
- Person: the canonical customer record (one per real person).
- Identifier(node that can point to a person). Types:
- user_id
- email (normalized, case-folded)
- mobile (E.164 normalized)
- nv_uid (NotifyVisitors anonymous ID)
- device_id (e.g., mobile/web device identifier; not a primary identifier)
Example (transitive collapse)
User A has alice@example.com; User B uses the same email and mobile +1532661; User C uses the same mobile. Because B links to both A and C, deterministic resolution collapses A, B, and C into one Person, updates the identity graph, and reorders that person’s event history by event time.
Merge Policy (what merges, what blocks)
We merge profiles when we have a deterministic signal that two records represent the same person, while preventing merges when core identifiers conflict.
Primary identifiers: UserID, Email, Mobile.
Rules
- Positive match: We merge profiles when a primary identifier matches (same UserID OR same Email OR same Mobile) and there is no conflicting, different, non-null UserID on both sides.
- Conflict guard: If both profiles have different, unique UserIDs, we do not merge them, even if Email/Mobile match. (This prevents account-takeover or household collisions.)
- Growth path for anonymous/partial profiles: Profiles without a primary identifier will be created and later merged once a shared primary identifier appears.
- Graph update: After every merge, we update the identity graph and rebuild a single, time-ordered event chronology.
Merging Scenarios (authoritative)
These five scenarios illustrate how profiles merge across devices and identifiers.
Scenario 1 — Web → Email → Mobile App (all linked properly)
User | UserID | Mobile | DeviceID | |
---|---|---|---|---|
A | U123 | null | null | DWeb01 |
A | U123 | alice@example.com | null | DWeb01 |
B | null | null | +15551234567 | DApp01 |
B | U123 | alice@example.com | +15551234567 | DApp01 |
Flow
- Visit on web (DWeb01): The user lands on your website for the first time. NotifyVisitors issues an anonymous ID and, when they log in or continue, assigns UserID = U123 to this browser/device.
- Adds email (same session): The user signs up or updates their profile with alice@example.com. Now, the web profile for U123 has a confirmed Email.
- Opens the mobile app (DApp01): Later, the user launches your app on a different device and signs in using mobile only (+1 555 123 4567). Because we don’t yet see a matching primary identifier, we create an anonymous app profile (B) to safely capture activity.
- Confirms identity (same app session): The user then provides UserID U123 (e.g., via SSO) and/or the same Email. That’s the deterministic signal we need—UserID matches A—so we link the app activity to the existing web person.
- What we unify: identifiers (UserID, Email, Mobile), devices (DWeb01, DApp01), and all events (re-ordered by event time). We also write an audit log of the merge.
Merge Result
Final profile A → UserID=U123, Email=alice@example.com, Mobile=+15551234567, DeviceIDs=[DWeb01, DApp01]
Scenario 2 — Mobile First → Adds Email → Gets New UserID
User | UserID | Mobile | DeviceID | |
---|---|---|---|---|
A | null | null | +15559876543 | DApp02 |
A | null | bob@example.com | +15559876543 | DApp02 |
B | U456 | null | null | DWeb02 |
B | U456 | bob@example.com | null | DWeb02 |
Flow
- Start on mobile (DApp02): The user begins in your app with only a phone number (+1 555 987 6543). We create an anonymous profile (A) to capture events and attributes until we have a firm match.
- Adds email: The user updates their app profile with bob@example.com. Now A has primary identifiers (Email and phone) that can later match other sessions.
- Later on web (DWeb02): The user visits your website and is assigned a new UserID = U456. This creates profile B. User B later on adds email bob@example.com
- Deterministic link via email: Because bob@example.com exists on both A and B—and A doesn’t have a conflicting UserID—NotifyVisitors merges A into B.
- What we unify: We keep U456 as the canonical UserID and attach bob@example.com + +1 555 987 6543. Devices (DApp02, DWeb02) and timelines are unified, and we record a detailed audit log.
Merge Result
Final profile B → UserID=U456, Email=bob@example.com, Mobile=+15559876543, DeviceIDs=[DApp02, DWeb02]
Scenario 3 — Different Primary Identifiers (cannot merge)
User | UserID | Mobile | DeviceID | |
---|---|---|---|---|
A | U111 | alice@example.com | null | DWeb03 |
B | U222 | alice@example.com | +15559876543 | DApp03 |
Flow
- Web profile (A): On the website, the user has UserID U111 and alice@example.com.
- App profile (B): On the app, the user signs in and ends up with UserID U222, the same email (alice@example.com), and +1 555 987 6543.
- Block merge: Even though the Email matches, both profiles have different, non-null UserIDs (U111 vs U222). Our conflict guard prevents merging in this situation to avoid cross-account contamination or account-takeover scenarios.
- What happens next: We keep A and B separate, preserve all data as-is.
Merge Result
❌ Profiles remain separate (A & B).
- A → UserID=U111, Email=alice@example.com
- B → UserID=U222, Email=alice@example.com, Mobile=+15559876543
Scenario 4 — Anonymous Device → Adds Email → Adds Phone
User | UserID | Mobile | DeviceID | |
---|---|---|---|---|
A | null | null | null | DWeb04 |
A | null | diana@example.com | null | DWeb04 |
A | null | diana@example.com | +15553456789 | DApp04 |
Flow
- Anonymous on web (DWeb04): The user browses your website without sharing identifiers. We track activity under an anonymous profile (A) linked to DWeb04.
- Email arrives: The user submits diana@example.com (e.g., newsletter signup). A becomes email-known, and future matching sessions can link here.
- App login & phone add (DApp04): Later, the user logs into the app with the same email and adds a phone number(+1 555 345 6789).
- Deterministic link via email: Because the Email matches, we attach the app device and phone to A.
- What we unify: Devices (DWeb04, DApp04), Email + Mobile, and the full activity stream (re-sorted by event time). An audit log records the identifiers added and the linkage basis.
Merge Result
Final profile A → Email=diana@example.com, Mobile=%2B15553456789, DeviceIDs=[DWeb04, DApp04]
Scenario 5 — No Primary Identifier Initially (email → mobile → email, merge)
User | UserID | Mobile | DeviceID | |
---|---|---|---|---|
A | null | alice@example.com | null | DWeb05 |
B | null | null | +15551234567 | DApp05 |
B | null | alice@example.com | +15551234567 | DApp05 |
Flow
- Email-only on web (A): On the website (DWeb05), the user leaves alice@example.com but doesn’t provide UserID or Mobile. We create profile A with Email as the primary signal.
- Mobile-only on app (B): On the app (DApp05), the user provides only a phone number (+1 555 123 4567). We create a separate anonymous profile B to capture app activity safely.
- Same app session adds email: In that same app session, the user also enters alice@example.com, which matches A on a primary identifier.
- Merge B → A: NotifyVisitors merges B into A deterministically via the Email match.
- What we unify: Email + Mobile, devices (DWeb05, DApp05), and all events in a single, time-ordered history—plus a complete audit record of the merge.
Merge Result
Final profile A → Email=alice@example.com, Mobile=+15551234567, DeviceIDs=[DWeb05, DApp05]
Secondary factors to consider while merging
While primary identifiers drive the merge, these rules decide which values win and how timelines behave after the merge:
- Base profile = oldest acquired
We keep the earliest created profile as the surviving shell and merge all others into it.
- Event chronology after merge
NotifyVisitors rebuilds a single timeline by re-sorting all events by event occurrence time (not ingestion time) to produce a continuous history.
- Consent = latest state wins (per channel)
For each channel (WhatsApp/SMS/Email), we honor the most recent consent timestamp.
- Phone = most-recent phone is active
We treat the newest phone as the active contact point; a new number requires fresh SMS consent per carrier/compliance guidance.
- Email = prefer verified; if both verified, most-recent wins
We choose a verified email over an unverified one; if multiple are verified, we pick the most recent as primary.
- Acquisition source = first touch (oldest)
For provenance fields like acquisition_source, NotifyVisitors persist the first user source/medium (from the earliest session) and do not overwrite on later merges.
Governance
Even after merging, we still preserve all individual profiles of the user. Every merge writes an audit log (what matched, which values won, and previous values).
Conclusion
This model delivers a deterministic, auditable single view of each customer. We stitch anonymous → known identities, retroactively attach pre-signup activity, and always order history by event time. Profiles merge when a primary identifier matches without conflicting distinct UserIDs; otherwise, merges are blocked. Secondary rules resolve attribute conflicts predictably (oldest base, latest consent, most-recent phone, verified-then-recent email, first-touch attribution). Every action is logged, ensuring trustworthy analytics and compliant messaging.
Was this article helpful?
That’s Great!
Thank you for your feedback
Sorry! We couldn't be helpful
Thank you for your feedback
Feedback sent
We appreciate your effort and will try to fix the article