---
source_url: https://www.pubnub.com/docs/chat/unreal-chat-sdk/build/features/messages/unread
title: Unread messages
updated_at: 2026-06-18T11:25:54.938Z
---

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

##### Usage in Blueprints and C++

You can use PubNub's functionality via Blueprints or directly in C++ code.

* In Blueprints, you can access PubNub from any Widget or Actor. Start by [initializing chat](https://www.pubnub.com/docs/chat/unreal-chat-sdk/build/configuration#initialize-pubnub-chat) using `InitChat` on `UPubnubChatSubsystem`. Afterwards, you can use the returned `UPubnubChat` object reference to call all Chat SDK functions.

:::warning Blueprint functions
The Blueprints provided in the documentation show how you can structure your application and may contain utility methods and elements like buttons that are not part of the Unreal Chat SDK and are meant to serve as guidance.
:::

* In C++, you can use UPubnubChatSubsystem as any other Game Instance Subsystem. #include "Kismet/GameplayStatics.h" #include "Engine/GameInstance.h" #include "PubnubChatSubsystem.h" // ACTION REQUIRED: Replace ASample_ChatSubsystem with name of your Actor class void ASample_ChatSubsystem::InitChatSample() { // Get PubnubChatSubsystem from GameInstance UGameInstance* GameInstance = UGameplayStatics::GetGameInstance(this); UPubnubChatSubsystem* PubnubChatSubsystem = GameInstance->GetSubsystem<UPubnubChatSubsystem>(); // Initialize Chat - InitChat may fail under some conditions so make sure to check the Result for errors before using the Chat FPubnubChatInitChatResult InitChatResult = PubnubChatSubsystem->InitChat(TEXT("demo"), TEXT("demo"), TEXT("Player_001")); UPubnubChat* PubnubChat = InitChatResult.Chat; } Chat now allows you to call all Chat SDK functions, for example, Chat->GetChannel("my_channel").

:::note Asynchronous and synchronous method execution
Most PubNub Unreal SDK methods are available in both asynchronous and synchronous variants.
* Asynchronous methods (Async suffix) return void and take an optional delegate parameter that fires when the operation completes. 1Chat->GetUnreadMessagesCountsAsync(OnGetUnreadMessagesCountsResponseDelegate); You can also use native callbacks that accept lambdas instead of dynamic delegates. Native callback types have the Native suffix (for example, FOnPubnubChatGetUnreadMessagesCountsResponseNative).
* Synchronous methods (no suffix) block the main game thread until the operation completes and return a result struct directly. 1FPubnubChatGetUnreadMessagesCountsResult Result = Chat->GetUnreadMessagesCounts();
:::

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

#### C++ / Input parameters

```cpp
Membership->GetLastReadMessageTimetoken();
```

#### Blueprint

#### Output

| Type | Description |
| --- | --- |
| `FString` | Value of the returned timetoken. |

### Sample code

:::tip Reference code
This example is a self-contained code snippet ready to be run. Set up your Unreal project and follow the instructions in the lines marked with `ACTION REQUIRED` before running the code. Use it as a reference when working with other examples in this document.
:::

Get the last-read message timetoken from a membership.

###### C++

###### Actor.h

```cpp
// blueprint.d72i5f4z
UFUNCTION(BlueprintCallable, Category = "PubnubChat|Samples|ChatMembership")
void GetLastReadMessageTimetokenSample();
```

###### Actor.cpp

```cpp
// ACTION REQUIRED: Replace ASample_ChatMembership with name of your Actor class
void ASample_ChatMembership::GetLastReadMessageTimetokenSample()
{
	// snippet.hide
	UPubnubChatMembership* Membership = nullptr;
	// snippet.show

	// Assumes Membership is a valid UPubnubChatMembership

	// Get last-read message timetoken from local cache
	FString LastReadTimetoken = Membership->GetLastReadMessageTimetoken();
}
```

###### Blueprint

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

```cpp
Membership->GetUnreadMessagesCount();
```

#### Output

| Parameter | Description |
| --- | --- |
| `FPubnubChatGetUnreadMessagesCountResult`Type: `struct` | Returned object containing `Result` and `Count`. |
| `> Result`Type: `FPubnubChatOperationResult` | Contains `Error` (bool) and `ErrorMessage` (FString). Check `Error` to determine if the operation succeeded. |
| `> Count`Type: `int` | The number of unread messages on the channel for this membership. |

### Sample code

Get the unread messages count for a membership.

```cpp
// ACTION REQUIRED: Replace ASample_ChatMembership with name of your Actor class
void ASample_ChatMembership::GetUnreadMessagesCountSample()
{
	// snippet.hide
	UPubnubChatMembership* Membership = nullptr;
	// snippet.show

	// Assumes Membership is a valid UPubnubChatMembership

	// Callback for when the operation completes (returns unread count)
	FOnPubnubChatGetUnreadMessagesCountResponseNative Callback;
	// ACTION REQUIRED: Replace ASample_ChatMembership with name of your Actor class
	Callback.BindUObject(this, &ASample_ChatMembership::OnGetUnreadMessagesCountResponse);
	Membership->GetUnreadMessagesCountAsync(Callback);
}

// ACTION REQUIRED: Replace ASample_ChatMembership with name of your Actor class
void ASample_ChatMembership::OnGetUnreadMessagesCountResponse(const FPubnubChatGetUnreadMessagesCountResult& Result)
{
	if (Result.Result.Error) { return; }
	int Count = Result.Count;
}
```

## Get unread messages count (all channels)

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

:::note Unread counts and filtering
Filters support `channel.*` fields plus `status`, `type`, `custom` (not `uuid.*`). Unread counts include your own messages. See [Memberships filters](https://www.pubnub.com/docs/general/metadata/filtering#memberships-filters).
:::

### Method signature

```cpp
Chat->GetUnreadMessagesCounts(
    int Limit = 0,
    FString Filter = "",
    FPubnubMembershipSort Sort = FPubnubMembershipSort(),
    FPubnubPage Page = FPubnubPage()
);
```

| Parameter | Description |
| --- | --- |
| `Limit`Type: `int`Default: `0` | Number of objects to return in response. |
| `Filter`Type: `FString` (Memberships filter)Default: `""` | 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: `FPubnubMembershipSort`Default: n/a | Struct defining the property to sort by, and a sort direction. Available options are `id`, `name`, and `updated`. |
| `Page`Type: `FPubnubPage`Default: n/a | Object used for pagination to define which previous or next result page you want to fetch. |
| `> Next`Type: `FString`Default: n/a | Random string returned from the server, indicating a specific position in a data set. Used for forward pagination, it fetches the next page, allowing you to continue from where you left off. |
| `> Prev`Type: `FString`Default: n/a | Random string returned from the server, indicating a specific position in a data set. Used for backward pagination, it fetches the previous page, enabling access to earlier data. Ignored if the `Next` parameter is supplied. |

#### Output

| Parameter | Description |
| --- | --- |
| `FPubnubChatGetUnreadMessagesCountsResult`Type: `struct` | Returned object containing `Result` and `UnreadMessagesCounts`. |
| `> Result`Type: `FPubnubChatOperationResult` | Operation result with `Error` (bool), `ErrorMessage` (FString), and `StepResults`. |
| `> UnreadMessagesCounts`Type: `TArray<FPubnubChatUnreadMessagesCountsWrapper>` | Array of structs containing `Channel`, `Membership`, and `Count`. |
| `>> Channel`Type: `UPubnubChatChannel*` | Channel with unread messages. |
| `>> Membership`Type: `UPubnubChatMembership*` | Returned [Membership object](https://www.pubnub.com/docs/chat/unreal-chat-sdk/learn/chat-entities/membership) showing the user-channel data. |
| `>> Count`Type: `int` | Total number of messages unread by the current user on a given channel. |

### Sample code

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

```cpp
// ACTION REQUIRED: Replace ASample_Chat with name of your Actor class
void ASample_Chat::GetUnreadMessagesCountsSample()
{
	// snippet.hide
	UPubnubChat* Chat = nullptr;
	// snippet.show

	// Assumes Chat is a valid and initialized instance of UPubnubChat

	// Get unread message counts per channel (e.g. for badge counts on channel list)
	FOnPubnubChatGetUnreadMessagesCountsResponseNative Callback;
	// ACTION REQUIRED: Replace ASample_Chat with name of your Actor class
	Callback.BindUObject(this, &ASample_Chat::OnGetUnreadMessagesCountsResponse);
	Chat->GetUnreadMessagesCountsAsync(Callback, 0);
}

// ACTION REQUIRED: Replace ASample_Chat with name of your Actor class
void ASample_Chat::OnGetUnreadMessagesCountsResponse(const FPubnubChatGetUnreadMessagesCountsResult& Result)
{
	if (Result.Result.Error) { return; }
	for (const FPubnubChatUnreadMessagesCountsWrapper& Wrapper : Result.UnreadMessagesCounts)
	{
		/* Wrapper.Channel has Wrapper.Count unread messages */
	}
}
```

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 enables [Read Receipts](https://www.pubnub.com/docs/chat/unreal-chat-sdk/build/features/messages/read-receipts) to track which member read which message. For non-public channels, these methods also emit a Receipt chat event so other clients can track read position.

### Method signature

#### C++ / Input parameters

* SetLastReadMessage() 1Membership->SetLastReadMessage(UPubnubChatMessage* Message);
* SetLastReadMessageTimetoken() 1Membership->SetLastReadMessageTimetoken(FString Timetoken);

| Parameter | Required by `SetLastReadMessage()` | Required by `SetLastReadMessageTimetoken()` | Description |
| --- | --- | --- | --- |
| `Message`Type: `UPubnubChatMessage*`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/unreal-chat-sdk/build/features/channels/membership#update) as the `custom` parameter called `lastReadMessageTimetoken`. |
| `Timetoken`Type: `FString`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/unreal-chat-sdk/build/features/channels/membership#update) as the `custom` parameter called `lastReadMessageTimetoken`. |

#### Blueprint

#### Output

| Type | Description |
| --- | --- |
| `FPubnubChatOperationResult` | Contains `Error` (bool) and `ErrorMessage` (FString). Check `Error` to determine if the operation succeeded. The membership is updated in place. |

### Sample code

Set the last-read message timetoken for a membership.

* `SetLastReadMessageTimetoken()`

#### C++

```cpp
// ACTION REQUIRED: Replace ASample_ChatMembership with name of your Actor class
void ASample_ChatMembership::SetLastReadMessageTimetokenSample()
{
	// snippet.hide
	UPubnubChatMembership* Membership = nullptr;
	// snippet.show

	// Assumes Membership is a valid UPubnubChatMembership

	// Timetoken of the message to mark as last read (17-digit PubNub timetoken)
	FString Timetoken = TEXT("17388000000000000");

	// Set last-read message timetoken (no result callback needed)
	Membership->SetLastReadMessageTimetokenAsync(Timetoken, nullptr);
}
```

#### Blueprint

* `SetLastReadMessage()`

```cpp
// ACTION REQUIRED: Replace ASample_ChatMembership with name of your Actor class
void ASample_ChatMembership::SetLastReadMessageSample()
{
	// snippet.hide
	UPubnubChatMembership* Membership = nullptr;
	// snippet.show

	// Assumes Membership is a valid UPubnubChatMembership

	// Message to mark as last read (e.g. from channel history)
	UPubnubChatMessage* Message = nullptr;

	// Set last-read message using a message object (no result callback needed)
	Membership->SetLastReadMessageAsync(Message, nullptr);
}
```

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

```cpp
Chat->MarkAllMessagesAsRead(
    int Limit = 0,
    FString Filter = "",
    FPubnubMembershipSort Sort = FPubnubMembershipSort(),
    FPubnubPage Page = FPubnubPage()
);
```

| Parameter | Description |
| --- | --- |
| `Limit`Type: `int`Default: `0` | Number of objects to return in response. |
| `Filter`Type: `FString` (Memberships filter)Default: `""` | 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: `FPubnubMembershipSort`Default: n/a | Struct defining the property to sort by, and a sort direction. Available options are `id`, `name`, and `updated`. |
| `Page`Type: `FPubnubPage`Default: n/a | Object used for pagination to define which previous or next result page you want to fetch. |
| `> Next`Type: `FString`Default: n/a | Random string returned from the server, indicating a specific position in a data set. Used for forward pagination, it fetches the next page, allowing you to continue from where you left off. |
| `> Prev`Type: `FString`Default: n/a | Random string returned from the server, indicating a specific position in a data set. Used for backward pagination, it fetches the previous page, enabling access to earlier data. Ignored if the `Next` parameter is supplied. |

#### Output

| Parameter | Description |
| --- | --- |
| `FPubnubChatMarkAllMessagesAsReadResult`Type: `struct` | Returned object containing `Result`, `Page`, `Total`, and `Memberships`. |
| `> Result`Type: `FPubnubChatOperationResult` | Operation result with `Error` (bool), `ErrorMessage` (FString), and `StepResults`. |
| `> Page`Type: `FPubnubPage` | Object used for pagination to define which previous or next result page you want to fetch. |
| `>> Next`Type: `FString` | Random string returned from the server, indicating a specific position in a data set. Used for forward pagination, it fetches the next page, allowing you to continue from where you left off. |
| `>> Prev`Type: `FString` | Random string returned from the server, indicating a specific position in a data set. Used for backward pagination, it fetches the previous page, enabling access to earlier data. Ignored if the `next` parameter is supplied. |
| `> Total`Type: `int` | Total number of messages marked as read. |
| `> Memberships`Type: `TArray<UPubnubChatMembership*>` | Array of all related 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.

```cpp
// ACTION REQUIRED: Replace ASample_Chat with name of your Actor class
void ASample_Chat::MarkAllMessagesAsReadSample()
{
	// snippet.hide
	UPubnubChat* Chat = nullptr;
	// snippet.show

	// Assumes Chat is a valid and initialized instance of UPubnubChat

	// Mark all messages as read for the current user's memberships
	FOnPubnubChatMarkAllMessagesAsReadResponseNative Callback;
	// ACTION REQUIRED: Replace ASample_Chat with name of your Actor class
	Callback.BindUObject(this, &ASample_Chat::OnMarkAllMessagesAsReadResponse);
	Chat->MarkAllMessagesAsReadAsync(Callback, 0);
}

// ACTION REQUIRED: Replace ASample_Chat with name of your Actor class
void ASample_Chat::OnMarkAllMessagesAsReadResponse(const FPubnubChatMarkAllMessagesAsReadResult& Result)
{
	if (Result.Result.Error) { return; }
	// Mark as read completed; Result.Memberships contains updated memberships
}
```