# Notification inbox

Requires v4.16+This feature requires SDK version 4.16 or later.

 Minimum SDK required: 4.16

Users who don’t have a version of your app with Customer.io SDK version 4.16 or later will not see this feature and related messages. If you send inbox messages to users without the minimum required version, they won’t be *delivered*.

## How it works[](#how-it-works)

Unlike other messages, inbox messages don’t necessarily appear immediately to users, and they don’t disappear when the user dismisses them. Instead, you’ll display these messages through a notification inbox that your audience can access at their leisure.

Customer.io delivers inbox messages as JSON payloads, not fully-rendered messages. The SDK helps you listen for these payloads, but you’ll determine how to display them in your own inbox client.

You can send an inbox message as a part of a [campaign, broadcast](/journeys/send-inbox/), or [transactional message](/journeys/send-inbox-txnl/).

## Get the inbox instance[](#get-the-inbox-instance)

You’ll access inbox functionality through the `inbox()` method on the in-app messaging module.

```kotlin
val inbox = CustomerIO.instance().inAppMessaging().inbox()
```

## Inbox methods[](#inbox-methods)

The inbox instance provides several methods to manage messages.

Method

Description

`getMessages(topic?)`

Suspend function to get messages from the inbox. Optionally filter by topic. Returns a list of messages.

`fetchMessages(callback)`

Fetch messages from the inbox with a callback. Returns a result with messages on success or error on failure.

`fetchMessages(topic, callback)`

Fetch messages filtered by topic with a callback. Returns a result with messages on success or error on failure.

`addChangeListener(listener)`

Add a listener to be notified when messages change.

`removeChangeListener(listener)`

Remove a previously added change listener.

`markMessageOpened(message)`

Mark a message as opened.

`markMessageUnopened(message)`

Mark a message as unopened.

`markMessageDeleted(message)`

Mark a message as deleted.

`trackMessageClicked(message, actionName?)`

Track a click on the message. The `actionName` parameter is optional.

## Inbox message payloads[](#inbox-message-payloads)

Inbox messages are delivered as a JSON payload. The SDK helps you listen for the payload, but you’ll render the content in your own inbox client.

The client payload includes the following fields, but you’re most concerned with the `properties` object, which represents your message content. By default, we’ll send a `title` and `body` field, but you can add other fields like an `image` or a `link`—whatever you set up your inbox to expect.

Make sure that your team members know what payloads to send—especially if you expect different payloads for different topics or types of messages.

Field

Type

Description

`messageId`

string

Unique identifier for the message.

`sentAt`

string

When the message was sent.

`expiresAt`

string

When the message will expire.

`opened`

boolean

Whether the message has been opened.

`topics`

array

The topics that the message belongs to.

`type`

string

The type of message.

`properties`

object

The properties of the message.

```json
{
    "messageId": "1234567890",
    "sentAt": "2026-02-05T12:00:00Z",
    "expiresAt": "2026-02-05T12:00:00Z",
    "opened": false,
    "topics": ["orders", "shipping"],
    "type": "order_shipped",
    "properties": {
        "title": "Hey Cool Person, your order shipped!",
        "body": "You can track your order #1234567890 here:",
        "link": "https://example.com/orders/1234567890"
    }
}
```

### Inbox topics and types[](#inbox-topics-and-types)

When you send an inbox message, you can assign it to one or more topics. You can use these topics to filter messages when you fetch them. You can also use the topics to determine how to render the messages in your notification inbox.

Messages also have a `type`. Think of this like a sub-category or topic for a message. For example, you might have `orders` and `sale` topics, where `orders` don’t have images but `sale` topics might. Or, within the `orders` topic, you might have `order_placed` and `order_shipped` types, where `order_placed` lists order details and images of purchased products and `order_shipped` provides a link to the tracking information for the order that opens in a new tab.

## Setup your notification inbox[](#setup-your-notification-inbox)

Inbox messages are just JSON payloads. You’ll need to build your own inbox client to display the messages. The code below gives you a starting point, but you can build your own inbox client however you want.

### Fetch messages[](#fetch-messages)

```kotlin
// Using suspend function (recommended)
val messages = notificationInbox.getMessages()

// Fetch messages filtered by topic
val promoMessages = notificationInbox.getMessages(topic = "promotions")

// Using callback
notificationInbox.fetchMessages { result ->
    runOnUiThread {
        result.onSuccess { messages ->
            // Update your UI with the messages
            updateInboxUI(messages)
        }.onFailure { error ->
            // Handle error
            Log.e("Inbox", "Failed to fetch messages", error)
        }
    }
}

// Using callback with topic filter
notificationInbox.fetchMessages("promotions") { result ->
    runOnUiThread {
        result.onSuccess { messages ->
            // Update your UI with filtered messages
            updatePromotionsUI(messages)
        }.onFailure { error ->
            Log.e("Inbox", "Failed to fetch messages", error)
        }
    }
}
```

### Listen for message updates[](#listen-for-message-updates)

```kotlin
// Create a change listener
val notificationInboxChangeListener = object : NotificationInboxChangeListener {
    override fun onMessagesChanged(messages: List<InboxMessage>) {
        // Update your UI with the new messages
        updateInboxUI(messages)
    }
}

// Add the listener
notificationInbox.addChangeListener(notificationInboxChangeListener)

// Don't forget to remove the listener when you're done
notificationInbox.removeChangeListener(notificationInboxChangeListener)
```

### Mark messages as opened or unopened[](#mark-messages-as-opened-or-unopened)

```kotlin
// Mark a message as opened
notificationInbox.markMessageOpened(message)

// Mark a message as unopened
notificationInbox.markMessageUnopened(message)
```

### Track message clicks[](#track-message-clicks)

```kotlin
// Track a click without an action name
notificationInbox.trackMessageClicked(message)

// Track a click with an action name
notificationInbox.trackMessageClicked(message, "view_order")
```

### Delete messages[](#delete-messages)

```kotlin
// Mark a message as deleted
notificationInbox.markMessageDeleted(message)
```

## Working with message properties[](#working-with-message-properties)

You can access message properties to display custom content in your inbox:

```kotlin
// Access message properties
val title = message.properties["title"] as? String
val body = message.properties["body"] as? String
val link = message.properties["link"] as? String
val imageUrl = message.properties["image"] as? String

// Handle message action when user taps
fun handleMessageTap(message: InboxMessage) {
    // Mark as opened
    notificationInbox.markMessageOpened(message)

    // Track the click
    notificationInbox.trackMessageClicked(message)

    // Open link if available
    val link = message.properties["link"] as? String
    if (link != null) {
        val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
        startActivity(intent)
    }
}
```