# Creating Channels

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](/chat/docs/unity/chat_permission_policies/) for your users.

There are two ways to create channels: by specifying a channel ID or by creating distinct channels.

## Creating a Channel Using a Channel ID

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.

<Tabs>

```kotlin label="Kotlin"
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
  }
}
```

```js label="JavaScript"
const channel = client.channel("messaging", "travel", {
  name: "Awesome channel about traveling",
});
// Here, 'travel' will be the channel ID
await channel.create();
```

```swift label="Swift"
/// 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)
  }
}
```

```dart label="Dart"
final channel = client.channel(
 "messaging",
 id: "travel",
 extraData: {
  "name": "Founder Chat",
  "image": "http://bit.ly/2O35mws",
  "members": ["thierry", "tommaso"],
 },
);
```

```js label="Node"
const channel = client.channel("messaging", "travel", {
  name: "Awesome channel about traveling",
  created_by_id: "myuserid",
});
await channel.create();
```

```ruby label="Ruby"
require 'getstream_ruby'

Models = GetStream::Generated::Models

response = client.chat.get_or_create_channel(
  'messaging',
  'general',
  Models::ChannelGetOrCreateRequest.new(
    data: Models::ChannelInput.new(created_by_id: 'jlahey')
  )
)
```

```cpp label="Unreal"
const FChannelProperties Properties{
  TEXT("messaging"),  // Type
  TEXT("travel"),    // Id
};
Client->CreateChannel(
  Properties,
  [](UChatChannel* Channel)
  {
    // Channel created
  });
```

```python label="Python"
from getstream.models import ChannelInput

channel = client.chat.channel("messaging", "travel")
channel.get_or_create(data=ChannelInput(created_by_id="myuserid"))
```

```php label="PHP"
use GetStream\ChatClient;
use GetStream\GeneratedModels as Models;

$response = $client->getOrCreateChannel("messaging", "general", new Models\ChannelGetOrCreateRequest(
    data: new Models\ChannelInput(
        createdByID: "thierry",
    ),
));
```

```csharp label="C#"
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"
        }
    });
```

```go label="Go"
channel := client.Chat().Channel("messaging", "general")
channel.GetOrCreate(ctx, &getstream.GetOrCreateChannelRequest{
	Data: &getstream.ChannelInput{CreatedByID: getstream.PtrTo("tommaso")},
})
```

```csharp label="Unity"
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
```

```java label="Java"
// Android SDK
ChannelClient channelClient = client.channel("messaging", "general");

Map<String, Object> extraData = new HashMap<>();
List<String> memberIds = new LinkedList<>();

channelClient.create(memberIds, extraData)
    .enqueue(result -> {
      if (result.isSuccess()) {
        Channel newChannel = result.data();
      } else {
       // Handle result.error()
     }
   });

// Backend SDK
chat.getOrCreateChannel("messaging", "general",
    GetOrCreateChannelRequest.builder()
        .data(ChannelInput.builder()
            .createdByID(userId)
            .custom(Map.of("custom_field", "custom_value")) // Optional custom fields
            .build())
        .build()).execute();
```

</Tabs>

## Distinct Channels

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.

<admonition type="info">

You cannot add members for channels created this way, but members can be removed.

</admonition>

<Tabs>

```kotlin label="Kotlin"
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
   }
}
```

```js label="JavaScript"
const channel = client.channel("messaging", {
  members: ["thierry", "tommaso"],
});
await channel.create();
```

```dart label="Dart"
client.channel("messaging", extraData: {"members": ["thierry", "tommaso"]});
```

```swift label="Swift"
/// 1: Use the `ChatClient` to create a `ChatChannelController` with a list of user ids
let 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)
  }
}
```

```js label="Node"
const channel = client.channel("messaging", {
  members: ["thierry", "tommaso"],
  created_by_id: "myuserid",
});
await channel.create();
```

```php label="PHP"
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"),
        ],
    ),
));
```

```ruby label="Ruby"
require 'getstream_ruby'

Models = GetStream::Generated::Models

response = client.chat.get_or_create_distinct_channel(
  'messaging',
  Models::ChannelGetOrCreateRequest.new(
    data: Models::ChannelInput.new(
      created_by_id: 'thierry',
      members: [
        Models::ChannelMemberRequest.new(user_id: 'thierry'),
        Models::ChannelMemberRequest.new(user_id: 'tommaso')
      ]
    )
  )
)
```

```cpp label="Unreal"
const FChannelProperties Properties = FChannelProperties{TEXT("messaging")}
            .SetMembers({TEXT("thierry"), TEXT("tommaso")});
Client->CreateChannel(
  Properties,
  [](UChatChannel* Channel)
  {
    // Channel created
  });
```

```python label="Python"
from getstream.models import ChannelInput, ChannelMemberRequest

response = client.chat.get_or_create_distinct_channel(
    type="messaging",
    data=ChannelInput(
        created_by_id="myuserid",
        members=[ChannelMemberRequest(user_id="thierry")],
    ),
)
```

```php label="PHP"
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"),
        ],
    ),
));
```

```csharp label="C#"
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" }
            }
        }
    });
```

```go label="Go"
channel := client.Chat().Channel("messaging", "")
channel.GetOrCreate(ctx, &getstream.GetOrCreateChannelRequest{
	Data: &getstream.ChannelInput{
		CreatedByID: getstream.PtrTo("tommaso"),
		Members: []getstream.ChannelMemberRequest{
			{UserID: "thierry"},
			{UserID: "tommaso"},
		},
	},
})
```

```csharp label="Unity"
var filters = new IFieldFilterRule[]
{
  UserFilter.Id.EqualsTo("other-user-id")
};
// Find user you want to start a chat with
var users = await Client.QueryUsersAsync(filters);

var otherUser = users.First();
var localUser = Client.LocalUserData.User;

// Start direct channel between 2 users
var channel = await Client.GetOrCreateChannelWithMembersAsync(ChannelType.Messaging, new[] { localUser, otherUser});
```

```java label="Java"
// Android SDK
ChannelClient channelClient = client.channel("messaging", "");

Map<String, Object> extraData = new HashMap<>();
List<String> memberIds = new LinkedList<>();
memberIds.add("thierry");
memberIds.add("tomasso");

channelClient.create(memberIds, extraData)
    .enqueue(result -> {
      if (result.isSuccess()) {
        Channel newChannel = result.data();
      } else {
       // Handle result.error()
     }
   });

// Backend SDK
chat.getOrCreateDistinctChannel("messaging",
    GetOrCreateDistinctChannelRequest.builder()
        .data(ChannelInput.builder()
            .createdByID(userId)
            .members(List.of(
                ChannelMemberRequest.builder().userID("user1").build(),
                ChannelMemberRequest.builder().userID("user2").build()))
            .build())
        .build()).execute();
```

</Tabs>

When you create a channel using one of the above approaches, you'll specify the following fields:

| name         | type   | description                                                                                                                             | default | optional |
| ------------ | ------ | --------------------------------------------------------------------------------------------------------------------------------------- | ------- | -------- |
| type         | string | The channel type. Default types are livestream, messaging, team, gaming and commerce. You can also create your own types.               | -       |          |
| id           | string | The channel id (optional). If you don't specify an ID the ID will be generated based on the list of members. (max length 64 characters) | -       | ✓        |
| channel data | object | Extra data for the channel. Must not exceed 5KB in size.                                                                                | default | ✓        |

## Channel Data

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. |
| custom_data | various types | Channels can contain up to 5KB of custom data.                                                                                                                                                                                                                      |

## Watching Channels

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()](/chat/docs/unity/query_channels/)—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.

<Tabs>

```kotlin label="Kotlin"
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
  }
}
```

```js label="JavaScript"
const channel = client.channel("messaging", "travel-channel");

const state = await channel.watch();
```

```dart label="Dart"
final state = await channel.watch();
```

```swift label="Swift"
// 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)
  }
}
```

```cpp label="Unreal"
const FChannelProperties Properties{
  TEXT("messaging"),  // Type
  TEXT("general"),    // Id
};
Client->WatchChannel(
  Properties,
  [](UChatChannel* Channel)
  {
    // Started watching channel
  });
```

```csharp label="Unity"
var filters = new IFieldFilterRule[]
{
  UserFilter.Id.EqualsTo("other-user-id")
};

// Find user you want to start chat with
var users = await Client.QueryUsersAsync(filters);
var otherUser = users.First();
var localUser = Client.LocalUserData.User;

// Get channel by ID
var channel = await Client.GetOrCreateChannelWithIdAsync(ChannelType.Messaging, channelId: "my-channel-id");

// Get channel with users combination
var channelWithUsers = await Client.GetOrCreateChannelWithMembersAsync(ChannelType.Messaging,
  new[] { localUser, otherUser });

// Access properties
Debug.Log(channel.Name);
Debug.Log(channel.Members);

// Subscribe to events so you can react to updates
channel.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 via
foreach (var c in Client.WatchedChannels)
{
  // Every queried channel is automatically watched and starts receiving updates from the server
}
```

```java label="Java"
ChannelClient channelClient = client.channel("messaging", "general");

channelClient.watch().enqueue(result -> {
  if (result.isSuccess()) {
    Channel channel = result.data();
  } else {
    // Handle result.error()
  }
});
```

</Tabs>


---

This page was last updated at 2026-04-22T16:43:09.973Z.

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