# User Permissions

Stream Chat ships with a configurable permission system that allows high resolution control over what users are permitted to do.

## Getting Started

There are multiple important terms to understand when it comes to permission management. Each permission check comes down to three things:

- `Subject` - an actor which attempts to perform certain Action. It could be represented by a User or by a ChannelMember

- `Resource` - an item that Subject attempts to perform an Action against. It could be a Channel, Message, Attachment or another User

- `Action` - the exact action that is being performed. For example `CreateChannel` , `DeleteMessage` , `AddLinks`

The purpose of permission system is to answer a question: **is** `Subject A` **allowed to perform** `Action B` **on** `Resource C` ?

Stream Chat provides several concepts which help to control which actions are available to whom:

- `Permission` - an object which represents actions a subject is allowed to perform

- `Role` - assigned to a User or Channel Member and is used to check their permissions

- `Grants` - the way permissions are assigned to roles, applicable across the entire application, or specific to a single channel type or channel.

Also important to know is permissions checking only happens on the client-side calls. Server-side allows everything so long as a valid API key and secret is provided.

## Role Management

To make it easy to get started, all Stream applications come with several roles already built in with permissions to represent the most common use cases. These roles can be customized if needed, and new roles can be created specific for your application

This is the process of assigning a role to users so they can be granted permissions. This represents `Subject A` in the permissions question. Users will have one role which grants them permissions for the entire application and additionally users can have channel roles which grant permissions for a single channel or for all channels with the same channel type.

By default all users have builtin role `user` assigned. To change the role of the User, you can use UpdateUser API endpoint:

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
await client.partialUpdateUser({
  id: "james_bond",
  set: { role: "special_agent" },
});
```

</codetabs-item>

<codetabs-item value="php" label="PHP">

```php
$client->updateUsersPartial(new Models\UpdateUsersPartialRequest(
    users: [new Models\UpdateUserPartialRequest(
        id: "james_bond",
        set: (object)["role" => "special_agent"],
    )],
));
```

</codetabs-item>

<codetabs-item value="python" label="Python">

```python
from getstream.models import UpdateUserPartialRequest

response = client.update_users_partial(users=[
    UpdateUserPartialRequest(id="james_bond", set={"role": "special_agent"})
])
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
var reqObj = UpdateUserPartialRequest.builder()
    .id("james_bond")
    .set(Map.of("role", "special_agent"))
    .build();
client.updateUsersPartial(UpdateUsersPartialRequest.builder()
    .users(List.of(reqObj))
    .build()).execute();
```

</codetabs-item>

<codetabs-item value="csharp" label="C#">

```csharp
await client.UpdateUsersPartialAsync(new UpdateUsersPartialRequest
{
    Users = new List<UpdateUserPartialRequest>
    {
        new UpdateUserPartialRequest
        {
            Id = "user-id",
            Set = new Dictionary<string, object> { { "role", "special_agent" } },
        },
    },
});
```

</codetabs-item>

<codetabs-item value="ruby" label="Ruby">

```ruby
require 'getstream_ruby'
Models = GetStream::Generated::Models

client.common.update_users_partial(Models::UpdateUsersPartialRequest.new(
  users: [Models::UpdateUserPartialRequest.new(
    id: 'james_bond',
    set: { role: 'special_agent' }
  )]
))
```

</codetabs-item>

<codetabs-item value="go" label="Go">

```go
resp, err := client.UpdateUsersPartial(ctx, &getstream.UpdateUsersPartialRequest{
  Users: []getstream.UpdateUserPartialRequest{
    {
      ID: "james_bond",
      Set: map[string]any{
        "role": "special_agent",
      },
    },
  },
})
```

</codetabs-item>

<codetabs-item value="unity" label="Unity">

```csharp
// This is a server-side only feature, choose any of our server-side SDKs to use it
```

</codetabs-item>

</codetabs>

Once you add user to the channel, `channel_member` role will be assigned to user's membership:

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
const result = await channel.addMembers([{ user_id: "james_bond" }]);
console.log(result.members[0].channel_role); // "channel_member"
```

</codetabs-item>

<codetabs-item value="php" label="PHP">

```php
$result = $client->updateChannel("messaging", "channel-id", new Models\UpdateChannelRequest(
    addMembers: [new Models\ChannelMemberRequest(userID: "james_bond")],
));
```

</codetabs-item>

<codetabs-item value="python" label="Python">

```python
from getstream.models import ChannelMemberRequest

result = channel.update(add_members=[ChannelMemberRequest(user_id="james_bond")])
print(result.data.members[0].channel_role)
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
var resp = chat.channel("channel_type", "channel_id")
    .update(UpdateChannelRequest.builder()
        .addMembers(List.of(ChannelMemberRequest.builder().userID("james_bond").build()))
        .build());
System.out.println(resp.getData().getMembers().get(0).getChannelRole());
```

</codetabs-item>

<codetabs-item value="csharp" label="C#">

```csharp
var addMembersResponse = await chat.UpdateChannelAsync("channel-type", "channel-id", new UpdateChannelRequest
{
    AddMembers = new List<ChannelMemberRequest>
    {
        new ChannelMemberRequest { UserID = "user-id" },
    },
});
Console.WriteLine(addMembersResponse.Members[0].ChannelRole); // channel role is equal to "channel_member"
```

</codetabs-item>

<codetabs-item value="ruby" label="Ruby">

```ruby
require 'getstream_ruby'
Models = GetStream::Generated::Models

resp = client.chat.update_channel('messaging', 'channel-id', Models::UpdateChannelRequest.new(
  add_members: [Models::ChannelMemberRequest.new(user_id: 'james_bond')]
))
puts resp.members[0].channel_role
```

</codetabs-item>

<codetabs-item value="go" label="Go">

```go
channel := client.Chat().Channel("messaging", "channel-id")
_, err = channel.Update(ctx, &getstream.UpdateChannelRequest{
  AddMembers: []getstream.ChannelMemberRequest{
    {UserID: "james_bond"},
  },
})
```

</codetabs-item>

<codetabs-item value="unity" label="Unity">

```csharp
// This is a server-side only feature, choose any of our server-side SDKs to use it
```

</codetabs-item>

</codetabs>

In order to change channel-level role of the user, you can either add user to the channel with a different role (if the SDK supports it) or update it later by role assignment:

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
// Add user to the channel with role set
await channel.addMembers([
  { user_id: "james_bond", channel_role: "channel_moderator" },
]);
// Assign new channel member role
await channel.assignRoles([
  { user_id: "james_bond", channel_role: "channel_member" },
]);
```

</codetabs-item>

<codetabs-item value="php" label="PHP">

```php
// Add user to the channel with role set
$client->updateChannel("messaging", "general", new Models\UpdateChannelRequest(
    addMembers: [new Models\ChannelMemberRequest(userID: "james_bond", channelRole: "channel_moderator")],
));

// Assign new channel member role
$client->updateChannel("messaging", "general", new Models\UpdateChannelRequest(
    assignRoles: [new Models\ChannelMemberRequest(userID: "james_bond", channelRole: "channel_member")],
));
```

</codetabs-item>

<codetabs-item value="python" label="Python">

```python
from getstream.models import ChannelMemberRequest

# Add user to the channel with role set
result = channel.update(add_members=[ChannelMemberRequest(user_id="james_bond", channel_role="channel_moderator")])

# Assign new channel member role
result = channel.update(assign_roles=[ChannelMemberRequest(user_id="james_bond", channel_role="channel_member")])
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
chat.channel(channel.getType(), channel.getId())
    .update(UpdateChannelRequest.builder()
        .assignRoles(List.of(ChannelMemberRequest.builder()
            .userID("james_bond")
            .channelRole("channel_member")
            .build()))
        .build());
```

</codetabs-item>

<codetabs-item value="csharp" label="C#">

```csharp
// User must be a member of the channel before you can assign channel role
var resp = await chat.UpdateChannelAsync("channel-type", "channel-id", new UpdateChannelRequest
{
    AssignRoles = new List<ChannelMemberRequest>
    {
        new ChannelMemberRequest { UserID = "user-id", ChannelRole = "channel_moderator" },
    },
});
```

</codetabs-item>

<codetabs-item value="ruby" label="Ruby">

```ruby
require 'getstream_ruby'
Models = GetStream::Generated::Models

# Add user to the channel with role set
client.chat.update_channel('messaging', 'channel-id', Models::UpdateChannelRequest.new(
  add_members: [Models::ChannelMemberRequest.new(user_id: 'james_bond', channel_role: 'channel_moderator')]
))

# Assign new channel member role
client.chat.update_channel('messaging', 'channel-id', Models::UpdateChannelRequest.new(
  assign_roles: [Models::ChannelMemberRequest.new(user_id: 'james_bond', channel_role: 'channel_member')]
))
```

</codetabs-item>

<codetabs-item value="go" label="Go">

```go
channel := client.Chat().Channel("messaging", "channel-id")
_, err = channel.Update(ctx, &getstream.UpdateChannelRequest{
  AssignRoles: []getstream.ChannelMemberRequest{
    {ChannelRole: getstream.PtrTo("channel_moderator"), UserID: "james_bond"},
  },
})
```

</codetabs-item>

<codetabs-item value="unity" label="Unity">

```csharp
// This is a server-side only feature, choose any of our server-side SDKs to use it
```

</codetabs-item>

</codetabs>

<admonition type="info">

changing channel member roles is not allowed client-side.

</admonition>

Subject

`Subject` can be represented by User or ChannelMember. ChannelMember subject is used only when user interacts with a channel that they are member of. Both User and ChannelMember have Role and permission system takes both roles into consideration when checking permissions.

## Builtin roles

There are some builtin roles in Stream Chat that cover basic chat scenarios:

| Role              | Level   | Description                                                                                                                         |
| ----------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| user              | User    | Default User role                                                                                                                   |
| guest             | User    | Used for guest users created by server-side endpoints. Guests are short-lived temporary users that could be created without a token |
| anonymous         | User    | Anonymous users are not allowed to perform any actions that write data. You should treat them as unathenticated clients             |
| admin             | User    | Role for users that perform administrative tasks with elevated permissions                                                          |
| channel_member    | Channel | Default role that gets assigned when user is added to the channel                                                                   |
| channel_moderator | Channel | Role for channel members that perform administrative tasks with elevated permissions                                                |

<admonition type="info">

It's worth noting that you cannot use user-level roles as channel-level roles vice-versa. This restriction only applies to builtin roles

</admonition>

## Ownership

Some Stream Chat entities have an owner and the fact of ownership can be considered when configuring access permissions. Ownership is supported in these entity types:

1. **Channel** - owned by its creator

2. **Message** - owned by its creator (sender)

3. **Attachment** - owned by user who uploaded a file

4. **User** - authenticated user owns itself

Using ownership concept, permissions could be set up in such a way that allows entity owners to perform certain actions. For example:

- **Update Own Message** - allows message senders to edit their messages

- **Update Own User** - allows users to change their own properties (except role and team)

- **Send Message in Own Channel** - allows channel creators to send messages in the channels that they created even if they are not members

## Custom Roles

In more sophisticated scenarios custom roles could be used. One Stream Chat application could have up to 25 custom roles. Roles are simple, and require only a name to be created. They do nothing until permissions are assigned to the role. To create new custom role you can use CreateRole API endpoint:

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
await client.createRole("special_agent");
```

</codetabs-item>

<codetabs-item value="php" label="PHP">

```php
$client->createRole(new Models\CreateRoleRequest(name: "special_agent"));
```

</codetabs-item>

<codetabs-item value="python" label="Python">

```python
client.create_role("special_agent")
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
client.createRole(CreateRoleRequest.builder().name("special_agent").build()).execute();
```

</codetabs-item>

<codetabs-item value="csharp" label="C#">

```csharp
await client.CreateRoleAsync(new CreateRoleRequest { Name = "special_agent" });
```

</codetabs-item>

<codetabs-item value="ruby" label="Ruby">

```ruby
require 'getstream_ruby'
Models = GetStream::Generated::Models

client.common.create_role(Models::CreateRoleRequest.new(name: 'special_agent'))
```

</codetabs-item>

<codetabs-item value="go" label="Go">

```go
client.CreateRole(ctx, &getstream.CreateRoleRequest{Name: "special_agent"})
```

</codetabs-item>

<codetabs-item value="unity" label="Unity">

```csharp
// This is a server-side only feature, choose any of our server-side SDKs to use it
```

</codetabs-item>

</codetabs>

To delete previously created role you can use DeleteRole API endpoint:

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
await client.deleteRole("agent_006");
```

</codetabs-item>

<codetabs-item value="php" label="PHP">

```php
$client->deleteRole("agent_006");
```

</codetabs-item>

<codetabs-item value="python" label="Python">

```python
client.delete_role("agent_006")
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
client.deleteRole("agent_006", DeleteRoleRequest.builder().build()).execute();
```

</codetabs-item>

<codetabs-item value="csharp" label="C#">

```csharp
await client.DeleteRoleAsync("special_agent");
```

</codetabs-item>

<codetabs-item value="ruby" label="Ruby">

```ruby
require 'getstream_ruby'
Models = GetStream::Generated::Models

client.common.delete_role('agent_006')
```

</codetabs-item>

<codetabs-item value="go" label="Go">

```go
client.DeleteRole(ctx, "agent_006", &getstream.DeleteRoleRequest{})
```

</codetabs-item>

<codetabs-item value="unity" label="Unity">

```csharp
// This is a server-side only feature, choose any of our server-side SDKs to use it
```

</codetabs-item>

</codetabs>

<admonition type="info">

In order to delete a role, you have to remove all permission grants that this role has and make sure that you don't have non-deleted users with this role assigned. Channel-level roles could be deleted without reassigning them, although, some users could lose access to channels where this role is used.

</admonition>

Once you have a role created you can start granting permissions to it. You can also grant or remove permissions for built in roles.

## Granting permissions

User access in Chat application is split across multiple scopes.

- **Application Permissions** : You can grant these using the .app scope. These permissions apply to operations that occur outside of channel-types including accessing and [modifying other users](/chat/docs/<framework>/update_users/), or [using moderation features](/chat/docs/<framework>/moderation/).

- **Channel-Type Permissions** : These apply permissions to all channels of a particular type.

- **Channel Permissions** : These apply permissions to a single channel and override channel-type permissions.

To list all available permissions you can you ListPermissions API endpoint:

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
const { permissions } = await client.listPermissions(); // List of Permission objects
```

</codetabs-item>

<codetabs-item value="php" label="PHP">

```php
$response = $client->listPermissions();
```

</codetabs-item>

<codetabs-item value="python" label="Python">

```python
response = client.list_permissions()
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
var response = client.listPermissions(ListPermissionsRequest.builder().build()).execute();
```

</codetabs-item>

<codetabs-item value="csharp" label="C#">

```csharp
var response = await client.ListPermissionsAsync();
```

</codetabs-item>

<codetabs-item value="ruby" label="Ruby">

```ruby
require 'getstream_ruby'
Models = GetStream::Generated::Models

response = client.common.list_permissions
```

</codetabs-item>

<codetabs-item value="go" label="Go">

```go
resp, err := client.ListPermissions(ctx, &getstream.ListPermissionsRequest{})
```

</codetabs-item>

<codetabs-item value="unity" label="Unity">

```csharp
// This is a server-side only feature, choose any of our server-side SDKs to use it
```

</codetabs-item>

</codetabs>

<admonition type="info">

You can also find all available permissions on [Permissions Reference](/chat/docs/<framework>/permissions_reference/) page

</admonition>

Each permission object contains these fields:

| Type        | Description | Description                                                              | Example                                                                                   |
| ----------- | ----------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------- |
| id          | string      | Unique permission ID                                                     | create-message-owner                                                                      |
| name        | string      | Human-readable permission name                                           | Create Message in Owned Channel                                                           |
| description | string      | Human-readable permission description                                    | Grants action CreateMessage which allows to send a new message, user should own a channel |
| action      | string      | Action which this permission grants                                      | CreateMessage                                                                             |
| owner       | boolean     | If true, Subject should be an owner of the Resource                      | true                                                                                      |
| same_team   | boolean     | If true, Subject should be a part of the team that Resource is a part of | true                                                                                      |

To manipulate granted permissions for certain channel type, you can use UpdateChannelType API endpoint:

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
// observe current grants of the channel type
const { grants } = await client.getChannelType("messaging");

// update "channel_member" role grants in "messaging" scope
await client.updateChannelType("messaging", {
  grants: {
    channel_member: [
      "read-channel", // allow access to the channel
      "create-message", // create messages in the channel
      "update-message-owner", // update own user messages
      "delete-message-owner", // delete own user messages
    ],
  },
});
```

</codetabs-item>

<codetabs-item value="php" label="PHP">

```php
// observe current grants of the channel type
$response = $client->getChannelType("messaging");

// update "channel_member" role grants in "messaging" scope
$client->updateChannelType("messaging", new Models\UpdateChannelTypeRequest(
    grants: [
        "channel_member" => ["read-channel", "create-message", "update-message-owner", "delete-message-owner"],
    ],
));
```

</codetabs-item>

<codetabs-item value="python" label="Python">

```python
# observe current grants of the channel type
response = client.chat.get_channel_type(name="messaging")
print(repr(response.data.grants))

# update "channel_member" role grants in "messaging" scope
client.chat.update_channel_type(
    name="messaging",
    automod="disabled",
    automod_behavior="flag",
    max_message_length=5000,
    grants={
        "channel_member": [
            "read-channel",     # allow access to the channel
            "create-message",    # create messages in the channel
            "update-message-owner", # update own user messages
            "delete-message-owner", # delete own user messages
        ]
    },
)
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
// observe current grants of the channel type
var response = chat.getChannelType("messaging", GetChannelTypeRequest.builder().build()).execute();
System.out.println(response.getData().getGrants());

// update "channel_member" role grants in "messaging" scope
var grants = new HashMap<String, List<String>>();
grants.put("channel_member", List.of(
  "read-channel",     // allow access to the channel
  "create-message",    // create messages in the channel
  "update-message-owner", // update own user messages
  "delete-message-owner" // delete own user messages
));

chat.updateChannelType("messaging", UpdateChannelTypeRequest.builder()
    .automod("disabled")
    .automodBehavior("flag")
    .maxMessageLength(5000)
    .grants(grants)
    .build()).execute();
```

</codetabs-item>

<codetabs-item value="csharp" label="C#">

```csharp
// observe current grants of the channel type
var channelType = await chat.GetChannelTypeAsync("messaging");
Console.WriteLine(channelType.Grants);

// update "channel_member" role grants in "messaging" scope
await chat.UpdateChannelTypeAsync("messaging", new UpdateChannelTypeRequest
{
    Grants = new Dictionary<string, List<string>>
    {
        {
            // This will replace all existing grants of "channel_member" role
            "channel_member", new List<string>
            {
                "read-channel", // allow access to the channel
                "create-message", // create messages in the channel
                "update-message-owner", // update own user messages
                "delete-message-owner", // delete own user messages
            }
        },
    },
});
```

</codetabs-item>

<codetabs-item value="ruby" label="Ruby">

```ruby
require 'getstream_ruby'
Models = GetStream::Generated::Models

# observe current grants of the channel type
response = client.chat.get_channel_type('messaging')
puts response.grants

# update "channel_member" role grants in "messaging" scope
client.chat.update_channel_type('messaging', Models::UpdateChannelTypeRequest.new(
  grants: { 'channel_member' => [
    'read-channel',         # allow access to the channel
    'create-message',       # create messages in the channel
    'update-message-owner', # update own user messages
    'delete-message-owner'  # delete own user messages
  ] }
))
```

</codetabs-item>

<codetabs-item value="go" label="Go">

```go
// observe current grants of the channel type
resp, err := client.Chat().GetChannelType(ctx, "messaging", &getstream.GetChannelTypeRequest{})
fmt.Print(resp.Data.Grants)

// update "channel_member" role grants in "messaging" scope
_, err = client.Chat().UpdateChannelType(ctx, "messaging", &getstream.UpdateChannelTypeRequest{
	Grants: map[string][]string{
		"channel_member": {
			"read-channel",         // allow access to the channel
			"create-message",       // create messages in the channel
			"update-message-owner", // update own user messages
			"delete-message-owner", // delete own user messages
		},
	},
})
```

</codetabs-item>

<codetabs-item value="unity" label="Unity">

```csharp
// This is a server-side only feature, choose any of our server-side SDKs to use it
```

</codetabs-item>

</codetabs>

This call will only change grants of roles that were mentioned in the request. You can remove all role grants with providing empty array ( `[]` ) as list of granted permissions:

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
await client.updateChannelType("messaging", {
  grants: {
    guest: [], // removes all grants of "guest" role
    anonymous: [], // removes all grants of "anonymous" role
  },
});
```

</codetabs-item>

<codetabs-item value="php" label="PHP">

```php
$client->updateChannelType("messaging", new Models\UpdateChannelTypeRequest(
    grants: [
        "guest" => [],     // removes all grants of "guest" role
        "anonymous" => [],  // removes all grants of "anonymous" role
    ],
));
```

</codetabs-item>

<codetabs-item value="python" label="Python">

```python
client.chat.update_channel_type(
    name="messaging",
    automod="disabled",
    automod_behavior="flag",
    max_message_length=5000,
    grants={
        "guest": [], # removes all grants of "guest" role
        "anonymous": [], # removes all grants of "anonymous" role
    },
)
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
var grants = new HashMap<String, List<String>>();
grants.put("guest", Collections.emptyList()); // removes all grants of "guest" role
grants.put("anonymous", Collections.emptyList()); // removes all grants of "anonymous" role

chat.updateChannelType("messaging", UpdateChannelTypeRequest.builder()
    .automod("disabled")
    .automodBehavior("flag")
    .maxMessageLength(5000)
    .grants(grants)
    .build()).execute();
```

</codetabs-item>

<codetabs-item value="csharp" label="C#">

```csharp
await chat.UpdateChannelTypeAsync("messaging", new UpdateChannelTypeRequest
{
    Grants = new Dictionary<string, List<string>>
    {
        { "guest", new List<string>() }, // removes all grants of "guest" role
        { "anonymous", new List<string>() }, // removes all grants of "anonymous" role
    },
});
```

</codetabs-item>

<codetabs-item value="ruby" label="Ruby">

```ruby
require 'getstream_ruby'
Models = GetStream::Generated::Models

client.chat.update_channel_type('messaging', Models::UpdateChannelTypeRequest.new(
  grants: {
    'guest' => [],     # removes all grants of "guest" role
    'anonymous' => []  # removes all grants of "anonymous" role
  }
))
```

</codetabs-item>

<codetabs-item value="go" label="Go">

```go
_, err = client.Chat().UpdateChannelType(ctx, "messaging", &getstream.UpdateChannelTypeRequest{
	Grants: map[string][]string{
		"anonymous": {}, // removes all grants of "anonymous" role
		"guest":     {}, // removes all grants of "guest" role
	},
})
```

</codetabs-item>

<codetabs-item value="unity" label="Unity">

```csharp
// This is a server-side only feature, choose any of our server-side SDKs to use it
```

</codetabs-item>

</codetabs>

If you want to reset the whole scope to default settings, you can explicitly provide `null` to `grants` field:

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
await client.updateChannelType("messaging", {
  grants: null, // resets the whole scope to default settings
});
```

</codetabs-item>

<codetabs-item value="php" label="PHP">

```php
$client->updateChannelType("messaging", new Models\UpdateChannelTypeRequest(
    grants: null, // resets the whole scope to default settings
));
```

</codetabs-item>

<codetabs-item value="python" label="Python">

```python
# reset the whole scope to default settings
client.chat.update_channel_type(
    name="messaging",
    automod="disabled",
    automod_behavior="flag",
    max_message_length=5000,
    grants=None,
)
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
chat.updateChannelType("messaging", UpdateChannelTypeRequest.builder()
    .automod("disabled")
    .automodBehavior("flag")
    .maxMessageLength(5000)
    .grants(Collections.emptyMap())
    .build()).execute();
```

</codetabs-item>

<codetabs-item value="csharp" label="C#">

```csharp
await chat.UpdateChannelTypeAsync("messaging", new UpdateChannelTypeRequest
{
    Grants = new Dictionary<string, List<string>>(),
});
```

</codetabs-item>

<codetabs-item value="ruby" label="Ruby">

```ruby
require 'getstream_ruby'
Models = GetStream::Generated::Models

client.chat.update_channel_type('messaging', Models::UpdateChannelTypeRequest.new(
  grants: nil
))
```

</codetabs-item>

<codetabs-item value="go" label="Go">

```go
_, err = client.Chat().UpdateChannelType(ctx, "messaging", &getstream.UpdateChannelTypeRequest{
	Grants: nil,
})
```

</codetabs-item>

<codetabs-item value="unity" label="Unity">

```csharp
// This is a server-side only feature, choose any of our server-side SDKs to use it
```

</codetabs-item>

</codetabs>

You can manipulate `.app` scope grants using UpdateApp API endpoint in exactly the same way:

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
// update grants of multiple roles in ".app" scope
await client.updateApp({
  grants: {
    anonymous: [],
    guest: [],
    user: ["search-user", "mute-user"],
    admin: ["search-user", "mute-user", "ban-user"],
  },
});
```

</codetabs-item>

<codetabs-item value="php" label="PHP">

```php
// update grants of multiple roles in ".app" scope
$client->updateApp(new Models\UpdateAppRequest(
    grants: [
        "anonymous" => [],
        "guest" => [],
        "user" => ["search-user", "mute-user"],
        "admin" => ["search-user", "mute-user", "ban-user"],
    ],
));
```

</codetabs-item>

<codetabs-item value="python" label="Python">

```python
# update grants of multiple roles in ".app" scope
client.update_app(grants={
 "anonymous": [],
 "guest": [],
 "user": [
  "search-user",
  "mute-user",
 ],
 "admin": [
  "search-user",
  "mute-user",
  "ban-user",
 ],
})
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
var grants = new HashMap<String, List<String>>();
grants.put("anonymous", Collections.emptyList());
grants.put("guest", Collections.emptyList());
grants.put("user", List.of("search-user", "mute-user"));
grants.put("admin", List.of("search-user", "mute-user", "ban-user"));

client.updateApp(UpdateAppRequest.builder().grants(grants).build()).execute();
```

</codetabs-item>

<codetabs-item value="csharp" label="C#">

```csharp
await client.UpdateAppAsync(new UpdateAppRequest
{
    Grants = new Dictionary<string, List<string>>
    {
        { "anonymous", new List<string>() },
        { "guest", new List<string>() },
        { "user", new List<string> { "search-user", "mute-user" } },
        { "admin", new List<string> { "search-user", "mute-user", "ban-user" } },
    },
});
```

</codetabs-item>

<codetabs-item value="ruby" label="Ruby">

```ruby
require 'getstream_ruby'
Models = GetStream::Generated::Models

client.common.update_app(Models::UpdateAppRequest.new(
  grants: {
    'anonymous' => [],
    'guest' => [],
    'user' => ['search-user', 'mute-user'],
    'admin' => ['search-user', 'mute-user', 'ban-user']
  }
))
```

</codetabs-item>

<codetabs-item value="go" label="Go">

```go
_, err := client.UpdateApp(ctx, &getstream.UpdateAppRequest{
  Grants: map[string][]string{
    "anonymous": {},
    "guest":     {},
    "user":      {"search-user", "mute-user"},
    "admin":     {"search-user", "mute-user", "ban-user"},
  },
})
```

</codetabs-item>

<codetabs-item value="unity" label="Unity">

```csharp
// This is a server-side only feature, choose any of our server-side SDKs to use it
```

</codetabs-item>

</codetabs>

## UI for configuring permissions

Stream Dashboard provides a user interface to edit permission grants. This UI is available on **Chat > Roles & Permissions** page which is available after switching to version 2 of permissions.

![](https://getstream.imgix.net/docs/4d6a4f5e-6e96-4f3a-9097-f14526b384f7.png?auto=compress&fit=clip&w=800&h=600)

## Channel-level permissions

In some cases it makes sense to slightly modify granted permissions for the channel without changing channel-type grants configuration. For this, you can use Grants Modifiers that you can set for each channel individually. Grants Modifiers look almost exactly the same as regular Grants object except it allows to revoke permissions as well as grant new ones. For example, if we want to disallow sending links for users with role "user" in channel "livestream:example" and allow creating reactions, we can do this:

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
await channel.updatePartial({
  set: {
    config_overrides: {
      grants: {
        user: ["!add-links", "create-reaction"],
      },
    },
  },
});
```

</codetabs-item>

<codetabs-item value="php" label="PHP">

```php
$client->updateChannelPartial("messaging", "general", new Models\UpdateChannelPartialRequest(
    set: (object)["config_overrides" => (object)[
        "grants" => ["user" => ["!add-links", "create-reaction"]],
    ]],
));
```

</codetabs-item>

<codetabs-item value="python" label="Python">

```python
channel.update_channel_partial(set={
 "config_overrides": {
  "grants": {
   "user": ["!add-links", "create-reaction"],
  }
 }
})
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
Map<String, Object> grants = new HashMap<>();
Map<String, Object> overrides = new HashMap<>();
grants.put("user", List.of("!add-links", "create-reaction"));
overrides.put("grants", grants);

chat.channel(channel.getType(), channel.getId())
    .updateChannelPartial(UpdateChannelPartialRequest.builder()
        .set(Map.of("config_overrides", overrides))
        .build());
```

</codetabs-item>

<codetabs-item value="csharp" label="C#">

```csharp
var grants = new Dictionary<string, object> { { "user", new List<string> { "!add-links", "create-reaction" } } };
var overrides = new Dictionary<string, object> { { "grants", grants } };
var resp = await chat.UpdateChannelPartialAsync("channel-type", "channel-id", new UpdateChannelPartialRequest
{
    Set = new Dictionary<string, object>
    {
        { "config_overrides", overrides },
    },
});
```

</codetabs-item>

<codetabs-item value="ruby" label="Ruby">

```ruby
require 'getstream_ruby'
Models = GetStream::Generated::Models

client.chat.update_channel_partial('livestream', 'example', Models::UpdateChannelPartialRequest.new(
  set: { 'config_overrides' => { 'grants' => { 'user' => ['!add-links', 'create-reaction'] } } }
))
```

</codetabs-item>

<codetabs-item value="go" label="Go">

```go
channel := client.Chat().Channel("livestream", "example")
_, err = channel.UpdateChannelPartial(ctx, &getstream.UpdateChannelPartialRequest{
  Set: map[string]any{
    "config_overrides": map[string]any{
      "grants": map[string]any{
        "user": []string{"!add-links", "create-reaction"},
      },
    },
  },
})
```

</codetabs-item>

<codetabs-item value="unity" label="Unity">

```csharp
// This is a server-side only feature, choose any of our server-side SDKs to use it
```

</codetabs-item>

</codetabs>

Exclamation mark ( `!` ) here means "revoke" and you can combine any number of "revoke" and "grant" modifiers

<admonition type="info">

After modifying the granted channel-level permissions, the API will enrich the channel response with the grants field under data.config.grants

</admonition>

<admonition type="info">

The field `config_overrides` can only be updated using server-side auth

</admonition>

## Broadcast and Reply-only Channels

A common example of changing the permission model of a channel type is to create a Telegram-style broadcast channel where privileged channel members can send messages and other members may have permissions restricted to reading, reactions, or replying.

The three Permission grants to modify these under the scope of the channel type are

- Read Channel
- Create Reaction
- Create Reply

## Multi-Tenancy

For grouping users into teams (or tenants) to keep their data strictly segregated, see [Multi-Tenancy](/chat/docs/<framework>/multi_tenant_chat/).


---

This page was last updated at 2026-03-13T13:16:23.546Z.

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