---
source_url: https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/moderation
title: Moderate misbehaving users as a chat administrator
updated_at: 2026-06-04T11:09:27.562Z
---

> Documentation Index
> For a curated overview of PubNub documentation, see: https://www.pubnub.com/docs/llms.txt
> For the full list of all documentation pages, see: https://www.pubnub.com/docs/llms-full.txt


# Moderate misbehaving users as a chat administrator

:::note Requires App Context
To work with stored user metadata, you must [enable App Context](https://youtu.be/9UEoSlngpYI) for your app's keyset in the [Admin Portal](https://admin.pubnub.com/).
:::

An administrator is a chat user whose Chat SDK instance was [initialized with the secretKey](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/configuration#additional-configuration-options).

Administrators can:

* Mute users on channels
* Ban users from accessing channels

Use [Access Manager](https://www.pubnub.com/docs/sdks/kotlin/api-reference/access-manager) to enforce these restrictions. See also [moderation for regular users](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/moderation-user).

## Mute or ban users as an administrator

As an admin, you can mute a specific user on a channel or ban them from accessing that channel using three setRestrictions() methods.

All methods produce the same result but take different parameters depending on which object you call them on.

When an admin mutes or bans a user on a channel, a [moderation](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/custom-events#events-for-user-moderation) event is created (of type `muted` or `banned`). You can [listen to these events](#listen-to-moderation-events) and, for example, remove user's [membership](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/channels/membership) on that channel.

Also, when an admin mutes or bans a user, an additional moderation membership is created for that user. This membership copies the ID of the channel and adds the `PUBNUB_INTERNAL_MODERATION_` prefix to it, even though no new channel gets created for that purpose. This moderation membership stores information about the user's current `mute` and `ban` restrictions under the `custom` property.

The reason behind creating an additional moderation membership was to have an object that could be secured with Access Manager and made inaccessible to users. The standard membership object couldn't serve this purpose as it stores info on the users' [lastReadMessageTimetoken](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/learn/chat-entities/membership) custom data that users should access to be able to see [unread messages](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/messages/unread) on channels.

The additional membership is created only for the moderation purposes - when fetching all channel memberships for a given user with the [getMemberships()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/channels/membership#get-membership) method, you won't see the moderation membership as Chat SDK filters it out automatically with [App Context Filtering Language](https://www.pubnub.com/docs/general/metadata/filtering).

When you lift restrictions on the user (unmute or unban them), the moderation membership is removed and a [moderation](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/custom-events#events-for-user-moderation) event of type `lifted` is created.

To learn if a user is muted on a given channel or banned, use the Chat SDK methods to [check moderation restrictions](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/moderation#check-restrictions).

:::note Requires Secret Key authentication
Mute and ban restrictions for the client devices should be set by servers initializing Chat SDK with a **Secret Key** (available on the Admin Portal on your app's keyset).
The `secretKey` should only be used within a secure server and never exposed to client devices. If the `secretKey` is ever compromised, it can be an extreme security risk to your application. If you suspect your `secretKey` has been compromised, you can generate a new `secretKey` for the existing PubNub keyset on the Admin Portal.
:::

### Method signature

These methods take the following parameters:

* setRestrictions() (on the Chat object) 1chat.setRestrictions(2 restriction: Restriction3): PNFuture<Unit> Definition of the Restriction class: 1class Restriction(2 val userId: String,3 val channelId: String,4 val ban: Boolean = false,5 val mute: Boolean = false,6 val reason: String?7)
* setRestrictions() (on the User object) 1user.setRestrictions(2 channel: Channel,3 ban: Boolean = false,4 mute: Boolean = false,5 reason: String?,6): PNFuture<Unit>
* setRestrictions() (on the Channel object) 1channel.setRestrictions(2 user: User,3 ban: Boolean = false,4 mute: Boolean = false,5 reason: String?6): PNFuture<Unit>

#### Input

| Parameter | Required for Chat | Required for User | Required for Channel | Description |
| --- | --- | --- | --- | --- |
| userId | String | Optional |  | Yes | No | No | [Unique User ID](https://www.pubnub.com/docs/general/setup/users-and-devices) that becomes your app's current user. It's a string of up to 92 characters that identifies a single client (end user, device, or server) that connects to PubNub. Based on User ID, PubNub calculates pricing for your apps' usage. User ID should be persisted and remain unchanged. If you don't set `userId`, you won't be able to connect to PubNub. In this method, `userId` stands for the user that you want to mute or ban. |
| channelId | String | Optional |  | Yes | No | No | ID of the channel on/from which the user should be muted or banned. |
| channel | Channel | Optional |  | No | Yes | No | [Channel](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/learn/chat-entities/channel) object on/from which the user should be muted or banned. |
| user | User | Optional |  | No | No | Yes | [User](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/learn/chat-entities/user) object to be muted or banned. |
| ban | Boolean | Optional | `false` | No | No | No | Value that represents the user's moderation restrictions. Set to `true` to ban the user from the channel or to `false` to unban them. |
| mute | Boolean | Optional | `false` | No | No | No | Value that represents the user's moderation restrictions. Set to `true` to mute the user on the channel or to `false` to unmute them. |
| reason | String | Optional |  | No | No | No | Reason why you want to ban or mute the user. |

#### Output

| Type | Description |
| --- | --- |
| `PNFuture<Unit>` | Function that returns an instance of `PNFuture` that will be completed with `Unit` when the `setRestrictions` operation is completed. |

### Sample code

#### Mute

Mute `support_agent_15` on the `support` channel.

* setRestrictions() (on the Chat object) 1// reference the "chat" object and invoke the "getUser()" method2chat.getUser("support_agent_15").async { userResult ->3 userResult.onSuccess { user ->4 if (user != null) {5 // get the "support" channel6 chat.getChannel("support").async { channelResult ->7 channelResult.onSuccess { channel ->8 if (channel != null) {9 // create a Restriction object to mute the user on the channel10 val restriction = Restriction(11 userId = user.id,12 channelId = channel.id,13 mute = true14 )15 16 // set the restriction using the Chat object17 chat.setRestrictions(restriction).async { restrictionResult ->18 restrictionResult.onSuccess {19 // handle success20 }.onFailure {21 // handle failure22 }23 }24 } else {25 // handle null channel26 }27 }.onFailure {28 // handle failure to get the channel29 }30 }31 } else {32 // handle null user33 }34 }.onFailure {35 // handle failure to get the user36 }37}
* setRestrictions() (on the User object) 1// reference the "chat" object and invoke the "getUser()" method2chat.getUser("support_agent_15").async { userResult ->3 userResult.onSuccess { user ->4 if (user != null) {5 // get the "support" channel6 chat.getChannel("support").async { channelResult ->7 channelResult.onSuccess { channel ->8 if (channel != null) {9 // set the restriction using the User object10 user.setRestrictions(channel, mute = true).async { restrictionResult ->11 restrictionResult.onSuccess {12 // handle success13 }.onFailure {14 // handle failure15 }16 }17 } else {18 // handle null channel19 }20 }.onFailure {21 // handle failure to get the channel22 }23 }24 } else {25 // handle null user26 }27 }.onFailure {28 // handle failure to get the user29 }30}
* setRestrictions() (on the Channel object) 1// reference the "chat" object and invoke the "getUser()" method2chat.getUser("support_agent_15").async { userResult ->3 userResult.onSuccess { user ->4 if (user != null) {5 // get the "support" channel6 chat.getChannel("support").async { channelResult ->7 channelResult.onSuccess { channel ->8 if (channel != null) {9 // set the restriction using the Channel object10 channel.setRestrictions(user, mute = true).async { restrictionResult ->11 restrictionResult.onSuccess {12 // handle success13 }.onFailure {14 // handle failure15 }16 }17 } else {18 // handle null channel19 }20 }.onFailure {21 // handle failure to get the channel22 }23 }24 } else {25 // handle null user26 }27 }.onFailure {28 // handle failure to get the user29 }30}

#### Ban

Ban `support_agent_15` from the `support` channel.

* setRestrictions() (on the Chat object) 1// reference the "chat" object and invoke the "getUser()" method2chat.getUser("support_agent_15").async { userResult ->3 userResult.onSuccess { user ->4 if (user != null) {5 // get the "support" channel6 chat.getChannel("support").async { channelResult ->7 channelResult.onSuccess { channel ->8 if (channel != null) {9 // create a Restriction object to ban the user on the channel10 val restriction = Restriction(11 userId = user.id,12 channelId = channel.id,13 ban = true14 )15 16 // set the restriction using the Chat object17 chat.setRestrictions(restriction).async { restrictionResult ->18 restrictionResult.onSuccess {19 // handle success20 }.onFailure {21 // handle failure22 }23 }24 } else {25 // handle null channel26 }27 }.onFailure {28 // handle failure to get the channel29 }30 }31 } else {32 // handle null user33 }34 }.onFailure {35 // handle failure to get the user36 }37}
* setRestrictions() (on the User object) 1// reference the "chat" object and invoke the "getUser()" method2chat.getUser("support_agent_15").async { userResult ->3 userResult.onSuccess { user ->4 if (user != null) {5 // get the "support" channel6 chat.getChannel("support").async { channelResult ->7 channelResult.onSuccess { channel ->8 if (channel != null) {9 // set the restriction using the User object10 user.setRestrictions(channel, ban = true).async { restrictionResult ->11 restrictionResult.onSuccess {12 // handle success13 }.onFailure {14 // handle failure15 }16 }17 } else {18 // handle null channel19 }20 }.onFailure {21 // handle failure to get the channel22 }23 }24 } else {25 // handle null user26 }27 }.onFailure {28 // handle failure to get the user29 }30}
* setRestrictions() (on the Channel object) 1// reference the "chat" object and invoke the "getUser()" method2chat.getUser("support_agent_15").async { userResult ->3 userResult.onSuccess { user ->4 if (user != null) {5 // get the "support" channel6 chat.getChannel("support").async { channelResult ->7 channelResult.onSuccess { channel ->8 if (channel != null) {9 // set the restriction using the Channel object10 channel.setRestrictions(user, ban = true).async { restrictionResult ->11 restrictionResult.onSuccess {12 // handle success13 }.onFailure {14 // handle failure15 }16 }17 } else {18 // handle null channel19 }20 }.onFailure {21 // handle failure to get the channel22 }23 }24 } else {25 // handle null user26 }27 }.onFailure {28 // handle failure to get the user29 }30}

## Check restrictions

### One user on one channel

Check if there are any `mute` or `ban` restrictions set for a user on one channel using the `getChannelRestrictions()` and `getUserRestrictions()` methods.

#### Method signature

These methods take the following parameters:

* getChannelRestrictions() 1user.getChannelRestrictions(channel: Channel): PNFuture<Restriction>
* getUserRestrictions() 1channel.getUserRestrictions(user: User): PNFuture<Restriction>

##### Input

| Parameter | Required in `getChannelRestrictions()` | Required in `getUserRestrictions()` | Description |
| --- | --- | --- | --- |
| `channel`Type: `Channel`Default: n/a | Yes | No | [Channel](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/learn/chat-entities/channel) object on/from which the user can be muted or banned. |
| `user`Type: `User`Default: n/a | No | Yes | [User](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/learn/chat-entities/user) object that can be muted or banned. |

##### Output

| Type | Description |
| --- | --- |
| `PNFuture<Restriction>` | `PNFuture` containing the `Restriction` object with these properties: `userId`, `channelId`, `ban` `mute`, and `reason`. |

#### Sample code

Check if the user `support_agent_15` has any restrictions set on the `support` channel.

* getChannelRestrictions() 1// reference the "chat" object and invoke the "getUser()" method2chat.getUser("support_agent_15").async { userResult ->3 userResult.onSuccess { user ->4 if (user != null) {5 // get the "support" channel6 chat.getChannel("support").async { channelResult ->7 channelResult.onSuccess { channel ->8 if (channel != null) {9 // get the channel restrictions for the user10 user.getChannelRestrictions(channel).async { restrictionResult ->11 restrictionResult.onSuccess { restriction ->12 // handle the restriction object13 }.onFailure {14 // handle failure15 }16 }17 } else {18 // handle null channel19 }20 }.onFailure {21 // handle failure to get the channel22 }23 }24 } else {25 // handle null user26 }27 }.onFailure {28 // handle failure to get the user29 }30}
* getUserRestrictions() 1// reference the "chat" object and invoke the "getUser()" method2chat.getUser("support_agent_15").async { userResult ->3 userResult.onSuccess { user ->4 if (user != null) {5 // get the "support" channel6 chat.getChannel("support").async { channelResult ->7 channelResult.onSuccess { channel ->8 if (channel != null) {9 // get the user restrictions for the channel10 channel.getUserRestrictions(user).async { restrictionResult ->11 restrictionResult.onSuccess { restriction ->12 // handle the restriction object13 }.onFailure {14 // handle failure15 }16 }17 } else {18 // handle null channel19 }20 }.onFailure {21 // handle failure to get the channel22 }23 }24 } else {25 // handle null user26 }27 }.onFailure {28 // handle failure to get the user29 }30}

### One user on all channels

Check if there are any `mute` or `ban` restrictions set for a user on all channels they are a member of using the `getChannelsRestrictions()` method.

#### Method signature

This method takes the following parameters:

```kotlin
user.getChannelsRestrictions(
    limit: Int?,
    page: PNPage?,
    sort: Collection<PNSortKey<PNMembershipKey>> = listOf(),
): PNFuture<GetRestrictionsResponse>
```

##### Input

| Parameter | Description |
| --- | --- |
| `limit`Type: `Int`Default: `100` | Number of objects to return in response. The default (and maximum) value is `100`. |
| `page`Type: `PNPage`Default: n/a | Object used for pagination to define which previous or next result page you want to fetch. |
| `sort`Type: `Collection<PNSortKey<PNMembershipKey>>`Default: `listOf()` | Key-value pair of a property to sort by, and a sort direction. Available options are `id`, `name`, and `updated`. Use `asc` or `desc` to specify the sorting direction, or specify `null` to take the default sorting direction (ascending). For example: `{name: "asc"}`. By default, the items are sorted by the last updated date. |

##### Output

| Type | Description |
| --- | --- |
| `PNFuture<GetRestrictionsResponse>` | `PNFuture` containing the `GetRestrictionsResponse` object with these properties: `restrictions`, `next`, `prev`, `total`, and `status`. |

#### Sample code

List all `mute` and `ban` restrictions set for the user `support_agent_15`.

```kotlin
// reference the "chat" object and invoke the "getUser()" method
chat.getUser("support_agent_15").async { result ->
    result.onSuccess { user ->
        if (user != null) {
            // fetch the channel restrictions for the user
            user.getChannelsRestrictions().async { restrictionsResult ->
                restrictionsResult.onSuccess { getRestrictionsResponse ->
                    // process the returned restrictions
                    for (restriction in getRestrictionsResponse.restrictions) {
                        if (restriction.mute || restriction.ban) {
                            // handle the restriction object (either muted or banned)
                            println("User is restricted on channel ${restriction.channelId}: mute=${restriction.mute}, ban=${restriction.ban}")
                        }
                    }
                }.onFailure {
                    // handle the failure of getting restrictions
                    println("Failed to get channel restrictions: ${it.message}")
                }
            }
        } else {
            // handle null user
            println("User is not found.")
        }
    }.onFailure {
        // handle failure
    }
}
```

### All users on one channel

Check if there are any `mute` or `ban` restrictions set for user of a given channel using the `getUsersRestrictions()` method.

#### Method signature

This method takes the following parameters:

```kotlin
channel.getUsersRestrictions(
    limit: Int?,
    page: PNPage?,
    sort: Collection<PNSortKey<PNMemberKey>> = listOf(),
): PNFuture<GetRestrictionsResponse>
```

##### Input

| Parameter | Description |
| --- | --- |
| `limit`Type: `Int`Default: `100` | Number of objects to return in response. The default (and maximum) value is `100`. |
| `page`Type: `PNPage`Default: n/a | Object used for pagination to define which previous or next result page you want to fetch. |
| `sort`Type: `Collection<PNSortKey<PNMemberKey>>`Default: `listOf()` | Key-value pair of a property to sort by, and a sort direction. Available options are `id`, `name`, and `updated`. Use `asc` or `desc` to specify the sorting direction, or specify `null` to take the default sorting direction (ascending). For example: `{name: "asc"}`. By default, the items are sorted by the last updated date. |

##### Output

| Type | Description |
| --- | --- |
| `PNFuture<GetRestrictionsResponse>` | `PNFuture` containing the `GetRestrictionsResponse` object with these properties: `restrictions`, `next`, `prev`, `total`, and `status`. |

#### Sample code

List all `mute` and `ban` restrictions set for the `support` channel.

```kotlin
// reference the "chat" object and invoke the "getChannel()" method
chat.getChannel("support").async { result ->
    result.onSuccess { channel ->
        if (channel != null) {
            // fetch the user restrictions for the channel
            channel.getUsersRestrictions().async { restrictionsResult ->
                restrictionsResult.onSuccess { getRestrictionsResponse ->
                    // process the returned restrictions
                    for (restriction in getRestrictionsResponse.restrictions) {
                        if (restriction.mute || restriction.ban) {
                            // handle the restriction object (either muted or banned)
                            println("User ${restriction.userId} is restricted in channel ${channel.id}: mute=${restriction.mute}, ban=${restriction.ban}")
                        }
                    }
                }.onFailure {
                    // handle the failure of getting restrictions
                    println("Failed to get user restrictions: ${it.message}")
                }
            }
        } else {
            // handle null channel
            println("Channel is not found.")
        }
    }.onFailure {
        // handle failure
    }
}
```

## Secure moderation

You could try to use only the [banning or muting restrictions](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/moderation#mute-or-ban-users-as-an-administrator) on client devices and enforce some UI moderation, like not displaying channels for users who are banned from them, or not displaying input field for users who are muted on a given channel so that they couldn't post a message.

Still, such solely client-side restrictions can easily be bypassed if not secured with an [additional server-side logic](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/moderation#server-side-restrictions) that uses Access Manager to allow or block user's access to PubNub resources (channels and users). This server-side can also be followed by additional [client-side errors](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/moderation#client-side-restrictions) that inform app users about their restrictions up front.

:::note Client-side moderation
For more information on client-side moderation for regular chat users, refer to [Moderation as user](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/moderation-user).
:::

### Server-side restrictions

It's recommended to use [Access Manager](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/permissions) alongside the Chat SDK methods and grant or revoke permissions from users based on their muting or banning restrictions.

:::warning Mute list and Access Manager
If you use Access Manager for [user moderation](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/moderation) within your chat app, and your users use the [client-side mute list](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/moderation-user) functionality with the `syncMutedUsers` parameter enabled, you must grant the Chat SDK user the following permissions:
* `read` permission to the `PN_PRV.$currentUserId.mute1` channel.
* `update`, `delete`, and `get` permissions for the `PN_PRV.$currentUserId.mute1` user.
Make sure to change `$currentUserId` to the user ID of the chat user that will use the mute list functionality.
:::

For example, you could have a UI moderation dashboard (like [Channel Monitor](https://www.pubnub.com/docs/bizops-workspace/channel-monitor#users)) where admins [set restrictions](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/moderation#mute-or-ban-users-as-an-administrator) on users by muting or banning them from specific channels. After that, you can use one of the Chat SDK methods to [get moderation restrictions](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/moderation#check-restrictions) for users and, based on results, call the Access Manager API to either generate or revoke grant tokens for PubNub resources (channels or users).

Let's look at sample steps that use [Chat SDK](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/moderation#server-side-restrictions) methods to configure a chat app, set up server permissions, listen to any permission changes in Channel Monitor UI, and invoke access grant or revoke request on the server side.

1. Enable Access Manager. Navigate to your app's keyset in the Admin Portal and turn on the ACCESS MANAGER option.
2. Initialize Chat SDK with token. On the frontend of your app, initialize the Chat SDK (init()) with the token (generated by server-side code). Use it for all requests made to PubNub APIs to authenticate users in your application and grant them access to PubNub resources (other users' metadata and channels). 1val userId = "your-user-id"2val token = "token-generated-by-server-side"3// ...4 5Chat.init(ChatConfiguration(), PNConfiguration.builder(UserId(userId), subscribeKey = "your-subscribe-key-from-admin-portal") {6 publishKey = "your-publish-key-from-admin-portal" 7 authToken = token8}.build()).async {9 it.onSuccess { chat ->10 // ...11 }.onFailure {12 // unable to initialize Chat13 }14}
3. Secure backend initialization. On the backend, initialize the Chat SDK with the secret key secretKey on your servers to secure your PubNub instance. Secret keysecretKey is a secret shared between your application's server and PubNub and it's used to administer Access Manager permissions for your client applications by signing and verifying the authenticity of messages and requests. Remember to never expose the secretKey to client devices. 1val serverId = "auth-server"2var token: String3val clientUserIdValue = "clientUserId"4// ...5 6Chat.init(ChatConfiguration(), PNConfiguration.builder(UserId(serverId), subscribeKey = "your-subscribe-key-from-admin-portal") {7 publishKey = "your-publish-key-from-admin-portal"8 secretKey = "your-secret-key-from-admin-portal"9}.build()).async {10 it.onSuccess { chat ->11 chat.pubNub.grantToken(12 ttl = 1,13 channels = listOf(ChannelGrant.name(get = true, name = "anyChannelForNow")),14 uuids = listOf(UUIDGrant.id(id = clientUserIdValue, get = true, update = true)) // this is important15 ).async { grantTokenResult ->16 grantTokenResult.onSuccess { grantToken ->17 token = grantToken.token 18 }.onFailure{19 // unable to generate token20 }21 }22 }.onFailure {23 // unable to initialize Chat24 }25}
4. Get user permissions. Retrieve detailed user restrictions and convert these details into a simplified permission format where each channel is marked with whether the user can read, write, or access it, based on such restrictions as bans or mutes. 1val userId: String2// ...3 4// Retrieve user information and channel restrictions5chat.getUser(userId).async {6 it.onSuccess { user ->7 user?.getChannelsRestrictions()?.async {8 it.onSuccess { restrictionsResponse ->9 val grants = restrictionsResponse.restrictions.map { restriction -> 10 ChannelGrant.name(restriction.channelId,11 read = !restriction.ban,12 write = (!restriction.mute && !restriction.ban),13 get = true,14 )15 }16 // use grants17 }.onFailure {18 // handle failed to get restrictions19 }20 }21 }.onFailure {22 // handle failed to get user23 }24}
5. Generate authorization token. Generate and assign an access token reflecting the user's permissions. The token contains information about which channels the user can access and how (read/write), and it's configured with a specific validity period. This token serves as a key for users to interact with the application according to their permissions. Operation-to-permission mappingRead the Permissions document for a complete list of available operations that users can do with PubNub resources in apps created with the Chat SDK. 1val restrictionGrants: List<ChannelGrant>2// ...3 4// Set up parameters for the authorization token5chat.pubNub.grantToken(ttl = 43200, authorizedUUID = userId, channels =6 listOf(7 ChannelGrant.pattern(".*", get = true, read = true, write = true),8 ) + restrictionGrants,9 uuids = listOf(10 UUIDGrant.id(userId, get = true, update = true)11 )12) Set short TTLsYou can mute or ban a user for an indefinite amount of time, but you can unmute/unban them at any time. To make sure that the permissions set with Access Manager reflect the most up-to-date muting/banning restrictions on the client-side, it's recommended to set short-lived tokens (TTLs) for grant calls (valid for seconds and minutes rather than hours or days). Alternatively, if new muting/banning restrictions are set on the frontend side of your app, you can revoke Access Manager permissions on the backend using the chat.pubNub.revokeToken() method.
6. Listen for moderation events. All moderation events generated by muting and banning actions are sent to a single PUBNUB_INTERNAL_MODERATION.[user_id] channel. You can listen to these events through Events & Actions in the Admin Portal. To do that, create a new event listener, choose Messages as event source, and configure the event listener as follows:
7. Act on moderation events. Create a Webhook type of action in Events & Actions where you must specify the URL of the server you want to hit for authentication token changes each time a new moderation event is generated. Link the action to the previously created listener.

### Client-side restrictions

Once you enable and define [server-side permissions](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/moderation#server-side-restrictions) with Access Manager, you can be sure that your muting and banning restrictions are always enforced.

:::note Client-side moderation
For more information on working with client-side restrictions for regular chat users, refer to [Client-side restrictions](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/moderation-user#client-side-restrictions).
:::

## Listen to moderation events

Monitor moderation events using `onRestrictionChanged()` on the `User` object. This fires when the user is muted, banned, or has restrictions lifted.

:::warning Deprecated method
`listenForEvents<EventContent.Moderation>()` is deprecated. Use `user.onRestrictionChanged()` instead, which provides a typed `Restriction` object.
:::

:::tip Events documentation
To read more about the events of type `moderation`, refer to the [Chat events](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/custom-events#events-for-user-moderation) documentation.
:::

### Method signature

This method has the following parameters:

```kotlin
user.onRestrictionChanged(callback: (restriction: Restriction) -> Unit): AutoCloseable
```

#### Input

| Parameter | Description |
| --- | --- |
| `callback` *Type: `(restriction: Restriction) -> Unit`Default: n/a | Function invoked with a `Restriction` event whenever the user's moderation status changes. |

The `Restriction` object contains:

| Property | Description |
| --- | --- |
| `userId`Type: `String` | The ID of the moderated user. |
| `channelId`Type: `String` | The channel where the restriction applies. |
| `ban`Type: `Boolean` | Whether the user is banned. |
| `mute`Type: `Boolean` | Whether the user is muted. |
| `reason`Type: `String?` | The reason for the restriction, if provided. |

#### Output

| Type | Description |
| --- | --- |
| `AutoCloseable` | Interface that lets you stop receiving moderation events by invoking the `close()` method. |

### Sample code

Listen for moderation events on a specific user.

```kotlin
val user: User
// ...

val subscription = user.onRestrictionChanged { restriction ->
    when {
        restriction.mute -> println("User ${restriction.userId} was muted on ${restriction.channelId}")
        restriction.ban -> println("User ${restriction.userId} was banned from ${restriction.channelId}")
        else -> println("Restrictions lifted for ${restriction.userId} on ${restriction.channelId}")
    }
}

// stop listening:
// subscription.close()
```