---
source_url: https://www.pubnub.com/docs/sdks/unreal/api-reference/message-actions
title: Message Actions API for Unreal SDK
updated_at: 2026-06-19T11:38:59.368Z
sdk_name: PubNub Unreal SDK
sdk_version: 2.0.5
---

> 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


# Message Actions API for Unreal SDK

PubNub Unreal SDK, use the latest version: 2.0.5

Add or remove actions on published messages to build features like receipts, reactions, or to associate custom metadata to messages. Clients can subscribe to a channel to receive message action events on that channel. They can also fetch past message actions from Message Persistence independently or when they fetch original messages.

:::tip Reactions
"Message Reactions" is a specific application of the Message Actions API for emoji or social reactions.
:::

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

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

* In Blueprints, the SDK is managed by a subsystem. Start by calling the Pubnub Subsystem node, then use Create Pubnub Client to create a UPubnubClient instance.
* In a C++ project, you have to add a dependency to PubnubLibrary: In your IDE, navigate to Source/_{YourProject}_/_{YourProject}_.Build.cs and add a dependency to PubnubLibrary. PrivateDependencyModuleNames.AddRange(new string[] { "PubnubLibrary" });, Compile the code and run the project.
* In C++, start by getting the UPubnubSubsystem (a Game Instance Subsystem), then create a UPubnubClient with your configuration. #include "Kismet/GameplayStatics.h"#include "PubnubSubsystem.h"#include "PubnubClient.h" UGameInstance* GameInstance = UGameplayStatics::GetGameInstance(this);UPubnubSubsystem* PubnubSubsystem = GameInstance->GetSubsystem<UPubnubSubsystem>(); FPubnubConfig Config;Config.PublishKey = "pub-c-...";Config.SubscribeKey = "sub-c-...";Config.UserId = "my-user-id"; UPubnubClient* PubnubClient = PubnubSubsystem->CreatePubnubClient(Config); PubnubClient allows you to call PubNub SDK functions, for example: PubnubClient->PublishMessageAsync("my-channel", "Hello!", OnPublishMessageResponseDelegate); The SDK supports multiple UPubnubClient instances within the same game for different contexts such as different users or keysets. For more information, refer to Configuration.

:::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. 1PubnubClient->AddMessageActionAsync(Channel, MessageTimetoken, ActionType, Value, OnAddMessageActionResponseDelegate); You can also use native callbacks that accept lambdas instead of dynamic delegates. Native callback types have the Native suffix (for example, FOnPubnubAddMessageActionResponseNative).
* Synchronous methods (no suffix) block the main game thread until the operation completes and return a result struct directly. 1FPubnubAddMessageActionResult Result = PubnubClient->AddMessageAction(Channel, MessageTimetoken, ActionType, Value);
:::

:::note Message Actions vs. Message Reactions
**Message Actions** is the flexible, low-level API for adding any metadata to messages (read receipts, delivery confirmations, custom data), while **Message Reactions** specifically refers to using Message Actions for emoji/social reactions.
In PubNub [Core](https://www.pubnub.com/docs/sdks) and [Chat](https://www.pubnub.com/docs/chat/overview) SDKs, the same underlying Message Actions API is referred to as **Message Reactions** when used for emoji reactions - it's the same functionality, just different terminology depending on the use case.
:::

## Add message action

:::warning Requires Message Persistence
This method requires that Message Persistence is [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/).
:::

Add an action to a published message.

### Method(s)

```cpp
// Synchronous
FPubnubAddMessageActionResult Result = PubnubClient->AddMessageAction(
    FString Channel, 
    FString MessageTimetoken, 
    FString ActionType,  
    FString Value
);

// Asynchronous
PubnubClient->AddMessageActionAsync(
    FString Channel, 
    FString MessageTimetoken, 
    FString ActionType,  
    FString Value,
    FOnPubnubAddMessageActionResponse OnAddMessageActionResponse
);
```

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| Channel | FString | Yes |  | Channel to publish the message action to. |
| MessageTimetoken | FString | Yes |  | Timetoken of the published message to apply the action to. |
| ActionType | FString | Yes |  | Message action type. |
| Value | FString | Yes |  | Message action value. |
| OnAddMessageActionResponse | FOnPubnubAddMessageActionResponse | Optional |  | Delegate for the operation result. You can also use a native callback of the type [FOnPubnubAddMessageActionResponseNative](#fonpubnubaddmessageactionresponsenative) to handle the result using a lambda. |

### Sample code

:::tip Reference code
Set up your Unreal project and follow the instructions in the lines marked with
ACTION REQUIRED
before running the code.
:::

###### C++

#### Actor.h

```cpp
#include "PubnubClient.h"

// blueprint.5ws2hz84
UFUNCTION(BlueprintCallable, Category = "Pubnub|Samples|Message Actions")
void AddMessageActionSample();
```

#### Actor.cpp

```cpp
// ACTION REQUIRED: Replace ASample_MessageActions with name of your Actor class
void ASample_MessageActions::AddMessageActionSample()
{
	// snippet.hide
	UPubnubClient* PubnubClient = GetPubnubClient();
	// snippet.show
	
	//Assumes PubnubClient is created and UserID is set
	
	// You should get this from a previously published message.
	FString MessageTimetoken = TEXT("17298418370000000");

	//Add a message action to a message
	FString Channel = TEXT("message-actions-channel");
	FString ActionType = TEXT("reaction");
	FString ActionValue = TEXT("smiley_face");
	PubnubClient->AddMessageActionAsync(Channel, MessageTimetoken, ActionType, ActionValue);
}
```

###### Blueprint

### Returns

* **Synchronous:** returns [FPubnubAddMessageActionResult](#fpubnubaddmessageactionresult) (Result, MessageActionData).
* **Asynchronous:** delegate [FOnPubnubAddMessageActionResponse](#fonpubnubaddmessageactionresponse) receives (Result, MessageActionData).

#### FPubnubAddMessageActionResult

| Field | Type | Description |
| --- | --- | --- |
| `Result` | [FPubnubOperationResult](https://www.pubnub.com/docs/sdks/unreal/api-reference/configuration#operation-result) | Result of the operation. |
| `MessageActionData` | [FPubnubMessageActionData](#fpubnubmessageactiondata) | The added message action data. |

#### FOnPubnubAddMessageActionResponse

| Field | Type | Description |
| --- | --- | --- |
| `Result` | [FPubnubOperationResult](https://www.pubnub.com/docs/sdks/unreal/api-reference/configuration#operation-result) | Result of the operation. |
| `MessageActionData` | [FPubnubMessageActionData](#fpubnubmessageactiondata) | Message action data. |

#### FOnPubnubAddMessageActionResponseNative

| Field | Type | Description |
| --- | --- | --- |
| `Result` | [const FPubnubOperationResult&](https://www.pubnub.com/docs/sdks/unreal/api-reference/configuration#operation-result) | Result of the operation. |
| `MessageActionData` | [const FPubnubMessageActionData&](#fpubnubmessageactiondata) | Message action data. |

#### FPubnubMessageActionData

| Field | Type | Description |
| --- | --- | --- |
| `Type` | `FString` | Message action type. |
| `Value` | `FString` | Message action value. |
| `UserID` | `FString` | User ID of the user who added the action. |
| `ActionTimetoken` | `FString` | Timetoken indicating when the message action was added. |
| `MessageTimetoken` | `FString` | Timetoken indicating when the message the action was added to had been sent. |

### Other examples

:::tip Reference code
Set up your Unreal project and follow the instructions in the lines marked with
ACTION REQUIRED
before running the code.
:::

#### Add a message action with lambda

You can use a lambda function to handle the response:

#### Actor.h

```cpp
#include "PubnubClient.h"

UFUNCTION(BlueprintCallable, Category = "Pubnub|Samples|Message Actions")
void AddMessageActionWithResultLambdaSample();
```

#### Actor.cpp

```cpp
// ACTION REQUIRED: Replace ASample_MessageActions with name of your Actor class
void ASample_MessageActions::AddMessageActionWithResultLambdaSample()
{
	// snippet.hide
	UPubnubClient* PubnubClient = GetPubnubClient();
	// snippet.show
	
	//Assumes PubnubClient is created and UserID is set
	
	// You should get this from a previously published message.
	FString MessageTimetoken = TEXT("17298418370000000");

	// Bind lambda to response delegate
	FOnPubnubAddMessageActionResponseNative OnAddMessageActionResponse;
	OnAddMessageActionResponse.BindLambda([](const FPubnubOperationResult& Result, const FPubnubMessageActionData& MessageActionData)
	{
		if(Result.Error)
		{
			UE_LOG(LogTemp, Error, TEXT("Failed to add message action. Status: %d, Reason: %s"), Result.Status, *Result.ErrorMessage);
		}
		else
		{
			UE_LOG(LogTemp, Log, TEXT("Successfully added message action. Type: '%s', Value: '%s', Timetoken: %s"), *MessageActionData.Type, *MessageActionData.Value, *MessageActionData.ActionTimetoken);
		}
	});
	
	//Add a message action to a message
	FString Channel = TEXT("message-actions-channel");
	FString ActionType = TEXT("reaction");
	FString ActionValue = TEXT("thumbs_up");
	PubnubClient->AddMessageActionAsync(Channel, MessageTimetoken, ActionType, ActionValue, OnAddMessageActionResponse);
}
```

#### Add a message action with result struct

You can use the result struct to handle the response:

#### Actor.h

```cpp
#include "PubnubClient.h"

UFUNCTION(BlueprintCallable, Category = "Pubnub|Samples|Message Actions")
void AddMessageActionWithResultSample();

UFUNCTION()
void OnAddMessageActionResponse(FPubnubOperationResult Result, FPubnubMessageActionData MessageActionData);
```

#### Actor.cpp

```cpp
// ACTION REQUIRED: Replace ASample_MessageActions with name of your Actor class
void ASample_MessageActions::AddMessageActionWithResultSample()
{
	// snippet.hide
	UPubnubClient* PubnubClient = GetPubnubClient();
	// snippet.show
	
	//Assumes PubnubClient is created and UserID is set
	
	// You should get this from a previously published message.
	FString MessageTimetoken = TEXT("17298418370000000");
	
	// Bind response delegate
	// ACTION REQUIRED: Replace ASample_MessageActions with name of your Actor class
	FOnPubnubAddMessageActionResponse OnAddMessageActionResponse;
	OnAddMessageActionResponse.BindDynamic(this, &ASample_MessageActions::OnAddMessageActionResponse);

	//Add a message action to a message
	FString Channel = TEXT("message-actions-channel");
	FString ActionType = TEXT("reaction");
	FString ActionValue = TEXT("heart");
	PubnubClient->AddMessageActionAsync(Channel, MessageTimetoken, ActionType, ActionValue, OnAddMessageActionResponse);
}

// ACTION REQUIRED: Replace ASample_MessageActions with name of your Actor class
void ASample_MessageActions::OnAddMessageActionResponse(FPubnubOperationResult Result, FPubnubMessageActionData MessageActionData)
{
	if(Result.Error)
	{
		UE_LOG(LogTemp, Error, TEXT("Failed to add message action. Status: %d, Reason: %s"), Result.Status, *Result.ErrorMessage);
	}
	else
	{
		UE_LOG(LogTemp, Log, TEXT("Successfully added message action. Type: '%s', Value: '%s', Timetoken: %s"), *MessageActionData.Type, *MessageActionData.Value, *MessageActionData.ActionTimetoken);
	}
}
```

## Remove message action

:::warning Requires Message Persistence
This method requires that Message Persistence is [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/).
:::

Remove a previously added action from a published message. The response is empty.

### Method(s)

```cpp
// Synchronous
FPubnubOperationResult Result = PubnubClient->RemoveMessageAction(
    FString Channel, 
    FString MessageTimetoken, 
    FString ActionTimetoken
);

// Asynchronous
PubnubClient->RemoveMessageActionAsync(
    FString Channel, 
    FString MessageTimetoken, 
    FString ActionTimetoken,
    FOnPubnubRemoveMessageActionResponse OnRemoveMessageActionResponse
);
```

| Parameter | Description |
| --- | --- |
| `Channel` *Type: `FString` | Channel the message action was published to. |
| `MessageTimetoken` *Type: `FString` | Timetoken of the published message to delete the action of. |
| `ActionTimetoken` *Type: `FString` | Timetoken of the published action to delete. |
| `OnRemoveMessageActionResponse`Type: [FOnPubnubRemoveMessageActionResponse](#fonpubnubremovemessageactionresponse) | Delegate for the operation result. You can also use a native callback of the type [FOnPubnubRemoveMessageActionResponseNative](#fonpubnubremovemessageactionresponsenative) to handle the result using a lambda. |

#### Returns

* **Synchronous:** returns [FPubnubOperationResult](https://www.pubnub.com/docs/sdks/unreal/api-reference/configuration#operation-result).
* **Asynchronous:** delegate [FOnPubnubRemoveMessageActionResponse](#fonpubnubremovemessageactionresponse) receives Result only.

#### FOnPubnubRemoveMessageActionResponse

| Field | Type | Description |
| --- | --- | --- |
| `Result` | [FPubnubOperationResult](https://www.pubnub.com/docs/sdks/unreal/api-reference/configuration#operation-result) | Result of the operation. |

#### FOnPubnubRemoveMessageActionResponseNative

| Field | Type | Description |
| --- | --- | --- |
| `Result` | [const FPubnubOperationResult&](https://www.pubnub.com/docs/sdks/unreal/api-reference/configuration#operation-result) | Result of the operation. |

### Sample code

:::tip Reference code
Set up your Unreal project and follow the instructions in the lines marked with
ACTION REQUIRED
before running the code.
:::

###### C++

#### Actor.h

```cpp
#include "PubnubClient.h"

// blueprint.7c_dos8t
UFUNCTION(BlueprintCallable, Category = "Pubnub|Samples|Message Actions")
void RemoveMessageActionSample();
```

#### Actor.cpp

```cpp
// ACTION REQUIRED: Replace ASample_MessageActions with name of your Actor class
void ASample_MessageActions::RemoveMessageActionSample()
{
	// snippet.hide
	UPubnubClient* PubnubClient = GetPubnubClient();
	// snippet.show
	
	//Assumes PubnubClient is created and UserID is set
	
	// ACTION REQUIRED: Replace with real Message timetoken that has an action added.
	FString MessageTimetoken = TEXT("17298418370000000");
	// ACTION REQUIRED: Replace with real Message Action timetoken.
	FString ActionTimetoken = TEXT("17298418390000000");

	//Remove a message action from a message
	FString Channel = TEXT("message-actions-channel");
	PubnubClient->RemoveMessageActionAsync(Channel, MessageTimetoken, ActionTimetoken);
}
```

###### Blueprint

### Other examples

:::tip Reference code
Set up your Unreal project and follow the instructions in the lines marked with
ACTION REQUIRED
before running the code.
:::

#### Remove a message action with lambda

You can use a lambda function to handle the response:

#### Actor.h

```cpp
#include "PubnubClient.h"

UFUNCTION(BlueprintCallable, Category = "Pubnub|Samples|Message Actions")
void RemoveMessageActionWithResultLambdaSample();
	
```

#### Actor.cpp

```cpp
// ACTION REQUIRED: Replace ASample_MessageActions with name of your Actor class
void ASample_MessageActions::RemoveMessageActionWithResultLambdaSample()
{
	// snippet.hide
	UPubnubClient* PubnubClient = GetPubnubClient();
	// snippet.show
	
	//Assumes PubnubClient is created and UserID is set

	// ACTION REQUIRED: Replace with real Message timetoken that has an action added.
	FString MessageTimetoken = TEXT("17298418370000000");
	// ACTION REQUIRED: Replace with real Message Action timetoken.
	FString ActionTimetoken = TEXT("17298418390000000");

	// Bind lambda to response delegate
	FOnPubnubRemoveMessageActionResponseNative OnRemoveMessageActionResponse;
	OnRemoveMessageActionResponse.BindLambda([](const FPubnubOperationResult& Result)
	{
		if(Result.Error)
		{
			UE_LOG(LogTemp, Error, TEXT("Failed to remove message action. Status: %d, Reason: %s"), Result.Status, *Result.ErrorMessage);
		}
		else
		{
			UE_LOG(LogTemp, Log, TEXT("Successfully removed message action."));
		}
	});
	
	//Remove a message action from a message
	FString Channel = TEXT("message-actions-channel");
	PubnubClient->RemoveMessageActionAsync(Channel, MessageTimetoken, ActionTimetoken, OnRemoveMessageActionResponse);
}
```

#### Remove a message action with result struct

You can use the result struct to handle the response:

#### Actor.h

```cpp
#include "PubnubClient.h"

UFUNCTION(BlueprintCallable, Category = "Pubnub|Samples|Message Actions")
void RemoveMessageActionWithResultSample();

UFUNCTION()
void OnRemoveMessageActionResponse(FPubnubOperationResult Result);
```

#### Actor.cpp

```cpp
// ACTION REQUIRED: Replace ASample_MessageActions with name of your Actor class
void ASample_MessageActions::RemoveMessageActionWithResultSample()
{
	// snippet.hide
	UPubnubClient* PubnubClient = GetPubnubClient();
	// snippet.show
	
	//Assumes PubnubClient is created and UserID is set
	
	// ACTION REQUIRED: Replace with real Message timetoken that has an action added.
	FString MessageTimetoken = TEXT("17298418370000000");
	// ACTION REQUIRED: Replace with real Message Action timetoken.
	FString ActionTimetoken = TEXT("17298418390000000");
	
	// Bind response delegate
	// ACTION REQUIRED: Replace ASample_MessageActions with name of your Actor class
	FOnPubnubRemoveMessageActionResponse OnRemoveMessageActionResponse;
	OnRemoveMessageActionResponse.BindDynamic(this, &ASample_MessageActions::OnRemoveMessageActionResponse);

	//Remove a message action from a message
	FString Channel = TEXT("message-actions-channel");
	PubnubClient->RemoveMessageActionAsync(Channel, MessageTimetoken, ActionTimetoken, OnRemoveMessageActionResponse);
}

// ACTION REQUIRED: Replace ASample_MessageActions with name of your Actor class
void ASample_MessageActions::OnRemoveMessageActionResponse(FPubnubOperationResult Result)
{
	if(Result.Error)
	{
		UE_LOG(LogTemp, Error, TEXT("Failed to remove message action. Status: %d, Reason: %s"), Result.Status, *Result.ErrorMessage);
	}
	else
	{
		UE_LOG(LogTemp, Log, TEXT("Successfully removed message action."));
	}
}
```

## Get message actions

:::warning Requires Message Persistence
This method requires that Message Persistence is [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/).
:::

Get a list of message actions in a channel. The response sorts actions by the action's timetoken in ascending order.

:::note Truncated response
The number of message actions in the response may be truncated when internal limits are hit. If the response is truncated, a `more` property is returned with additional parameters. Send iterative calls to Message Persistence, adjusting the parameters to fetch more message actions.
:::

### Method(s)

```cpp
// Synchronous
FPubnubGetMessageActionsResult Result = PubnubClient->GetMessageActions(
    FString Channel,
    FString Start = "", 
    FString End = "", 
    int Limit = 0
);

// Asynchronous
PubnubClient->GetMessageActionsAsync(
    FString Channel,
    FOnPubnubGetMessageActionsResponse OnGetMessageActionsResponse,
    FString Start = "", 
    FString End = "", 
    int Limit = 0
);
```

| Parameter | Description |
| --- | --- |
| `Channel` *Type: `FString` | Channel the message action was published to. |
| `OnGetMessageActionsResponse`Type: [FOnPubnubGetMessageActionsResponse](#fonpubnubgetmessageactionsresponse) | Delegate for the operation result. You can also use a native callback of the type [FOnPubnubGetMessageActionsResponseNative](#fonpubnubgetmessageactionsresponsenative) to handle the result using a lambda. |
| `Start`Type: `FString` | Previously returned cursor bookmark for fetching the next page. Use `""` to avoid paginating with a start bookmark. |
| `End`Type: `FString` | Previously returned cursor bookmark for fetching the previous page. Ignored if you also supply the start parameter. Use `""` to avoid paginating with an end bookmark. |
| `Limit`Type: `int` | Number of objects to return in the response. Default is 100. |

### Sample code

:::tip Reference code
Set up your Unreal project and follow the instructions in the lines marked with
ACTION REQUIRED
before running the code.
:::

###### C++

#### Actor.h

```cpp
#include "PubnubClient.h"

// blueprint._bkkj7ln
UFUNCTION(BlueprintCallable, Category = "Pubnub|Samples|Message Actions")
void GetMessageActionsSample();
	
UFUNCTION()
void OnGetMessageActionsResponse(FPubnubOperationResult Result, const TArray<FPubnubMessageActionData>& MessageActions);
```

#### Actor.cpp

```cpp
// ACTION REQUIRED: Replace ASample_MessageActions with name of your Actor class
void ASample_MessageActions::GetMessageActionsSample()
{
	// snippet.hide
	UPubnubClient* PubnubClient = GetPubnubClient();
	// snippet.show
	
	//Assumes PubnubClient is created and UserID is set
	
	// Bind response delegate
	// ACTION REQUIRED: Replace ASample_MessageActions with name of your Actor class
	FOnPubnubGetMessageActionsResponse OnGetMessageActionsResponse;
	OnGetMessageActionsResponse.BindDynamic(this, &ASample_MessageActions::OnGetMessageActionsResponse);
	
	FString StartTimetoken = TEXT("17298418380000000"); // Newer timetoken
	FString EndTimetoken = TEXT("17298418360000000");   // Older timetoken

	//Get message actions from a channel
	FString Channel = TEXT("message-actions-channel");
	PubnubClient->GetMessageActionsAsync(Channel, OnGetMessageActionsResponse, StartTimetoken, EndTimetoken);
}

// ACTION REQUIRED: Replace ASample_MessageActions with name of your Actor class
void ASample_MessageActions::OnGetMessageActionsResponse(FPubnubOperationResult Result, const TArray<FPubnubMessageActionData>& MessageActions)
{
	if(Result.Error)
	{
		UE_LOG(LogTemp, Error, TEXT("Failed to get message actions. Status: %d, Reason: %s"), Result.Status, *Result.ErrorMessage);
	}
	else
	{
		UE_LOG(LogTemp, Log, TEXT("Successfully got message actions. Number of actions: %d"), MessageActions.Num());
		//List all received message actions
		for (const FPubnubMessageActionData& Action : MessageActions)
		{
			UE_LOG(LogTemp, Log, TEXT("- Type: '%s', Value: '%s', UserID: %s, Action Timetoken: %s"), *Action.Type, *Action.Value, *Action.UserID, *Action.ActionTimetoken);
		}
	}
}
```

###### Blueprint

### Returns

* **Synchronous:** returns [FPubnubGetMessageActionsResult](#fpubnubgetmessageactionsresult) (Result, MessageActions).
* **Asynchronous:** delegate [FOnPubnubGetMessageActionsResponse](#fonpubnubgetmessageactionsresponse) receives (Result, MessageActions).

#### FPubnubGetMessageActionsResult

| Field | Type | Description |
| --- | --- | --- |
| `Result` | [FPubnubOperationResult](https://www.pubnub.com/docs/sdks/unreal/api-reference/configuration#operation-result) | Result of the operation. |
| `MessageActions` | `TArray<FPubnubMessageActionData>` | Array of [FPubnubMessageActionData](#fpubnubmessageactiondata) structs for the actions sent on a given channel. |

#### FOnPubnubGetMessageActionsResponse

| Field | Type | Description |
| --- | --- | --- |
| `Result` | [FPubnubOperationResult](https://www.pubnub.com/docs/sdks/unreal/api-reference/configuration#operation-result) | Result of the operation. |
| `MessageActions` | `const TArray<FPubnubMessageActionData>&` | Array of [FPubnubMessageActionData](#fpubnubmessageactiondata) structs for the actions sent on a given channel. |

#### FOnPubnubGetMessageActionsResponseNative

| Field | Type | Description |
| --- | --- | --- |
| `Result` | [const FPubnubOperationResult&](https://www.pubnub.com/docs/sdks/unreal/api-reference/configuration#operation-result) | Result of the operation. |
| `MessageActions` | `const TArray<FPubnubMessageActionData>&` | Array of [FPubnubMessageActionData](#fpubnubmessageactiondata) structs for the actions sent on a given channel. |

#### FPubnubMessageActionData

| Field | Type | Description |
| --- | --- | --- |
| `Type` | `FString` | Message action type. |
| `Value` | `FString` | Message action value. |
| `UserID` | `FString` | User identifier of the user who added the action. |
| `ActionTimetoken` | `FString` | Timetoken indicating when the message action was added. |
| `MessageTimetoken` | `FString` | Timetoken indicating when the message, to which the action was added, had been sent. |

### Other examples

:::tip Reference code
Set up your Unreal project and follow the instructions in the lines marked with
ACTION REQUIRED
before running the code.
:::

#### Get message actions with lambda

You can use a lambda function to handle the response:

#### Actor.h

```cpp
#include "PubnubClient.h"

UFUNCTION(BlueprintCallable, Category = "Pubnub|Samples|Message Actions")
void GetMessageActionsWithLambdaSample();
```

#### Actor.cpp

```cpp
// ACTION REQUIRED: Replace ASample_MessageActions with name of your Actor class
void ASample_MessageActions::GetMessageActionsWithLambdaSample()
{
	// snippet.hide
	UPubnubClient* PubnubClient = GetPubnubClient();
	// snippet.show
	
	//Assumes PubnubClient is created and UserID is set

	// Bind lambda to response delegate
	FOnPubnubGetMessageActionsResponseNative OnGetMessageActionsResponse;
	OnGetMessageActionsResponse.BindLambda([](const FPubnubOperationResult& Result, const TArray<FPubnubMessageActionData>& MessageActions)
	{
		if(Result.Error)
		{
			UE_LOG(LogTemp, Error, TEXT("Failed to get message actions. Status: %d, Reason: %s"), Result.Status, *Result.ErrorMessage);
		}
		else
		{
			UE_LOG(LogTemp, Log, TEXT("Successfully got message actions. Number of actions: %d"), MessageActions.Num());
			//List all received message actions
			for (const FPubnubMessageActionData& Action : MessageActions)
			{
				UE_LOG(LogTemp, Log, TEXT("- Type: '%s', Value: '%s', UserID: %s, Action Timetoken: %s"), *Action.Type, *Action.Value, *Action.UserID, *Action.ActionTimetoken);
			}
		}
	});
	
	FString StartTimetoken = TEXT("18000000000000000"); // Newer timetoken
	FString EndTimetoken = TEXT("17000000000000000");   // Older timetoken
	
	//Get message actions from a channel
	FString Channel = TEXT("message-actions-channel");
	PubnubClient->GetMessageActionsAsync(Channel, OnGetMessageActionsResponse, StartTimetoken, EndTimetoken);
}
```

#### Get message actions from a channel with a specific time window and limit

You can add a time window and limit to the request:

#### Actor.h

```cpp
#include "PubnubClient.h"

UFUNCTION(BlueprintCallable, Category = "Pubnub|Samples|Message Actions")
void GetMessageActionsWithSettingsSample();

UFUNCTION()
void OnGetMessageActionsResponse_WithSettings(FPubnubOperationResult Result, const TArray<FPubnubMessageActionData>& MessageActions);
```

#### Actor.cpp

```cpp
// ACTION REQUIRED: Replace ASample_MessageActions with name of your Actor class
void ASample_MessageActions::GetMessageActionsWithSettingsSample()
{
	// snippet.hide
	UPubnubClient* PubnubClient = GetPubnubClient();
	// snippet.show
	
	//Assumes PubnubClient is created and UserID is set
	
	// Bind response delegate
	// ACTION REQUIRED: Replace ASample_MessageActions with name of your Actor class
	FOnPubnubGetMessageActionsResponse OnGetMessageActionsResponse;
	OnGetMessageActionsResponse.BindDynamic(this, &ASample_MessageActions::OnGetMessageActionsResponse_WithSettings);

	//Get message actions from a channel with a specific time window and limit
	FString Channel = TEXT("message-actions-channel");
	FString StartTimetoken = TEXT("17298418380000000"); // Newer timetoken
	FString EndTimetoken = TEXT("17298418360000000");   // Older timetoken
	int Limit = 5;
	PubnubClient->GetMessageActionsAsync(Channel, OnGetMessageActionsResponse, StartTimetoken, EndTimetoken, Limit);
}

// ACTION REQUIRED: Replace ASample_MessageActions with name of your Actor class
void ASample_MessageActions::OnGetMessageActionsResponse_WithSettings(FPubnubOperationResult Result, const TArray<FPubnubMessageActionData>& MessageActions)
{
	if(Result.Error)
	{
		UE_LOG(LogTemp, Error, TEXT("Failed to get message actions. Status: %d, Reason: %s"), Result.Status, *Result.ErrorMessage);
	}
	else
	{
		UE_LOG(LogTemp, Log, TEXT("Successfully got message actions. Number of actions: %d"), MessageActions.Num());
		//List all received message actions
		for (const FPubnubMessageActionData& Action : MessageActions)
		{
			UE_LOG(LogTemp, Log, TEXT("- Type: '%s', Value: '%s', UserID: %s, Action Timetoken: %s"), *Action.Type, *Action.Value, *Action.UserID, *Action.ActionTimetoken);
		}
	}
}
```

## History with message reactions

You can choose to return message actions when fetching historical messages. Refer to [Fetch History](https://www.pubnub.com/docs/sdks/unreal/api-reference/storage-and-playback#fetch-history) for more details.

## Complete example

:::tip Reference code
Set up your Unreal project and follow the instructions in the lines marked with
ACTION REQUIRED
before running the code.
:::

#### ASample_MessageActionsFull.h

```cpp
#pragma once

#include "PubnubClient.h"

#include "GameFramework/Actor.h"
#include "CoreMinimal.h"
#include "Sample_MessageActionsFull.generated.h"

// ACTION REQUIRED: Replace PUBNUBLIBRARYTESTS_API with your project's module API macro (usually ProjectName_API)
UCLASS()
class PUBNUBLIBRARYTESTS_API ASample_MessageActionsFull : public AActor
{
	GENERATED_BODY()

protected:
	virtual void BeginPlay() override;

public:

	UFUNCTION(BlueprintCallable, Category = "Pubnub|FullExamples|MessageActions")
	void RunMessageActionsFullExample();
	
private:
	UPROPERTY()
	UPubnubClient* PubnubClient = nullptr;

	FString TestChannel = "message-actions-full-channel";
	FString TestMessageTimetoken = "";
	FString LikeActionTimetoken = "";

	void AddFirstMessageAction();
	void AddSecondMessageAction();
	void GetAllMessageActions();
	void RemoveFirstMessageAction();

	UFUNCTION()
	void OnPublishResponse(FPubnubOperationResult Result, FPubnubMessageData Message);
	
	UFUNCTION()
	void OnAddFirstMessageActionResponse(FPubnubOperationResult Result, FPubnubMessageActionData MessageActionData);

	UFUNCTION()
	void OnAddSecondMessageActionResponse(FPubnubOperationResult Result, FPubnubMessageActionData MessageActionData);
	
	UFUNCTION()
	void OnGetMessageActionsResponse(FPubnubOperationResult Result, const TArray<FPubnubMessageActionData>& MessageActions);

	UFUNCTION()
	void OnRemoveMessageActionResponse(FPubnubOperationResult Result);
	
};
```

#### ASample_MessageActionsFull.cpp

```cpp
#include "Samples/Sample_MessageActionsFull.h"
#include "Kismet/GameplayStatics.h"
#include "Engine/GameInstance.h"
#include "PubnubSubsystem.h"
#include "PubnubClient.h"

void ASample_MessageActionsFull::BeginPlay()
{
	Super::BeginPlay();

	//Run the example on BeginPlay
	RunMessageActionsFullExample();
}

void ASample_MessageActionsFull::RunMessageActionsFullExample()
{
	//Get PubnubSubsystem from GameInstance
	UGameInstance* GameInstance = UGameplayStatics::GetGameInstance(this);
	UPubnubSubsystem* PubnubSubsystem = GameInstance->GetSubsystem<UPubnubSubsystem>();
	
	//Create Pubnub Client using Pubnub Subsystem
	FPubnubConfig Config;
	Config.PublishKey = TEXT("demo");   //replace with your Publish Key from Admin Portal
	Config.SubscribeKey = TEXT("demo"); //replace with your Subscribe Key from Admin Portal
	Config.UserID = TEXT("Player_001");
	PubnubClient = PubnubSubsystem->CreatePubnubClient(Config);

	UE_LOG(LogTemp, Log, TEXT("Message Actions example: Pubnub Client is created"));

	// 1. Publish a message to get a timetoken
	FOnPubnubPublishMessageResponse OnPublishResponse;
	OnPublishResponse.BindDynamic(this, &ASample_MessageActionsFull::OnPublishResponse);
	PubnubClient->PublishMessageAsync(TestChannel, TEXT("{\"message\":\"What a great play!\"}"), OnPublishResponse);
}

void ASample_MessageActionsFull::OnPublishResponse(FPubnubOperationResult Result, FPubnubMessageData Message)
{
	if (!Result.Error)
	{
		UE_LOG(LogTemp, Log, TEXT("Message Actions example: message published successfully. Timetoken: %s"), *Message.Timetoken);
		TestMessageTimetoken = Message.Timetoken;
		AddFirstMessageAction();
	}
	else
	{
		UE_LOG(LogTemp, Error, TEXT("Message Actions example: failed to publish message. Status: %d, Reason: %s"), Result.Status, *Result.ErrorMessage);
	}
}

void ASample_MessageActionsFull::AddFirstMessageAction()
{
	// 2. Add a "like" action
	UE_LOG(LogTemp, Log, TEXT("Message Actions example: adding 'like' action..."));
	FOnPubnubAddMessageActionResponse OnAddMessageActionResponse;
	OnAddMessageActionResponse.BindDynamic(this, &ASample_MessageActionsFull::OnAddFirstMessageActionResponse);
	PubnubClient->AddMessageActionAsync(TestChannel, TestMessageTimetoken, TEXT("reaction"), TEXT("like"), OnAddMessageActionResponse);
}

void ASample_MessageActionsFull::OnAddFirstMessageActionResponse(FPubnubOperationResult Result, FPubnubMessageActionData MessageActionData)
{
	if (!Result.Error)
	{
		UE_LOG(LogTemp, Log, TEXT("Message Actions example: 'like' action added. Action Timetoken: %s"), *MessageActionData.ActionTimetoken);
		LikeActionTimetoken = MessageActionData.ActionTimetoken;
		AddSecondMessageAction();
	}
	else
	{
		UE_LOG(LogTemp, Error, TEXT("Message Actions example: failed to add 'like' action. Status: %d, Reason: %s"), Result.Status, *Result.ErrorMessage);
	}
}

void ASample_MessageActionsFull::AddSecondMessageAction()
{
	// 3. Add a "smiley" action
	UE_LOG(LogTemp, Log, TEXT("Message Actions example: adding 'smiley_face' action..."));
	FOnPubnubAddMessageActionResponse OnAddMessageActionResponse;
	OnAddMessageActionResponse.BindDynamic(this, &ASample_MessageActionsFull::OnAddSecondMessageActionResponse);
	PubnubClient->AddMessageActionAsync(TestChannel, TestMessageTimetoken, TEXT("reaction"), TEXT("smiley_face"), OnAddMessageActionResponse);
}

void ASample_MessageActionsFull::OnAddSecondMessageActionResponse(FPubnubOperationResult Result, FPubnubMessageActionData MessageActionData)
{
	if (!Result.Error)
	{
		UE_LOG(LogTemp, Log, TEXT("Message Actions example: 'smiley_face' action added."));
		GetAllMessageActions();
	}
	else
	{
		UE_LOG(LogTemp, Error, TEXT("Message Actions example: failed to add 'smiley_face' action. Status: %d, Reason: %s"), Result.Status, *Result.ErrorMessage);
	}
}

void ASample_MessageActionsFull::GetAllMessageActions()
{
	// 4. Get all actions for the channel
	UE_LOG(LogTemp, Log, TEXT("Message Actions example: getting all actions..."));
	FOnPubnubGetMessageActionsResponse OnGetMessageActionsResponse;
	OnGetMessageActionsResponse.BindDynamic(this, &ASample_MessageActionsFull::OnGetMessageActionsResponse);
	FString StartTimetoken = TEXT("18000000000000000"); // Newer timetoken
	FString EndTimetoken = TEXT("17000000000000000");   // Older timetoken
	PubnubClient->GetMessageActionsAsync(TestChannel, OnGetMessageActionsResponse, StartTimetoken, EndTimetoken);
}

void ASample_MessageActionsFull::OnGetMessageActionsResponse(FPubnubOperationResult Result, const TArray<FPubnubMessageActionData>& MessageActions)
{
	if (!Result.Error)
	{
		UE_LOG(LogTemp, Log, TEXT("Message Actions example: successfully got message actions. Found %d action(s)."), MessageActions.Num());
		for (const FPubnubMessageActionData& Action : MessageActions)
		{
			UE_LOG(LogTemp, Log, TEXT("- Type: '%s', Value: '%s', UserID: %s, Action Timetoken: %s"), *Action.Type, *Action.Value, *Action.UserID, *Action.ActionTimetoken);
		}
		RemoveFirstMessageAction();
	}
	else
	{
		UE_LOG(LogTemp, Error, TEXT("Message Actions example: failed to get message actions. Status: %d, Reason: %s"), Result.Status, *Result.ErrorMessage);
	}
}

void ASample_MessageActionsFull::RemoveFirstMessageAction()
{
	// 5. Remove the "like" action
	UE_LOG(LogTemp, Log, TEXT("Message Actions example: removing 'like' action..."));
	FOnPubnubRemoveMessageActionResponse OnRemoveMessageActionResponse;
	OnRemoveMessageActionResponse.BindDynamic(this, &ASample_MessageActionsFull::OnRemoveMessageActionResponse);
	PubnubClient->RemoveMessageActionAsync(TestChannel, TestMessageTimetoken, LikeActionTimetoken, OnRemoveMessageActionResponse);
}

void ASample_MessageActionsFull::OnRemoveMessageActionResponse(FPubnubOperationResult Result)
{
	if (!Result.Error)
	{
		UE_LOG(LogTemp, Log, TEXT("Message Actions example: successfully removed 'like' action."));
		UE_LOG(LogTemp, Log, TEXT("Message Actions example finished."));
	}
	else
	{
		UE_LOG(LogTemp, Error, TEXT("Message Actions example: failed to remove 'like' action. Status: %d, Reason: %s"), Result.Status, *Result.ErrorMessage);
	}
}
```

## Deprecated methods

### Add message action (deprecated)

:::warning Deprecated
Use `PubnubClient->AddMessageAction` or `PubnubClient->AddMessageActionAsync` instead.
:::

```cpp
PubnubSubsystem->AddMessageAction(
    FString Channel, 
    FString MessageTimeToken, 
    FString ActionType,  
    FString Value,
    FOnAddMessageActionResponse OnAddMessageActionResponse
);
```

### Remove message action (deprecated)

:::warning Deprecated
Use `PubnubClient->RemoveMessageAction` or `PubnubClient->RemoveMessageActionAsync` instead.
:::

```cpp
PubnubSubsystem->RemoveMessageAction(
    FString Channel, 
    FString MessageTimeToken, 
    FString ActionTimeToken,
    FOnRemoveMessageActionResponse OnRemoveMessageActionResponse
);
```

### Get message actions (deprecated)

:::warning Deprecated
Use `PubnubClient->GetMessageActions` or `PubnubClient->GetMessageActionsAsync` instead.
:::

```cpp
PubnubSubsystem->GetMessageActions(
    FString Channel,
    FOnGetMessageActionsResponse OnGetMessageActionsResponse,
    FString Start = "", 
    FString End = "", 
    int Limit = 0
);
```

## Terms in this document

* **Channel** - A pathway for sending and receiving messages between devices, created automatically when you first use it, that can handle any number of users and messages for different communication needs, like 1-1 text chats, group conversations, and other data streaming.
* **Channel pattern** - A way to group and analyze channel data to track performance metrics like message counts and user engagement over time with PubNub Insights.