---
source_url: https://www.pubnub.com/docs/chat/unreal-chat-sdk/migrate/unreal-chat-v10-migration-guide
title: Unreal Chat SDK 1.0.0 migration guide
updated_at: 2026-05-18T12:47:51.817Z
---

> 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


# Unreal Chat SDK 1.0.0 migration guide

The 1.0.0 release of the Unreal Chat SDK rebuilds the SDK on top of the [PubNub Unreal Engine Core SDK](https://www.pubnub.com/docs/sdks/unreal) (`PubnubLibrary`) and removes the dependency on the bundled C++ Chat SDK (`cpp-chat.dll`). The API surface is rearchitected: all network-calling methods now return structured result types, every method gains an asynchronous variant, and real-time listeners use entity-owned delegate properties instead of callback parameters.

This guide is for developers who use Unreal Chat SDK `0.x.x` in existing applications.

If your application uses `0.x.x`, it will not compile against `1.0.0` without changes. This guide summarizes the differences between the versions and shows how to migrate to Unreal Chat SDK `1.0.0`.

Most notable changes include:

* **New dependency**: Unreal Chat SDK `0.x.x` was a self-contained plugin. Version `1.0.0` requires the [PubNub Unreal Engine Core SDK](https://www.pubnub.com/docs/sdks/unreal) (`PubnubLibrary`) as a separate plugin installed alongside the Chat SDK.
* **Type renames**: Most classes and structs gained a `Chat` infix (`UPubnubUser` → `UPubnubChatUser`, `FPubnubMessageAction` → `FPubnubChatMessageAction`, and others).
* **Result structs**: Every method now returns a typed result struct wrapping `FPubnubChatOperationResult` (with `Error` and `ErrorMessage` fields) alongside the domain data. No more raw pointer returns or silent `nullptr` on failure.
* **Sync/Async split**: Every network method is available in both a synchronous blocking variant and an `Async` variant that takes a delegate callback.
* **Entity-first delegates**: Real-time listeners use multicast delegate properties on entity objects. Bind delegates before calling the streaming method; stop with an explicit `StopStreaming*()` call.
* **Multiple chat instances**: The subsystem now supports multiple simultaneous chat sessions keyed by `UserID`.
* **Synced objects**: Entity objects (channels, users, memberships, messages) are now synchronized by ID. All variables that reference the same entity ID share the latest locally known data — updating one updates all.

## Differences between 0.x.x and 1.0.0

See the major differences between the versions:

| Feature/Method | `0.x.x` | `1.0.0` |
| --- | --- | --- |
| Core dependency | Self-contained plugin (no additional dependency) | Requires [PubNub Unreal Engine Core SDK](https://www.pubnub.com/docs/sdks/unreal) plugin (`PubnubLibrary`) |
| Method return types | Raw pointer or `void` | Typed result struct (e.g. `FPubnubChatUserResult`) containing `FPubnubChatOperationResult` |
| Async methods | Not available | `*Async` suffix variants added for every network method |
| Real-time streaming | `Stream*(Callback)` returns `UPubnubCallbackStop*` | `Stream*()` (no callback); bind `OnUpdated`/`OnXxx` delegate; stop with `StopStreaming*()` |
| `Join` behavior | `Join()` also calls `Connect()` internally | `Join()` and `Connect()` are separate — call both to receive messages after joining |
| Read receipts | `StreamReadReceipts()` returns a list of `UserID`s (limited to 100 members) | Split into `FetchReadReceipts()` (paginated snapshot with timetokens) and `StreamReadReceipts()` (per-user real-time updates) |
| Soft/hard delete | `DeleteMessage()` and `DeleteMessageHard()` | `Delete(bool Soft = false)` |
| `Forward` parameter | `Message->Forward(FString ChannelID)` | `Message->Forward(UPubnubChatChannel* Channel)` |
| `GetUsers` / `GetChannels` parameter order | `(Filter, Sort, Limit, Page)` | `(Limit, Filter, Sort, Page)` — `Sort` type changed to `FPubnubGetAllSort` |
| `SetRestrictions` on `Chat` | `Chat->SetRestrictions(FString UserID, FString ChannelID, FPubnubRestriction)` | `Chat->SetRestrictions(FPubnubChatRestriction)` — `UserID` and `ChannelID` now inside the struct |
| Subsystem `InitChat` return | `UPubnubChat*` | `FPubnubChatInitChatResult` containing `.Chat` and error info |
| Subsystem `GetChat` / `DestroyChat` | No arguments | Require `FString UserID` argument |
| Multiple chat instances | Not supported | Supported — keyed by `UserID` |
| `MessageDraft` change listener | `AddChangeListener()` / `AddChangeListenerWithSuggestions()` | Bind `OnMessageDraftUpdated` / `OnMessageDraftUpdatedWithSuggestions` delegate properties |
| Object identity | Independent snapshots. Two variables with the same entity ID can hold different data simultaneously | Synchronized by ID. All references to the same entity ID share the latest locally known data. |

## Join and Connect

:::warning Important change
This change affects any code that calls `Join()` and then listens for messages. Update it before releasing to users.
:::

In `0.x.x`, calling `Join()` on a channel also subscribed to receive messages on that channel automatically. In `1.0.0`, `Join()` creates the membership server-side but does **not** subscribe. Call `Connect()` separately after `Join()` to start receiving messages.

###### 0.x.x

```cpp
// Join implicitly started message delivery
FPubnubChatOperationResult Result = Channel->Join();

// Messages arrived via the channel delegate
```

###### 1.0.0

```cpp
// Join creates the membership
FPubnubChatJoinResult JoinResult = Channel->Join();
if (JoinResult.Result.Error) { return; }

// Connect separately to start receiving messages
Channel->OnMessageReceivedNative.AddUObject(this, &AMyActor::OnMessageReceived);
FPubnubChatOperationResult ConnectResult = Channel->Connect();
```

## Dependency

Unreal Chat SDK `0.x.x` was a self-contained plugin. Version `1.0.0` depends on the [PubNub Unreal Engine Core SDK](https://github.com/pubnub/unreal-engine) (`PubnubLibrary`). If you installed from FAB, the [PubNub Gaming SDK](https://www.fab.com/listings/9501a8d6-f9e6-4cf8-8b56-d173bdb71fc4) already includes both plugins. No additional downloads are needed.

To install the Core SDK from GitHub:

1. Download the [PubNub Unreal Engine Core SDK](https://github.com/pubnub/unreal-engine) and place it in your project's `Plugins` folder alongside the Chat SDK plugin.
2. Delete the `Intermediate` and `Binaries` folders from your project.
3. Rebuild the project.

No changes to your `.Build.cs` file are required.

## Type renames

Rename all entity class references. The `Chat` infix was added to distinguish Chat SDK types from the underlying UE Core SDK types.

| Old type | New type |
| --- | --- |
| `UPubnubUser` | `UPubnubChatUser` |
| `UPubnubChannel` | `UPubnubChatChannel` |
| `UPubnubMessage` | `UPubnubChatMessage` |
| `UPubnubMembership` | `UPubnubChatMembership` |
| `UPubnubThreadChannel` | `UPubnubChatThreadChannel` |
| `UPubnubAccessManager` | `UPubnubChatAccessManager` |
| `FPubnubMessageAction` | `FPubnubChatMessageAction` |
| `FPubnubRestriction` | `FPubnubChatRestriction` |
| `FPubnubEvent` | `FPubnubChatEvent` |
| `FPubnubSendTextParams` | `FPubnubChatSendTextParams` |
| `FPubnubCreatedChannelWrapper` | `FPubnubChatCreateGroupConversationResult` / `FPubnubChatCreateDirectConversationResult` |
| `FPubnubChannelsResponseWrapper` | `FPubnubChatGetChannelsResult` |
| `FPubnubUsersResponseWrapper` | `FPubnubChatGetUsersResult` |
| `FPubnubEventsHistoryWrapper` | `FPubnubChatEventsResult` |

Also rename the `CustomDataJson` field to `Custom` on `FPubnubChatChannelData`, `FPubnubChatUserData`, and `FPubnubChatMembershipData`:

### 0.x.x

```cpp
FPubnubChatChannelData Data;
Data.CustomDataJson = TEXT("{\"color\":\"blue\"}");
```

### 1.0.0

```cpp
FPubnubChatChannelData Data;
Data.Custom = TEXT("{\"color\":\"blue\"}");
```

Update enum value names. All enum values now use PascalCase after the prefix:

| Old value | New value |
| --- | --- |
| `PCET_TYPING` | `PCET_Typing` |
| `PCET_CUSTOM` | `PCET_Custom` |
| `PCS_Connected` | `PCCS_ConnectionOnline` |
| `PMAT_Reaction` | `PCMAT_Reaction` |
| `PMAT_Edited` | `PCMAT_Edited` |
| `PMAT_Deleted` | `PCMAT_Deleted` |

## Result structs

Every method that previously returned a raw pointer or `void` now returns a typed result struct. The struct always contains a `Result` field of type `FPubnubChatOperationResult` with `Error` (bool) and `ErrorMessage` (FString). Check `Error` before accessing the payload.

### 0.x.x

```cpp
UPubnubUser* User = Chat->CreateUser("user_alice", Data);
if (!User)
{
    // error only visible in logs
}
```

### 1.0.0

```cpp
FPubnubChatUserResult Result = Chat->CreateUser("user_alice", Data);
if (Result.Result.Error)
{
    UE_LOG(LogTemp, Error, TEXT("%s"), *Result.Result.ErrorMessage);
    return;
}
UPubnubChatUser* User = Result.User;
```

Methods that previously returned `void` now return `FPubnubChatOperationResult`:

#### 0.x.x

```cpp
Channel->SendText(TEXT("Hello"));
// no way to know if it succeeded
```

#### 1.0.0

```cpp
FPubnubChatOperationResult Result = Channel->SendText(TEXT("Hello"));
if (Result.Error)
{
    UE_LOG(LogTemp, Error, TEXT("%s"), *Result.ErrorMessage);
}
```

## Sync and Async methods

Every network method is now available in two variants. Use the synchronous form when you need the result immediately; use the `Async` form to avoid blocking the game thread.

### Synchronous (blocking)

```cpp
// Blocks until complete. Returns result struct directly.
FPubnubChatUserResult Result = Chat->GetUser("user_alice");
if (!Result.Result.Error)
{
    UPubnubChatUser* User = Result.User;
}
```

### Asynchronous (non-blocking)

```cpp
// Returns void immediately. Callback fires on the game thread when done.
Chat->GetUserAsync("user_alice", OnGetUserDelegate);

// Or with a native lambda:
FOnPubnubChatUserResponseNative Callback;
Callback.BindLambda([](const FPubnubChatUserResult& Result)
{
    if (!Result.Result.Error)
    {
        UPubnubChatUser* User = Result.User;
    }
});
Chat->GetUserAsync("user_alice", Callback);
```

## Multiple chat instances

The subsystem now supports multiple simultaneous chat sessions, one per `UserID`. Update all `InitChat`, `GetChat`, and `DestroyChat` calls.

### 0.x.x

```cpp
UPubnubChat* Chat = PubnubChatSubsystem->InitChat("pub-key", "sub-key", "Player_001");

UPubnubChat* Chat = PubnubChatSubsystem->GetChat();

PubnubChatSubsystem->DestroyChat();
```

### 1.0.0

```cpp
FPubnubChatInitChatResult InitResult = PubnubChatSubsystem->InitChat("pub-key", "sub-key", "Player_001");
if (InitResult.Result.Error) { return; }
UPubnubChat* Chat = InitResult.Chat;

UPubnubChat* Chat = PubnubChatSubsystem->GetChat("Player_001");

PubnubChatSubsystem->DestroyChat("Player_001");
// Or destroy all instances at once:
PubnubChatSubsystem->DestroyAllChats();
```

## Synced and mutable objects

In `0.x.x`, entity objects (channels, users, memberships, messages) were independent snapshots. Two variables that pointed to the same entity ID could hold different data at the same time. Calling `Update()` on one returned a new object with the updated data, leaving any other variable unchanged.

In `1.0.0`, every entity object is synchronized by ID. All variables that hold a reference to the same entity ID share the latest locally known data. Calling `Update()` on any of them updates the underlying shared state, so every other reference reflects the change immediately.

### 0.x.x

```cpp
// Channel and Channel2 are independent snapshots
UPubnubChannel* Channel = Chat->CreatePublicConversation(ChannelID, ChannelData1);
UPubnubChannel* Channel2 = Chat->GetChannel(ChannelID);

Channel2 = Channel2->Update(ChannelData2);
// Channel still holds ChannelData1; Channel2 holds ChannelData2
```

### 1.0.0

```cpp
// Channel and Channel2 both reference the same synchronized object
UPubnubChatChannel* Channel = Chat->CreatePublicConversation(ChannelID, ChannelData1).Channel;
UPubnubChatChannel* Channel2 = Chat->GetChannel(ChannelID).Channel;

Channel2->Update(ChannelData2);
// Both Channel and Channel2 now reflect ChannelData2
```

This applies to all entity types: channels, users, memberships, and messages. This is the default in `1.0.0`. However, review any code that intentionally holds multiple pointers to the same entity ID and relies on them being independent. That pattern no longer works.

## Entity-first delegates

Real-time listeners no longer accept a callback parameter. Bind to the entity's delegate property before calling the streaming method. Call the matching `StopStreaming*()` method to unsubscribe.

### 0.x.x

```cpp
FOnPubnubMessageStreamUpdateReceived Callback;
Callback.BindDynamic(this, &AMyActor::OnMessageUpdated);
UPubnubCallbackStop* Stop = Message->StreamUpdates(Callback);

// Stop:
Stop->Stop();
```

### 1.0.0

```cpp
// Bind the delegate first, then start streaming.
Message->OnUpdatedNative.AddUObject(this, &AMyActor::OnMessageUpdated);
Message->StreamUpdates();

// Stop:
Message->StopStreamingUpdates();

// Callback signature:
void AMyActor::OnMessageUpdated(FString Timetoken, const FPubnubChatMessageData& MessageData) { }
```

The same pattern applies to all streaming methods across all entities (`Channel->StreamPresence()`, `Channel->StreamTyping()`, `User->StreamMentions()`, `Membership->StreamUpdates()`, etc.).

## Read receipts

The read receipts API was reworked to scale beyond 100 channel members and to separate snapshot fetching from real-time streaming.

In `0.x.x`, `StreamReadReceipts()` returned a list of `UserID`s representing members who had read the message. This worked only for channels with 100 or fewer members.

In `1.0.0`:

* [FetchReadReceipts()](https://www.pubnub.com/docs/chat/unreal-chat-sdk/build/features/messages/read-receipts#fetch-read-receipts) returns a paginated snapshot of the last-read timetoken for each member. Use this for initial load or on-demand checks.
* [StreamReadReceipts()](https://www.pubnub.com/docs/chat/unreal-chat-sdk/build/features/messages/read-receipts#get-read-receipts) delivers real-time updates per user via the channel's `OnReadReceiptReceived` delegate. Each update contains a single user's read position, not a full member list.

### 0.x.x

```cpp
// Returns TArray<FString> UserIDs — limited to 100 members
UPubnubCallbackStop* Stop = Channel->StreamReadReceipts(OnReadReceiptsDelegate);
```

### 1.0.0

```cpp
// One-time paginated snapshot with timetokens
FPubnubChatFetchReadReceiptsResult FetchResult = Channel->FetchReadReceipts();

// Real-time per-user updates (no member limit)
Channel->OnReadReceiptReceivedNative.AddUObject(this, &AMyActor::OnReadReceipt);
Channel->StreamReadReceipts();

// Stop:
Channel->StopStreamingReadReceipts();
```

## Delete message

The separate soft-delete and hard-delete methods are unified into a single `Delete(bool Soft)` method.

### 0.x.x

```cpp
// Soft delete
UPubnubMessage* SoftDeleted = Message->DeleteMessage();

// Hard delete
bool bSuccess = Message->DeleteMessageHard();
```

### 1.0.0

```cpp
// Soft delete (marks as deleted; data remains retrievable)
FPubnubChatOperationResult Result = Message->Delete(/*Soft=*/ true);

// Hard delete (permanently removes from Message Persistence)
FPubnubChatOperationResult Result = Message->Delete(/*Soft=*/ false);
```

## Message accessors

Several `Message` accessors were renamed for consistency with the `Get*` / `Is*` / `Has*` naming convention.

| Old | New |
| --- | --- |
| `Message->Text()` | `Message->GetCurrentText()` |
| `Message->GetTimetoken()` | `Message->GetMessageTimetoken()` |
| `Message->Deleted()` → `bool` | `Message->IsDeleted()` → `FPubnubChatIsDeletedResult` |
| `Message->Type()` → `EPubnubChatMessageType` | `Message->GetType()` → `FString` |
| `Message->Reactions()` → `TArray<FPubnubMessageAction>` | `Message->GetReactions()` → `FPubnubChatGetReactionsResult` |
| `Message->HasThread()` → `bool` | `Message->HasThread()` → `FPubnubChatHasThreadResult` |

### 0.x.x

```cpp
FString Text = Message->Text();
FString Timetoken = Message->GetTimetoken();
bool bDeleted = Message->Deleted();
```

### 1.0.0

```cpp
FString Text = Message->GetCurrentText();
FString Timetoken = Message->GetMessageTimetoken();
FPubnubChatIsDeletedResult DeletedResult = Message->IsDeleted();
bool bDeleted = DeletedResult.IsDeleted;
```

## Forward message

`Forward` now takes a `UPubnubChatChannel*` object instead of a string channel ID.

### 0.x.x

```cpp
Message->Forward(TEXT("target-channel-id"));
```

### 1.0.0

```cpp
FPubnubChatChannelResult ChannelResult = Chat->GetChannel(TEXT("target-channel-id"));
if (!ChannelResult.Result.Error)
{
    Message->Forward(ChannelResult.Channel);
}
```

## GetUsers and GetChannels parameter order

The parameter order for `GetUsers()` and `GetChannels()` changed. `Limit` is now the first parameter. The `Sort` parameter type also changed from `FString` to a typed struct.

### 0.x.x

```cpp
// GetUsers(Filter, Sort, Limit, Page)
Chat->GetUsers(TEXT(""), TEXT("name:asc"), 25, FPubnubPage());

// GetChannels(Filter, Sort, Limit, Page)
Chat->GetChannels(TEXT(""), TEXT("id:asc"), 25, FPubnubPage());
```

### 1.0.0

```cpp
// GetUsers(Limit, Filter, Sort, Page)
FPubnubGetAllSort SortOptions;
Chat->GetUsers(25, TEXT(""), SortOptions, FPubnubPage());

// GetChannels(Limit, Filter, Sort, Page)
Chat->GetChannels(25, TEXT(""), SortOptions, FPubnubPage());
```

## SetRestrictions on Chat

`Chat->SetRestrictions()` now accepts a single `FPubnubChatRestriction` struct that includes `UserID` and `ChannelID` directly, replacing the three separate parameters.

### 0.x.x

```cpp
FPubnubRestriction Restriction;
Restriction.Ban = true;
Restriction.Reason = TEXT("Spam");
Chat->SetRestrictions(TEXT("user_123"), TEXT("support"), Restriction);
```

### 1.0.0

```cpp
FPubnubChatRestriction Restriction;
Restriction.UserID = TEXT("user_123");
Restriction.ChannelID = TEXT("support");
Restriction.Ban = true;
Restriction.Reason = TEXT("Spam");
Chat->SetRestrictions(Restriction);
```

## MessageDraft

`MessageDraft` was rewritten as a native Unreal implementation. Key changes:

* Change listeners replaced by delegate properties.
* Message elements are now plain structs instead of UObjects.
* New `AppendText()` method for easily appending plain text to the end of a draft.
* Draft mutations are now validated internally — the SDK prevents operations that would corrupt existing mentions or links (for example, inserting text in the middle of a mention offset range).

### 0.x.x

```cpp
// Adding a change listener
Draft->AddChangeListener(Callback);

// Adding a mention target (UObject)
UPubnubMentionTarget* Target = UPubnubMentionTarget::CreateUserMentionTarget(TEXT("user-id"));
Draft->AddMention(5, 4, Target);

// Reading elements (UObjects)
TArray<UPubnubMessageElement*> Elements = /* delivered via callback */;
FString Text = Elements[0]->GetText();
```

### 1.0.0

```cpp
// Bind delegate property instead of AddChangeListener
Draft->OnMessageDraftUpdatedNative.AddUObject(this, &AMyActor::OnDraftUpdated);

// Append plain text to the draft
Draft->AppendText(TEXT(" — see you there!"));

// Mention target is now a struct created via a utilities class
FPubnubChatMentionTarget Target = UPubnubChatMessageDraftUtilities::CreateUserMentionTarget(TEXT("user-id"));
Draft->AddMention(5, 4, Target);

// Elements are now value-type structs
TArray<FPubnubChatMessageElement> Elements = Draft->GetMessageElements();
FString Text = Elements[0].Text;
```

## Sample widget

Unreal Chat SDK 1.0.0 includes a sample UI widget (`W_PubnubChatSample`) in the plugin's `Content` folder. The widget demonstrates initialization, sending and receiving messages, presence, and moderation. Open it in the Unreal Editor to see a working reference implementation before building your own UI.

## New capabilities in 1.0.0

The following features are new in `1.0.0` and have no counterpart in `0.x.x`. Consider adopting them after completing your migration.

| Feature | Description |
| --- | --- |
| **Connection status** | `Chat->OnConnectionStatusChanged` delegate fires with `EPubnubChatConnectionStatus` (`PCCS_ConnectionOnline`, `PCCS_ConnectionOffline`, `PCCS_ConnectionError`) whenever the underlying PubNub connection changes. Refer to [connection management](https://www.pubnub.com/docs/chat/unreal-chat-sdk/build/features/connection-management) for more details. |
| **Disconnect / Reconnect** | `Chat->DisconnectSubscriptions()` drops all active subscriptions; `Chat->ReconnectSubscriptions(FString Timetoken)` restores them, optionally catching up from a timetoken. |
| **Rate limiter** | `FPubnubChatRateLimiterConfig` in `FPubnubChatConfig` lets you configure per-channel-type send rate limits and an exponential backoff factor. See `Config → RateLimiter` in [Initial configuration](https://www.pubnub.com/docs/chat/unreal-chat-sdk/build/configuration). |
| **EmitReadReceiptEvents config** | `FPubnubChatConfig.EmitReadReceiptEvents` (`TMap<FString, bool>`) controls which channel types emit read receipt signals (`public`, `group`, `direct`). |
| **Global presence** | `StoreUserActivityTimestamps` and `StoreUserActivityInterval` in `FPubnubChatConfig` track the last active time for all users. See [global presence](https://www.pubnub.com/docs/chat/unreal-chat-sdk/build/features/users/presence#global-presence). |
| **InitChatAsync()** | Non-blocking initialization via `PubnubChatSubsystem->InitChatAsync(PubKey, SubKey, UserID, OnInitChatDelegate)`. |
| **Native lambda callbacks** | All `*Async` methods accept a `*Native` delegate variant that takes a lambda instead of a `UObject` delegate. For example, `FOnPubnubChatOperationResponseNative`. |

## Migration steps

To migrate from Unreal Chat SDK `0.x.x` to `1.0.0`:

1. Install the Core SDK. If you use FAB, install the [PubNub Gaming SDK](https://www.fab.com/listings/9501a8d6-f9e6-4cf8-8b56-d173bdb71fc4) which includes both plugins. If you use GitHub, download the [PubNub Unreal Engine Core SDK](https://github.com/pubnub/unreal-engine) plugin and place it in your project's `Plugins` folder. Delete the `Intermediate` and `Binaries` folders and rebuild the project.
2. Update every `Join()` call that expects to receive messages: add a `Connect()` call after `Join()` and bind the channel's message delegate before calling `Connect()`.
3. Rename all entity types using the [type renames table](#type-renames). Rename `CustomDataJson` to `Custom` on data structs.
4. Update enum value names to the new PascalCase format.
5. Update `InitChat`, `GetChat`, and `DestroyChat` calls to pass a `UserID` argument. Extract the `Chat` pointer from `FPubnubChatInitChatResult`.
6. Update all method return-value handling to check `Result.Error` before using the payload.
7. Migrate streaming calls to the entity-first delegate pattern: bind to the entity's delegate property, call the parameterless `Stream*()` method, and call `StopStreaming*()` when done.
8. Update read receipts usage: replace `StreamReadReceipts()` list-based calls with `FetchReadReceipts()` for snapshots and bind `OnReadReceiptReceived` for real-time updates.
9. Replace `DeleteMessage()` and `DeleteMessageHard()` with `Delete(bool Soft)`.
10. Rename message accessors: `Text()` → `GetCurrentText()`, `GetTimetoken()` → `GetMessageTimetoken()`, `Deleted()` → `IsDeleted()`, `Type()` → `GetType()`, `Reactions()` → `GetReactions()`.
11. Update `Forward()` calls to pass a `UPubnubChatChannel*` object instead of a string ID.
12. Swap `GetUsers()` and `GetChannels()` parameter order to `(Limit, Filter, Sort, Page)` and update the `Sort` argument type to `FPubnubGetAllSort`.
13. Update `Chat->SetRestrictions()` to use the unified `FPubnubChatRestriction` struct.
14. Migrate `MessageDraft` change listeners to `OnMessageDraftUpdated` delegate properties and update `MentionTarget` and `MessageElement` usage to the new struct types.
15. Consider adopting `*Async` method variants to avoid blocking the game thread where appropriate.
16. Review any code that holds multiple pointers to the same entity ID and relies on them being independent snapshots. In `1.0.0`, all references to the same entity ID share the latest locally known data, so patterns that depended on independent copies will behave differently.

If you encounter issues during migration, [contact PubNub Support](https://support.pubnub.com).