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:
- Convert the native token you get from the device into an Expo Push Token,
- 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)
- FCM V1 service account: Firebase console → Project Settings → Service Accounts → Generate new private key. Keep the downloaded JSON file.
- APNs key (iOS): You've most likely already uploaded a
.p8key to Firebase. You can reuse the same key (if not, see the steps to create one in the iOS guide). - Create a project on expo.dev and note its project ID (UUID).
- 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
- 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)
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:
{ "data": { "expoPushToken": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]" } }
This is the endpoint used internally by Expo's own
expo-notificationslibrary, and Expo does not document it separately.
3. Registering with NotiPilot
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
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) }
}
iOS — Getting the APNs token with Firebase Messaging
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
final token = Platform.isIOS
? await FirebaseMessaging.instance.getAPNSToken()
: await FirebaseMessaging.instance.getToken();
Full code: Flutter
React Native — @react-native-firebase/messaging
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_NOTIFICATIONSpermission 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
.p8key 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