> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerix.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Android (Kotlin) — Push Notifications

> Configure Firebase Cloud Messaging (FCM) delivery for your native Android app — with zero Firebase setup on your part.

## Introduction

Lerix delivers push notifications on Android using Firebase Cloud Messaging (FCM) — but unlike a typical FCM integration, **you don't add a `google-services.json` file or the Google Services Gradle plugin to your app**. The SDK registers your device against Lerix's own Firebase project internally, using this Lerix project's Sender ID (fetched from the backend automatically). After you complete the [Android installation](/frameworks/android/installation), follow the steps below to enable push delivery.

<Note>
  Make sure you have initialized the SDK with `Lerix.initialize()` before configuring push notifications.
</Note>

## Dashboard configuration

For the backend to actually send to your devices, it needs to authenticate as *your own* Firebase project. This is the only Firebase-related step required:

1. Create or open your app in the [Firebase Console](https://console.firebase.google.com/).
2. Go to **Project Settings → Service Accounts → Generate new private key**.
3. Upload the downloaded JSON file to the Lerix dashboard, under **Notifications → Settings**.

Once uploaded, the **Android** card in **Notifications → Settings** shows a **Configured** badge — click it again any time to review it or replace it.

## Manifest permissions and components

Nothing to add here — the SDK's own manifest already declares the `INTERNET` and `POST_NOTIFICATIONS` permissions, its Firebase Messaging service, and its notification-tap receiver. Android's manifest merger folds all of it into your app automatically; there's no service, receiver, or permission for you to declare yourself.

## Request permission and register

```kotlin theme={"dark"}
import com.lerix.sdk.Lerix
import com.lerix.sdk.notifications.LerixPermissionStatus

lifecycleScope.launch {
    val granted = Lerix.notifications.requestPermissions()
}

Lerix.notifications.setOnNotificationReceived { payload ->
    // Fired while the app is open (foreground) — show an in-app banner, etc.
    Log.d("MyApp", "Notification received: ${payload.title} — ${payload.body}")
}

Lerix.notifications.setOnNotificationTapped { payload ->
    // Fired when the user taps the notification — including one that
    // launched the app from a fully closed state (see the note below).
    Log.d("MyApp", "Notification tapped: ${payload.metadata}")
}

val status = Lerix.notifications.checkPermissionStatus()
// LerixPermissionStatus.AUTHORIZED, .DENIED, .NOT_DETERMINED
```

`requestPermissions()` is a suspend function — call it from a coroutine scope (e.g. `lifecycleScope.launch`). On Android 13+ it requests the runtime `POST_NOTIFICATIONS` permission and, once granted, fetches and registers the FCM token; on older Android versions there's no runtime permission to request, so it registers immediately.

Register `setOnNotificationTapped` somewhere with access to your navigation, since a tap almost always means routing to a specific screen based on `payload.metadata` rather than just logging it.

<Note>
  A tap is delivered reliably even when it's what launched the app from a
  fully terminated state — call `setOnNotificationTapped` as soon as your
  launcher Activity starts, and any tap that happened before your app
  finished starting up is replayed automatically once the callback is
  registered. Use `Lerix.notifications.getInitialNotificationTap()` if you
  need to check for one synchronously instead.
</Note>

## The notification payload

`setOnNotificationReceived` and `setOnNotificationTapped` both receive a `LerixNotificationPayload`:

```kotlin theme={"dark"}
data class LerixNotificationPayload(
    val notificationId: String?, // this notification's id in Lerix
    val title: String?,
    val body: String?,
    val imageUrl: String?, // whatever you passed as `imageUrl` when sending, if any
    val metadata: Map<String, String>, // whatever you passed as `metadata` when sending
)
```

```kotlin theme={"dark"}
Lerix.notifications.setOnNotificationTapped { payload ->
    val screen = payload.metadata["screen"]
    val orderId = payload.metadata["orderId"]
    // Navigate based on payload.metadata, etc.
}
```

## Device identifiers

Three different methods return three different things — use the right one for what you're doing:

| Method | Returns | Use it for |
| - | - | - |
| `getRegisteredTokenId()` | Lerix's own id for this registered device, assigned once `requestPermissions()` succeeds and the token is registered with the backend | The value to put in **device tokens** when sending a notification to this specific device — from the dashboard's **Send notification** tab, or the [REST API](/api-reference/push-notifications) |
| `getDeviceToken()` | The raw FCM registration token | Rarely needed directly; useful for diagnostics or if you're calling FCM yourself outside of Lerix |
| `getDeviceId()` | `Settings.Secure.ANDROID_ID` — a local Android system identifier, unrelated to Lerix's backend | Local diagnostics only; **not** what the dashboard's send flow expects |

```kotlin theme={"dark"}
val tokenId = Lerix.notifications.getRegisteredTokenId()
Log.d("MyApp", "Send test notifications to: $tokenId")
```

<Warning>
  `getRegisteredTokenId()` is the Android equivalent of the Flutter SDK's
  `getDeviceId()` — the naming differs between the two SDKs, but both return
  the same kind of value: the id the dashboard and REST API expect in their
  device-targeting field. Pasting this SDK's `getDeviceId()` (the
  `ANDROID_ID` value) into the dashboard's send flow will fail with a "not
  found" error — it was never registered with the backend.
</Warning>

All three return `null` until notification permission is granted and registration completes.

## Sending a custom sound

Set `sound` when sending a notification — from the dashboard's **Send notification** tab, or the REST API's [`sound` parameter](/api-reference/push-notifications) — to play a bundled sound file instead of the device default.

* The file must already ship inside your app under `res/raw/` — Lerix only passes the filename through, it never hosts or uploads sound files.
* Pass the filename **without** its extension, e.g. `notif` for `res/raw/notif.mp3`. Supported formats: `.wav`, `.mp3`, `.ogg`.

<Note>
  A sound is locked in the first time it's used on a given device — Android
  fixes a notification channel's sound at creation and never lets it change
  afterward. Sending a different `sound` value later creates a new channel
  rather than updating the old one; that's expected Android behavior, not a
  bug.
</Note>

No SDK code is required to play the sound — it happens automatically as part of the system notification.

## Sending metadata

Attach arbitrary custom data with `metadata` (a JSON object) when sending — from the dashboard's **Send notification** tab, or the REST API's [`metadata` parameter](/api-reference/push-notifications). It's delivered back to the device inside `payload.metadata`:

```json theme={"dark"}
{
  "title": "New message from John",
  "body": "Hey, are you free later?",
  "metadata": { "chatId": "456", "screen": "chat" }
}
```

```kotlin theme={"dark"}
Lerix.notifications.setOnNotificationTapped { payload ->
    val chatId = payload.metadata["chatId"]
    // Navigate to the chat screen, etc.
}
```

## Sending an image

Set `imageUrl` when sending a notification — from the dashboard's **Send notification** tab, or the REST API's [`imageUrl` parameter](/api-reference/push-notifications) — to show a rich image in the notification. This works automatically on Android, with no extra setup: the SDK's bundled messaging service downloads the image and renders it as a `BigPictureStyle` notification.

The image is also available in Kotlin as `payload.imageUrl` (in `setOnNotificationReceived`/`setOnNotificationTapped`) if you want to show it in a custom in-app banner, in addition to the OS rendering it natively.

## Target a user on every device

If your product also ships on other platforms (web, mobile or desktop), add
them to the same project and link each install to your own user ID after
login. Your backend can then send to `externalUserIds` and reach that person
on every device in one request.

```kotlin theme={"dark"}
Lerix.setUser("user_123") // after login
Lerix.clearUser()          // on logout
```

See [Identify users](/modules/users-identity) for identity verification and a
sending example.

## Topics

Instead of targeting specific device tokens, a device can subscribe to a named topic — send one notification to the topic and every subscriber receives it. A topic doesn't need to be created ahead of time; it's created automatically the first time any device subscribes to it. You can also view, create, and delete topics from the **Notifications → Topics** tab in the dashboard.

```kotlin theme={"dark"}
lifecycleScope.launch {
    Lerix.notifications.subscribeToTopic("promotions")
    Lerix.notifications.unsubscribeFromTopic("promotions")
}
```

To send to a topic, use the dashboard's **Send notification** tab (select "Topic" as the audience), or call the [REST API](/api-reference/push-notifications) with `sendByTopic: true`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.