# Push notifications

> Notify mobile customers of agent replies when your app is closed. APNs setup for iOS, Firebase Cloud Messaging for Android, forwarding pushes when your app has its own handlers, and React Native specifics.

Source: https://developer.kustomer.com/docs/chat/push-notifications

When an agent replies while your app is in the background, Kustomer sends a push through Apple Push Notification service (APNs) on iOS, or Firebase Cloud Messaging (FCM) on Android. Tapping the notification opens the conversation in the SDK's UI.

```mermaid
sequenceDiagram
  autonumber
  participant App as Your app (chat SDK)
  participant OS as APNs / FCM
  participant K as Kustomer
  participant Agent
  App->>OS: Request a device token
  OS-->>App: token
  App->>K: Register token for this customer
  Agent->>K: Reply in conversation
  K->>OS: Push to the customer's devices
  OS-->>App: Notification
  App->>App: Tap opens the conversation
```

Push goes to the customer the device is registered to. Log the customer in with a [JWT](https://developer.kustomer.com/docs/chat/identity) to reach them on every device, and log out when they sign out so the next user doesn't get their notifications.

## iOS

#### Create an APNs key

In the Apple Developer portal, under **Certificates, Identifiers & Profiles › Keys**, create a key with **Apple Push Notifications service (APNs)** enabled and download the `.p8` file. Note the **Key ID** and your **Team ID**. Make sure your app ID has the Push Notifications capability.

#### Upload it to Kustomer

In Kustomer, go to **Apps › Chat**, open the **SDK** tab, and in the iOS push settings enter the `.p8` key, Team ID, Key ID, and every bundle ID that should receive pushes.

#### Enable the capability

In Xcode, add the **Push Notifications** capability to your app target.

#### Forward the device token

`Kustomer.setup` registers for remote notifications. Pass the result to the SDK:

```swift title="AppDelegate.swift"
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
  Kustomer.didRegisterForRemoteNotifications(deviceToken: deviceToken)
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
  Kustomer.didFailToRegisterForRemoteNotifications(error: error)
}
```

#### Ask for permission

Ask when it makes sense to the customer, for example after their first chat message:

```swift
Kustomer.requestAuthorizationForPush()
```

This requests alert, sound and badge permission. If your app already asks for notification permission, you don't need it.

### Your own notification delegate

If nothing else in your app sets `UNUserNotificationCenter.current().delegate`, `setup` installs the SDK's delegate and you're done. If your app has its own delegate, set it **before** `Kustomer.setup` and forward Kustomer's notifications:

```swift title="AppDelegate.swift"
extension AppDelegate: UNUserNotificationCenterDelegate {
  func userNotificationCenter(_ center: UNUserNotificationCenter,
                              willPresent notification: UNNotification,
                              withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    if notification.isFromKustomer() {
      Kustomer.pushProvider.userNotificationCenter(center, willPresent: notification, withCompletionHandler: completionHandler)
    } else {
      completionHandler([.banner, .sound])
    }
  }

  func userNotificationCenter(_ center: UNUserNotificationCenter,
                              didReceive response: UNNotificationResponse,
                              withCompletionHandler completionHandler: @escaping () -> Void) {
    if response.notification.isFromKustomer() {
      Kustomer.pushProvider.userNotificationCenter(center, didReceive: response, withCompletionHandler: completionHandler)
    } else {
      completionHandler()
    }
  }
}
```

The SDK opens the conversation when a notification is tapped, and hides Kustomer notifications while your app is in the foreground (in-app banners cover that case). A notification that launched the app is handled by `setup`. Don't also use the deprecated `Kustomer.unUserNotificationCenterDelegate`; combining them can recurse.

### Test with development builds

Debug builds get sandbox device tokens. Set `options.pushEnvironment = .development`, and turn on **Test push notifications** in the brand's chat install settings. Production pushes for that brand are paused while it's on, so turn it off when you're done.

## Android

#### Add Firebase to your app

Register the app in the Firebase console, add `google-services.json`, and apply the `com.google.gms.google-services` Gradle plugin. The SDK already depends on `firebase-messaging`.

#### Upload the service account key

In the Firebase console, under **Project settings › Service accounts**, generate a new private key. Upload the JSON file in Kustomer under **Apps › Chat › Install Chat**, in the Android section. (Legacy FCM server keys no longer work.)

#### Register the device

After `Kustomer.init` succeeds:

```kotlin
Kustomer.init(application = this, apiKey = key, options = options) { result ->
  if (result is KusResult.Success) Kustomer.getInstance().registerDevice()
}
```

#### Request notification permission

The SDK doesn't declare `POST_NOTIFICATIONS`. On Android 13 and later, declare it and request it at a sensible moment:

```xml title="AndroidManifest.xml"
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
```

```kotlin
if (Build.VERSION.SDK_INT >= 33) {
  requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
```

That's all when Kustomer is your only push sender: the SDK registers its own `FirebaseMessagingService`, re-registers when the token changes, and posts notifications.

### Your own FirebaseMessagingService

Android delivers FCM messages to one service. If your app has its own, forward Kustomer's messages and tokens to the SDK:

```kotlin title="MyMessagingService.kt"
import com.kustomer.ui.utils.helpers.KusNotificationService

class MyMessagingService : FirebaseMessagingService() {
  override fun onMessageReceived(remoteMessage: RemoteMessage) {
    if (KusNotificationService.onMessageReceived(remoteMessage, applicationContext)) return
    // your own notifications
  }

  override fun onNewToken(token: String) {
    CoroutineScope(Dispatchers.IO).launch {
      Kustomer.getInstance().registerDeviceToken(token)
    }
    // your own token handling
  }
}
```

`onMessageReceived` returns `true` when the message was Kustomer's and it handled it. Pass a `TaskStackBuilder` as the third argument to control the back stack when the customer taps the notification.

### Customize notifications

| To change                        | Do this                                                                                 |
| -------------------------------- | --------------------------------------------------------------------------------------- |
| Small icon                       | Add a drawable named `ic_kus_chat_notification`                                         |
| Channel ID, name and description | Override the strings `kus_channel_id`, `kus_channel_name` and `kus_channel_description` |
| Foreground banners               | `KustomerOptions.Builder().showInAppNotifications(false)` turns them off                |

The SDK creates its channel with high importance, the default sound, lights and vibration.

## React Native

React Native uses the platform mechanisms above.

**Android** is automatic when `@react-native-firebase/messaging` is installed: `KustomerChat.configure` registers the FCM token and handles messages. You still need the Firebase setup, the uploaded service account key, and the `POST_NOTIFICATIONS` permission. If your app already handles FCM, set `skipFCMPushRegistration: true` and forward:

```ts
import messaging from '@react-native-firebase/messaging'
import KustomerChat from '@kustomer/chat-react-native'

messaging().getToken().then(KustomerChat.setPushToken)
messaging().onTokenRefresh(KustomerChat.setPushToken)
messaging().onMessage((message) => {
  if (KustomerChat.isKustomerNotification(message)) KustomerChat.onRemoteMessage(message)
})
```

**iOS** needs the `.p8` upload and the native `AppDelegate` token forwarding from the [iOS steps](#ios). Set `pushEnvironment: PushEnvironment.development` in `configure` options while testing debug builds. The package's automatic token registration works only on Android, so turn it off on iOS:

```ts
import { Platform } from 'react-native'

await KustomerChat.configure(KUSTOMER_CHAT_KEY, {
  skipFCMPushRegistration: Platform.OS === 'ios',
})
```

`KustomerChat.unsetPushToken()` deregisters the device on both platforms.

## Sign-out and opt-out

|                             | iOS                                                                  | Android                                            | React Native            |
| --------------------------- | -------------------------------------------------------------------- | -------------------------------------------------- | ----------------------- |
| Stop pushes for this device | `Kustomer.deregisterCurrentDeviceForPushNotifications { result in }` | `deregisterDeviceForPushNotifications()` (suspend) | `KustomerChat.logOut()` |
| Logging out deregisters     | Yes                                                                  | **No**, deregister first                           | Yes                     |
| Never register              | `options.disablePush = true`                                         | Don't call `registerDevice()`                      | `disablePush: true`     |

On iOS, `disablePush` only stops registration; a device that registered earlier keeps receiving pushes until it's deregistered.

## Troubleshooting

| Symptom                                            | Check                                                                                                                                               |
| -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| No pushes on iOS debug builds                      | `pushEnvironment` is `.development`, and **Test push notifications** is on.                                                                         |
| No pushes on iOS release builds                    | The bundle ID is listed in Kustomer, and **Test push notifications** is off.                                                                        |
| Pushes stopped after adding another push SDK (iOS) | It replaced the notification delegate. Forward Kustomer notifications as shown above.                                                               |
| No pushes on Android 13+                           | `POST_NOTIFICATIONS` is declared and granted.                                                                                                       |
| No pushes on Android, permission granted           | Your own `FirebaseMessagingService` isn't forwarding to `KusNotificationService`, or the service account JSON is from a different Firebase project. |
| The previous user's notifications arrive           | Deregister on sign-out. On Android, `logOut()` alone doesn't deregister.                                                                            |
