Integrating Push Notifications

This integration guide includes selected snippets from the SDK spec documentation. For more detail, always check the spec documentation of your specific SDK.

Push Notifications are a great way to keep your users engaged with your application. Countly Push Notifications gives product managers, marketers, and developers a simple way to send, manage, and analyze push notifications for applications on the world's most popular platforms - iOS, Android, Huawei devices, and web browsers.

Countly relies on platform-specific push notification providers to deliver messages:

  • APN (Apple Push Notification service) - for iOS, iPadOS, watchOS, tvOS, and macOS apps
  • FCM (Firebase Cloud Messaging) - for Android apps with Google Play Services
  • HMS (Huawei Mobile Services) - for Huawei devices without Google Play Services
  • Web Push (VAPID) - for websites, delivered through the push service of each visitor's browser

This guide walks you through the complete setup - from acquiring provider credentials and uploading them to your Countly Server, to integrating the SDK in your application.

Availability: Push Notifications are available in Countly Lite, Countly Enterprise, and as an add-on in Flex. Countly Lite has limited feature and view availability. Enterprise Edition includes additional geolocations support.

To understand how you can leverage Push Notifications in your dashboard, check the guide below:

How Push Notifications Work

When a user opens your app for the first time, the Countly SDK automatically retrieves a device token from the respective push notification provider (Firebase or Huawei Push Kit for Android, APN for iOS) and sends it to your Countly Server for storage. A token is unique to a device, meaning it identifies that particular device.

On websites, the Web SDK creates a push subscription (the web equivalent of a device token) once the visitor allows notifications, and sends it to your Countly Server the same way.

The high-level flow is:

  1. Your App (SDK) - retrieves a device token from the provider on first launch.
  2. APN / FCM / HMS - issues the token to the device.
  3. Countly Server - stores the token and uses your uploaded credentials to send notification payloads.
  4. APN / FCM / HMS - delivers the notification to the user's device.

Token retrieval and submission to the Countly Server are handled automatically by the Countly SDK. You won't need to worry about this process once you have integrated the Countly SDK properly as shown in the steps below. On websites, your page asks the visitor for permission once, from a click; the Web SDK handles everything after that, including visitors who allowed notifications on an earlier visit.

Select your provider:

FCM
APN
HMS
Web Push

Server Side Setup

To send push notifications to Android devices, you need a Firebase project with your Android app registered and the Firebase Cloud Messaging API (V1) enabled. Countly uses the FCM HTTP v1 API, which requires a Service Account JSON file from your Firebase project.

  1. Open the Firebase Console and select your project.
  2. Go to Project Settings (gear icon) > Service accounts tab.
  3. Click Generate new private key and confirm. A JSON file will be downloaded - store it securely.
  4. Ensure that the Firebase Cloud Messaging API (V1) is enabled for your project. You can verify this in the Google Cloud Console API Library.
  5. In the Countly Dashboard, navigate to Management > Applications > Your App > Push Notifications. Upload the Service Account JSON file under the FCM section and click Save changes.

Important: Do not confuse google-services.json (used by the client SDK in your app) with the Service Account JSON (used by the server to send notifications). They are different files. The Service Account JSON is the one downloaded from the Service accounts tab in the Firebase Console.

To send push notifications to iOS devices, you need an active Apple Developer Account. Countly supports two authentication methods for APN: Auth Key (.p8) and Certificate (.p12). We strongly recommend using the Auth Key approach - it does not expire, works for both sandbox and production environments, and is simpler to manage.

Auth Key (.p8) - Recommended Certificate (.p12)
  1. Sign in to the Apple Developer Portal and navigate to Certificates, Identifiers & Profiles > Keys.
  2. Click the + button to create a new key. Enable Apple Push Notifications service (APNs), give it a name, and click Register.
  3. Download the .p8 file. You can only download this file once, so store it securely.
  4. Note the Key ID (shown on the key details page), your Team ID (found under Membership Details), and the Bundle ID of your app.
  5. In the Countly Dashboard, go to Management > Applications > Your App > Push Notifications. Upload the .p8 file, and fill in the Key ID, Team ID, and Bundle ID fields. Click Save changes.

Countly will validate the credentials by initiating a test connection to APN. If validation fails, double-check that the Key ID, Team ID, and Bundle ID are correct.

To send push notifications to Huawei devices, you need a Huawei AppGallery Connect project with Push Kit enabled.

  1. Sign in to Huawei AppGallery Connect, select your project, and navigate to Grow > Push Kit.
  2. Enable Push Kit if it is not already enabled.
  3. Navigate to Project settings > General information to find your App ID and App Secret (under App information).
  4. In the Countly Dashboard, navigate to Management > Applications > Your App > Push Notifications. Enter the App ID and App Secret under the HMS section and click Save changes.

To send push notifications to websites, Countly uses the standard Web Push service of each browser, authenticated with a VAPID key pair. You do not need an account with a push provider: Countly generates the keys for you. Web push needs Countly 26.10 or later, set up in the new dashboard.

  1. In the Countly Dashboard, go to Manage > Projects, select your project and open its Settings. Under Push Settings, click Configure next to Web Push (VAPID).
  2. Enter a Contact (VAPID subject): a mailto: address or an https:// URL the push services can use to reach you.
  3. Click Generate keypair, then Download keys and store the file securely.
  4. Click Save credentials.
  5. Click Copy public key. You will give this VAPID public key to the Web SDK in the SDK side setup.

Important: The private key cannot be recovered from the dashboard after you save. If it is lost, or if you generate new keys, existing subscribers stop receiving notifications until they visit your site again: with the new public key set as push_vapid_public_key, the Web SDK moves returning visitors who allowed notifications to the new key.

SDK Side Setup

All code samples assume you have already integrated the Countly SDK into your project. If you haven't, please refer to the Getting Started with SDKs guide first.

Select an SDK:

Android Flutter React Native Unity

Make sure you have followed Google's guide for adding Firebase to your project. Then add the Firebase Messaging dependency to your app-level build.gradle:

// app/build.gradle
// use the latest firebase-messaging version that is available
implementation 'com.google.firebase:firebase-messaging:LATEST_VERSION'

Add a service definition to your AndroidManifest.xml:

<service android:name=".DemoFirebaseMessagingService">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

Add a class for the service:

public class DemoFirebaseMessagingService extends FirebaseMessagingService {

    @Override
    public void onNewToken(@NonNull String token) {
        super.onNewToken(token);
        CountlyPush.onTokenRefresh(token);
    }

    @Override
    public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);
        CountlyPush.Message message = CountlyPush.decodeMessage(remoteMessage.getData());
        if (message != null) {
            message.recordAction(getApplicationContext());
            CountlyPush.displayMessage(getApplicationContext(), message, R.drawable.ic_message, null);
        }
    }
}

Initialize CountlyPush in your Application class. Then initialize the Countly SDK as usual:

// In your Application.onCreate()
CountlyConfigPush countlyConfigPush = new CountlyConfigPush(this);
CountlyPush.init(countlyConfigPush);

CountlyConfig config = new CountlyConfig(this, "APP_KEY", "https://YOUR_SERVER");
Countly.sharedInstance().init(config);

Intent Redirection Security

By default, the Countly Android SDK enables additional intent redirection checks to protect against Intent Redirection Vulnerability. You can allowlist specific intents if needed:

CountlyConfigPush countlyConfigPush = new CountlyConfigPush(this)
    .setAllowedIntentClassNames(allowedClassNames)
    .setAllowedIntentPackageNames(allowedPackageNames);
CountlyPush.init(countlyConfigPush);

Select an SDK:

iOS Flutter React Native Unity

Xcode Configuration

Under the Signing & Capabilities section of Xcode, enable the following for your target:

  1. Click + Capability and add Push Notifications.
  2. Click + Capability again and add Background Modes. Enable Remote notifications.

SDK Initialization

Start Countly in the application:didFinishLaunchingWithOptions: method of your app. Do not forget to specify CLYPushNotifications in the features array on the CountlyConfig object:

#import "Countly.h"

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    CountlyConfig* config = CountlyConfig.new;
    config.appKey = @"APP_KEY";
    config.host = @"https://YOUR_COUNTLY_SERVER";
    config.features = @[CLYPushNotifications];
    [Countly.sharedInstance startWithConfig:config];

    return YES;
}

Note: Make sure you start Countly iOS SDK on the main thread.

The Countly iOS SDK handles everything automatically - requesting notification permission, retrieving the device token, processing incoming notifications, and managing Notification Service Extensions for rich push (media attachments and custom action buttons).

The Unity SDK does not support Huawei Push Kit.

Select an SDK:

Android Flutter React Native

Assuming you have already integrated HMS Core into your app, add the Huawei Push Kit dependency to your app-level build.gradle (use the latest dependency version):

implementation 'com.huawei.hms:push:LATEST_VERSION'

Add a service definition to AndroidManifest.xml:

<service
    android:name=".DemoHuaweiMessagingService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.huawei.push.action.MESSAGING_EVENT" />
    </intent-filter>
</service>

Add the service class:

public class DemoHuaweiMessagingService extends HmsMessageService {

    @Override
    public void onNewToken(String token) {
        super.onNewToken(token);
        CountlyPush.onTokenRefresh(token, Countly.CountlyMessagingProvider.HMS);
    }

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);
        CountlyPush.Message message = CountlyPush.decodeMessage(remoteMessage.getDataOfMap());
        if (message != null) {
            message.recordAction(getApplicationContext());
            CountlyPush.displayMessage(getApplicationContext(), message, R.drawable.ic_message, null);
        }
    }
}

Initialize CountlyPush with HMS as the preferred provider, then initialize the Countly SDK:

// In your Application.onCreate()
CountlyConfigPush countlyConfigPush = new CountlyConfigPush(this)
    .setProvider(Countly.CountlyMessagingProvider.HMS);
CountlyPush.init(countlyConfigPush);

CountlyConfig config = new CountlyConfig(this, "APP_KEY", "https://YOUR_SERVER");
Countly.sharedInstance().init(config);

Web push needs Countly Web SDK 26.8.0 or later and a site served over HTTPS (localhost also works for testing). It works in browsers that support the Push API: Chrome, Edge, Firefox, Opera, Samsung Internet and Safari 16.1 or later on macOS 13 or later. On iPhone and iPad (iOS/iPadOS 16.4 or later), it works only for sites added to the Home Screen. See Browser Support and Limitations.

  1. Serve the Countly service worker from your own site. Take lib/countly_sw.js of the SDK version your pages use, from the countly-sdk-web npm package or from the SDK's GitHub repository, and serve it from the root of your site, for example as https://your-site.com/countly_sw.js. Replace it with the new copy whenever you upgrade the SDK.
  2. Give the SDK your VAPID public key during initialization:

    Countly.init({
      app_key: "YOUR_APP_KEY",
      url: "https://YOUR_SERVER",
      push_vapid_public_key: "YOUR_VAPID_PUBLIC_KEY",
      // only needed if the file is not served at /countly_sw.js
      push_service_worker_path: "/countly_sw.js"
    });
  3. Ask for permission from a button the visitor clicks:

    document.getElementById("enable-push").addEventListener("click", function () {
      Countly.enable_push_notifications().then(function (result) {
        // result.subscribed is true on success, otherwise result.reason says why
      });
    });

Browsers show the notification permission prompt only while handling a click or tap. Call enable_push_notifications directly in the click handler, after the SDK has loaded, not through Countly.q. Visitors who already allowed notifications are registered again silently on later visits, and notification clicks are recorded automatically. See Enabling Push.

The SDK registers countly_sw.js under its own scope (/countly-push/), so it does not interfere with a service worker your site already has. To run Countly push inside your own service worker instead, import countly_sw.js into it and pass its registration as push_service_worker_registration. See Using Your Own Service Worker.

Customizing Notification Sound

Countly supports custom notification sounds on every platform that exposes a native sound API. The sound file must be present on the device — push payloads can reference it by name or URI, but cannot link to an audio file on the internet. The device-side setup is described below per platform; sending the sound is then done from the Countly Dashboard Send sound field when creating a push notification.

Web browsers always use the system's notification sound; custom sounds are not available for web push.

Custom sound files must ship inside the app bundle (iOS) or under the Android res/raw resource folder. They cannot be downloaded at notification time.

Android iOS Flutter React Native Unity

Place the sound file in your Android project's res/raw folder, then build the resource URI:

String soundUri = ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + getApplicationContext().getPackageName() + "/" + R.raw.notif_sound;

Send this URI in the Send sound field of the Countly Dashboard when creating a push notification. This covers devices running Android with SDK version below 26.

For Android SDK 26 and above, the sound is bound to the notification channel rather than the individual notification. Provide the same URI when you create the notification channel:

AudioAttributes audioAttributes = new AudioAttributes.Builder()
    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
    .setUsage(AudioAttributes.USAGE_NOTIFICATION)
    .build();

channel.setSound(soundUri, audioAttributes);

If your app requires GDPR compliance or uses Countly's consent management feature, you must grant the push consent for push notifications to function. Without this consent, the SDK will not send the device token to the server.

Android iOS Flutter React Native Unity Web
// Give push consent during initialization
config.setConsentEnabled(new String[]{"push"});

// Or after initialization
Countly.sharedInstance().consent().giveConsent(new String[]{"push"});

// Revoke consent
Countly.sharedInstance().consent().removeConsent(new String[]{"push"});

The Countly SDK does not persistently store the status of given consents except for push notifications. You are expected to handle receiving consent from end-users using proper UIs depending on your app's context, and call the giveConsent method on each app launch depending on the permissions you managed to get from the end-users. The Web SDK does not store any consent, push included: give the consents again on every page load.

Separating Marketing and Informative Push Notifications

SDK consent acts as a channel-wide gate. When the push consent is not granted, no notifications reach the device, including informative ones such as transaction receipts or service status updates. To deliver informative push to every user while reserving marketing pushes for opted-in users, grant SDK consent unconditionally and segment campaigns on the server through a custom user property.

  1. Grant SDK consent unconditionally. Regardless of the user's marketing preference, grant the push consent shown above. This keeps the device eligible to receive any push notification, whether informative or marketing.
  2. Track the marketing preference as a custom user property. When the user accepts or declines marketing communications in your app, record that decision as a user property such as marketing_consent:

    // Android
    Countly.sharedInstance().userProfile().setProperty("marketing_consent", "true");
    Countly.sharedInstance().userProfile().save();

    Equivalent user-profile methods exist in every Countly SDK. See the User Profile section of the SDK documentation for your platform.

  3. Segment campaigns in Countly. Use marketing_consent as a segmentation filter when creating a push notification:
    • Informative campaigns (transactional messages, service updates): no filter on marketing_consent, so every push-enabled user is reachable.
    • Marketing campaigns (promotions, news): filter by marketing_consent = "true", so only opted-in users receive them.

Set marketing_consent explicitly for every user during onboarding, even when the user skips the consent prompt. If the property is never recorded, those users may unintentionally pass through marketing filters depending on how the dashboard condition is configured.

Testing Push Notifications

After completing both the server-side credential upload and SDK integration, follow these steps to verify everything is working:

  1. Run your app on a physical device (push notifications do not work on most simulators/emulators). On iOS, accept the notification permission prompt when it appears.
  2. Check the Countly Dashboard. Navigate to Push Notifications. You should see at least 1 push-enabled user for your platform. This confirms that the device token was successfully sent to the Countly Server.
  3. Send a test notification. Click + New Message, select the platform, compose a message, and choose Send to test users. The notification should appear on your test device within seconds.
  4. Websites: open your site over HTTPS (or on localhost) in a supported browser, click the button that calls enable_push_notifications and allow notifications. In the new dashboard, create a push with New push, select Web and send it to your test users. With debug: true, the browser console shows each step, including the service worker's lines marked [SW].

Test users can be defined in the Countly Dashboard under Management > Applications > Your App > Push Notifications settings. This allows you to send test push notifications to specific devices before targeting your full audience.

Troubleshooting & FAQ

For common issues, error codes, and frequently asked questions about push notifications, see our dedicated troubleshooting page:

For the full Push Notifications reference, check the SDK documentation of your platform:

Was this page helpful?
Reach out to us for any other questions.
Helpful?

Looking For More Help?