OneSignal is putting a hard ceiling on its Free plan. Starting soon, mobile push and in-app messaging stop being free above 1,000 monthly active users. New accounts hit the wall on September 1, 2026, existing accounts on October 1. If your app has more than a thousand active users a month, you now have three choices: stay and pay, cut your audience, or move.

What’s changing and when

OneSignal Growth plan cost calculator showing $31 per month at 1,000 mobile active users
Source: OneSignal
  • The Free plan keeps working below 1,000 MAU. Above that, mobile push and in-app messaging move to a paid tier.
  • The dates: the limit hits new accounts on September 1, 2026 and existing accounts on October 1, 2026, per OneSignal’s billing FAQ.
  • The threshold: fewer than 1,000 MAU for mobile push and in-app. Cross it and those channels stop sending until you upgrade.
  • What’s untouched: web push, email and SMS continue to work within the same plan limits as before. The change is specific to mobile.
  • Journeys are affected too: any journey step that sends a push or an in-app message simply skips mobile subscriptions.
  • Deleting subscribers is not a way out. If you delete mobile subscriptions that were active in the last 30 days, your plan can only re-enable 30 days after the deletion, and only if you stay under the cap for that whole period.

How the migration works

If you decided to move, here is the part that changes how you plan it: we run the migration for you. Your side of the work is three items: an export file, your push credentials, and swapping the SDK in your next app release. Everything else (cleaning the data, mapping platforms, recreating tags, importing the audience, verifying deliverability) is on us.

The switch runs on two tracks at once. A one-time import moves your existing base, so your audience stays reachable from day one, before a single user updates the app. The SDK then takes over each device as its owner installs the new build. You need both, and neither blocks the other.

1. What we can and cannot migrate

You do not have to audit this yourself. Here is the full picture up front so nothing surprises you mid-migration.

ChannelMigrated?How
iOS push (APNs)YesWe import your existing device tokens. They keep working, because a token belongs to your app and your APNs key, not to OneSignal.
Android push (FCM)YesSame: tokens belong to your Firebase project.
Huawei push (HMS)YesSame, using your HMS credentials.
Email and SMS subscribersYes, with channel setupAddresses and phone numbers are imported. Sending also needs the channel configured on our side: a verified sending domain with DKIM for email, a sender or provider for SMS. We set that up with you before the first send.
Web pushNo, re-subscription insteadBrowser subscriptions are cryptographically bound to OneSignal's keys and cannot be transferred by any provider. Your subscribers come back silently: see the web push section.
Message history, delivery stats, journeysNoHistorical data stays in OneSignal. Export any reports you want to keep before you close the account.
Segment definitionsRebuilt, not importedOneSignal's API returns segment names and counts but not their filters, so there is nothing to import. We recreate them in Pushwoosh.
Channel
1 / 7
iOS push (APNs)
Migrated?
Yes
How
We import your existing device tokens. They keep working, because a token belongs to your app and your APNs key, not to OneSignal.
Channel
2 / 7
Android push (FCM)
Migrated?
Yes
How
Same: tokens belong to your Firebase project.
Channel
3 / 7
Huawei push (HMS)
Migrated?
Yes
How
Same, using your HMS credentials.
Channel
4 / 7
Email and SMS subscribers
Migrated?
Yes, with channel setup
How
Addresses and phone numbers are imported. Sending also needs the channel configured on our side: a verified sending domain with DKIM for email, a sender or provider for SMS. We set that up with you before the first send.
Channel
5 / 7
Web push
Migrated?
No, re-subscription instead
How
Browser subscriptions are cryptographically bound to OneSignal's keys and cannot be transferred by any provider. Your subscribers come back silently: see the web push section.
Channel
6 / 7
Message history, delivery stats, journeys
Migrated?
No
How
Historical data stays in OneSignal. Export any reports you want to keep before you close the account.
Channel
7 / 7
Segment definitions
Migrated?
Rebuilt, not imported
How
OneSignal's API returns segment names and counts but not their filters, so there is nothing to import. We recreate them in Pushwoosh.

How long it takes

The SDK swap and a clean test send are a day’s work for one developer. The import runs in parallel on our side, and it is what keeps your reach intact from the first day: imported devices are deliverable before anyone updates the app. Your app’s own audience then moves onto the Pushwoosh SDK at the pace your users install the new build, which is a matter of weeks and never reaches every last device. That is exactly why the import exists.

The order matters:

  1. Export your audience. Send us your OneSignal app_id and an App API key and we pull the export ourselves, or export the CSV yourself. Details below.
  2. Send us your push credentials. The same keys OneSignal already sends with. We upload them before the import, so every imported device is deliverable.
  3. Swap the SDK. Remove the OneSignal SDK, add the Pushwoosh SDK, initialize it with your Pushwoosh application code and device API token. Platform steps are in Versions A to C.
  4. Verify delivery. Register a test device and send yourself a push before you touch production traffic.
  5. Approve the tag sheet. We build it from your export; you cross out dead tags and mark multi-value ones. We import and recreate your segments.
  6. Switch sending over. Once test delivery is clean and the import is confirmed, point your campaigns at Pushwoosh and stop sending from OneSignal.

2. Export your audience

Option A (recommended): we do it for you. Send us your OneSignal app_id and an App API key and we pull the export ourselves. Your part ends here.

Option B: you do it. In OneSignal, go to Audience > Subscriptions and export the CSV, or call the export endpoint:

Terminal window
curl -X POST 'https://api.onesignal.com/players/csv_export?app_id=YOUR_APP_ID' \
-H 'Authorization: Key YOUR_APP_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"extra_fields":["external_user_id","timezone_id","notification_types"]}'

The response returns csv_file_url, a gzip-compressed CSV that stays downloadable for three days.

OneSignal Subscription Records screen with the Export button highlighted in the top right
Source: OneSignal

These columns must be in the file. Anything else is optional and we ignore it.

ColumnWhy we need it
identifierThe push token itself. A row without it cannot be migrated.
idThe OneSignal subscription id. It becomes the device identifier on our side.
device_typeTells us the platform: iOS, Android, Huawei, email, SMS.
invalid_identifierMarks unsubscribed rows so we skip them.
tagsYour custom tags. We recreate them in Pushwoosh.
external_user_idYour own user id. It keeps a device from being duplicated once our SDK registers it.
timezone_idEnables Send by Timezone in Pushwoosh.
Column
1 / 7
identifier
Why we need it
The push token itself. A row without it cannot be migrated.
Column
2 / 7
id
Why we need it
The OneSignal subscription id. It becomes the device identifier on our side.
Column
3 / 7
device_type
Why we need it
Tells us the platform: iOS, Android, Huawei, email, SMS.
Column
4 / 7
invalid_identifier
Why we need it
Marks unsubscribed rows so we skip them.
Column
5 / 7
tags
Why we need it
Your custom tags. We recreate them in Pushwoosh.
Column
6 / 7
external_user_id
Why we need it
Your own user id. It keeps a device from being duplicated once our SDK registers it.
Column
7 / 7
timezone_id
Why we need it
Enables Send by Timezone in Pushwoosh.

Note: external_user_id and timezone_id are not part of the default export. Request them explicitly via the extra_fields parameter above, or the column picker in the dashboard.

3. Send us your push credentials

These are the same credentials OneSignal already uses to send on your behalf, so nothing new has to be created. We cannot pull them out of OneSignal ourselves: an uploaded key is never downloadable again, so this step is yours.

PlatformWhat we needWhere to get it
iOSAPNs Auth Key (.p8), Key ID, Team ID, app bundle idApple Developer > Certificates, Identifiers & Profiles > Keys. Do not revoke the key OneSignal uses; one key can serve both.
AndroidFirebase service account JSON (FCM v1)Firebase Console > Project settings > Service accounts. It must be the same Firebase project your app already uses.
HuaweiApp ID and App SecretAppGallery Connect > your project > App information.
Platform
1 / 3
iOS
What we need
APNs Auth Key (.p8), Key ID, Team ID, app bundle id
Where to get it
Apple Developer > Certificates, Identifiers & Profiles > Keys. Do not revoke the key OneSignal uses; one key can serve both.
Platform
2 / 3
Android
What we need
Firebase service account JSON (FCM v1)
Where to get it
Firebase Console > Project settings > Service accounts. It must be the same Firebase project your app already uses.
Platform
3 / 3
Huawei
What we need
App ID and App Secret
Where to get it
AppGallery Connect > your project > App information.

We upload the credentials to your Pushwoosh application before the import starts. This order matters: an import without credentials produces a database full of devices that nothing can deliver to.

4. Review your tags and segments

OneSignal Segment Editor showing a segment built on a Last Session less than 168 hours ago filter
Source: OneSignal

Tags are migrated, and you do not inventory them yourself. In OneSignal a tag is a plain key/value string with no declared type. In Pushwoosh each tag is declared once per application with a type (String, Integer, Boolean, Date, List or Price), and only then holds values.

As soon as we have your export, we send you a tag review sheet built from the file itself. It lists every tag we found, with the data filled in: sample values, how many devices carry a value, and the type we propose. Your part is two columns: cross out tags you no longer use, and mark the ones that can hold several values at once. Everything else is a proposal you can approve as-is.

Why we ask rather than guess: a tag’s type is fixed once created, so a multi-value tag we create as a plain String has to be deleted and re-imported. A tag that looks single-value in the export is exactly the case we cannot detect from the data. If we get no answer, we import every tag with the type we inferred and tell you which ones we guessed at.

Segments are rebuilt. OneSignal’s API can filter an export by a segment and list your segment names, but it does not return the filters behind them, so there is nothing to import. Two ways forward:

  1. Rebuild the conditions (recommended). Send us your segment list with their filters; screenshots are fine. We recreate them over the imported tags. Rebuilt segments are dynamic: they keep updating as your audience changes.
  2. Freeze the membership. We take one export per segment and stamp each file with a marker tag, for example os_segment = vip_users. Fast, but the result is a snapshot that does not update itself.

Segments built on OneSignal’s own behavioural data (session counts, playtime, “Active Users”, “Engaged Users”) cannot be reproduced at import time, because that history stays in OneSignal. Their Pushwoosh equivalents start filling up as soon as our SDK ships in your app.

5. What we do on our side

  1. Create and configure your Pushwoosh application and upload the credentials from step 3.
  2. Clean the export: drop unsubscribed rows and rows with an empty token, map OneSignal platform codes to ours, convert tags, and map your external_user_id to our User ID.
  3. Create the tag schema, then import the audience in batches, verifying each batch.
  4. Send a test push to a small control group and compare the result with what you expect.
  5. Report back: how many subscriptions were in the file, how many imported, and the reason for every row we skipped.

6. Ship the Pushwoosh SDK in your next app release

The import makes your existing audience reachable immediately, but it is a bridge, not the destination. Only the Pushwoosh SDK inside your app can pick up a new token when the OS rotates it (reinstall, restore, OS upgrade), register users who install after the migration, and report opens, in-app messages and uninstalls.

Remove the OneSignal SDK in the same release. Two push SDKs in one build compete for the same notification callbacks, and we do not test that combination. Keeping OneSignal sending while the new build rolls out is fine and expected; keeping both SDKs inside one build is not. Platform-specific steps are in Versions A to C below.

7. Web push: how your subscribers come back

Web push does not import, and this is a hard technical limit, not a Pushwoosh choice. A web push subscription is signed with the VAPID key pair of whoever created it, and OneSignal’s private key never leaves OneSignal — their own documentation marks the web subscription keys as available to OneSignal SDKs only. No provider can import another provider’s web subscriptions. What works instead is silent re-subscription:

  1. Remove the OneSignal snippet and explicitly unregister its service worker. Leaving the old worker in place makes two workers compete on the same domain.
  2. Install the Pushwoosh Web Push SDK, with our service worker at the root of your domain.
  3. Initialize it with your application code and your device API token (apiToken), then enable automatic subscription (autoSubscribe: true, or call Pushwoosh.subscribe()). Without the token the SDK’s calls come back as 401.

A returning visitor is then re-subscribed silently. The notification permission the browser stores belongs to your domain, not to your previous provider, so no second prompt appears and the user notices nothing. How fast your base recovers depends on how fast people return: most of the audience typically comes back within a week, with a tail over the following month.

Three cases to be aware of:

  • Visitors who blocked notifications cannot be re-subscribed. The browser refuses and will not prompt them again. They stay out, which is the correct outcome.
  • Visitors who opted out on your site while the browser permission stayed granted would be re-subscribed silently. Technically valid, but it brings back people who deliberately left. If you have a suppression list (by your own user id or email), send it to us and we exclude those users from every campaign. If you don’t, we recommend subscribing on an explicit click (a bell or a prompt) instead of automatically.
  • If your web push ran on a subdomain provided by your previous vendor rather than on your own domain, the permission belongs to that subdomain. Those subscribers cannot be recovered and have to opt in again on your site. Check which setup you have before you plan the switch.

8. What to expect after the import

  • A device may appear twice for a while. The imported record carries the OneSignal identifier; once our SDK runs on the same device, it registers with its own identifier. The stale record is removed by uninstall tracking or the automatic 90-day inactivity cleanup. Supplying external_user_id keeps both records under one user profile in the meantime.
  • Dead tokens leave on your first campaign. Apple and Google only reveal that a token is invalid when a message is actually sent, so the first send after the migration also cleans your base.
  • Your imported number will be lower than your OneSignal counter. Unsubscribed rows, rows with an empty token and web push rows are excluded by design. Our report tells you exactly how many fell into each group.

9. Where to click

Exact paths for the items on your side, so nobody has to hunt through dashboards.

TaskClick path
OneSignal: export the audienceAudience > Subscriptions > optional segment filter > column picker > Export
OneSignal: App ID and API keySettings > Keys & IDs. Take the App ID and an App API key; the export request sends it as Authorization: Key <App API key>
Apple: APNs Auth Keydeveloper.apple.com > Certificates, Identifiers & Profiles > Keys > + > Apple Push Notification service (APNs) > Continue > Register > Download. The .p8 file downloads only once; the Key ID is on the same screen, and the Team ID sits under Membership details.
Firebase: service account JSONconsole.firebase.google.com > your project > gear icon > Project settings > Service accounts > Generate new private key
Huawei: App ID and App SecretAppGallery Connect > My projects > your project > your app > Project settings > App information
Pushwoosh: application code and device API tokenControl Panel > your application > Settings > API Access. The token must have permission for that application.
Your website: remove the old workerDelete OneSignal's service worker files from your site root, and unregister the running worker: navigator.serviceWorker.getRegistrations().then(rs => rs.forEach(r => r.unregister()))
Task
1 / 7
OneSignal: export the audience
Click path
Audience > Subscriptions > optional segment filter > column picker > Export
Task
2 / 7
OneSignal: App ID and API key
Click path
Settings > Keys & IDs. Take the App ID and an App API key; the export request sends it as Authorization: Key <App API key>
Task
3 / 7
Apple: APNs Auth Key
Click path
developer.apple.com > Certificates, Identifiers & Profiles > Keys > + > Apple Push Notification service (APNs) > Continue > Register > Download. The .p8 file downloads only once; the Key ID is on the same screen, and the Team ID sits under Membership details.
Task
4 / 7
Firebase: service account JSON
Click path
console.firebase.google.com > your project > gear icon > Project settings > Service accounts > Generate new private key
Task
5 / 7
Huawei: App ID and App Secret
Click path
AppGallery Connect > My projects > your project > your app > Project settings > App information
Task
6 / 7
Pushwoosh: application code and device API token
Click path
Control Panel > your application > Settings > API Access. The token must have permission for that application.
Task
7 / 7
Your website: remove the old worker
Click path
Delete OneSignal's service worker files from your site root, and unregister the running worker: navigator.serviceWorker.getRegistrations().then(rs => rs.forEach(r => r.unregister()))

Pre-October 1 checklist

Keep this open as you go. Nothing here needs a form to download.

  • Export sent to us, or app_id plus an App API key handed over so we pull it
  • Push credentials sent: APNs key, FCM service account JSON, HMS keys where used
  • Email and SMS channels configured with us, if you migrate those subscribers
  • Tag review sheet returned, dead tags removed, multi-value tags marked
  • Segment filters sent to us (screenshots are fine) for rebuild
  • Pushwoosh SDK integrated in a test build, test push received
  • Import completed, our imported and skipped report reviewed
  • Web push SDK live with the old service worker unregistered, if you use web push
  • Test delivery clean on a production build
  • Sending switched to Pushwoosh, OneSignal sends stopped

Version A: native iOS and Android

iOS. Add the Pushwoosh iOS SDK, then set two keys in Info.plist: Pushwoosh_APPID with your Pushwoosh application code and PW_API_TOKEN with your device API token. Call registerForPushNotifications() where you currently trigger the OneSignal prompt, and follow the iOS quick start for the exact initialization snippet for your SDK version. Remove the OneSignal SDK and its registration call so the two do not both request tokens.

Android. Add the com.pushwoosh:pushwoosh-firebase dependency, then add two meta-data entries inside the <application> tag of AndroidManifest.xml: com.pushwoosh.appid with your application code and com.pushwoosh.apitoken with your device API token. Call Pushwoosh.getInstance().registerForPushNotifications() from your initialization logic. Your Firebase configuration stays as it is, with google-services.json in the project; the FCM credentials go into the Control Panel under your Android platform configuration.

Set your tags with setTags() and your user identifier with setUserId() at the same points where you called the OneSignal equivalents, so segmentation keeps working after the swap.

Version B: Flutter and FlutterFlow

FlutterFlow wraps the Pushwoosh Flutter SDK, so the migration is mostly configuration rather than code.

  1. Add the Pushwoosh Flutter package as a custom dependency in your project.
  2. In a custom action, initialize the SDK with your application code and register for push notifications, following the Flutter quick start for the current initialization API.
  3. Set the native credentials the way any Flutter app does: Pushwoosh_APPID and PW_API_TOKEN in Info.plist for iOS, com.pushwoosh.appid and com.pushwoosh.apitoken in AndroidManifest.xml for Android.
  4. Remove the OneSignal integration so both SDKs are not registering at once.
  5. Map tags and user id with setTags() and setUserId() in your custom actions.

Version C: React Native

  1. Install the plugin: npm install pushwoosh-react-native-plugin --save, then pod install for iOS.
  2. Initialize and register in your root component:
import Pushwoosh from 'pushwoosh-react-native-plugin';
Pushwoosh.init({ pw_appid: "YOUR_APPLICATION_CODE" });
Pushwoosh.register();
  1. Add the device API token natively: PW_API_TOKEN in Info.plist for iOS, com.pushwoosh.apitoken as meta-data in AndroidManifest.xml for Android. On Android keep google-services.json in the project — the FCM credentials themselves live in the Control Panel.
  2. Remove the OneSignal React Native package and its init call.
  3. Carry your tags and user id across with setTags() and setUserId() from the plugin API.
Planning a move from OneSignal?

Get in touch with our team for assistance.

Book a call

FAQ

The SDK swap and a clean test send are a day's work for one developer. The import of your existing base runs in parallel on our side and keeps your audience reachable from day one, so nothing waits on app-store review or user updates. Send us the export or the API key early so we can start it.

Questions at any point go to your onboarding contact at Pushwoosh. We would rather answer before the import than reconcile numbers after it.


Pushwoosh Team
Content Team at Pushwoosh
Share

Related articles

View all