Push troubleshooting
Push notifications work only when your app setup and your Dotdigital Marketing push configuration match each other. If push messages do not arrive, or you see a high number of failures, work through this page in order.
Triage order
Most push problems fall into one of six stages. Find the first stage that fails, then go to the section for that stage. This saves you checking configuration when the real problem is in your app code, or the reverse.
| Stage | Question | If the answer is no |
|---|---|---|
| 1 | Does the contact exist in Dotdigital Marketing? | No contact for your app user |
| 2 | Does the contact have a device on the Devices tab? | The contact has no devices |
| 3 | Does the device show a value in Registration ID? | The device has no Registration ID |
| 4 | Does the push message reach the device? | Nothing arrives on the device |
| 5 | Does the device display the notification? | The message arrives but no notification appears |
| 6 | Does a tap do what you expect? | Tapping a notification does not open the deep link |
To check stages 1 to 3, view the contact in Dotdigital Marketing and open the Devices tab. See Checking a contact's device data.
Make sure your user has allowed push notificationsIf your app user does not allow push notifications, your app receives no push token and cannot use push. This happens when the user declines the first prompt, or removes permission for your app later in their phone settings.
On Android 13 and later, the user must also grant the
POST_NOTIFICATIONSruntime permission separately. See Android 13 and later drops notifications silently.
Check your configuration first
Before you investigate a specific symptom, confirm these four points:
- You have set up your Dotdigital Marketing push notification profile correctly, and it really matches your app.
- Your app registers a unique but consistent push profile ID for each app user, and an email address. See Register your app users for push.
- Your app acquires a native push token and registers it with our SDK. For iOS, check that the token format is correct and that the APNS environment matches the build type.
- Your app asks the user for notification permission, and the user has granted it.
For the platform code, see the guide for your technology:
If all of the above is correct, the problem is more likely to be in your app code or in device connectivity. Use the sections below.
Check how the message was configured
Some problems come from the message rather than from your app. Your app can be correct and still do nothing, because of the options chosen when the message was sent.
Check the On tap setting of the message that you are testing:
| On tap option | What your app receives | What happens on a tap | Tap tracked? |
|---|---|---|---|
| Open app | No dd_deepLink and no dd_data | Your app opens at its launcher screen | No |
| Open deep link | dd_deepLink, with a url and trackingUrl | Your app opens the target screen | Yes |
| Pass data | dd_data | Your app acts on the data | No |
| Open a survey, page, or form | dd_deepLink, with a web address | The browser opens the page | Yes |
Also check the Silent push notification setting. When it is set to Yes, Dotdigital Marketing removes the alert from the payload, so the device displays nothing by design.
For the full detail of each option, see Send a deep link or custom data with a push message.
Ask which options the message usedYour marketing team usually sends the message, and your development team builds the app. When you investigate a fault, ask which On tap option the message used, whether it was silent, and which values were entered for each platform. Many reported faults turn out to be a message that did not carry the values that the app expects.
Test on real hardware
Test push notifications on a physical device. Simulators and emulators cannot always register for push:
- The iOS Simulator cannot obtain a real APNS device token, so token registration fails.
- Android emulators need a system image that includes Google Play Services, otherwise FCM cannot register.
If push works on a physical device but not in a simulator, this is expected behaviour rather than a fault.
Gather evidence
Before you change anything, collect the following. It usually identifies the stage that fails.
- Turn up the SDK log level. Each SDK logs why a registration or session call failed:
- Check the Devices tab for the contact, and note whether the Registration ID field has a value.
- Note the build type, because the APNS environment depends on it.
- Note which SDK variant you use, because iOS behaves differently with CocoaPods and with Swift Package Manager.
- Note the message settings, including the On tap option, the Silent push notification setting, and the values entered for each platform.
Turn the log level back down before you releaseDebug level logs can include request URLs, headers, and bodies. Do not ship a release build at debug level.
Common problems
No contact for your app user
Dotdigital Marketing creates a contact only when the push profile has both a push token and an email address. If either is missing, no contact appears.
Check that your app passes an email address to the SDK. See Update the profile with the email address.
The contact has no devices
A device appears only after the SDK registers a push token against an active session.
- Confirm that a session exists before you register the token. The
setAPNSPushDetails,setFCMPushDetails, andsetPushTokenmethods all require an active session, and they fail if none exists. - In JavaScript, the token can arrive from your push plugin before the SDK has a session. This is a race condition. Store the token, then register it again once a session exists. See Why you must store the token.
The device has no Registration ID
An empty Registration ID field means the push token never reached Dotdigital Marketing. Check the following, in this order:
- The user has not granted notification permission. On iOS, the user must accept the prompt. On Android 13 and later, the user must grant
POST_NOTIFICATIONS. - Your app is not registering the token with the SDK. Check your app code:
- The push profile has incorrect APNS or FCM details. Check your settings again.
Expo apps must send the native tokenIf you use Expo, make sure you send the token from
getDevicePushTokenAsync(), not fromgetExpoPushTokenAsync(). An Expo push token looks likeExponentPushToken[xxxxxxx]. The SDK accepts it without an error, and the device then never receives a push message. See Get a registration ID with React Native and Expo.
Nothing arrives on the device
If the contact, the device, and the Registration ID all look correct, but no push message arrives:
- Check the APNS environment. A sandbox token is not valid in production, and a production token is not valid in the sandbox. Apple returns no error when they do not match. See APNS (iOS Push).
- Check the token has not expired. If the user uninstalled your app or restored a backup, the stored token may be stale. Dotdigital Marketing clears an expired token only after a send attempt fails.
- Check network conditions. See Network conditions for APNS, and FCM network availability for Android.
The message arrives but no notification appears
This is often expected behaviour rather than a fault. The operating system displays a notification automatically only when your app is in the background.
Check the message first:
- Was the message silent? If Silent push notification is set to Yes, Dotdigital Marketing removes the alert from the payload. The device displays nothing by design, and your app must act on the data instead. See Send a silent push message.
- Did the message use Pass data? A Pass data message can arrive without an alert for the operating system to display, so your app must build the notification itself.
On iOS:
- If you have not set the
UNUserNotificationCenterdelegate, iOS calls none of your notification methods. See Set the notification centre delegate. - If you have not implemented
willPresent, iOS suppresses notifications while your app is in the foreground. See Displaying push notifications when the app is in the foreground. - Check whether the user has a Focus mode or scheduled notification summary enabled, because both delay or group notifications.
On Android:
- A data-only payload has no
notificationobject, so Android renders nothing automatically. Your app must build the notification. See Displaying push notifications when the app is in the foreground. - Android 8 and later requires a notification channel. If the channel does not exist, Android drops the notification.
In JavaScript:
- On Android, set the
forceShowproperty of the push plugin totrueto display notifications automatically in the foreground, or handle the display yourself. See Displaying push notifications while the app is in the foreground. - Create an Android notification channel. See Create an Android notification channel.
Android 13 and later drops notifications silently
On Android 13 and later (API 33), the operating system discards every notification if the user has not granted the POST_NOTIFICATIONS runtime permission. It reports no error to your app.
This is a common cause of confusion, because token registration still succeeds and the contact still appears in Dotdigital Marketing with a valid Registration ID. The problem therefore looks like a delivery fault rather than a permission fault.
Request the permission in your app, then test on an Android 13 or later device:
Your app cannot start a session, or you see 403 Invalid JWT
The SDK cannot start a session if your JWT fails validation. Check each of the following against the Authentication section of your push notification profile:
- The
issclaim matches the Issuer value. - The
audclaim matches the Audience value. - The claim that holds the user identifier is named by the ID claim value. The default name is
sub. - The token is signed with the HS256 algorithm, using the Shared secret value.
- The
iatandexpclaims are in seconds since the epoch, not milliseconds. A millisecond value can be rejected, or it can create a token that never expires. - The token includes the
noncethat the SDK passed to your challenge function.
For the claim reference and code samples, see Create a JWT. You can also paste a token into JWT.io to check its contents.
JavaScript: check that your secret is not cast to hexadecimalThe jsrsasign library checks whether the secret is a hexadecimal number, and casts it if it is. We always use string based secrets, so cast the secret to a UTF-8 string with
{utf8: <Your secret value>}. A 403 - Invalid JWT error is the usual symptom.
You see duplicate contacts, or pushes reach the wrong device
The sub claim in your JWT is the push profile ID. If this value changes between sessions or between installs, Dotdigital Marketing creates a new push profile each time.
- Use a value that is unique for each user and stays the same for that user, such as your own user ID.
- If your app supports anonymous users, use a stable device-scoped GUID that you store in the app. Contacts are then limited to one device.
- End the session when a user logs out. If you do not, the same push profile can attach to more than one contact.
See The role of the sub claim.
Tapping a notification does not open the deep link
Check the message first. If On tap is set to Open app, the message carries no deep link, and your app opens at its launcher screen by design. This is the most common cause, and it is a message setting rather than a fault in your app. To send a deep link, the message must use Open deep link or Open a survey, page, or form. See Choose what happens when a user taps.
If the message does carry a deep link, the behaviour then differs by platform.
On iOS, the SDK calls canOpenURL before it opens the link. On iOS 9 and later, canOpenURL returns false for any scheme that your app has not declared in the LSApplicationQueriesSchemes array in Info.plist. The SDK then writes "Cannot open URL" to the log, and nothing appears to happen. Add your deep link scheme to that array. See Register a custom URL scheme.
On Android, check that the target Activity declares an intent filter for your scheme and host, and that android:exported is true. See Handling deep links.
In JavaScript, the SDK does not open the link. It reports the tap and returns the URL to you, and your app must then open it. This is a difference from the native SDKs. See Deep link support and link tracking.
The deep link works on one platform but not the other
The Open deep link option has a separate field for each platform, and the Pass data option has a separate tab for each platform. If only one is completed, the message works on that platform and does nothing on the other. Nothing warns the sender about the empty field or tab.
Check that both platforms have a value, and that each value uses the URL scheme registered by that platform's build. See Enter values for each platform separately.
A long URL can also fit on Android and fail on iOS, because the iOS field allows fewer bytes. See Deep link length limits.
Your custom data does not arrive, or your app cannot parse it
- Check that the message used Pass data. Dotdigital Marketing sends custom data only with the Pass data option, and it clears any data that you entered if you then change the On tap option. See Send custom data.
- Check where your app reads the value. Your push plugin may place
dd_dataat the top level of the payload, underdata, or underadditionalData. On Android, useComapiClient.parsePushMessage(), which returns the value as an object. If you readRemoteMessage.getData()directly, you get a JSON string that you must parse yourself. - Check the JSON for a personalisation fault. Dotdigital Marketing replaces a personalisation token with the contact's value as plain text, and does not adjust the result to keep the JSON valid. An empty value, or a value that contains a quotation mark, can produce malformed JSON for some contacts and not others. See Using personalisation in the data payload.
Push clicks are missing from your reporting
Dotdigital Marketing records a tap only when both of these are true:
- Your app passes the notification to the SDK. Call
handlePushNotificationon Android,handleNotificationResponseon iOS, orhandlePushAndGetMessageDetailsin JavaScript. If you never call these, no tap is recorded. - The message uses a tap action that carries a tracking URL. The SDK reports the tap by calling the
trackingUrlvalue inside thedd_deepLinkobject. Dotdigital Marketing adds one only for the Open deep link and Open a survey, page, or form actions. An Open app or Pass data message carries no tracking URL, so no tap is recorded.
If you need click reporting for a message, use a tap action that carries a tracking URL. See Choose what happens when a user taps.
The notification icon is a white or grey square on Android
Android requires a transparent silhouette image for the small notification icon. If you supply a full-colour icon, Android renders it as a solid block.
Provide a white, transparent-background icon and set it as the default. See Changing the icon or colour of push notifications.
Another push SDK is also handling messages
If your app contains a second push SDK, that SDK may consume FCM messages before ours receives them, or it may try to handle our messages.
Use the origin check to identify our messages, then route each message to the correct handler:
- Android: detecting push messages from Dotdigital Marketing
- iOS: detecting push messages from Dotdigital Marketing
- JavaScript: detecting push messages from Dotdigital Marketing
APNS (iOS Push)
When you configure APNS, consider the following:
- The private key is uploaded to Dotdigital Marketing, and the key ID is set correctly.
- The Apple provisioning profile that signs your app has push enabled.
- The app entitlements include push and the correct APNS environment.
- The Apple team ID that you use owns your app.
To set up your APNS settings, see Enter your APNS credentials in Dotdigital Marketing.
Development builds
During development, an Apple provisioning profile signs your app and deploys it to a handset. Check the following:
- Your app provisioning profile includes the Push Notifications entitlement.
- You have uploaded the correct p8 private key to Dotdigital Marketing, and supplied the correct bundle identifier, Apple team ID, and private key ID.
- Your APNS environment resolves to
development. How you set this depends on your SDK variant. See How the SDK decides which APNS environment to use. - Your app entitlements contain the following entry. Xcode normally manages this for you when you add the Push Notifications capability:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
</dict>
</plist>App Store, distribution, and TestFlight builds
For any release build, whether for the App Store, TestFlight, or enterprise deployment, check the following:
- Your app provisioning profile includes the correct push entitlement.
- You have uploaded the correct p8 private key to Dotdigital Marketing, and supplied the correct bundle identifier, Apple team ID, and private key ID.
- Your APNS environment resolves to
production. See How the SDK decides which APNS environment to use. - Your app entitlements contain the following entry:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>production</string>
</dict>
</plist>How the SDK decides which APNS environment to use
The iOS SDK detects the environment differently depending on how you installed it. Check the method that applies to your variant.
| SDK variant | How the environment is set |
|---|---|
| CocoaPods | The SDK reads the DEBUG compiler macro. DEBUG set means sandbox. DEBUG not set means production. |
| Swift Package Manager | You must set it explicitly with setApnsEnvironment, using development or production. |
Swift Package Manager: initialisation fails without an APNS environmentIn the SPM version, initialisation returns
nilifapnsEnvironmentis not set to exactlydevelopmentorproduction. The SDK writes an error to the log, and you get no client object at all. If nothing in the SDK works, check this first.
Network conditions
If push delivery is still intermittent or unreliable, consider the following.
Network availability
APNS prefers cellular data, and always uses it when it is available. Poor cellular coverage can therefore affect delivery.
For devices without a SIM, or where cellular data is unavailable, the device uses WiFi instead. WiFi access needs certain ports open, and not routed through a proxy server, to get an optimal connection. See Apple's list of ports used by Apple products.
Connection reliability
Whatever the network type, the operating system uses a single connection to Apple. These connections are optimised for power and data efficiency, so they typically have long keep-alive times. Apple does not document the exact values.
Depending on the network that the device is attached to, a router or gateway between the device and Apple's servers may have a shorter connection timeout than the device. The network appliance then kills the connection, but the device does not notice until it next sends a keep-alive packet. The device re-establishes the connection at that point. In between, there is a period with no active connection, and Apple cannot deliver the push message.
In this case, Apple buffers the push message temporarily until the device reconnects. Apple buffers only one message for each device, so a later send overwrites the pending message. Apple does not say how long they hold it.
These scenarios are difficult to capture evidence for. We do see more of them on corporate WiFi networks, where a large number of devices connect to each access point and connections are closed more aggressively.
Apple improves connection reliability with each operating system release, so these problems are becoming less common.
Try this if you suspect the APNS connection is brokenIf a device does not receive a push message during a test, put the device into flight mode for a few seconds, then turn flight mode off again. This forces the device to re-establish its connections, and the next push message should then arrive normally.
FCM (Android Push)
FCM needs less configuration than APNS. You normally use one set of configuration for both development and release builds.
For FCM to work, check the following:
- The package name in your push notification profile matches the package name of your app.
- The service account key JSON file that you uploaded to Dotdigital Marketing belongs to the same Firebase project as the
google-services.jsonfile in your app. - The
google-services.jsonfile is in your app module folder, not the project root. - You have applied the Google Services plugin in your module-level build file. Without it, the build does not process
google-services.json.
To set these values, see Enter your FCM credentials in Dotdigital Marketing.
If you still have a legacy Server Key configuredEarlier versions of this integration used an FCM Server Key and a Sender ID. Google has retired the legacy FCM API, so Dotdigital Marketing now uses a service account key JSON file instead.
If your push notification profile still refers to a Server Key, generate a service account key and upload it. Push messages fail while the profile uses retired credentials. See Enter your FCM credentials in Dotdigital Marketing.
Sender IDs and the native Android SDKThe native Android SDK does not take a Sender ID. It reads the project details from
google-services.json. You supply asenderIDonly when you use a JavaScript framework push plugin, such as the Cordova or Capacitor plugins.
FCM network availability
An Android device must be able to open a socket to Google's FCM servers to receive push messages. Android uses either WiFi or mobile data, and prefers WiFi. Make sure that the network ports that FCM uses are open and unrestricted.
The mobile network can also cause problems. Android sometimes does not send heartbeat packets often enough on the FCM connection, so the mobile network closes the connection. The device is then cut off from FCM until the next heartbeat, which can be up to 28 minutes later.
If you have access to the affected handset, you can view information and logs about the FCM connection. Open the dialler and enter:
*#*#426#*#*
The dialler code does not work on every deviceThis code opens a diagnostic screen that ships with Google Play Services. It is not available on every device or Android version, and some manufacturers remove it. If nothing happens, use Logcat instead.
Slow FCM delivery
Google's FCM service delivers push messages on Android. Device manufacturers tune the Android kernel settings that balance performance against battery consumption. When a manufacturer leans too far towards battery saving, FCM delivery can be delayed.
Alongside these power settings, some older Samsung devices have known problems where the memory manager shuts down the FCM service.
Also check the following on the affected devices:
- Background data for mobile networks is still enabled.
- Battery optimisation is not restricting your app. Manufacturers including Xiaomi, Huawei, Oppo, and Samsung apply aggressive restrictions, and often need your app added to a protected or auto-start list.
- Data Saver mode is off, or your app is allowed unrestricted data use.
Still having issues?If you have worked through this page and push messages still do not arrive, contact your Customer Success representative. Include the following, because it speeds up the investigation:
- The contact's email address, and a screenshot of the Devices tab.
- The platform, operating system version, and device model.
- The build type, and whether it is a development or production build.
- Your SDK version, and for iOS whether you use CocoaPods or Swift Package Manager.
- The message settings, including the On tap option and whether the message was silent.
- SDK logs from the affected device, captured at debug level.
Updated 27 days ago