---
source_url: https://www.pubnub.com/docs/chat/unity-chat-sdk/build/features/messages/unread
title: Unread messages
updated_at: 2026-05-22T11:04:38.903Z
---

> 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/unity-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/unity-chat-sdk/build/features/channels/join) or [Invite()](https://www.pubnub.com/docs/chat/unity-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

`GetLastReadMessageTimeToken()` 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.

### Method signature

This method has the following signature:

```csharp
membership.GetLastReadMessageTimeToken()
```

#### Input

This method doesn't take any parameters.

#### Output

| Type | Description |
| --- | --- |
| `string` | Retrieved timetoken marking the user's last read position on a given channel. |

### Sample code

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

```csharp
using System.Linq;
using System.Threading.Tasks;
using PubnubApi;
using PubnubChatApi;
using UnityEngine;

// Configuration
PubnubChatConfig chatConfig = new PubnubChatConfig();
        
PNConfiguration pnConfiguration = new PNConfiguration(new UserId("myUniqueUserId"))
{
    SubscribeKey = "demo",
    PublishKey = "demo",
    Secure = true
};

// Initialize Unity Chat
var chatResult = await UnityChat.CreateInstance(chatConfig, pnConfiguration);
if (!chatResult.Error)
{
    chat = chatResult.Result;
}
// reference the "support_agent_15" user
var userResult = await chat.GetUser("support_agent_15");
if (!userResult.Error)
{
    var user = userResult.Result;
    Debug.Log($"Found user with name {user.UserName}");
            
    // get the list of all user memberships
    var membershipsResponse = await user.GetMemberships();
            
    if (!membershipsResponse.Error)
    {
        // extract the actual memberships (support channel only) from the response
        var memberships = membershipsResponse.Result.Memberships.Where(x => x.ChannelId == "support").ToList();
                
        // since we filtered for the "support" channel, we should find it directly
        var membership = memberships.FirstOrDefault();
                
        if (membership != null)
        {
            // retrieve the last read message timetoken
            var lastReadToken = membership.LastReadMessageTimeToken;
            Debug.Log($"The last read message timetoken for user {user.UserName} on channel 'support' is {lastReadToken}");
        }
        else
        {
            Debug.Log("The user 'support_agent_15' is not a member of the 'support' channel.");
        }
    }
}
else
{
    Debug.Log("User 'support_agent_15' not found.");
}
```

## Get unread messages count (one channel)

`GetUnreadMessagesCount()` returns the number of messages you didn't read on a given 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:

```csharp
membership.GetUnreadMessagesCount()
```

#### Input

This method doesn't take any parameters.

#### Output

| Type | Description |
| --- | --- |
| `Task<int>` | Retrieves from Message Persistence the number of all messages unread by a given user on a given channel from the last read message. |

### Sample code

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

```csharp
using System.Linq;
using System.Threading.Tasks;
using PubnubApi;
using PubnubChatApi;
using UnityEngine;

// Configuration
PubnubChatConfig chatConfig = new PubnubChatConfig();
        
PNConfiguration pnConfiguration = new PNConfiguration(new UserId("myUniqueUserId"))
{
    SubscribeKey = "demo",
    PublishKey = "demo",
    Secure = true
};

// Initialize Unity Chat
var chatResult = await UnityChat.CreateInstance(chatConfig, pnConfiguration);
if (!chatResult.Error)
{
    chat = chatResult.Result;
}
// reference the "support_agent_15" user
var userResult = await chat.GetUser("support_agent_15");
if (!userResult.Error)
{
    var user = userResult.Result;
    Debug.Log($"Found user with name {user.UserName}");
            
    // get the list of all user memberships using the GetMemberships method
    var membershipsResponse = await user.GetMemberships();
            
    if (!membershipsResponse.Error)
    {
        // extract the actual memberships from the response
        var memberships = membershipsResponse.Result.Memberships;
                
        // filter out the membership for the "support" channel
        var membership = memberships.FirstOrDefault(m => m.ChannelId == "support");
                
        if (membership != null)
        {
            // retrieve the number of unread messages
            var unreadMessagesCountResult = await membership.GetUnreadMessagesCount();
            if (!unreadMessagesCountResult.Error)
            {
                Debug.Log($"The number of unread messages for user {user.UserName} on channel 'support' is {unreadMessagesCountResult.Result}");
            }
        }
        else
        {
            Debug.Log("The user 'support_agent_15' is not a member of the 'support' channel.");
        }
    }
}
else
{
    Debug.Log("User 'support_agent_15' not found.");
}
```

## Get unread messages count (all channels)

`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.

:::note Unread counts and filtering
`GetUnreadMessagesCounts` 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:

```csharp
chat.GetUnreadMessagesCounts(
    string filter = "",
    string sort = "",
    int limit = 0,
    PNPageObject page = null
)
```

#### Input

| Parameter | Description |
| --- | --- |
| `filter`Type: `string` (Memberships filter)Default: empty string | 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: `string`Default: empty string | 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. |
| `limit`Type: `number`Default: `0` | Number of objects to return in response. |
| `page`Type: `PNPageObject`Default: `null` | Object used for pagination to define which previous or next result page you want to fetch. |

#### Output

| Type | Description |
| --- | --- |
| `Task<List<UnreadMessageWrapper>>` | An awaitable `Task` with a list of `UnreadMessageWrapper` objects containing the filtered, sorted, and paginated unread message counts. |

### Sample code

Get the number of all messages unread by the current user.

```csharp
using System.Linq;
using System.Threading.Tasks;
using PubnubApi;
using PubnubChatApi;
using UnityEngine;

// Configuration
PubnubChatConfig chatConfig = new PubnubChatConfig();
        
PNConfiguration pnConfiguration = new PNConfiguration(new UserId("myUniqueUserId"))
{
    SubscribeKey = "demo",
    PublishKey = "demo",
    Secure = true
};

// Initialize Unity Chat
var chatResult = await UnityChat.CreateInstance(chatConfig, pnConfiguration);
if (!chatResult.Error)
{
    chat = chatResult.Result;
}
// retrieve the current user
var currentUserResult = await chat.GetCurrentUser();
if (!currentUserResult.Error)
{
    var currentUser = currentUserResult.Result;
    Debug.Log($"Current user is {currentUser.UserName}");
            
    // retrieve the unread message counts for the current user with default parameters
    var unreadMessageCountsResult = await chat.GetUnreadMessagesCounts();
    if (!unreadMessageCountsResult.Error)
    {
        // process and display the retrieved unread message counts
        foreach (var unreadMessage in unreadMessageCountsResult.Result)
        {
            Debug.Log($"Channel ID: {unreadMessage.ChannelId}, Unread Messages: {unreadMessage.Count}");
        }
    }
}
else
{
    Debug.Log("Current user not found.");
}
```

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()` let you set the timetoken of the last read message on a given channel to set a timeline from which you can count unread messages. You choose on your own which user action it is bound to.

Setting the last read message for users lets you implement the [Read Receipts](https://www.pubnub.com/docs/chat/unity-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)
* SetLastReadMessageTimeToken() 1membership.SetLastReadMessageTimeToken(string timeToken)

#### 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 as the `lastReadMessageTimetoken` property to the [Membership object](https://www.pubnub.com/docs/chat/unity-chat-sdk/learn/chat-entities/membership). |
| `timeToken`Type: `string`Default: n/a | No | Yes | Timetoken of the last read message on a given channel that gets added as the `lastReadMessageTimetoken` property to the [Membership object](https://www.pubnub.com/docs/chat/unity-chat-sdk/learn/chat-entities/membership). |

#### Output

An awaitable `Task`.

### 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() using System.Linq; using System.Threading.Tasks; using PubnubApi; using PubnubChatApi; using UnityEngine; // Configuration PubnubChatConfig chatConfig = new PubnubChatConfig(); PNConfiguration pnConfiguration = new PNConfiguration(new UserId("myUniqueUserId")) { SubscribeKey = "demo", PublishKey = "demo", Secure = true }; // Initialize Unity Chat var chatResult = await UnityChat.CreateInstance(chatConfig, pnConfiguration); if (!chatResult.Error) { chat = chatResult.Result; } // reference the "support_agent_15" user var userResult = await chat.GetUser("support_agent_15"); if (!userResult.Error) { var user = userResult.Result; Debug.Log($"Found user with name {user.UserName}"); // get the list of all user memberships var membershipsResponse = await user.GetMemberships(); if (!membershipsResponse.Error) { // filter out the right channel var membership = membershipsResponse.Result.Memberships.FirstOrDefault(m => m.ChannelId == "support"); if (membership != null) { Debug.Log($"Found membership for channel: {membership.ChannelId}"); // reference the "support" channel var channelResult = await chat.GetChannel("support"); if (!channelResult.Error) { var channel = channelResult.Result; Debug.Log($"Found channel with name {channel.Name}"); // return the message object with the "16200000000000001" timetoken var messageResult = await channel.GetMessage("16200000000000001"); if (!messageResult.Error) { var message = messageResult.Result; Debug.Log($"Is deleted?: {message.IsDeleted}"); // set the last read message for the membership await membership.SetLastReadMessage(message); Debug.Log($"Last read message set for user {user.UserName} in channel {channel.Name}"); } else { Debug.Log("Message with the specified timetoken not found."); } } else { Debug.Log("Channel 'support' not found."); } } else { Debug.Log("Membership for 'support' channel not found."); } } } else { Debug.Log("User 'support_agent_15' not found."); }
* SetLastReadMessageTimeToken() using System.Linq; using System.Threading.Tasks; using PubnubApi; using PubnubChatApi; using UnityEngine; // Configuration PubnubChatConfig chatConfig = new PubnubChatConfig(); PNConfiguration pnConfiguration = new PNConfiguration(new UserId("myUniqueUserId")) { SubscribeKey = "demo", PublishKey = "demo", Secure = true }; // Initialize Unity Chat var chatResult = await UnityChat.CreateInstance(chatConfig, pnConfiguration); if (!chatResult.Error) { chat = chatResult.Result; } // reference the "support_agent_15" user var userResult = await chat.GetUser("support_agent_15"); if (!userResult.Error) { var user = userResult.Result; Debug.Log($"Found user with name {user.UserName}"); // get the list of all user memberships var membershipsResponse = await user.GetMemberships(); if (!membershipsResponse.Error) { // filter out the right channel var membership = membershipsResponse.Result.Memberships.FirstOrDefault(m => m.ChannelId == "support"); if (membership != null) { Debug.Log($"Found membership for channel: {membership.ChannelId}"); // reference the "support" channel var channelResult = await chat.GetChannel("support"); if (!channelResult.Error) { var channel = channelResult.Result; Debug.Log($"Found channel with name {channel.Name}"); // set the last read message timetoken for the membership string timeToken = "16200000000000001"; await membership.SetLastReadMessageTimeToken(timeToken); Debug.Log($"Last read message timetoken set for user {user.UserName} in channel {channel.Name}"); } else { Debug.Log("Channel 'support' not found."); } } else { Debug.Log("Membership for 'support' channel not found."); } } } else { Debug.Log("User 'support_agent_15' not found."); }

## Mark messages as read (all channels)

`MarkAllMessagesAsRead()` lets you mark as read all messages you didn't read on all joined channels.

### Method signature

This method has the following signature:

```csharp
chat.MarkAllMessagesAsRead(
    string filter = "",
    string sort = "",
    int limit = 0,
    PNPageObject page = null
)
```

#### Input

| Parameter | Description |
| --- | --- |
| `filter`Type: `string`Default: empty string | Expression used to filter the results. Returns only these unread messages whose properties satisfy the given expression. The filter language is [defined here](https://www.pubnub.com/docs/general/metadata/filtering). |
| `sort`Type: `string`Default: empty string | 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. |
| `limit`Type: `number`Default: `0` | Number of objects to return in response. |
| `page`Type: `PNPageObject`Default: `null` | Object used for pagination to define which previous or next result page you want to fetch. |

#### Output

| Type | Description |
| --- | --- |
| `Task<MarkMessagesAsReadWrapper>` | An awaitable `Task` with a wrapper object containing the filtered, sorted, and paginated list of user memberships. |

### 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.

```csharp
using System.Linq;
using System.Threading.Tasks;
using PubnubApi;
using PubnubChatApi;
using UnityEngine;

// Configuration
PubnubChatConfig chatConfig = new PubnubChatConfig();
        
PNConfiguration pnConfiguration = new PNConfiguration(new UserId("myUniqueUserId"))
{
    SubscribeKey = "demo",
    PublishKey = "demo",
    Secure = true
};

// Initialize Unity Chat
var chatResult = await UnityChat.CreateInstance(chatConfig, pnConfiguration);
if (!chatResult.Error)
{
    chat = chatResult.Result;
}
// simulating a previously retrieved page token from the PubNub server
string previouslyReturnedNextPageToken = "NPT";

// create an instance of the Page class with the previously returned next page token
PNPageObject nextPage = new PNPageObject
{
    Next = previouslyReturnedNextPageToken
};

// mark a total of 50 messages as read from the next page
var result = await chat.MarkAllMessagesAsRead(limit: 50, page: nextPage);

// process the result as needed
Debug.Log("Messages marked as read successfully");
```