Case study
Zero notifications delivered on a live flow: the token was registered on the anonymous profile
A flow with no conditions, every send failing. With the three most common causes ruled out in half an hour, the token turned out to be written on the anonymous profile instead of the one with an email.
A consumer loyalty app with game mechanics, on iOS and Android, with an analytics system already installed and working.
The constraint: the existing analytics platform had to stay where it was. The new messaging tool had to live alongside it, not replace it, and the in-app product features stayed on the existing one. Every change to the app went through an external development agency, so every fix carried the cost of a release.
The result: from a first flow with zero deliveries to notifications being delivered, linking straight to the four screens that matter instead of the home screen.
Below is how we ruled out the three most common causes in half an hour, where the token was actually ending up, and the second problem that surfaced after the fix.
What this case covers
- how to rule out user behaviour when sending fails 100% of the time
- how to work out which profile a messaging system registers a device identifier on
- how to make a direct link into the app work inside emails too
- how to order infrastructure checks relative to building content
Contents
- The client and the starting point
- Diagnosing the token and the fix
- Direct links from push and from email
- Method note on the numbers
- What didn’t work
- What we would do now
- Conclusion
The client and the starting point
A loyalty app with points collection, rewards and a games section, on both mobile platforms. Development handled by an external agency. Analytics and remote configuration already active and working, to be left untouched.
The project involved adding a messaging tool alongside the existing one, for push notifications and email segmentation. The agreed plan had five steps: event mapping on a shared whiteboard, alignment with the developers, implementation on both platforms, internal verification, release.
The starting position, with the source of each figure:
| Metric | Value | Source |
|---|---|---|
| Estimated project duration | 3 weeks, tests included | alignment call, 16 February |
| Priority stated by the client | push before email | alignment call |
| Pre-existing analytics | active, not to be touched | alignment call |
| Notifications delivered on the first flow | 0 | delivery dashboard |
Diagnosing the token and the fix
IN SHORT
What we did: ran a controlled test on internal accounts to rule out the causes tied to user behaviour.
Why: notifications fail for mundane reasons far more often than for technical ones, and those causes have to be eliminated first.
What we found: the device token was registered on the anonymous profile instead of the one with an email.
The number for this section: 0 deliveries on a flow with no conditions, then live deliveries from the next release.
The first flow we switched on was deliberately as simple as possible: a single welcome notification after signup, no conditions, no segmentation. It existed to verify that the channel was open.
Every send came back with the same error, referring to a missing token.
The most frequent causes of failure are three, and they all concern the user: app uninstalled, notification permission denied, device switched off for a long time. They are also the ones you can do nothing about, so they have to be ruled out before looking at the integration.
We ruled them all out at once with a controlled test on internal accounts: app installed that day, permission granted by hand, device switched on and in hand while watching the dashboard. Same error, so user behaviour was out of the question.
The cause was in the order of calls inside the app.
The messaging tool creates a profile as soon as it starts on the device. It is an anonymous profile, identified only by the device identifier. When the user grants permission and the operating system issues the token, that token is written to whichever profile is active at that moment.
If the user has not yet identified themselves with their email, that profile is the anonymous one. Later the person signs up, the app sends the email, and the identified profile is created. But the token stays where it was. The two profiles coexist: one with the email and no token, the other with the token and no email.
Flows send to profiles that have an email, look for the token there, and don’t find it.
The fix is in the ordering: identify the user before requesting permission, not after. That way, when the token arrives, the active profile already has an email attached. For first-time signups, where the token necessarily comes into existence before the email, the rule is to re-propagate it immediately after registration.
What worked
The test on controlled accounts. It is the only setup in which the three most common causes are ruled out all at once and in half an hour, instead of one at a time on hypotheses.
Direct links: what works in push doesn’t work in email
IN SHORT
What we did: mapped four screens as direct destinations, then set up the variant compatible with email clients.
Why: a notification that opens the home screen forces the user to go looking for the thing the message is about.
What we found: in-app links work in notifications but are blocked in emails.
The number for this section: 4 screens mapped as direct destinations.
With the token sorted, notifications were going out but all of them landed on the home screen.
We mapped four destinations: rewards, games, receipt upload, points balance. The home screen stays without a link, because it is the default destination of a tap.
In-app links work in push notifications. In emails they don’t. Email clients do not open application-internal addresses for a security reason, because it would be an easy way to have software launched from a message received from a stranger.
The compatible solution takes two extra steps. You use ordinary web addresses and declare to the operating system that the domain belongs to that application, publishing two configuration files on the site, one per platform. From then on, the same address opens the screen inside the app if the app is installed, and the web page if it isn’t.
In between, the messaging tool’s click tracking also has to be handled, since it rewrites addresses and must let them through without breaking the chain.
What worked
Treating the two channels separately instead of looking for a single solution. The two destinations have different constraints and the same configuration does not cover both.
Method note on the numbers
Declared source of truth: the messaging tool’s delivery dashboard, cross-checked against the profile record of individual test users.
| Published number | Source | Window | Model |
|---|---|---|---|
| 0 deliveries | delivery dashboard | first flow activated | all sends in the flow, not a sample |
| 4 screens | agreed mapping | project | destinations with a direct link |
| 3 weeks estimated | alignment call, 16 February | initial statement | expected duration, tests included |
What we are not able to measure, and with what degree of certainty.
The delivery rate reached after the fix is not quantified in this document. The verified fact is that the channel went from no deliveries to live deliveries, observed on the dashboard after the fix was released. A steady-state delivery percentage requires an observation window over volume, which was not set up.
The effect of direct links on open rate and conversion is not measured. It would take a comparison between notifications with and without a direct destination on the same audience.
We are flagging a gap in our own documentation. The archive sources on this project cover the planning phase but not the debugging phase. The technical steps described above come from the direct recollection of whoever did the work, not from contemporaneous notes. It is a documentation gap on our side, and on the method we treat it as such.
What didn’t work
We built the flows before verifying the infrastructure. Three weeks of event mapping, configuration and internal testing were completed before we discovered that the channel could not deliver. On the same project the same wrong order repeated itself on email, where the sending domain’s records had not yet propagated and the platform was blocking sends silently, with no visible error in the flow builder. What we did instead: the order is reversed. First you verify domain, permissions and one real test delivery, then you build the content. Building first is more satisfying and leaves weeks of work stuck behind a blocker that doesn’t announce itself.
We did not document the debugging phase while we were doing it. No contemporaneous notes remain of the technical work on this project, only a reconstruction after the fact. That means the details cannot be verified against a dated source, which is a problem in a kind of work we sell on verifiability. What we did instead: every debugging session now produces a dated note with the observed values, even when it lasts half an hour.
What we would do now
Measure the steady-state delivery rate. The channel works, but we have no baseline over volume to compare against over time.
Compare notifications with and without a direct destination. It is the only way to know whether the four mapped screens are worth the work of maintaining them.
Verify behaviour for users who registered before the integration. For anyone who signed up earlier, the token was issued in a session where identification was not being called. The fix should recover them on their next launch, and that needs confirming in the field.
Define routing between the two notification systems. Both tools can receive the device token. Without an explicit routing rule, either one can intercept notifications that aren’t its own.
Conclusion
A live flow with zero notifications delivered, caused by a token registered on the anonymous profile instead of the identified one. After the call ordering was fixed, the channel delivers and notifications lead straight to the four mapped screens.
What this engagement leaves behind, picking the four opening points back up.
How to rule out user behaviour: you run a test on controlled accounts, with the app installed and permission granted by hand. If that fails too, the problem is in the integration.
How to work out where a device identifier ends up: you look at which profile was active at the moment it was issued, not which profile is looking for it.
How to make an app link work inside emails: you switch to ordinary web addresses and declare domain ownership to the operating system, because internal addresses are blocked.
How to order the checks: infrastructure first, content after. A configuration blocker produces no visible errors and can stall weeks of work without announcing itself.
When this work makes sense, and when it doesn’t
It makes sense if you have a mobile app and want to add a messaging channel alongside an analytics system already in use, without replacing it.
It doesn’t make sense if your active user volume is such that notification segmentation changes no decision. And it doesn’t make sense if you have no access to the team that develops the app, because fixes of this kind live in the application code and not in the tool’s configuration.
The first step is to open the profile record of an email-identified user who has the app installed and look at whether it contains a notification token. If it isn’t there, it is on another profile.