Push notifications

The service stores your app's push tokens and sends notifications through Firebase Cloud Messaging (Android, web, and iOS apps that use FCM) or APNs directly (iOS). Add the credentials on the dashboard's Settings page: a Firebase service account for FCM, an APNs key for APNs.

Send notifications from the dashboard's Push page or from your backend (Admin API): to every device, a topic, a device, or users by uid.

Registering the device

Get the push token from the platform, then POST /push/devices:

Field Value
provider fcm, or apns for a raw APNs device token (hex) on iOS
platform ios, android or web
token The push token
app_version, language Optional, shown in the dashboard
device_id The id from an earlier registration, when the token changed, so the device keeps its topics

Send the access token when signed in: the device is then linked to the user (for notifications to users) until that sign-in ends. Register again after sign-in, after sign-out, and whenever the token changes. The answer (201 new, 200 known) has the device id: keep it.

// Flutter: firebase_messaging
final token = await FirebaseMessaging.instance.getToken();
final json = await call('POST', '/push/devices', accessToken: session.accessToken, body: {
  'provider': 'fcm',
  'platform': Platform.isIOS ? 'ios' : 'android',
  'token': token,
  'app_version': appVersion,
  if (deviceId != null) 'device_id': deviceId,
});
deviceId = json['data']['id']; // keep it
FirebaseMessaging.instance.onTokenRefresh.listen((_) { /* register again */ });
// React Native: @react-native-firebase/messaging
import messaging from '@react-native-firebase/messaging';

const token = await messaging().getToken();
const { data } = await call('POST', '/push/devices', {
  provider: 'fcm',
  platform: Platform.OS,
  token,
  app_version: APP_VERSION,
  ...(deviceId ? { device_id: deviceId } : {}),
}, accessToken);
deviceId = data.id; // keep it
messaging().onTokenRefresh(() => { /* register again */ });
// iOS with APNs directly: in the AppDelegate after registerForRemoteNotifications()
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    let token = deviceToken.map { String(format: "%02x", $0) }.joined()
    Task {
        var body: [String: Any] = ["provider": "apns", "platform": "ios", "token": token]
        if let deviceId = UserDefaults.standard.string(forKey: "push_device_id") { body["device_id"] = deviceId }
        let json = try await call("POST", "/push/devices", body: body, accessToken: session.currentAccessToken)
        UserDefaults.standard.set((json["data"] as! [String: Any])["id"] as! String, forKey: "push_device_id")
    }
}
// Android: Firebase Cloud Messaging
FirebaseMessaging.getInstance().token.addOnSuccessListener { token ->
    scope.launch(Dispatchers.IO) {
        val body = JSONObject().put("provider", "fcm").put("platform", "android").put("token", token)
            .put("app_version", BuildConfig.VERSION_NAME)
        prefs.getString("push_device_id", null)?.let { body.put("device_id", it) }
        val data = call("POST", "/push/devices", body, session.accessToken).getJSONObject("data")
        prefs.edit().putString("push_device_id", data.getString("id")).apply()
    }
}
// and in your FirebaseMessagingService.onNewToken(token): register again
// Web: the Firebase JS SDK's messaging, with your VAPID key and a service worker
import { getMessaging, getToken } from 'firebase/messaging';

const token = await getToken(getMessaging(firebaseApp), { vapidKey: VAPID_KEY });
const { data } = await call('POST', '/push/devices', {
  provider: 'fcm', platform: 'web', token, ...(deviceId ? { device_id: deviceId } : {}),
}, accessToken);
localStorage.setItem('push_device_id', data.id);

Only the token-fetching part uses Firebase's SDK; the service never needs your users to have Firebase accounts.

Topics

  • PUT /push/devices/{id}/topics/{topic} follows a topic (204), DELETE unfollows.
  • Topic names: letters, digits and -_.~%, up to 100 characters; at most 200 per device.
  • GET /push/devices/{id} shows the device with its topics.
await call('PUT', `/push/devices/${deviceId}/topics/offers`);

Turning notifications off

DELETE /push/devices/{id} forgets the device (204). Devices whose tokens FCM or APNs reject are disabled automatically.