> This page is part of the [Customer.io documentation](https://docs.customer.io). For the complete index, see [llms.txt](https://docs.customer.io/llms.txt).
> Last updated: November 13, 2026
> To take actions in Customer.io from an agent, use the [Customer.io CLI](https://docs.customer.io/ai/cli/get-started.md) or the [MCP server](https://docs.customer.io/ai/mcp/get-started.md).

# Silent push

A silent push wakes your app in the background and hands it the data set on the message, without showing anything to the person. Use this page to set your app up to receive silent pushes.

## How it works

When you send a [silent push](https://docs.customer.io/messaging/channels/push/silent-push/), the device wakes your app in the background. The SDK reports *Delivered* to Customer.io, and your JavaScript gets the key/value data set on the message. A silent push never shows anything, so the SDK never reports *Opened*.

Silent push needs `customerio-reactnative` 7.3.0 or later. Customer.io sends silent pushes to every iOS and Android device a message targets, and doesn’t check which SDK version the device uses. An app that doesn’t handle silent pushes shows nothing.

Handle silent pushes in one place, depending on what receives push in your app:

Your app

Handle silent pushes with

The Customer.io SDK handles push, through APNs or FCM

[`setBackgroundPushHandler`](#with-the-customer-io-sdk)

`@react-native-firebase/messaging` receives your messages

[React Native Firebase’s handlers](#with-react-native-firebase)

If your app handles silent pushes in React Native Firebase and also sets `setBackgroundPushHandler`, each push can reach your code twice.

### What a silent push contains

A silent push carries your data as text keys and values, plus two keys that Customer.io adds:

*   `CIO-Background-Delivery-ID`: identifies the delivery.
*   `CIO-Background-Delivery-Token`: the device token the push was sent to.

`setBackgroundPushHandler` removes those two keys from the data and gives you their values as `deliveryId` and `deliveryToken`. React Native Firebase passes them through in `remoteMessage.data`.

## With the Customer.io SDK

Set the handler in your entry file (`index.js`), before `AppRegistry.registerComponent`. On Android, a silent push that starts your app in the background runs `index.js` without mounting your app, so a handler you set inside a component would miss it.

```
// index.js
import { AppRegistry } from 'react-native';
import { CustomerIO } from 'customerio-reactnative';
import App from './src/App';

CustomerIO.pushMessaging.setBackgroundPushHandler(async ({ deliveryId, data }) => {
  // data holds the keys and values set on the message.
  // Key your work on deliveryId: the same push can arrive more than once.
  await refreshAccount(data, { idempotencyKey: deliveryId });
});

AppRegistry.registerComponent('MyApp', () => App);
```

*   If your handler throws or rejects, the SDK logs the error and counts your handler as finished.
*   Setting a new handler replaces the previous one. Call `remove()` on the subscription that `setBackgroundPushHandler` returns to stop receiving silent pushes.
*   Your handler can run before any component mounts, so `CustomerIO.initialize` might not have run yet. To call the SDK from your handler, initialize the SDK in `index.js` too.
*   You only need `deliveryToken` to report the delivery yourself with `CustomerIO.trackMetric` when you turn off automatic push tracking.

### Android

You don’t need to do anything else on Android. The SDK runs your handler in a Headless JS task from a WorkManager job, either in your running app or in a React Native instance that it starts in the background. Your `Application` must provide a `ReactHost`, as the React Native template does.

A push that starts your app when it isn’t running reaches your handler once your JavaScript has set the handler at least once. If a later version of your app stops setting the handler, the first push that finds no handler within 25 seconds turns this off. Calling `remove()` turns it off right away.

### iOS

1.  Add the **Remote notifications** background mode. In Xcode, go to *Signing & Capabilities* > *Background Modes* and check **Remote notifications**, or add `remote-notification` to `UIBackgroundModes` in your `Info.plist`.
2.  If you wrapped your `AppDelegate` with `CioAppDelegateWrapper`, as in [push setup](https://docs.customer.io/integrations/sdk/react-native/push-notifications/push/), you don’t need to do anything else. Once your JavaScript sets the handler, in this launch or an earlier one, the wrapper hands each Customer.io silent push to it and tells iOS when your handler finishes. Until then, and for every other notification, your app delegate gets the push as before. If iOS launched your app for the push, the push waits until your JavaScript sets the handler.
3.  If you use FCM with `CioAppDelegateWrapper` and Firebase’s app delegate proxy on, which is Firebase’s default, your app needs GoogleUtilities 8.1.5 or later. With earlier versions, silent pushes never reach `CioAppDelegateWrapper`. If Firebase comes from CocoaPods, run `pod update GoogleUtilities`. If it comes from Swift Package Manager, update your Firebase package. You can also set `FirebaseAppDelegateProxyEnabled` to `NO` in your `Info.plist`.

As on Android, if a later version of your app stops setting the handler, the first push that finds no handler turns this off, and `remove()` turns it off right away.

If your app delegate isn’t wrapped with `CioAppDelegateWrapper`, forward background notifications to the SDK yourself:

```
import customerio_reactnative

func application(
  _ application: UIApplication,
  didReceiveRemoteNotification userInfo: [AnyHashable: Any],
  fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
  if NativeMessagingPush.handleBackgroundPush(userInfo: userInfo, completionHandler: completionHandler) {
    return // A Customer.io silent push. The SDK reports Delivered and calls completionHandler.
  }
  completionHandler(.noData) // Or pass the notification to your other handlers.
}
```

## With React Native Firebase

If `@react-native-firebase/messaging` receives your messages, handle Customer.io silent pushes in React Native Firebase’s handlers on both platforms, and don’t set `setBackgroundPushHandler`. `messaging().onMessage` receives a silent push while your app is in the foreground, and `messaging().setBackgroundMessageHandler` receives it while your app is in the background or not running.

Keep forwarding messages to Customer.io as described in [multiple push providers](https://docs.customer.io/integrations/sdk/react-native/push-notifications/multiple-push-providers/), and don’t show a notification for a silent push yourself:

```
messaging().setBackgroundMessageHandler(async (remoteMessage) => {
  const handled = await CustomerIO.pushMessaging.onBackgroundMessageReceived(remoteMessage);
  if (remoteMessage.data?.['CIO-Background-Delivery-ID']) {
    // A Customer.io silent push: read your keys from remoteMessage.data.
    await refreshAccount(remoteMessage.data);
    return;
  }
  if (!handled) {
    // Not a Customer.io message: handle it as before.
  }
});
```

*   **Android**: `onMessageReceived` and `onBackgroundMessageReceived` show nothing for a silent push, report *Delivered*, and resolve `true`. React Native Firebase starts a service to run `setBackgroundMessageHandler`, and Android 8 and later refuse to start it for a normal-priority message once your app has been in the background for a few minutes. When that happens, neither your handler nor Customer.io sees the push. On this route, set the message’s **Android priority** to **High**. Firebase may still lower the priority of high-priority messages that keep showing no notification, and a lowered message can miss this route the same way.
*   **iOS**: Add the **Remote notifications** background mode, as described in [iOS](#ios). `onMessageReceived` and `onBackgroundMessageReceived` do nothing on iOS and resolve `true`. The SDK reports *Delivered* as the push passes through `CioAppDelegateWrapper`.

## What to plan for

Apple and Google decide when to deliver a silent push, and don’t always deliver it:

*   **Repeats**: The same push can arrive more than once. Use the delivery ID to skip work you already did.
*   **Time**: Keep your work short. The SDK waits up to 25 seconds for your handler, including the time React Native takes to start.
*   **Delays**: Android can hold normal-priority messages while the device is idle. iOS throttles background notifications, and keeps only the newest one for a device that’s offline.
*   **Force-quit apps**: iOS doesn’t wake an app the person force-quit.
