Universal Push (Any Framework)

Push notifications for ANY app framework — Flutter, bare React Native, native Swift, native Kotlin, .NET, Unity — by registering APNs and FCM tokens directly instead of Expo tokens.

Native Notify started as an Expo push notification service, and the Expo SDK path is still the fastest setup for Expo apps. Universal push is the framework-agnostic path: your app registers its own APNs (iOS) and FCM (Android) device tokens, and Native Notify sends through Apple's and Google's push services directly.

Any client that can make an HTTPS request can use it — Flutter, bare React Native, native Swift, native Kotlin, .NET, Unity, or a backend. No SDK is required.

Expo SDK pathUniversal push
Token typeExpo push tokens (ExponentPushToken[...])APNs device tokens + FCM registration tokens
RegistrationregisterNNPushToken() / SDK functionsPOST /api/universal/device/register
Who talks to Apple/GoogleExpo's push serviceNative Notify, with your FCM project + Apple key
Delivery evidenceExpo delivery receipts (~15 min after a send)APNs/FCM accept-or-reject at send time — no receipts
Extra featuresNotification Inboxes, follow/topic pushes, analytics app-opensDevice/subscriber audiences, health + test-send probes

You can use both paths in one account — different apps, or even the same app.

Endpoints

All endpoints live on https://app.nativenotify.com and authenticate with your app id + app token (the pair on your app in the dashboard), exactly like the rest of the API.

EndpointAuthWhat it does
POST /api/universal/device/registerbody appId + appTokenRegister a device + its APNs/FCM token(s). Idempotent upsert.
POST /api/universal/device/deregisterbody appId + appTokenRemove a device (logout / uninstall flow). Idempotent.
POST /api/universal/notifications/sendbody appId + appTokenSend one notification to all devices, a device list, or a subscriber list.
POST /api/universal/test-sendbody appId + appTokenSend to ONE device or token and get the honest result, with a masked token.
GET /api/universal/health/:appId/:appTokenurl pairDevice + token counts (live/dead) from the universal service's own storage.
POST /api/universal/credentials/validatesession (admin)Live check of the app's stored FCM service account + APNs key.

The model

  • A device is identified by a deviceId you choose — a stable per-install key (vendor id, Android ID, or a generated UUID you persist). Re-registering the same deviceId updates the device instead of creating a duplicate; a token that arrives with a different deviceId (reinstall, device transfer) is moved to that device.
  • subscriberId makes a device an indie device. Send the subscriber id when your app has logged-in users; omit it for a mass (all-users) device. One subscriber can have many devices — that is a supported pattern.
  • APNs tokens belong to iOS devices, FCM tokens to Android devices. A platform mismatch is rejected with a 400 (token_platform_mismatch); there is exactly ever one row per (app, token type, token).
  • Errors are machine-readable: { "error": { "code", "message", "field"? } } — stable codes like no_tokens_provided, invalid_token_format, app_not_found.
  • No receipts on this path. APNs and FCM answer a send immediately, and that acceptance is the strongest signal a native token can give — responses say delivered: null rather than fabricating a number. See Verify Delivery.

Credentials required:

Universal sends use your push credentials: an FCM v1 service-account JSON (Android) and an Apple APNs .p8 key (iOS). The legacy FCM server key is no longer accepted — Google shut it down. See Push Credentials.

Next steps