Channels must be created before users can start chatting. Channel creation can occur either client-side or server-side, depending on your app’s requirements. Client-side creation is ideal for apps where users can freely start conversations (for example, a Slack-style workforce management app). Server-side creation is preferred in apps that require business logic before a chat can begin, such as dating apps where users must match first. To limit channel creation to server-side only, remove Create Channel permissions for your users.
There are two ways to create channels: by specifying a channel ID or by creating distinct channels.
This approach works best when your app already has a database object that naturally maps to a chat channel. For example, in a Twitch-style live-streaming service, each streamer has a unique ID you can reuse as the channel ID, making it easy to route users to the correct chat. Using explicit IDs keeps channels predictable and easy to reference throughout your application.
val channelClient = client.channel(channelType = "messaging", channelId = "general")channelClient.create(memberIds = emptyList(), extraData = emptyMap()).enqueue { result -> if (result is Result.Success) { val newChannel: Channel = result.value } else { // Handle Result.Failure }}
const channel = client.channel("messaging", "travel", { name: "Awesome channel about traveling",});// Here, 'travel' will be the channel IDawait channel.create();
/// 1: Create a `ChannelId` that represents the channel you want to create.let channelId = ChannelId(type: .messaging, id: "general")/// 2: Use the `ChatClient` to create a `ChatChannelController` with the `ChannelId`.let channelController = chatClient.channelController(for: channelId)/// 3: Call `ChatChannelController.synchronize` to create the channel.channelController.synchronize { error in if let error = error { /// 4: Handle possible errors print(error) }}
const FChannelProperties Properties{ TEXT("messaging"), // Type TEXT("travel"), // Id};Client->CreateChannel( Properties, [](UChatChannel* Channel) { // Channel created });
from getstream.models import ChannelInputchannel = client.chat.channel("messaging", "travel")channel.get_or_create(data=ChannelInput(created_by_id="myuserid"))
use GetStream\ChatClient;use GetStream\GeneratedModels as Models;$response = $client->getOrCreateChannel("messaging", "general", new Models\ChannelGetOrCreateRequest( data: new Models\ChannelInput( createdByID: "thierry", ),));
using GetStream;using GetStream.Models;var client = new StreamClient("{{ api_key }}", "{{ api_secret }}");var chat = new ChatClient(client);var resp = await chat.GetOrCreateChannelAsync("messaging", "general", new ChannelGetOrCreateRequest { Data = new ChannelInput { CreatedByID = "thierry" } });
var channel = await Client.GetOrCreateChannelWithIdAsync(ChannelType.Messaging, "my-channel-id");//Once you get or query a channel it is also added to Client.WatchedChannels list
Distinct channels are ideal when you want a single, unique conversation for a specific set of users. By leaving the channel ID empty and specifying only the channel type and members, Stream automatically generates a channel ID by hashing the list of members (order does not matter). This ensures that the same group of users will always reference the same channel, preventing duplicate conversations.
You cannot add members for channels created this way, but members can be removed.
client.createChannel( channelType = "messaging", channelId = "", memberIds = listOf("thierry", "tomasso"), extraData = emptyMap()).enqueue { result -> if (result is Result.Success) { val channel = result.value } else { // Handle Result.Failure }}
/// 1: Use the `ChatClient` to create a `ChatChannelController` with a list of user idslet channelController = try chatClient.channelController( createDirectMessageChannelWith: ["thierry", "tomasso"], extraData: [:])/// 2: Call `ChatChannelController.synchronize` to create the channel.channelController.synchronize { error in if let error = error { /// 4: Handle possible errors print(error) }}
use GetStream\ChatClient;use GetStream\GeneratedModels as Models;$response = $client->getOrCreateChannel("messaging", "channel-id", new Models\ChannelGetOrCreateRequest( data: new Models\ChannelInput( createdByID: "thierry", members: [ new Models\ChannelMemberRequest(userID: "thierry"), new Models\ChannelMemberRequest(userID: "jenny"), ], ),));
use GetStream\ChatClient;use GetStream\GeneratedModels as Models;$response = $client->getOrCreateDistinctChannel("messaging", new Models\ChannelGetOrCreateRequest( data: new Models\ChannelInput( createdByID: "thierry", members: [ new Models\ChannelMemberRequest(userID: "thierry"), new Models\ChannelMemberRequest(userID: "tommaso"), ], ),));
using GetStream;using GetStream.Models;var client = new StreamClient("{{ api_key }}", "{{ api_secret }}");var chat = new ChatClient(client);var resp = await chat.GetOrCreateDistinctChannelAsync("messaging", new ChannelGetOrCreateRequest { Data = new ChannelInput { CreatedByID = "thierry", Members = new List<ChannelMemberRequest> { new ChannelMemberRequest { UserID = "thierry" }, new ChannelMemberRequest { UserID = "tommaso" } } } });
var filters = new IFieldFilterRule[]{ UserFilter.Id.EqualsTo("other-user-id")};// Find user you want to start a chat withvar users = await Client.QueryUsersAsync(filters);var otherUser = users.First();var localUser = Client.LocalUserData.User;// Start direct channel between 2 usersvar channel = await Client.GetOrCreateChannelWithMembersAsync(ChannelType.Messaging, new[] { localUser, otherUser});
Channel data can include any number of custom fields as long as the total payload stays under 5KB. Some fields are reserved—such as members—and our UI components also use name and image when rendering channel lists and headers. In general, you should store only the data that's essential to your chat experience and avoid adding fields that change frequently.
Name
Type
Description
name
string
The channel name. No special meaning, but by default the UI component will try to render this if the property is present.
image
string
The channel image. Again there is no special meaning but by default, the UI component will try to render this if the property is present.
members
array
The members participating in this Channel. Note that you don't need to specify members for a live stream or other public chat. You only need to specify the members if you want to limit access of this chat to these members and subscribe them to future updates.
Once a channel exists—or as it is being created on the client—it can be watched by client devices. Watching a channel subscribes the client’s WebSocket connection to real-time updates, such as new messages, membership changes, and reactions. This allows the SDKs to keep channel state and UI in sync automatically.
If your app lets users navigate to a channel client-side, you should use channel.watch(). This is a get-or-create method: it fetches and watches the channel if it already exists, or creates and watches it if it doesn't. channel.watch() returns the full channel state—including members, watchers, and messages—so your UI can render immediately.
For loading many channels at once, use client.queryChannels()—this fetches and watches multiple channels in a single call, reducing API traffic.
Note that watching a channel is different from being a channel member. A watcher is a temporary, real-time subscription to updates, while a member is a persistent association with the channel.
val channelClient = client.channel(channelType = "messaging", channelId = "general")channelClient.watch().enqueue { result -> if (result is Result.Success) { val channel: Channel = result.value } else { // Handle Result.Failure }}
const channel = client.channel("messaging", "travel-channel");const state = await channel.watch();
final state = await channel.watch();
// Channels are watched automatically when they're synchronized./// 1: Create a `ChannelId` that represents the channel you want to watch.let channelId = ChannelId(type: .messaging, id: "general")/// 2: Use the `ChatClient` to create a `ChatChannelController` with the `ChannelId`.let channelController = chatClient.channelController(for: channelId)/// 3: Call `ChatChannelController.synchronize` to watch the channel.channelController.synchronize { error in if let error = error { /// 4: Handle possible errors print(error) }}
const FChannelProperties Properties{ TEXT("messaging"), // Type TEXT("general"), // Id};Client->WatchChannel( Properties, [](UChatChannel* Channel) { // Started watching channel });
var filters = new IFieldFilterRule[]{ UserFilter.Id.EqualsTo("other-user-id")};// Find user you want to start chat withvar users = await Client.QueryUsersAsync(filters);var otherUser = users.First();var localUser = Client.LocalUserData.User;// Get channel by IDvar channel = await Client.GetOrCreateChannelWithIdAsync(ChannelType.Messaging, channelId: "my-channel-id");// Get channel with users combinationvar channelWithUsers = await Client.GetOrCreateChannelWithMembersAsync(ChannelType.Messaging, new[] { localUser, otherUser });// Access propertiesDebug.Log(channel.Name);Debug.Log(channel.Members);// Subscribe to events so you can react to updateschannel.MessageReceived += OnMessageReceived;channel.MessageUpdated += OnMessageUpdated;channel.MessageDeleted += OnMessageDeleted;channel.ReactionAdded += OnReactionAdded;channel.ReactionUpdated += OnReactionUpdated;channel.ReactionRemoved += OnReactionRemoved;// You can also access all currently watched channels viaforeach (var c in Client.WatchedChannels){ // Every queried channel is automatically watched and starts receiving updates from the server}