---
source_url: https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/messages/unread
title: Unread messages
updated_at: 2026-06-15T12:11:35.417Z
---

> Documentation Index
> For a curated overview of PubNub documentation, see: https://www.pubnub.com/docs/llms.txt
> For the full list of all documentation pages, see: https://www.pubnub.com/docs/llms-full.txt


# Unread messages

Track unread message counts for users who reconnect after being offline.

`lastReadMessageTimetoken` on the [Membership](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/learn/chat-entities/membership) object stores when a user last read messages on a channel. This is set automatically on [join()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/channels/join) or [invite()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/channels/invite#invite-one-user). Update it based on user actions (scrolling, app focus, etc.) using [mark as read](#mark-messages-as-read-one-channel) methods.

:::note Requires App Context and Message Persistence
Enable [App Context](https://youtu.be/9UEoSlngpYI) and [Message Persistence](https://youtu.be/qLMtbINWGig) in the [Admin Portal](https://admin.pubnub.com/).
:::

## Get last read message

`lastReadMessageTimetoken` returns the timetoken marking the user's last read position on a channel. This timetoken doesn't always correspond to an actual message. Use it to display unread markers in your UI.

### Sample code

Get the timetoken of the last message read by the `support_agent_15` user on the `support` channel.

```kotlin
chat.getUser("support_agent_15").async { result ->
    result.onSuccess { user ->
        user?.getMemberships(filter = "channel.id == 'support'")?.async { membershipsResult ->
            membershipsResult.onSuccess { membershipsResponse ->
                val membership = membershipsResponse.memberships.firstOrNull()
                println("Last read message timetoken: ${membership?.lastReadMessageTimetoken}")
            }.onFailure {
                // handle failure
            }
        }
    }.onFailure {
        // handle failure
    }
}
```

## Get unread messages count (one channel)

`getUnreadMessagesCount()` returns the number of unread messages on a channel. This counts all messages after the last read timetoken, including your own messages.

### Method signature

This method has the following signature:

```kotlin
membership.getUnreadMessagesCount(): PNFuture<Long?>
```

#### Input

This method doesn't take any parameters.

#### Output

| Type | Description |
| --- | --- |
| `PNFuture<Long?>` | Retrieves from Message Persistence the number (count) of all messages unread by a given user on a given channel from the last read message. The method returns `null` when there is no [last read message timetoken](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/messages/unread#mark-messages-as-read-one-channel) set on the [Message object](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/learn/chat-entities/message). |

### Sample code

Get the number of all messages unread by the `support_agent_15` user on the `support` channel.

```kotlin
// get the details of the "support_agent_15" user
chat.getUser("support_agent_15").async { userResult ->
    userResult.onSuccess { user ->
        user?.getMemberships(filter = "channel.id == 'support'")?.async { membershipsResult ->
            membershipsResult.onSuccess { membershipsResponse ->
                val membership = membershipsResponse.memberships.firstOrNull()
                membership?.getUnreadMessagesCount()?.async { unreadCountResult ->
                    unreadCountResult.onSuccess { unreadCount ->
                        if (unreadCount != null) {
                            println("Unread messages count: $unreadCount")
                        } else {
                            println("No unread messages count available.")
                        }
                    }.onFailure {
                        // handle failure
                    }
                }
            }.onFailure {
                // handle failure
            }
        }
    }.onFailure {
        // handle failure
    }
}
```

## Get unread messages count (all channels)

`fetchUnreadMessagesCounts()` returns unread counts for all joined channels with unread messages (channels with zero unread are excluded). Counts include all messages after the last read timetoken, including your own.

##### Under the hood

`fetchUnreadMessagesCounts()` calls the Message Persistence API and the Kotlin SDK [messageCounts()](https://www.pubnub.com/docs/sdks/kotlin/api-reference/storage-and-playback#message-counts) method.

:::note Unread counts and filtering
`fetchUnreadMessagesCounts` uses the Memberships API. Filters here may target `channel.*` fields only (plus common fields: `status`, `type`, `custom`). You can't use `uuid.*` fields.
Unread counts always include all messages published after your last read timetoken including messages you sent yourself. You can't filter unread counts by sender but you can query Message Persistence from the last read timetoken and filter client-side.
See filterable fields: • [Memberships filters](https://www.pubnub.com/docs/general/metadata/filtering#memberships-filters) • [Members filters](https://www.pubnub.com/docs/general/metadata/filtering#members-filters)
:::

### Method signature

This method has the following signature:

```kotlin
chat.fetchUnreadMessagesCounts(
    limit: Int? = null,
    page: PNPage? = null,
    filter: String? = null,
    sort: Collection<PNSortKey<PNMembershipKey>> = listOf()
): PNFuture<UnreadMessagesCounts>
```

#### Input

| Parameter | Description |
| --- | --- |
| `limit`Type: `Int`Default: `null` | Number of objects to return in response. The default (and maximum) value is `100`. |
| `page`Type: `PNPage`Default: `null` | Object used for pagination to define which previous or next result page you want to fetch. |
| `filter`Type: `String` (Memberships filter)Default: `null` | Filter expression evaluated against [channel memberships](https://www.pubnub.com/docs/general/metadata/filtering#memberships-filters) only. Allowed targets: `channel.*` plus common fields (`status`, `type`, `custom`). `uuid.*` fields aren't supported. |
| `sort`Type: `Collection<PNSortKey<PNMembershipKey>>`Default: `listOf()` | Key-value pair of a property to sort by, and a sort direction. Available options are `id`, `name`, and `updated`. Use `asc` or `desc` to specify the sorting direction, or specify `null` to take the default sorting direction (ascending). For example: `{name: "asc"}`. By default, the items are sorted by the last updated date. |

#### Output

| Parameter | Description |
| --- | --- |
| `PNFuture<UnreadMessagesCounts>`Type: `object` | Returned object containing these fields: `countsByChannel` and `page`. |
| `> countsByChannel`Type: `Array<object>` | Array of objects containing channel, membership, and count information. |
| `>> channel`Type: `Channel` | Channel with unread messages. |
| `>> membership`Type: `Membership` | Returned [Membership object](https://www.pubnub.com/docs/chat/chat-sdk/learn/chat-entities/membership) showing the user-channel data. |
| `>> count`Type: `number` | Total number of messages unread by the current user on a given channel. |
| `> page`Type: `object` | Object containing pagination information. |
| `>> next`Type: `string | undefined` | Token for fetching the next page of results. |
| `>> prev`Type: `string | undefined` | Token for fetching the previous page of results. |

### Sample code

Get the number of all messages unread by the `support_agent_15` user on all joined channels.

```kotlin
// reference the "support_agent_15" user
chat.getUser("support_agent_15").async { result ->
    result.onSuccess { user ->
        user?.let {
            // get all messages unread by that user
            chat.fetchUnreadMessagesCounts().async { unreadResult ->
                unreadResult.onSuccess { unreadMessagesCounts ->
                    // access the counts for each channel
                    val countsByChannel = unreadMessagesCounts.countsByChannel
                    
                    // access pagination tokens if needed
                    val next = unreadMessagesCounts.page.next
                    val prev = unreadMessagesCounts.page.prev
                }.onFailure {
                    // handle failure
                }
            }
        }
    }.onFailure {
        // handle failure
    }
}
```

To avoid counting your own recently sent messages as unread, ensure your app updates the [last read timetoken](#mark-messages-as-read-one-channel).

## Mark messages as read (one channel)

`setLastReadMessage()` and `setLastReadMessageTimetoken()` set the last read timetoken for counting unread messages. Bind these to user actions in your app.

Setting the last read message for users lets you implement the [Read Receipts](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/messages/read-receipts) feature and monitor which channel member read which message.

### Method signature

These methods take the following parameters:

* setLastReadMessage() 1membership.setLastReadMessage(message: Message): PNFuture<Membership>
* setLastReadMessageTimetoken() 1membership.setLastReadMessageTimetoken(timetoken: Long): PNFuture<Membership>

#### Input

| Parameter | Required by `setLastReadMessage()` | Required by `setLastReadMessageTimetoken()` | Description |
| --- | --- | --- | --- |
| `message`Type: `Message`Default: n/a | Yes | No | Last read message on a given channel with the timestamp that gets added to the [user-channel membership](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/channels/membership#update) as the `lastReadMessageTimetoken` property. |
| `timetoken`Type: `Long`Default: n/a | No | Yes | Timetoken of the last read message on a given channel that gets added to the [user-channel membership](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/channels/membership#update) as the `lastReadMessageTimetoken` property. |

#### Output

| Type | Description |
| --- | --- |
| `PNFuture<Membership>` | Returned `Membership` object updated with the `lastReadMessageTimetoken` parameter. |

### Sample code

Set the message with the `16200000000000001` timetoken as the last read message for the `support_agent_15` user on the `support` channel.

* setLastReadMessage() 1// retrieve the "support_agent_15" user2chat.getUser("support_agent_15").async { userResult ->3 userResult.onSuccess { user ->4 // retrieve membership details for the "support" channel5 user?.getMemberships(filter = "channel.id == 'support'")?.async { membershipsResult ->6 membershipsResult.onSuccess { membershipsResponse ->7 val membership = membershipsResponse.memberships.firstOrNull()8 // retrieve the specific message with the given timetoken9 chat.getChannel("support").getMessage(16200000000000001).async { messageResult ->10 messageResult.onSuccess { message ->11 // Set the last read message12 membership?.setLastReadMessage(message)?.async { setResult ->13 setResult.onSuccess {14 // handle success15 }.onFailure {16 // handle failure17 }18 }19 }.onFailure {20 // handle failure21 }22 }23 }.onFailure {24 // handle failure25 }26 }27 }.onFailure {28 // handle failure29 }30}
* setLastReadMessageTimetoken() 1// retrieve the "support_agent_15" user2chat.getUser("support_agent_15").async { userResult ->3 userResult.onSuccess { user ->4 // retrieve membership details for the "support" channel5 user?.getMemberships(filter = "channel.id == 'support'")?.async { membershipsResult ->6 membershipsResult.onSuccess { membershipsResponse ->7 val membership = membershipsResponse.memberships.firstOrNull()8 // set the last read message timetoken9 membership?.setLastReadMessageTimetoken(16200000000000001)?.async { setResult ->10 setResult.onSuccess {11 // handle success12 }.onFailure {13 // handle failure14 }15 }16 }.onFailure {17 // handle failure18 }19 }20 }.onFailure {21 // handle failure22 }23}

## Mark messages as read (all channels)

`markAllMessagesAsRead()` marks all unread messages as read on all joined channels.

### Method signature

This method has the following signature:

```kotlin
chat.markAllMessagesAsRead(
    limit: Int?,
    page: PNPage?,
    filter: String?,
    sort: Collection<PNSortKey<PNMembershipKey>> = listOf()
): PNFuture<MarkAllMessageAsReadResponse>
```

#### Input

| Parameter | Description |
| --- | --- |
| `limit`Type: `Int`Default: `100` | Number of objects to return in response. The default (and maximum) value is `100`. |
| `page`Type: `PNPage`Default: n/a | Object used for pagination to define which previous or next result page you want to fetch. |
| `filter`Type: `String`Default: n/a | Expression used to filter the results. Returns only these messages whose properties satisfy the given expression. The filter language is [defined here](https://www.pubnub.com/docs/general/metadata/filtering). |
| `sort`Type: `Collection<PNSortKey<PNMembershipKey>>`Default: `listOf()` | Key-value pair of a property to sort by, and a sort direction. Available options are `id`, `name`, and `updated`. Use `asc` or `desc` to specify the sorting direction, or specify `null` to take the default sorting direction (ascending). For example: `{name: "asc"}`. By default, the items are sorted by the last updated date. |

#### Output

| Type | Description |
| --- | --- |
| `PNFuture<MarkAllMessageAsReadResponse>` | Returned object with a list of memberships and these fields: `next`, `prev`, `total`, and `status`. |

### Sample code

Mark the total number of 50 messages as read and specify you want to fetch the results from the next page using a string that was previously returned from the PubNub server.

```kotlin
val nextPageString: String = "your_next_page_string"

chat.markAllMessagesAsRead(
    limit = 50,
    page = PNPage(nextPageString, null)
).async { result ->
    result.onSuccess {
        // handle success
    }.onFailure {
        // handle failure
    }
}
```

## Get unread messages count (all channels) (deprecated)

:::warning Deprecated
The `getUnreadMessagesCounts()` method is deprecated. Use [fetchUnreadMessagesCounts()](#get-unread-messages-count-all-channels) instead.
:::

`getUnreadMessagesCounts()` returns info on all messages you didn't read on joined channels that have unread messages.

This method only returns channels with unread message counts greater than 0 - channels with zero unread messages are filtered out. For each returned channel, this includes all messages published after your last read timetoken, including messages sent by yourself and all other channel members. The method cannot filter messages by sender - it counts all unread messages regardless of who published them. You can display this number on UI in the channel list of your chat app.

### Method signature

This method has the following signature:

```kotlin
chat.getUnreadMessagesCounts(
    limit: Int?,
    page: PNPage?,
    filter: String?,
    sort: Collection<PNSortKey<PNMembershipKey>> = listOf()
): PNFuture<List<GetUnreadMessagesCounts>>
```

#### Input

| Parameter | Description |
| --- | --- |
| `limit`Type: `Int`Default: `100` | Number of objects to return in response. The default (and maximum) value is `100`. |
| `page`Type: `PNPage`Default: n/a | Object used for pagination to define which previous or next result page you want to fetch. |
| `filter`Type: `String`Default: n/a | Expression used to filter which channel memberships to include in the results. Returns unread message counts only for channels whose membership properties satisfy the given expression. The filter language is [defined here](https://www.pubnub.com/docs/general/metadata/filtering). |
| `sort`Type: `Collection<PNSortKey<PNMembershipKey>>`Default: `listOf()` | Key-value pair of a property to sort by, and a sort direction. Available options are `id`, `name`, and `updated`. Use `asc` or `desc` to specify the sorting direction, or specify `null` to take the default sorting direction (ascending). For example: `{name: "asc"}`. By default, the items are sorted by the last updated date. |

#### Output

| Type | Description |
| --- | --- |
| `PNFuture<List<GetUnreadMessagesCounts>>` | `PNFuture` containing the number (count) of all messages unread by a given user on all joined channels after the last read message. It returns these details: `channel`, `membership`, `count`. |

### Sample code

Get the number of all messages unread by the `support_agent_15` user on all joined channels.

```kotlin
// reference the "chat" object and invoke the "getUser()" method
chat.getUser("support_agent_15").async { result ->
    result.onSuccess { user ->
        user?.let {
            chat.getUnreadMessagesCounts(
                filter = "user.id == 'support_agent_15'"
            ).async { unreadMessagesResult ->
                unreadMessagesResult.onSuccess { unreadMessagesCounts ->
                    // handle success
                    unreadMessagesCounts.forEach { unreadCount ->
                        println("Channel: ${unreadCount.channelId}, Unread messages count: ${unreadCount.count}")
                    }
                }.onFailure {
                    // handle failure
                }
            }
        }
    }.onFailure {
        // handle failure
    }
}
```