Moving from OneSignal

What it actually costs, what cannot come with you, and every call mapped to its equivalent.

The shapes are similar enough that most of a port is renaming things: both products register a device token, attach it to a person, tag that person, and send to a segment. This page is the mapping, plus the two places where the similarity stops.

Read the second one before you plan a date. Mobile subscribers travel. Web push subscribers do not, unless you hold a key OneSignal does not export. That is a property of web push itself, not of either product, and no migration guide can make it go away — so it belongs at the top rather than in a footnote.

What travels

WhatComes across?Why
Android subscribers (FCM)Yes The tokens were issued by your Firebase project. Any provider holding your service account can send to them, and nobody has to re-opt-in.
iOS subscribers (APNs)Yes Bound to your Apple team and bundle id, not to a vendor. Same story.
Web push subscribersOnly with the original VAPID pair A browser subscription is cryptographically bound to the key pair that created it. Without that exact pair every send is a 403, forever. OneSignal does not hand out the private half.
People, emails, phone numbers, tagsYes Rows in a CSV. A person with an address and no device is a valid import and is reachable by email or SMS immediately.
Campaign history and delivery logsNo Export what you need for reporting before you close the account. Nothing imports somebody else's delivery events, here or anywhere.

The practical shape of this: your mobile audience moves on a Tuesday and never notices. Your web audience has to re-subscribe, so keep both providers running for web until the new subscriptions have built up, or accept the loss knowingly. Importing an audience has the detail, including what to do if you do hold the VAPID pair.

Four steps

  1. Bring your own credentials. Upload your Firebase service-account JSON and your APNs .p8 under Settings → Push platforms. These are the same credentials your current provider uses; notifications go out on your own keys, from your own Firebase project and Apple team. Nothing is shared and nothing is proxied through a vendor identity.
  2. Export and import the audience. A OneSignal CSV export is detected automaticallyidentifier becomes the push token, device_type becomes the platform. Upload it under Audience → Import. Nothing is written until you have seen a full report of what would happen, row by row, with a reason for every rejection.
  3. Swap the SDK. One dependency out, one in. The table below is the whole surface. Ship it, and both providers hold live tokens for the same devices — which is fine, and is what makes step four safe.
  4. Move the sends. Point one campaign at Notibase, compare the numbers, then move the rest. There is no cutover moment to get right: a device registered with both providers can be sent to from either, so you can move channel by channel and roll back by not sending.

Sending: the API

OneSignal's POST /notifications becomes POST /v1/messages. Targeting moves out of a flat list of include_* keys into one audience object, because they were always mutually exclusive and a flat list lets you write two of them.

OneSignalNotibaseNotes
included_segments: ["Subscribed Users"]"audience": {"all": true}
included_segments: ["My segment"]"audience": {"segment_id": "…"}Segments are saved filters here too; the id comes from POST /v1/segments.
include_subscription_ids"audience": {"device_ids": […]}A OneSignal subscription id is a device; the equivalent id is returned when you register one.
include_aliases: {external_id: […]}"audience": {"filter": {"attr": "external_id", "op": "in", "value": […]}}
filters: [{field, relation, value}, …]"audience": {"filter": <AST>}A nested boolean tree rather than a flat array with {"operator": "OR"} separators — see Audience filters. Anything you can express there you can express here.
headings: {en: "…"}"content": {"title": "…"}One language per send today. Multi-language is not built.
contents: {en: "…"}"content": {"body": "…"}
subtitle"content": {"subtitle": "…"}iOS renders it; the others ignore it, in both products.
url"content": {"url": "…"}
data: {…}"content": {"data": {…}}Handed to your app untouched.
big_picture / chrome_web_image / ios_attachments"content": {"image": "…"}One field for all three. Per-platform overrides still exist under content.android, content.web and content.ios when you want them to differ. On iOS an image needs the Notification Service Extension in your app, exactly as it does today.
large_icon / small_icon"content": {"android": {"largeIcon": …, "smallIcon": …}}
android_channel_id"content": {"android": {"channelId": "…"}}
ios_interruption_level"content": {"ios": {"interruptionLevel": "…"}}Same four values.
buttons"content": {"buttons": […]}Up to five — see Message content for which platforms draw them and what your app has to register.
collapse_id"content": {"advanced": {"collapseId": "…"}}
priority / TTL"content": {"advanced": {"priority": "high", "ttl": 43200}}
send_after"send_at"An absolute instant. Prefer send_at_local + timezone whenever a person typed the time — why.
delayed_option: "timezone" + delivery_time_of_day"local_time": "09:00"The same hour in every recipient's own zone. Paid plans.
delayed_option: "last-active"Not built. Send-time optimisation by last-active is not modelled.
throttle_rate_per_minuteNot exposed. Delivery is paced per channel from the provider's own rate limits.
template_idNot built. Templates are on the list and are not there yet; content is written per send or generated by your own code.
"platforms": ["ios"]No equivalent needed — restricting a send to some platforms is one field rather than a channel with empty content.

Two things ride the same send with no OneSignal equivalent: content.inapp writes a persistent in-app inbox item, and content.email / content.sms reach the same audience by address. One campaign, one audience, five channels.

Identifying people: the SDK

OneSignalNotibase
OneSignal.initialize(appId)Notibase.init(context, clientKey) — Android · Notibase.configure(clientKey:) — iOS, Flutter and React Native · new Notibase({clientKey}) — web
OneSignal.login(externalId)Notibase.identify(externalId) · nb.identify(externalId). Both take an HMAC signature your backend mints once identity verification is switched on — see Security & keys.
OneSignal.logout()Nothing to call. Identity is per device and re-identifying replaces it.
OneSignal.User.addTag(k, v)Attributes go on the person: identify(id, {attributes: {…}}), or POST /v1/users from your server, which is the better place for anything your server already knows. See User attributes.
OneSignal.Notifications.requestPermission()nb.promptForPush() on web. On mobile you ask the platform yourself, or prime it with an in-app message so the one system prompt an install gets is spent on somebody who already said yes.
OneSignal.User.pushSubscription.optOut()nb.unsubscribe() · POST /v1/unsubscribe, which can also opt a person out across every device, address and number at once.
Outcomes / eventsPOST /v1/events · Notibase.track(name, properties). Purchases feed attribution revenue.

What Notibase does not have

Written down here rather than discovered after a migration.

If any of those is load-bearing for you, the honest answer is to wait or to stay.

The pricing question

The reason most people read this page in 2026, so it is worth being precise rather than promotional.

OneSignal's own billing documentation states that “on the Free plan, you must have fewer than 1,000 monthly active users (MAU) to send mobile push and in-app messages”, applying to new customers from 1 September 2026 and to existing customers from 1 October 2026. Their documentation is the authority on their own plans and it is where you should confirm this before making a decision: documentation.onesignal.com/docs/en/billing-faq. Read there on 28 August 2026; plans change, and this page will not always be the first thing to notice.

What is worth understanding is the shape rather than the number. A cap on monthly active users prices the size of your audience. A cap on messages sent prices what you actually do with it. Notibase meters sends: subscribers are unlimited on every plan including the free one, and an app with fifty thousand users who gets one notification a month is a smaller bill than an app with five thousand who gets one a day — which is the right way round, because the second app is the one using the service.

Our own numbers are on the pricing page, live from the same source the product bills against, so they cannot go stale the way a hand-typed comparison table does.

← Importing an audienceREST API →