Skip to main content
The Courier Android SDK provides prebuilt UI components and APIs for building notification experiences in Kotlin. It handles authentication, token management, and real-time message delivery so you can focus on your app.
  • Inbox — prebuilt notification center for Jetpack Compose and XML layouts
  • Push Notifications — automatic FCM token syncing and delivery tracking
  • Preferences — prebuilt UI for users to manage their notification settings
Available on GitHub.
RequirementValue
Min Android SDK23
Gradle8.4+
Package managerJitpack

Installation

1

Add the Jitpack repository

In your settings.gradle or settings.gradle.kts:
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }
    }
}
2

Add the dependency

In your app’s build.gradle:
dependencies {
    implementation 'com.github.trycourier:courier-android:5.2.14'
}
3

Initialize the SDK

Call Courier.initialize() in your Application class before using other SDK features. This gives Courier access to SharedPreferences for persisting state across sessions.
class YourApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        Courier.initialize(this)
    }
}
If you only plan to use CourierClient APIs directly, you can skip the initialize step.

Authentication

All SDK features (Inbox, Push, Preferences) require a signed-in user. Authentication is JWT-based; your backend generates a token and the SDK manages credentials across app sessions.
For a full walkthrough of JWT generation, see the Inbox Authentication guide.
1

Generate a JWT on your backend

Call the Issue Token endpoint from your server:
curl -X POST https://api.courier.com/auth/issue-token \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "scope": "user_id:YOUR_USER_ID write:user-tokens inbox:read:messages inbox:write:events read:preferences write:preferences read:brands",
    "expires_in": "2 days"
  }'
2

Sign in the user

Pass the JWT to the SDK where you manage user state. Credentials persist across app sessions. If the token expires, generate a new one from your backend and call signIn again; the SDK does not handle token refresh automatically.
lifecycleScope.launch {
    Courier.shared.signIn(
        accessToken = jwt,
        userId = "your_user_id"
    )
}
3

Sign out when done

lifecycleScope.launch {
    Courier.shared.signOut()
}
You can listen for authentication state changes:
val listener = Courier.shared.addAuthenticationListener { userId ->
    print(userId ?: "No user signed in")
}

listener.remove()

Inbox

Courier Inbox provides a prebuilt notification center UI for Jetpack Compose and XML layouts. It supports theming, custom renderers, and real-time updates.
Inbox requires the Courier Inbox provider to be enabled in your workspace. If using JWT authentication, enable JWT support in the provider settings.JWT toggle in Courier provider settings
Your app theme must extend Theme.MaterialComponents for the prebuilt UI to render correctly. Set this in your res/values/themes.xml.
For an overview of how Courier Inbox works and how to send messages to it from your backend, see Get Started with Inbox and Send an Inbox Message.

Prebuilt UI

CourierInbox(
    modifier = Modifier.padding(innerPadding),
    onClickMessageListener = { message, index ->
        if (message.isRead) message.markAsUnread() else message.markAsRead()
    },
    onLongPressMessageListener = { message, index ->
        message.markAsArchived()
    },
    onClickActionListener = { action, message, index ->
        print(message.toString())
    },
    onScrollInboxListener = { offsetInDp ->
        print(offsetInDp.toString())
    }
)

Theming

Pass a CourierInboxTheme to customize fonts, colors, unread indicators, swipe actions, and button styles. Both light and dark themes are supported.
CourierInbox(
    modifier = Modifier.padding(innerPadding),
    canSwipePages = true,
    lightTheme = yourLightTheme,
    darkTheme = yourDarkTheme,
    ...
)
Default InboxStyled Inbox
You can also apply branding from Courier Studio. The SDK supports primary color and footer visibility from your brand settings.

Custom Inbox UI

For full control over rendering, use addInboxListener to receive raw message data and build your own UI:
lifecycleScope.launch {
    val listener = Courier.shared.addInboxListener(
        onLoading = {
            // Show loading state
        },
        onError = { error ->
            // Handle error
        },
        onMessagesChanged = { messages, canPaginate, feed ->
            // Update your custom UI with messages
        },
        onUnreadCountChanged = { count ->
            // Update badge indicators
        },
        onTotalCountChanged = { count, feed ->
            // Track total messages per feed
        },
        onPageAdded = { messages, canPaginate, isFirstPage, feed ->
            // Efficiently append new pages during pagination
        },
        onMessageEvent = { message, index, feed, event ->
            // React to individual events: READ, UNREAD, ARCHIVED, OPENED, ADDED
        }
    )

    // Clean up when done
    listener.remove()
}

Message Actions

lifecycleScope.launch {
    Courier.shared.readMessage(messageId = "...")
    Courier.shared.unreadMessage(messageId = "...")
    Courier.shared.archiveMessage(messageId = "...")
    Courier.shared.readAllInboxMessages()
}

// Or via message object
message.markAsRead()
message.markAsUnread()
message.markAsArchived()

Push Notifications

The SDK simplifies push notification setup with automatic FCM token syncing, delivery tracking, and permission management.
Push notifications require a physical device. Emulators do not reliably support push token registration or notification delivery.

Provider Setup

ProviderToken Syncing
Firebase FCMAutomatic (via CourierService)
ExpoManual
OneSignalManual
Pusher BeamsManual
For step-by-step provider credential setup, see the FCM integration guide. Initialize the Firebase SDK in your project before continuing.

Automatic Token Syncing (FCM)

1

Create a notification service

Create a class that extends CourierService. This automatically syncs FCM tokens and tracks delivery.
import com.courier.android.notifications.presentNotification
import com.courier.android.service.CourierService
import com.google.firebase.messaging.RemoteMessage

class YourNotificationService : CourierService() {
    override fun showNotification(message: RemoteMessage) {
        super.showNotification(message)
        message.presentNotification(
            context = this,
            handlingClass = MainActivity::class.java,
            icon = android.R.drawable.ic_dialog_info
        )
    }
}
2

Register the service in AndroidManifest.xml

<service
    android:name=".YourNotificationService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>
3

Extend CourierActivity for click handling

class MainActivity : CourierActivity() {
    override fun onPushNotificationClicked(message: RemoteMessage) {
        // Handle notification click
    }

    override fun onPushNotificationDelivered(message: RemoteMessage) {
        // Handle notification delivery
    }
}

Manual Token Syncing

If you don’t want to use CourierService, sync tokens manually:
lifecycleScope.launch {
    Courier.shared.setToken(
        provider = CourierPushProvider.FIREBASE_FCM,
        token = "your_fcm_token"
    )
}

Requesting Permission

For Android 13+ (API 33), you need to request notification permission at runtime. You can also check the current status without prompting.
Courier.shared.requestNotificationPermission(activity)

val isGranted = Courier.shared.isPushPermissionGranted(context)

Send a Test Notification

Once you’ve completed the setup above, send a test push using the Send API with push as the routing channel. See the FCM sending guide for a complete example.

Preferences

Courier Preferences provides a prebuilt UI for users to manage which notification topics and channels they subscribe to.
Topics and sections are configured in the Preferences Editor. See Preferences Overview for how preference enforcement works at send time.
CourierPreferences(
    mode = CourierPreferences.Mode.Topic,
    onError = { error ->
        print(error.toString())
    }
)

Preference Modes

  • Topic mode (Mode.Topic): shows subscription topics the user can toggle on or off
  • Channels mode (Mode.Channels(listOf(PUSH, SMS, EMAIL))): shows per-channel controls for each topic

Theming

Pass a CourierPreferencesTheme to customize fonts, colors, toggle styles, and section headers. Light and dark themes are both supported, and Courier Studio branding is automatically applied when a brandId is provided.
Default PreferencesStyled Preferences

CourierClient

For advanced use cases, CourierClient provides direct access to the Courier API:
val client = CourierClient(
    jwt = "...",
    userId = "your_user_id"
)

// Token management
client.tokens.putUserToken(token = "...", provider = "firebase-fcm")

// Inbox
val messages = client.inbox.getMessages(paginationLimit = 25)
val unreadCount = client.inbox.getUnreadMessageCount()
client.inbox.read(messageId = "...")
client.inbox.readAll()

// Inbox websocket (real-time updates across devices)
client.inbox.socket.apply {
    receivedMessage = { message -> print(message) }
    receivedMessageEvent = { event -> print(event) } // READ, UNREAD, ARCHIVE, OPENED
    connect()
    sendSubscribe()
}

// Preferences
val prefs = client.preferences.getUserPreferences()
client.preferences.putUserPreferenceTopic(
    topicId = "...",
    status = CourierPreferenceStatus.OPTED_IN,
    hasCustomRouting = true,
    customRouting = listOf(CourierPreferenceChannel.PUSH)
)

// Branding
val brand = client.brands.getBrand(brandId = "...")

// URL tracking (from push payloads or inbox messages)
client.tracking.postTrackingUrl(url = "courier_tracking_url", event = CourierTrackingEvent.DELIVERED)
See the full API reference on GitHub.