# Unity Introduction

The Stream Chat Unity SDK lets you ship production-grade chat and messaging into any Unity project — from mobile apps and PC/console games to WebGL and XR. The SDK is a C# library that talks to the [Stream Chat](https://getstream.io/chat/) service and keeps your local chat state — channels, messages, users, threads, polls — automatically in sync with the server over a realtime WebSocket connection.

If you'd like a guided walk-through first, try the [Unity Introduction Tutorial](https://getstream.io/chat/unity/tutorial/).

## Key features

- **Stateful, realtime client.** `IStreamChatClient`, `IStreamChannel`, `IStreamMessage`, `IStreamUser`, `IStreamThread`, and `IStreamPoll` always reflect the latest server state. The SDK maintains the connection and patches the local model as events arrive.

- **Subscribe to live events with plain C# events.** React to new messages, reactions, member changes, typing indicators, thread replies, presence changes, poll votes, channel invites, and more.

- **Full chat feature set.** Direct messages, group channels, livestream chat, threads & replies, reactions, polls, message search, file/image uploads, pinned messages, mentions, channel invites, typing indicators, read state, unread counts, moderation, push notifications.

- **All Unity platforms.** Android, iOS, WebGL, Windows, macOS, Linux, consoles, and the other targets Unity supports.

- **IL2CPP friendly.** Works with IL2CPP and AOT-only platforms.

- **Open source.** Full C# source on [GitHub](https://github.com/GetStream/stream-chat-unity), actively developed.

## Before you start

When your app connects to Stream Chat, it connects as a specific user identified by three values: `apiKey`, `userId`, and a `userToken`. This guide focuses on a regular authenticated user; [guests and anonymous users](/chat/docs/unity/authless-users/) are also possible. To obtain credentials:

1. [Register for a free account](https://getstream.io/try-for-free/) and copy the `apiKey` of your application from the [Stream dashboard](https://dashboard.getstream.io/).

2. In the dashboard, open your application and use the **Explorer** tab to create a user and copy its `userId`.

3. Generate a `userToken` with our [online token generator](/chat/docs/unity/tokens-and-authentication/) for quick experiments. For production, your backend should issue tokens — see [Connect a User](#connect-a-user) below.

## Getting started

Download the latest Stream Chat SDK from the [Releases page](https://github.com/GetStream/stream-chat-unity/releases/latest) and import the `.unitypackage` into your project.

![](https://getstream.imgix.net/docs/706fdba3-8a13-422d-abde-ac7781a89f07.png?auto=compress&fit=clip&w=800&h=600)

### Newtonsoft.Json conflict

The Unity SDK uses Unity's Newtonsoft Json package for serialization. If your project already includes a Newtonsoft.Json library you may see a duplicate-type compiler error:

![undefined](https://www.evernote.com/shard/s453/sh/28bd4dd1-f3d2-909b-adab-1e3565185b7f/5baafedc7421b808be37e430c8dd4af2/res/451c12cc-115f-4054-fd31-0e189ba3d90c)

Resolve it by deleting one of the duplicates — either the `com.unity.nuget.newtonsoft-json@3.0.2` folder at `StreamChat/Libs/Serialization` (to keep your existing copy) or the Newtonsoft.Json package already in your project (to keep the one bundled with the SDK). The SDK-bundled copy is configured to work with Unity Engine and fully supports IL2CPP.

## Initialize the client

The entry point to the API is `IStreamChatClient`. Create an instance with `StreamChatClient.CreateDefaultClient()`:

<Tabs>

```csharp label="Unity"
using StreamChat.Core;
using UnityEngine;

public class ChatManager : MonoBehaviour
{
    private IStreamChatClient _chatClient;

    private void Start()
    {
        _chatClient = StreamChatClient.CreateDefaultClient();
    }
}
```

</Tabs>

The SDK runs its own update loop, so you don't need any per-frame work — just keep the client alive for the lifetime of your chat session.

## Connect a User

Call `ConnectUserAsync` with your API key, user id, and user token. The returned `IStreamLocalUserData` exposes the connected user and live unread counters.

<Tabs>

```csharp label="Unity"
public async Task ConnectAsync()
{
    // For prototyping you can generate an insecure dev token with
    // StreamChatClient.CreateDeveloperAuthToken("user-id").
    // In production, your backend must issue tokens.
    var localUserData = await _chatClient.ConnectUserAsync("api-key", "user-id", "user-token");
    Debug.Log($"User {localUserData.UserId} is connected!");
}
```

</Tabs>

For production use, pass an `ITokenProvider` so the SDK can fetch a fresh token from your backend whenever it needs one (initial connection, reconnect, after expiration). This keeps the WebSocket connected across token refreshes with no extra work on your side:

<Tabs>

```csharp label="Unity"
using StreamChat.Libs.Auth;

var tokenProvider = new YourTokenProvider();
await _chatClient.ConnectUserAsync("api-key", "user-id", tokenProvider);

// Your backend must authenticate the caller before issuing a Stream token —
// never expose an endpoint that returns a token for any userId.
public class YourTokenProvider : ITokenProvider
{
    public Task<string> GetTokenAsync(string userId)
        => FetchTokenFromYourBackendAsync(userId);
}
```

</Tabs>

See [Tokens & Authentication](/chat/docs/unity/tokens-and-authentication/) for the full auth guide.

## Create or get a Channel

A channel is where messages live. There are two ways to obtain one:

### Channel with an id

Use this for general-purpose channels — clans, clubs, public/private groups that users can join or leave:

<Tabs>

```csharp label="Unity"
var channel = await _chatClient.GetOrCreateChannelWithIdAsync(ChannelType.Messaging, "my-channel-id");
```

</Tabs>

### Channel for a group of users

Use this for direct messages and "shared history per unique group of users" — calling this with the same set of users always returns the same channel, regardless of order:

<Tabs>

```csharp label="Unity"
var filters = new IFieldFilterRule[]
{
    UserFilter.Id.In("friend-user-id-1", "friend-user-id-2")
};

var friends = await _chatClient.QueryUsersAsync(filters);

var groupToChat = new List<IStreamUser>();
groupToChat.AddRange(friends);                        // Add friends
groupToChat.Add(_chatClient.LocalUserData.User);      // Add local user

var channel = await _chatClient.GetOrCreateChannelWithMembersAsync(ChannelType.Messaging, groupToChat);
```

</Tabs>

### Channel Type

The channel type controls the default permissions and settings. Use one of the predefined types (`ChannelType.Messaging`, `ChannelType.Livestream`, `ChannelType.Team`, `ChannelType.Commerce`, `ChannelType.Gaming`) or [create a custom type](/chat/docs/unity/channel-features/) in the dashboard. `ChannelType.Messaging` is a good default for classic chat applications.

## Send a Message

Once you hold an `IStreamChannel`, sending a message is one line:

<Tabs>

```csharp label="Unity"
var message = await channel.SendNewMessageAsync("Hello");
```

</Tabs>

For richer messages — mentions, threaded replies, pinning, quoting, custom data — use the `StreamSendMessageRequest` overload:

<Tabs>

```csharp label="Unity"
var message = await channel.SendNewMessageAsync(new StreamSendMessageRequest
{
    Text = "Hello @someone!",
    MentionedUsers = new List<IStreamUser> { someUser },     // Mention a user
    ParentId = parentMessage.Id,                             // Reply in a thread
    QuotedMessage = otherMessage,                            // Quote another message
    Pinned = true,                                           // Pin the message
    PinExpires = DateTimeOffset.Now.AddDays(7),              // Pin for 7 days
    ShowInChannel = true,                                    // Show the thread reply in the channel too
    CustomData = new StreamCustomDataRequest
    {
        { "my_lucky_numbers", new List<int> { 7, 13, 81 } }
    }
});
```

</Tabs>

## Read Messages

Every channel exposes its loaded messages via `IStreamChannel.Messages`. The collection stays up to date automatically as messages are sent, edited, deleted, or reacted to.

<Tabs>

```csharp label="Unity"
var channel = await _chatClient.GetOrCreateChannelWithIdAsync(ChannelType.Messaging, "my-channel-id");

foreach (var message in channel.Messages)
{
    Debug.Log(message.Text);            // Message text
    Debug.Log(message.User);            // Message author
    Debug.Log(message.ReactionCounts);  // Per-type reaction counts
    Debug.Log(message.Attachments);     // Attached files / images / links
}

// Paginate older messages on demand (e.g. as the user scrolls up)
await channel.LoadOlderMessagesAsync();
```

</Tabs>

## Listen to Realtime Events

This is what makes the Unity SDK stateful: as soon as you obtain a channel, you can subscribe to plain C# events on it and let the SDK push updates to your UI as they happen on the server — for the local user **and** every other participant.

<Tabs>

```csharp label="Unity"
// Client-level events fire for the local user across all channels
_chatClient.Connected            += localUserData => Debug.Log($"Connected as {localUserData.UserId}");
_chatClient.Disconnected         += () => Debug.Log("Disconnected");
_chatClient.ChannelDeleted       += (cid, id, type) => Debug.Log($"Channel {cid} deleted");
_chatClient.AddedToChannelAsMember += (ch, member) => Debug.Log("Added to a channel");
_chatClient.ChannelInviteReceived  += (ch, invitee) => Debug.Log("Invite received");

// Channel-level events fire on a channel you are watching
channel.MessageReceived  += (ch, msg) => Debug.Log($"New message: {msg.Text}");
channel.MessageUpdated   += (ch, msg) => Debug.Log($"Edited: {msg.Text}");
channel.MessageDeleted   += (ch, msg, isHardDelete) => Debug.Log("Message deleted");

channel.ReactionAdded    += (ch, msg, reaction) => Debug.Log($"{reaction.User.Name} reacted {reaction.Type}");
channel.MemberAdded      += (ch, member) => Debug.Log($"{member.User.Name} joined");
channel.MemberRemoved    += (ch, member) => Debug.Log($"{member.User.Name} left");

channel.UserStartedTyping += (ch, user) => Debug.Log($"{user.Name} is typing…");
channel.UserStoppedTyping += (ch, user) => Debug.Log($"{user.Name} stopped typing");

// User presence (online / offline) updates flow through the IStreamUser instance
foreach (var member in channel.Members)
{
    member.User.PresenceChanged += (user, isOnline, lastActive) =>
        Debug.Log($"{user.Name} online: {isOnline}");
}
```

</Tabs>

Channels returned by `GetOrCreateChannel*` and `QueryChannelsAsync` are automatically _watched_ — they receive realtime updates. You can inspect `channel.IsWatched` and `_chatClient.WatchedChannels` at any time. See the [Events](/chat/docs/unity/event-object/) guide for the full event catalog (channel, message, thread, user, poll).

## Query Channels

Use `QueryChannelsAsync` with strongly typed filter builders to load multiple channels at once. A very common pattern is "show channels the local user belongs to":

<Tabs>

```csharp label="Unity"
public async Task QueryChannelsAsync()
{
    var filters = new IFieldFilterRule[]
    {
        ChannelFilter.Members.In(_chatClient.LocalUserData.UserId)
    };

    // Optional: sort by latest activity, member count, custom fields, …
    var sort = ChannelSort.OrderByDescending(ChannelSortFieldName.LastMessageAt);

    var channels = await _chatClient.QueryChannelsAsync(filters, sort, limit: 30, offset: 0);

    foreach (var channel in channels)
    {
        Debug.Log(channel.Id);
        Debug.Log(channel.Name);
        Debug.Log(channel.Messages.Count); // Messages
        Debug.Log(channel.Members.Count);  // Members
    }
}
```

</Tabs>

Combine multiple `ChannelFilter.*` builders for complex queries (`EqualsTo`, `In`, `Autocomplete`, `GreaterThan`, custom fields, etc.). See [Querying Channels](/chat/docs/unity/query-channels/) for the full guide, including pagination, sorting, and filter composition.

## Send a Reaction

`IStreamMessage.SendReactionAsync` lets the local user react to any message. Reactions count toward `ReactionCounts` and `ReactionScores`, and other participants receive a `ReactionAdded` event in realtime.

<Tabs>

```csharp label="Unity"
// Simple reaction with a score of 1
await message.SendReactionAsync("like");

// Reaction with a custom score value (e.g. for cumulative "claps")
await message.SendReactionAsync("clap", 10);

// Replace all previous reactions from this user with a new one
await message.SendReactionAsync("love", enforceUnique: true);
```

</Tabs>

## More features

The Unity SDK supports the full Stream Chat feature set. Some of the most commonly used building blocks:

- **[Threads & Replies](/chat/docs/unity/threads/)** — `message.GetThreadAsync()`, `message.LoadRepliesAsync()`, `Client.QueryThreadsAsync()`, live `thread.ReplyReceived` events, per-thread read state.

- **[Reactions](/chat/docs/unity/send-reaction/)** — custom score, enforce-unique, `message.ReactionCounts`, `OwnReactions`.

- **[Message Search](/chat/docs/unity/search/)** — `Client.SearchMessagesAsync()` with channel and message filters, free-text queries, sort by relevance, cursor pagination.

- **[Polls](/chat/docs/unity/polls-api/)** — `Client.Polls.CreatePollAsync()`, `poll.CastVoteAsync()`, live `VoteCasted` / `VoteChanged` events.

- **[File & Image Uploads](/chat/docs/unity/file-uploads/)** — `channel.UploadFileAsync()`, `channel.UploadImageAsync()`, attach with `StreamAttachmentRequest`.

- **[Pinned Messages](/chat/docs/unity/pinned-messages/)** — `message.PinAsync()` with optional expiry, `channel.PinnedMessages`.

- **[Typing Indicators](/chat/docs/unity/typing-indicators/)** — `channel.SendTypingStartedEventAsync()`, `channel.TypingUsers`.

- **[Read State & Unread Counts](/chat/docs/unity/unread/)** — `channel.MarkChannelReadAsync()`, `localUserData.TotalUnreadCount`, `Client.GetLatestUnreadCountsAsync()`.

- **[Channel Members](/chat/docs/unity/channel-members/) & [Invites](/chat/docs/unity/channel-invites/)** — `channel.AddMembersAsync()`, `channel.InviteMembersAsync()`, `channel.AcceptInviteAsync()`.

- **[User Presence](/chat/docs/unity/presence-format/)** — `user.PresenceChanged` event, `user.Online`, `user.LastActive`, `user.MarkInvisibleAsync()`.

- **[Moderation](/chat/docs/unity/moderation/)** — `channel.BanUserAsync()`, `channel.ShadowBanUserAsync()`, `message.FlagAsync()`, `user.MuteAsync()`.

- **[Push Notifications](/chat/docs/unity/push-introduction/)** — register a device and let Stream deliver pushes via FCM / APNS / Huawei.

- **[Permissions](/chat/docs/unity/chat-permission-policies/)** — `channel.OwnCapabilities`, role-based permission system.

## Disconnect User

Call `DisconnectUserAsync` to close the WebSocket and clear local chat state (e.g. when signing the user out or quitting the app):

<Tabs>

```csharp label="Unity"
public async Task DisconnectAsync()
{
    await _chatClient.DisconnectUserAsync();
    Debug.Log("User disconnected");
}
```

</Tabs>

## Next steps

- Explore the sample project that ships with the SDK — it contains a full UGUI chat UI built on the APIs introduced above.

- Read the [Architecture & Benchmark](/chat/docs/unity/architecture-and-benchmark/) guide to learn how the stateful client is structured internally.

- Found a bug or have a feature request? [Open an issue on GitHub](https://github.com/GetStream/stream-chat-unity/issues) — we read every one.


---

This page was last updated at 2026-07-10T16:05:24.214Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/unity/](https://getstream.io/chat/docs/unity/).