Quickstart

Register an APNs, FCM or browser push token with the Native Notify API — Swift, Kotlin, Jetpack Compose, React Native, Expo, Flutter and web examples — then send your first push notification.

Register a device and send yourself a push notification. About 10 minutes on web or Android; the first iOS setup adds Apple-console time. Everything is plain HTTPS — no SDK.

1. Create your app

About 2 minutes. Create an app in the dashboard, then open App Settings → App keys. Every request carries that app's app id and app token — in the body for POSTs, in the URL for GETs.

2. Save your push credentials

About 5 minutes with the keys in hand — web push needs none, so web-only setups can skip straight to step 3.

Real sends go out with your push credentials: an FCM v1 service-account JSON for Android and an Apple APNs .p8 key (with its Key ID, your Team ID and the app's bundle id) for iOS. Save them under App Settings → Push credentials. Each save is verified live against Apple and Google. Where to get each one: Push Credentials.

3. Register a device

About 5 minutes — this is the step your app runs at launch.

POST https://app.nativenotify.com/api/universal/device/register
{
  "appId": 123,
  "appToken": "yourAppToken",
  "deviceId": "A1B2C3D4-install-key",
  "platform": "ios",
  "tokens": { "apnsToken": "0c9d8e…" }
}

Registration stores two things for the device: the device id you generate once and keep, and the platform's push token — APNs on iOS, FCM on Android, a PushSubscription on the web. The tabs above show the exact platform and tokens shape for each. Registration is an idempotent upsert — call it on every app launch and whenever the token changes.

Examples

Ask for permission, let APNs hand you the device token, and register it:

import UIKit
import UserNotifications

@main
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        UNUserNotificationCenter.current().delegate = self
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, _ in
            guard granted else { return }
            DispatchQueue.main.async { application.registerForRemoteNotifications() }
        }
        return true
    }

    // APNs delivers the device token here.
    func application(_ application: UIApplication,
                     didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        let apnsToken = deviceToken.map { String(format: "%02x", $0) }.joined()
        Task { await register(apnsToken: apnsToken) }
    }

    func register(apnsToken: String) async {
        var request = URLRequest(url: URL(string: "https://app.nativenotify.com/api/universal/device/register")!)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = try? JSONSerialization.data(withJSONObject: [
            "appId": 123,
            "appToken": "yourAppToken",
            "deviceId": DeviceIdentity.current,   // a stable id you persist (Keychain, UserDefaults)
            "platform": "ios",
            "tokens": ["apnsToken": apnsToken]
        ])
        _ = try? await URLSession.shared.data(for: request)   // 201 = registered
    }
}

The full Swift walkthrough — including the stable deviceId and tap handling — is the Swift recipe.

4. Send yourself a test push

About 1 minute. Send to the device you just registered and read the honest per-transport result:

curl -X POST https://app.nativenotify.com/api/universal/test-send \
  -H "Content-Type: application/json" \
  -d '{
    "appId": 123,
    "appToken": "yourAppToken",
    "title": "Hello from Native Notify",
    "message": "If you can read this, APNs/FCM accepted it.",
    "deviceId": "YOUR_DEVICE_ID"
  }'

5. Send to everyone

About 1 minute. Reach every registered device — change the audience:

curl -X POST https://app.nativenotify.com/api/universal/notifications/send \
  -H "Content-Type: application/json" \
  -d '{
    "appId": 123,
    "appToken": "yourAppToken",
    "title": "Hello everyone",
    "message": "Our first push notification.",
    "audience": { "type": "all" }
  }'

Stuck?

SymptomLikely cause
401, invalid app id or tokenThe appToken is wrong — or a publishable web key (nnweb_…) was used from a native device. The web key belongs only in the appToken field of a platform: "web" registration.
400 invalid_token_formatThe token isn't the raw APNs or FCM string. Expo push tokens are rejected by this API — pass the native token (getDevicePushTokenAsync()); see the Expo recipe.
400 token_platform_mismatchplatform and the token field disagree: apnsToken → ios, fcmToken → android, webPush → web.
test-send refused with a reasonCodeThe transport rejected it — nearly always the credential from step 2 (bundle id, team id, or a sandbox/production mix-up). Verify Delivery reads every reason.
Accepted, but nothing shows on AndroidFCM delivers data messages, so your app displays them itself — see the display section of your platform guide: Kotlin, Jetpack Compose, Flutter.
Accepted, but nothing shows on iPhone or iPad (web)Safari delivers web push only to sites added to the Home Screen first.
403 plan_upgrade_requiredThe plan doesn't include that audience — Premium allows one subscriber or "all", not groups or device lists. See Plans & Billing.

Next steps