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

> 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
Enable [App Context](https://youtu.be/9UEoSlngpYI) in the [Admin Portal](https://admin.pubnub.com/) to work with user metadata.
:::

Administrators are chat users with SDK instances [initialized with secretKey](https://www.pubnub.com/docs/chat/chat-sdk/build/configuration#additional-configuration-options). Admin moderation capabilities:

* Mute users on channels
* Ban users from accessing channels

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

## Mute or ban users as an administrator

Mute or ban users with `setRestrictions()` on the `Chat`, `User`, or `Channel` object. All three produce the same output with different input parameters.

**How it works:**

* Muting/banning creates a [moderation](https://www.pubnub.com/docs/chat/chat-sdk/build/features/custom-events#events-for-user-moderation) event (`muted` or `banned`)
* A moderation membership is created with `PUBNUB_INTERNAL_MODERATION_` prefix (secured via Access Manager, filtered from [getMemberships()](https://www.pubnub.com/docs/chat/chat-sdk/build/features/channels/membership#get-membership) results)
* Lifting restrictions removes the moderation membership and creates a `lifted` event

[Listen to moderation events](#listen-to-moderation-events) to trigger actions like removing [memberships](https://www.pubnub.com/docs/chat/chat-sdk/build/features/channels/membership). [Check restrictions](#check-restrictions) to verify user status.

:::note Secret Key required
Initialize with **Secret Key** (from [Admin Portal](https://admin.pubnub.com/)) for admin operations. Never expose `secretKey` to clients. If compromised, generate a new one in the Admin Portal.
:::

### Method signature

##### Under the hood

All `setRestrictions()` methods call App Context API and the JavaScript SDK [setChannelMembers()](https://www.pubnub.com/docs/sdks/javascript/api-reference/objects#set-channel-members) (when you mute or ban the user) or [removeChannelMembers()](https://www.pubnub.com/docs/sdks/javascript/api-reference/objects#remove-channel-members) method (when you unmute or unban the user).

These methods take the following parameters:

* setRestrictions() (on the Chat object) 1chat.setRestrictions(2 userId: string,3 channelId: string,4 {5 ban?: boolean,6 mute?: boolean,7 reason?: string8 }9): Promise<void>
* setRestrictions() (on the User object) 1user.setRestrictions(2 channel: Channel,3 {4 ban?: boolean,5 mute?: boolean,6 reason?: string7 }8): Promise<void>
* setRestrictions() (on the Channel object) 1channel.setRestrictions(2 user: User,3 {4 ban?: boolean,5 mute?: boolean,6 reason?: string7 }8): Promise<void>

#### 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/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/chat-sdk/learn/chat-entities/user) object to be muted or banned. |
| > ban | boolean | Optional |  | 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 |  | 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 |
| --- | --- |
| `Promise<void>` | Method returns no output data. |

#### Errors

Whenever you try to mute or ban a user on a client that was not initialized with a Secret Key, you'll get the `Moderation restrictions can only be set by clients initialized with a Secret Key` error.

### Sample code

#### Mute

Mute `support_agent_15` on the `support` channel.

* setRestrictions() (on the Chat object) 1await chat.setRestrictions("support_agent_15", "support", { mute: true })
* setRestrictions() (on the User object) 1// reference "support-agent-15"2const user = await chat.getUser("support_agent_15")3 4// reference the "support" channel5const support = await chat.getChannel("support")6 7// mute the user8await user.setRestrictions(support, { mute: true })
* setRestrictions() (on the Channel object) 1// reference "support-agent-15"2const mutedUser = await chat.getUser("support_agent_15")3 4// reference the "support" channel5const channel = await chat.getChannel("support")6 7// mute the user8await channel.setRestrictions(mutedUser, { mute: true })

#### Ban

Ban `support_agent_15` from the `support` channel.

* setRestrictions() (on the Chat object) 1// reference "support-agent-15"2const user = await chat.getUser("support_agent_15")3 4// reference the "support" channel5const channel = await chat.getChannel("support")6 7// ban the user8await chat.setRestrictions("support_agent_15", "support", { ban: true })
* setRestrictions() (on the User object) 1// reference "support-agent-15"2const user = await chat.getUser("support_agent_15")3 4// reference the "support" channel5const support = await chat.getChannel("support")6 7// ban the user8await user.setRestrictions(support, { ban: true })
* setRestrictions() (on the Channel object) 1// reference "support-agent-15"2const mutedUser = await chat.getUser("support_agent_15")3 4// reference the "support" channel5const channel = await chat.getChannel("support")6 7// ban the user8await channel.setRestrictions(mutedUser, { ban: true })

## Check restrictions

### One user on one channel

Check `mute` or `ban` restrictions for a user on a specific channel with `getChannelRestrictions()` or `getUserRestrictions()`.

##### Under the hood

`getChannelRestrictions()` and `getUserRestrictions()` call App Context API and the JavaScript SDK [getChannelMembers()](https://www.pubnub.com/docs/sdks/javascript/api-reference/objects#get-channel-members) method.

#### Method signature

These methods take the following parameters:

* getChannelRestrictions() 1user.getChannelRestrictions(2 channel: Channel3): Promise<{4 ban: boolean,5 mute: boolean,6 reason: string | number | boolean,7}>
* getUserRestrictions() 1channel.getUserRestrictions(2 user: User3): Promise<{4 ban: boolean;5 mute: boolean;6 reason: string | number | boolean,7}>

##### Input

| Parameter | Required in `getChannelRestrictions()` | Required in `getUserRestrictions()` | Description |
| --- | --- | --- | --- |
| `channel`Type: `Channel`Default: n/a | Yes | No | [Channel](https://www.pubnub.com/docs/chat/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/chat-sdk/learn/chat-entities/user) object that can be muted or banned. |

##### Output

| Parameter | Description |
| --- | --- |
| `Promise<>`Type: `object` | Returned object containing two fields: `ban` and `mute`. |
| `> ban`Type: `boolean` | Info whether the user is banned from the channel. |
| `> mute`Type: `boolean` | Info whether the user is muted on the channel. |
| `> reason`Type: `string`, `number`, or `boolean` | Reason why the user was banned or muted. |

#### Sample code

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

* getChannelRestrictions() 1// reference "support-agent-15"2const user = await chat.getUser("support_agent_15")3 4// reference the "support" channel5const support = await chat.getChannel("support")6 7// check user restrictions8await user.getChannelRestrictions(support)
* getUserRestrictions() 1// reference "support-agent-15"2const restrictedUser = await chat.getUser("support_agent_15")3 4// reference the "support" channel5const channel = await chat.getChannel("support")6 7// check user restrictions8await channel.getUserRestrictions(restrictedUser)

### One user on all channels

Check `mute` or `ban` restrictions for a user across all their channels with `getChannelsRestrictions()`.

##### Under the hood

`getChannelsRestrictions()` calls App Context API and the JavaScript SDK [getChannelMembers()](https://www.pubnub.com/docs/sdks/javascript/api-reference/objects#get-channel-members) method.

#### Method signature

This method takes the following parameters:

```ts
user.getChannelsRestrictions(
    {
        limit?: number,
        page?: {
            next?: string,
            prev?: string
        },
        sort?: object
    }
): Promise<{
    page: {
        next: string,
        prev: string,
    },
    total: number,
    status: number,
    restrictions: {
        ban: boolean,
        mute: boolean,
        reason: string | number | boolean,
        channelId: string,
    }[],
}>
```

##### Input

| Parameter | Description |
| --- | --- |
| `limit`Type: `number`Default: `100` | Number of objects to return in response. The default (and maximum) value is `100`. |
| `page`Type: `object`Default: n/a | Object used for pagination to define which previous or next result page you want to fetch. |
| `> next`Type: `string`Default: n/a | Random string returned from the server, indicating a specific position in a data set. Used for forward pagination, it fetches the next page, allowing you to continue from where you left off. |
| `> prev`Type: `string`Default: n/a | Random string returned from the server, indicating a specific position in a data set. Used for backward pagination, it fetches the previous page, enabling access to earlier data. Ignored if the `next` parameter is supplied. |
| `sort`Type: `object`Default: n/a | 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

| Parameter | Description |
| --- | --- |
| `Promise<>`Type: `object` | Returned object containing these fields: `page`, `total`, `status`, and `restrictions`. |
| `> page`Type: `object` | Object used for pagination to define which previous or next result page you want to fetch. |
| `>> next`Type: `string` | Random string returned from the server, indicating a specific position in a data set. Used for forward pagination, it fetches the next page, allowing you to continue from where you left off. |
| `>> prev`Type: `string` | Random string returned from the server, indicating a specific position in a data set. Used for backward pagination, it fetches the previous page, enabling access to earlier data. Ignored if the `next` parameter is supplied. |
| `> total`Type: `number` | Total number of restrictions. |
| `> status`Type: `number` | Status code of a server response, like `200`. |
| `> restrictions`Type: `object` | Object containing a list of restrictions. |
| `>> ban`Type: `boolean` | Info whether the user is banned from the given channel. |
| `>> mute`Type: `boolean` | Info whether the user is muted on the given channel. |
| `>> reason`Type: `string`, `number`, or `boolean` | Reason why the user was banned or muted. |
| `>> channelId`Type: `string` | ID of the channel containing user restrictions. |

#### Sample code

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

```ts
// reference "support-agent-15"
const user = await chat.getUser("support_agent_15")

// list all restrictions set for that user 
await user.getChannelsRestrictions()
```

### All users on one channel

Check `mute` or `ban` restrictions for all members of a channel with `getUsersRestrictions()`.

##### Under the hood

`getUsersRestrictions()` calls App Context API and the JavaScript SDK [getChannelMembers()](https://www.pubnub.com/docs/sdks/javascript/api-reference/objects#get-channel-members) method.

#### Method signature

This method takes the following parameters:

```ts
channel.getUsersRestrictions(
    {
        limit?: number,
        page?: {
            next?: string,
            prev?: string
        },
        sort?: object
    }
): Promise<{
    page: {
        next: string,
        prev: string,
    },
    total: number,
    status: number,
    restrictions: {
        ban: boolean,
        mute: boolean,
        userId: string,
    }[],
}>
```

##### Input

| Parameter | Description |
| --- | --- |
| `limit`Type: `number`Default: `100` | Number of objects to return in response. The default (and maximum) value is `100`. |
| `page`Type: `object`Default: n/a | Object used for pagination to define which previous or next result page you want to fetch. |
| `> next`Type: `string`Default: n/a | Random string returned from the server, indicating a specific position in a data set. Used for forward pagination, it fetches the next page, allowing you to continue from where you left off. |
| `> prev`Type: `string`Default: n/a | Random string returned from the server, indicating a specific position in a data set. Used for backward pagination, it fetches the previous page, enabling access to earlier data. Ignored if the `next` parameter is supplied. |
| `sort`Type: `object`Default: n/a | 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

| Parameter | Description |
| --- | --- |
| `Promise<>`Type: `object` | Returned object containing these fields: `page`, `total`, `status`, and `restrictions`. |
| `> page`Type: `object` | Object used for pagination to define which previous or next result page you want to fetch. |
| `>> next`Type: `string` | Random string returned from the server, indicating a specific position in a data set. Used for forward pagination, it fetches the next page, allowing you to continue from where you left off. |
| `>> prev`Type: `string` | Random string returned from the server, indicating a specific position in a data set. Used for backward pagination, it fetches the previous page, enabling access to earlier data. Ignored if the `next` parameter is supplied. |
| `> total`Type: `number` | Total number of restrictions. |
| `> status`Type: `number` | Status code of a server response, like `200`. |
| `> restrictions`Type: `object` | Object containing a list of restrictions. |
| `>> ban`Type: `boolean` | Info whether the user is banned from the given channel. |
| `>> mute`Type: `boolean` | Info whether the user is muted on the given channel. |
| `>> userId`Type: `string` | ID of the restricted user. |

#### Sample code

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

```ts
// reference the "support" channel
const channel = await chat.getChannel("support")

// list all restrictions on the "support" channel
await channel.getUsersRestrictions()
```

## Secure moderation

Client-side UI restrictions (hiding channels, disabling input) can be bypassed. Secure with [server-side logic](#server-side-restrictions) using Access Manager, plus optional [client-side feedback](#client-side-restrictions).

:::note
See [Moderation as user](https://www.pubnub.com/docs/chat/chat-sdk/build/features/users/moderation-user) for client-side moderation options.
:::

### Server-side restrictions

Use [Access Manager](https://www.pubnub.com/docs/chat/chat-sdk/build/features/users/permissions) with Chat SDK methods to grant/revoke permissions based on muting/banning restrictions.

:::warning Access Manager permissions
With `syncMutedUsers` enabled, grant these permissions (replace `$currentUserId` with actual user ID):
* `read` on `PN_PRV.$currentUserId.mute1` channel
* `update`, `delete`, `get` on `PN_PRV.$currentUserId.mute1` user
:::

**Recommended workflow:**

1. Admin [sets restrictions](#mute-or-ban-users) via dashboard (e.g., [Channel Monitor](https://www.pubnub.com/docs/bizops-workspace/channel-monitor#users))
2. [Get moderation restrictions](#check-restrictions) for users
3. Call Access Manager API to generate/revoke tokens

**Implementation steps:**

1. Enable Access Manager in the Admin Portal.
2. Initialize Chat SDK with authKey on the frontend to authenticate users and grant access to PubNub resources. 1import { Chat } from "@pubnub/chat"2 3const userId = "your-user-id"4const authToken = "token-from-your-server"5...6 7const chat = await Chat.init({8 subscribeKey: "your-subscribe-key-from-admin-portal",9 publishKey: "your-publish-key-from-admin-portal",10 userId: userId,11 authKey: authToken12})
3. Initialize backend with secretKey (secretKey) to secure your PubNub instance. tipsecretKey signs and verifies messages for Access Manager. Never expose to clients. 1import { Chat } from "@pubnub/chat"2 3const serverId = "auth-server"4 5export const chat = await Chat.init({6 subscribeKey: "your-subscribe-key-from-admin-portal",7 publishKey: "your-publish-key-from-admin-portal",8 secretKey: "your-secret-key-from-admin-portal",9 userId: serverId,10})
4. Get user permissions - Retrieve restrictions and convert to permission format (read/write/access per channel). 1async function defineUserPermissions(userId: string) {2 // Retrieve user information and channel restrictions3 const user = await chat.getUser(userId);4 const userRestrictions = await user.getChannelsRestrictions();5 6 // Simplify the detailed channel restrictions into a format suitable for the authorization token7 const reducedChannels = userRestrictions.restrictions.reduce((acc, curr) => {8 return {9 ...acc,10 [curr.channelId]: {11 read: !curr.ban,12 write: (!curr.mute && !curr.ban),13 get: true,14 }15 };16 }, {});17 18 // Return the reduced set of user permissions19 return reducedChannels;20}
5. Generate authorization token - Assign an access token with channel permissions and validity period. tipSee Permissions for the complete operation-to-permission mapping. 1async function generateAuthToken(userId: string, reducedChannels: Record<string, any>) {2 // Set up parameters for the authorization token3 const grantTokenParams = {4 ttl: 43200,5 authorized_uuid: userId,6 patterns: {7 channels: {8 ".*": {9 get: true,10 read: true,11 write: true,12 },13 },14 },15 resources: {16 channels: reducedChannels,17 uuids: {18 [userId]: {19 get: true,20 update: true,21 }22 }23 },24 }25 26 // Generate and return the authorization token27 return chat.sdk.grantToken(grantTokenParams);28} Short TTLs recommendedUse short-lived tokens (TTLs) for up-to-date restrictions, or revoke tokens with chat.sdk.revokeToken().
6. Listen for moderation events - All events are sent to PUBNUB_INTERNAL_MODERATION.[user_id]. Configure via Events & Actions in the Admin Portal with Messages as the event source.
7. Act on moderation events - Create a Webhook action in Events & Actions pointing to your server URL for token changes. Link to the event listener.

### Client-side restrictions

With [server-side permissions](#server-side-restrictions) enforced, add optional client-side UI feedback.

:::note
See [Client-side restrictions](https://www.pubnub.com/docs/chat/chat-sdk/build/features/users/moderation-user#client-side-restrictions) for regular user moderation.
:::

## Listen to moderation events

:::warning Deprecated method
`listenForEvents({ type: "moderation" })` is deprecated. Use `user.onRestrictionChanged()` instead.
:::

```ts
user.onRestrictionChanged(callback: (restriction: { userId: string; channelId: string; ban: boolean; mute: boolean; reason?: string }) => void): () => void
```

### Sample code

```ts
const user = await chat.getUser("support_agent_15")
const stopListening = user.onRestrictionChanged((restriction) => {
    if (restriction.mute) {
        console.log(`User ${restriction.userId} was muted on ${restriction.channelId}`)
    } else if (restriction.ban) {
        console.log(`User ${restriction.userId} was banned from ${restriction.channelId}`)
    }
})
```

## Flag/Report users (deprecated)

`DEPRECATED_report()` flags a user and reports them to admins. Requires a reason. Creates a [report event](https://www.pubnub.com/docs/chat/chat-sdk/build/features/custom-events#chat-events) on `PUBNUB_INTERNAL_ADMIN_CHANNEL`.

##### Under the hood

`DEPRECATED_report()` calls PubNub Pub/Sub API and the JavaScript SDK [publish()](https://www.pubnub.com/docs/sdks/javascript/api-reference/publish-and-subscribe#publish) method.

#### Method signature

This method takes the following parameters:

```ts
user.DEPRECATED_report(reason: string): Promise<any>
```

##### Input

| Parameter | Description |
| --- | --- |
| `reason` *Type: `string`Default: n/a | Reason for reporting/flagging a given user. |

##### Output

| Type | Description |
| --- | --- |
| `Promise<any>` | Returned object with a value of any type. |

#### Sample code

Report `support_agent_15` to the admin for posting offensive images on the `support` channel.

```ts
// reference the "chat" object and invoke the "getUser()" method
const user = await chat.getUser("support_agent_15")
// report "support_agent_15" to the admin and provide the reason
await user.DEPRECATED_report("Could you please mute Mike as he keeps sending offensive photos?")
```

## Listen to report events (deprecated)

Monitor user reports with `listenForEvents()` for moderation dashboard alerts.

#### Method signature

##### Under the hood

`listenForEvents()` calls PubNub Pub/Sub API and the JavaScript SDK [publish()](https://www.pubnub.com/docs/sdks/javascript/api-reference/publish-and-subscribe#publish) method.

This method has the following parameters:

```ts
chat.listenForEvents({
    channel: string;
    type?: "report";
    callback: (event: Event<"report">) => unknown;
}): () => void
```

###### Input

| Parameter | Description |
| --- | --- |
| `channel` *Type: `string`Default: n/a | Channel to listen for new `report` events. Set this value to a dedicated `PUBNUB_INTERNAL_ADMIN_CHANNEL` where all `report` events are sent. |
| `type`Type: `string`Default: n/a | Type of events. `report` is the type defined for all events emitted when a user is flagged/reported by someone for misbehaving. |
| `callback` *Type: n/aDefault: n/a | Callback function passed as a parameter. It defines the custom behavior to be executed whenever a `report` event type is detected on the specific channel. |
| methodType: stringDefault: n/a | This parameter is deprecated. You no longer have to provide a method used to send this event type as the method is now passed automatically. PubNub method used to send events you listen for. Use publish for all events related to reporting. |

###### Output

| Type | Description |
| --- | --- |
| `() => void` | Function you can call to disconnect (unsubscribe) from the channel and stop receiving `report` events. |

#### Sample code

Notify an admin when one user reports another user.

```ts
user.DEPRECATED_report("Inappropriate content");

(async () => {
  await chat.listenForEvents({
    channel: "admin",
    type: "report",
    callback: (event) => {
      console.log(`User ${event.userId} reported user ${event.payload.reported} for ${event.payload.reason}`);
    },
  });
})();
```