MoEngage Web SDK · 30 September 2026
Two people enquiring from one browser produced one profile, carrying the wrong number. It was not a merge: the first person's history was relabelled with the second person's number, and they became unreachable under their own. This is what we changed, and how we know it works.
Reproduced 29 September 2026
The first person's profile was relabelled with the second person's number. Their behaviour now sits under someone else's identity, and they are unreachable under their own.
Profiles expected
2
Two people, two mobile numbers, one browser
Profiles found
1
Labelled with the second person's number
History it carried
10 sessions
First seen 14 Sep, which is the first person's history
Reachable under their own number
0/2
One number now points at someone else, the other at nothing
The exposure is not unusual: a sales gallery tablet, a shared family device, or a channel partner entering several clients' enquiries in one sitting.
The cause
This is not a fault in MoEngage. identifyUser updates the ID of the current user.
Without an intervening logoutUser() there is no second profile for identifyUser to create. The SDK holds one bound identity per browser and rewrites it in place.
Rustomjee's website has no login, so there was no natural logout moment. There was never a moment at which to unbind one person before binding the next.
Two different meanings of "logout". Here it has nothing to do with a website login, and
nothing to do with a visitor leaving. identifyUser binds the browser to a profile and
logoutUser unbinds it. Firing logout when someone leaves would be wrong, because it would
discard recognition of every returning visitor.
MoEngage's data-corruption warning is written for workspaces with the feature switched on. The damage was observed here regardless, so the finding and the fix hold either way.
Interactive
One browser, three people. Turn the gate off to see the original behaviour, and on to see what is live now.
Identified as nobody yet
Submit an enquiry as:
The change
| The browser's state | What the gate does | Unbinds |
|---|---|---|
| Nothing to identify | There is nothing to identify, so there is nothing to unbind. | No |
| First enquiry in this browser | The first enquiry in a browser. Identify runs on a clean slate. | No |
| The same person again | Deliberately does nothing. Unbinding a returning visitor would discard recognition of them. | No |
| A different person | The only state that calls logoutUser(). This is the case that used to relabel the first person. | Yes |
Only the last calls logoutUser(), and it is exactly the case that used to relabel the first
person. The decision reads our own record of who this browser was last identified as, because we are the ones
who set it. MoEngage's own store is read as a cross-check, which matters for browsers bound before this
change shipped.
Evidence
Compare MoEngage IDs, not session counts. The same id would mean the same underlying profile, so three distinct ids is what proves a genuine split rather than a rename.
| Enquiry | Number | MoEngage profile |
|---|---|---|
| Phone A | 91XXXXXX9122 | 6aa7bc54 |
| Phone B | 91XXXXXX1542 | 6abc159e |
| Phone C | 91XXXXXX0052 | 6abc2022 |
Phone A 91XXXXXX9122
MoEngage ID 6aa7bc54
unchanged, and now carries a User Logout event
Phone B 91XXXXXX1542
no profile at all
MoEngage ID 6abc159e
User Logout is MoEngage's own derived event on Web and cannot be produced from our side. Its absence from both Activity Info exports of 29 September is what proved the first attempt had failed; its presence is what proves the second one worked.
Each new profile's lead_submitted carries First Session: true. A relabelled profile would carry the previous person's history and read false. Confirmed on Contact Us and the project enquiry modal.
Our own record and MoEngage's own store independently name the same bound identity, on the published container rather than in Preview.
our own record 91XXXXXX0052
MoEngage's store 91XXXXXX0052 (key uid)
decision logout, unbind called: yes
The gate runs 4 times during a single enquiry, because of how the tags chain. Only the first run can log out. It writes the record immediately, so every later run reads same-person.
Stated, not hidden
A browser MoEngage had already bound before the gate shipped carries no record of ours. If MoEngage's own store also comes back empty on such a browser, its first submission will not log out, so one relabel can still happen once per such browser.
Measured since: getUserIdentities() does return the bound number on such a browser, so it is covered by the fallback on its own. That matters because on publish day every returning visitor is one of these browsers.
Worst case: no logout, which is the behaviour before this change rather than a new harm.
Over to MoEngage
Confirm that calling logoutUser() before identifyUser(), only when the number submitted differs from the one that browser is already identified as, is the right primitive for a site with no login, and that nothing further is required.
getUserIdentities() is documented as the asynchronous getter. It returns synchronously. Building to the documented behaviour is what made our first attempt fail silently, and it will cost the next customer the same cycle.
Identity Resolution is configured per workspace. We have access to TEST only, so nothing observed there predicts LIVE. Please confirm the LIVE setting before the switch rather than at it.
The change is published as version 3, dated 30 September 2026. It contained exactly 2 changes. The identity gate was added, and Identify User was rechained behind it. No triggers, no variables, no other tags. Republish v2 from the Versions list. The container environment remains TEST, so this is not the same as going live.