Unreal Introduction

The Unreal SDK enables you to build any type of chat or messaging experience for Android, iOS, Windows, macOS or Linux. Support for more platforms is coming soon!

The SDK is implemented as an Unreal Engine Plugin, which includes low-level client access to the Stream Chat service as well as an early preview of some UI widgets which are ready to be dropped into your project.

Before reading the docs, consider trying our online API tour, it is a nice way to learn how the API works. It's in-browser so Javascript-based but the ideas are pretty much the same as Unreal.

This SDK is the client half of your integration. App settings, permissions, webhooks, push templates and data retention are configured from your backend with a server-side SDK or the REST API, and the token your app connects with has to be signed there too. See the server-side overview for what belongs there.

Requirements

Unreal Engine5.7, 5.8
PlatformsWindows (Win64), macOS, Linux, Android, iOS
LanguageC++ and Blueprint. Some lower-level and pagination APIs are C++ only.

Only the engine versions exercised by the SDK's CI and release matrix are listed as supported.

Plugin version 2.0.0 dropped support for UE 4.27, 5.0 and 5.1. Those three engine versions are covered by v1.3.0, the last release that supports them, and no release supports 5.2 through 5.6. If your project is on one of those, plan an engine upgrade to 5.7 or 5.8 before adopting 2.0.0.

Feature support

The Unreal SDK is in beta and does not cover the whole Chat API yet. Its surface may still change between releases, and C++ and Blueprint coverage can differ per operation.

Available today:

  • Messaging: send, edit, fetch, full-text search, soft delete through the channel API, hard delete through the lower-level C++ API, and pagination in both directions from C++
  • Channels: query, watch, create, update, truncate, hide and show, freeze, and member management
  • Reactions: send with score and enforce-unique semantics, remove, and paginate from C++
  • Read state: mark read, mark all read, unread counts, and per-user read tracking
  • Typing indicators: keystroke debouncing and stop events
  • Moderation: ban, shadow ban, mute users, mute channels, block and unblock users, flag messages and users, and query banned users
  • Presence: online state, watchers, and channel own capabilities
  • Real-time events: WebSocket transport with health checks and reconnection, plus typed event subscription from C++ and Blueprint
  • Slow mode with a configurable cooldown, and push notification device registration
  • UI widgets: an early preview of channel list, message list, composer, reactions and avatar widgets

Not implemented in Unreal yet, so the pages in these docs that describe them do not apply to this SDK:

  • Attachments, and file or image uploads
  • Sending threaded replies, and the thread list
  • Quoted messages, mentions, and pinning messages
  • Offline persistence and optimistic sending
  • Channel archiving and channel pinning
  • Polls, draft messages, message reminders, and location sharing

Open an issue if you need one of these.

Getting started

This guide will quickly help you get up to speed on Stream’s Chat API. The API is flexible and allows you to build any type of chat or messaging application.

First, you want to make sure you've downloaded the latest release of the Stream Chat plugin from the Releases page of the GitHub repository and copied it to the Plugins directory of your project.

Next, make sure you have the Stream Chat plugin enabled in the Plugins panel of your project.

The GitHub releases page is the recommended way to install the plugin. If you previously acquired it through the Epic Games Launcher, it stays available in your Vault, but new projects should start from the release above.

Set n.VerifyPeer before you package

If you are going to package for iOS or Android, add this now. Without it the packaged app cannot connect to Stream at all, and the error does not point at the cause.

Add to Config/DefaultEngine.ini:

[/Script/Engine.NetworkSettings]
n.VerifyPeer=True

The SDK talks to Stream over a secure WebSocket, so it needs a bundle of CA root certificates to verify Stream's server certificate. Unreal ships one at Engine/Content/Certificates/ThirdParty/cacert.pem, and the editor reads it straight from disk, which is why chat works in the editor whatever you do here.

Packaging is where it goes wrong. Whether that bundle is copied into your build is gated on n.VerifyPeer, and the check does not behave the way its default suggests:

// CopyBuildToStagingDirectory.Automation.cs
bool bStageSSLCertificates = true;
PlatformEngineConfig.GetBool("/Script/Engine.NetworkSettings", "n.VerifyPeer", out bStageSSLCertificates);

GetBool writes to the out parameter even when the key is missing, so leaving it unset does not preserve that true. It replaces it with false and no certificates are staged. Nothing sets a default, so every new project starts in that state, and the packaged app has no way to verify any TLS connection:

LogChatSocket: Initiating WebSocket connection
LogWebSockets: Warning: Lws(Error): SSL error: unable to get local issuer certificate (preverify_ok=0;err=20;depth=2)
LogChatSocket: Error: Failed to connect to WebSocket [Error=client connect failed]
LogChatSocket: Enqueuing a reconnecting attempt [Attempt=1, Delay=1.02866]

The client then retries with backoff indefinitely. It reads like a network, token or firewall problem rather than a missing file.

Setting n.VerifyPeer=True does not make your app less strict. At runtime the value only takes effect if the key is present, so an unset key already means "verify the peer". Setting it explicitly just makes packaging agree with the behaviour you already had.

To confirm the bundle made it into a staged build:

strings -a Saved/StagedBuilds/IOS/*/cookeddata/*/content/paks/*.pak | grep cacert

You do not need to copy cacert.pem into your project. Unreal stages its own copy. If you do want to pin a specific bundle, put it at Content/Certificates/cacert.pem and it is staged in preference to the engine's.

Chat client

You'll need to place the StreamChatClientComponent ActorComponent on one of the Actors of your project. Recommended places include your HUD or GameState actors.

// MyHud.h

UCLASS()
class AStreamChatSampleHud final : public AHUD
{
  GENERATED_BODY()

public:
  AStreamChatSampleHud();
  virtual void BeginPlay() override;

  UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
  UStreamChatClientComponent* Client;
};
// MyHud.cpp

AStreamChatSampleHud::AStreamChatSampleHud()
{
  // Create a new instance of [UStreamChatClientComponent] passing the api_key obtained
  //from your project dashboard.
  Client = CreateDefaultSubobject<UStreamChatClientComponent>(TEXT("Client"));
  Client->ApiKey = TEXT("{{ api_key }}");
}

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

  /// Set the current user and connect the websocket.
  /// In a production scenario, this should be done using a backend to generate
  /// a user token using our server SDK.
  /// Please see the following for more information:
  /// https://getstream.io/chat/docs/unreal/tokens-and-authentication/
  const FUser User{TEXT("super-band-9")};
  const FString Token{TEXT("{{ chat_user_token }}")};
  Client->ConnectUser(
    User,
    Token,
    [](const FUserRef& UserRef)
    {
      // Successfully connected
    });
}

The user token is typically provided by your backend when you login or register in the app. If authentication is disabled for your app you can also use a Client->DevToken(UserId) to generate an insecure token for development. You should never launch into production with authentication disabled.

For more complex token generation and expiration examples have a look at the token provider documentation.

Channels

Let’s continue by watching your first channel. A channel contains messages and a list of members who are permanently associated with the channel and a list of watchers currently watching the channel. The example below shows how to set up a channel:

FChannelProperties Props = FChannelProperties::WithType(TEXT("messaging"));
Props.SetId(TEXT("unrealdevs"));
Props.ExtraData.SetString(TEXT("name"), TEXT("Unreal devs"));

Props.ExtraData.SetNumber(TEXT("my_custom_number"), 123);
Client->WatchChannel(
  Props,
  [](UChatChannel* Channel)
  {
    // Started watching channel
  });

We construct a FChannelProperties struct with a Channel Type and a Channel ID ( messaging and unrealdevs in this case). The Channel ID is optional; if you leave it out, the ID is determined based on the list of members, which can be set instead using SetMembers() .

The Channel Type controls the settings we’re using for this channel.

There are 5 default types of channels:

  • livestream

  • messaging

  • team

  • gaming

  • commerce

These five options above provide you with the most suitable defaults for their use cases. You can also define custom channel types if one of the defaults don’t work for your use case.

The channel properties struct also contains a member ExtraData which contains the extra channel data. You can add as many custom fields as you would like as long as the total size of the resulting JSON object is less than 5 KB. Here we set the name field which is used by convention for the human-readable name of the channel.

Messages

Now that we have the channel set up, let's send our first chat message:

FMessage Message{TEXT("I told them I was pesca-pescatarian. Which is one who eats solely fish who eat other fish.")};
Message.ExtraData.SetNumber(TEXT("custom_field"), 123);
Channel->SendMessage(Message);

Similar to users and channels, the SendMessage method allows you to add custom fields. When you send a message to a channel, Stream Chat automatically broadcasts to all the people that are watching this channel and updates in real-time.

Querying channels

The Client->QueryChannels method enables you to retrieve a list of channels. You can specify a filter and sort order. The client keeps the channels list updates as new messages, reactions and new channels arrive.

const FFilter Filter = FFilter::And({
  FFilter::In(TEXT("members"), {TEXT("thierry")}),
  FFilter::Equal(TEXT("type"), TEXT("messaging")),
});
const TArray<FChannelSortOption> SortOptions{{EChannelSortField::LastMessageAt, ESortDirection::Descending}};
Client->QueryChannels(
  [](const TArray<UChatChannel*> ReceivedChannels)
  {
    // Started watching channels
  },
  Filter,
  SortOptions);

To learn more about which fields you can query and sort on have a look at the query channels documentation.

Conclusion

Now that you understand the building blocks of a fully-functional chat integration, you can take a tour of the Unreal In-Game Chat tutorial to understand how to add the UI/UX directly into your app.

We also recommend to take a look at some of the samples, which include a number of examples of integration into different types of games and applications.

In the next sections of the documentation, we dive deeper into details on each API endpoint.