Developers · API v1.0.0

Integration for Projects Using Firebase (FCM)

This guide is for teams whose apps already use Firebase Cloud Messaging (FCM) — whether native Android/iOS, Flutter, React Native Firebase, or Capacitor. It explains how to add NotiPilot without disrupting your existing Firebase setup. For the general API reference, see the Overview.

Supported versions: Firebase Android BoM 33+ · Firebase iOS SDK 10+ · firebase_messaging (Flutter) 15+ · @react-native-firebase/messaging 20+

The one thing you need to know

NotiPilot 1.0.0 delivers notifications through the Expo Push Service, and Expo in turn uses FCM on Android and APNs on iOS. So your Firebase infrastructure stays exactly as it is; you only need to:

  1. Convert the native token you get from the device into an Expo Push Token,
  2. Register that token with NotiPilot.
Platform Token to send for conversion Firebase API
Android FCM registration token FirebaseMessaging.getInstance().getToken()
iOS APNs device token (not the FCM token!) Messaging.messaging().apnsToken

⚠️ On iOS, don't send the FCM token provided by Firebase. Expo delivers to iOS directly via APNs, so it needs the raw APNs token.

You can also register a raw FCM token directly with the API (provider: "fcm"); the device will be registered and appear in segments, but in 1.0.0 notifications are not delivered to these devices.

1. Firebase and Expo credentials (one time)

  1. FCM V1 service account: Firebase console → Project Settings → Service Accounts → Generate new private key. Keep the downloaded JSON file.
  2. APNs key (iOS): You've most likely already uploaded a .p8 key to Firebase. You can reuse the same key (if not, see the steps to create one in the iOS guide).
  3. Create a project on expo.dev and note its project ID (UUID).
  4. expo.dev → Project → Credentials:
    • Android → FCM V1 service account key → the JSON from step 1
    • iOS → Add Bundle ID → Push Key → .p8 + Key ID + Team ID
  5. Add your app in the NotiPilot dashboard: Expo Project ID, Expo Access Token (expo.dev → Access Tokens), Android Package, iOS Bundle ID.

2. Token conversion (the same HTTP request for all platforms)

HTTP
POST https://exp.host/--/api/v2/push/getExpoPushToken
Content-Type: application/json

{
  "type": "fcm",                        // Android: "fcm", iOS: "apns"
  "deviceId": "8f14e45f-ceea-467a-9575-0b3f5c6b2a11", // same as device_uid (lowercase UUID)
  "development": false,                 // true for iOS debug builds (APNs sandbox)
  "appId": "com.example.app",           // Android package / iOS bundle id
  "deviceToken": "<FCM or APNs token>",
  "projectId": "<Expo project ID>"
}

Response:

JSON
{ "data": { "expoPushToken": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]" } }

This is the endpoint used internally by Expo's own expo-notifications library, and Expo does not document it separately.

3. Registering with NotiPilot

HTTP
POST https://app.notipilot.com/api/v1/register-device
Content-Type: application/json

{
  "app_id": "<Expo project ID>",
  "device_uid": "8f14e45f-ceea-467a-9575-0b3f5c6b2a11",
  "token": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]",
  "platform": "android",
  "provider": "expo",
  "attributes": { "locale": "tr", "country": "TR", "city": "Istanbul" }
}

4. Where to hook into your existing Firebase code

Android — FirebaseMessagingService

Kotlin
override fun onNewToken(token: String) {
    // Your existing code (e.g. sending it to your own backend) can stay as is
    scope.launch { NotiPilot.registerDevice(applicationContext, fcmToken = token) }
}

Full code: Kotlin · Java

iOS — Getting the APNs token with Firebase Messaging

Swift
import FirebaseMessaging

extension AppDelegate: MessagingDelegate {
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
        // FCM token refreshed; use the APNs token for NotiPilot
        guard let apnsToken = Messaging.messaging().apnsToken else { return }
        Task { try? await NotiPilot.shared.registerDevice(apnsToken: apnsToken) }
    }
}

If FirebaseAppDelegateProxyEnabled is disabled, make sure you assign Messaging.messaging().apnsToken = deviceToken inside didRegisterForRemoteNotificationsWithDeviceToken. Full code: Swift · Objective-C

Flutter — firebase_messaging

Dart
final token = Platform.isIOS
    ? await FirebaseMessaging.instance.getAPNSToken()
    : await FirebaseMessaging.instance.getToken();

Full code: Flutter

React Native — @react-native-firebase/messaging

TypeScript
const token = Platform.OS === 'ios'
  ? await messaging().getAPNSToken()
  : await messaging().getToken();

Full code: React Native — Method B

5. Reading incoming notifications

Expo sends notifications to Android via FCM with the following fields. Write your code so it supports both formats:

Information Location
Title notification.title or data["title"]
Body notification.body or data["message"]
Custom data sent from the dashboard data["body"] (JSON string)
Channel data["channelId"] = "default"

On iOS, the title and body arrive in the standard aps.alert; custom data is under the userInfo["body"] key.

6. Things to watch out for when using alongside Firebase

  • You can keep sending notifications via FCM from your own backend; NotiPilot doesn't interfere with that.
  • To avoid duplicate notifications, don't send the same campaign from both your own system and NotiPilot.
  • The Firebase Web SDK (web push) is not supported in 1.0.0.
  • The POST_NOTIFICATIONS permission is required on Android 13+, and user permission is required on iOS (if you already request these for Firebase, there's nothing extra to do).

Checklist

  • FCM V1 service account JSON and APNs .p8 key uploaded to the Expo project
  • On iOS, the APNs token (not the FCM token) is being converted
  • NotiPilot registration is refreshed in onNewToken / didReceiveRegistrationToken
  • Expo Project ID, package name, and bundle ID in the dashboard are correct