Push Notifications Not Working? Fix It in Despia
Push notifications not working in your app? The usual causes are local-only push, a missing OneSignal ID, permissions turned off, or an unlinked user.

You send a notification and nothing arrives. Or it shows up fine while the app is open and goes dead the moment it is closed. Push failures in a web-to-native app almost always trace back to one of four causes, and they are quick to tell apart once you know what to look for. Work through them in order.
First, know what push actually means here
Despia bundles the native OneSignal SDK into your app at runtime and registers the device with OneSignal automatically when the app launches. Real push, the kind that reaches a user when the app is closed, runs through OneSignal over APNs on iOS and FCM on Android. That is the delivery path that matters. If your notifications only appear while the app is in the foreground, you are not actually using it, which is the first cause below.
Cause 1: you built local push, not remote push
This is the most common one, and AI builders cause it constantly. When you ask an agent to add notifications, it often wires up the browser Notification API or an in-app toast. That code only fires while the app is open and the page is running. It cannot wake a closed app, because there is no server and no APNs or FCM behind it. It looks like push in testing and then silently does nothing in production once the user leaves the app.
Remote push has to be sent from your backend through OneSignal. If your notifications only work with the app open, stop debugging permissions and delivery, and move the send to OneSignal's REST API instead.
// Backend: this is what actually reaches a closed device
await fetch('https://onesignal.com/api/v1/notifications', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${process.env.ONESIGNAL_REST_API_KEY}`,
},
body: JSON.stringify({
app_id: process.env.ONESIGNAL_APP_ID,
include_external_user_ids: [userId],
headings: { en: 'Title' },
contents: { en: 'Message body' },
}),
})
Cause 2: push permission is off on the device
If the user denied the permission prompt, or never saw it, nothing will deliver no matter how correct the rest of your setup is. Check the permission state and send the user to their device settings when it is off.
const isDespia = navigator.userAgent.toLowerCase().includes('despia')
async function ensurePushEnabled() {
if (!isDespia) return
const result = await despia('checkNativePushPermissions://', ['nativePushEnabled'])
if (!result.nativePushEnabled) {
// Permission is denied or not yet granted. Open the OS settings
// for this app so the user can flip it on.
despia('settingsapp://')
}
}
A good pattern is to check on load and show a quiet banner when push is off, rather than blocking the user. Once they enable it in settings, delivery starts working with no rebuild.
Cause 3: not registered in OneSignal, or the bundle ID is missing
If you never set OneSignal up, or the OneSignalNotificationServiceExtension bundle ID was never registered on your Apple Developer account, iOS push cannot be delivered at all. The device has nothing to register against, so it never becomes a valid push target.
The fix here needs a fresh build, not an over-the-air update. The OneSignal SDK and the notification service extension are compiled into the app binary and signed against that bundle ID, so registering the ID and toggling the integration on only takes effect after you rebuild in Despia. Editing the settings alone does nothing until the next build.
Concretely, if push has never worked on iOS:
Register the OneSignal ID on Apple Developer: your core bundle ID with
.OneSignalNotificationServiceExtensionappended (for examplecom.despia.myapp.OneSignalNotificationServiceExtension, using your own bundle ID). Enable Associated Domains and Push Notifications on it, and Push Notifications on your core bundle ID too.Add your OneSignal App ID in the Despia editor under Integrations.
Rebuild. After the rebuild, push starts working.
If you skip the rebuild, setonesignalplayerid:// resolves silently and your backend requests succeed at the API level but never reach the device. When notifications stop after a settings change, rebuild before anything else.
Cause 4: registered, but the user was never linked
This one is sneaky because everything looks correct. OneSignal is set up, the build is fresh, permission is granted, and your backend gets a success response. But notifications never land, because the device was never tied to the user you are targeting.
Despia links a device to a user with setonesignalplayerid://. The ID you pass becomes the user's external_id in OneSignal, and that is the same value your backend targets with include_external_user_ids. If that call never runs, or runs with the wrong ID, OneSignal has no device to deliver to.
The mistake most people make is calling it once, at login. Single-page apps re-render and re-route without a full reload, and if that first call missed for any reason, the link never gets set. Call it on every authenticated load and after every route change, so the device stays linked no matter where the user is in the app.
const isDespia = navigator.userAgent.toLowerCase().includes('despia')
function linkPushUser(userId) {
if (!isDespia || !userId) return
// Safe to call repeatedly. This keeps the device tied to the user
// even if the login-time call was missed.
despia(`setonesignalplayerid://?user_id=${userId}`)
}
// On every authenticated load
linkPushUser(currentUser?.id)
// And after every navigation, so it stays in sync
router.afterEach(() => linkPushUser(currentUser?.id))
Then make sure the value you link on the client is the same user ID your backend knows. Whatever you pass to setonesignalplayerid:// is what you send to. Store that ID against your user record, and target it from the backend to reach exactly that person.
// Backend: target the same ID you linked on the client
async function sendToUser(userId, title, message) {
return fetch('https://onesignal.com/api/v1/notifications', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${process.env.ONESIGNAL_REST_API_KEY}`,
},
body: JSON.stringify({
app_id: process.env.ONESIGNAL_APP_ID,
include_external_user_ids: [userId],
headings: { en: title },
contents: { en: message },
}),
})
}
The order to check
When push is not working, go down the list in this order and you will find it fast:
Do notifications only show while the app is open? You built local push, not remote. Send through OneSignal.
Is the OS permission granted? Check with
checkNativePushPermissions://, route tosettingsapp://if not.Is OneSignal set up and the extension bundle ID registered, with a build made after? If not, register and rebuild.
Is the device linked to the user on every load and route change, and does the backend target that same ID? If not, fix the link.
Add this to your app
Native push in Despia is the bundled OneSignal SDK plus two client calls, from your existing web codebase. No Xcode, no native project to maintain.





