Flag, Mute & Ban
Overview
Stream provides moderation APIs for flagging content, muting users, banning users, and blocking users. These actions integrate with the moderation review queue and dashboard. All endpoints use the v2 moderation API and are available through Stream's server-side SDKs.
Not every action works outside Stream's products. Flag, ban, and bypass also work for your own (standalone) content and users. Mute, block, and channel bans only take effect inside Stream Chat, Feeds, and Video.
| Action | Standalone content and users | Stream Chat, Feeds, Video |
|---|---|---|
| Flag | Yes. Any entity_type; custom types need server-side auth |
Yes |
| Ban | Yes. Stream records the ban; your app enforces it | Yes. Stream enforces it |
| Channel ban | No | Stream Chat only |
| Mute | No | Yes |
| Block | No | Yes |
| Bypass | Yes. Skips Check for the user's content | Yes |
Flag
Flagging allows users or moderators to report content for review. Flagged content is automatically added to the moderation review queue. You can flag messages, users, activities, reactions, or any custom entity type.
Flag Content
Use the flag method to report an entity for moderation review. You can attach a reason, custom metadata, and a moderation payload containing the content to be reviewed.
client.Moderation().Flag(ctx, &getstream.FlagRequest{
EntityType: "stream:chat:v1:message",
EntityID: messageId,
EntityCreatorID: getstream.PtrTo(creatorId),
Reason: getstream.PtrTo("spam"),
Custom: getstream.PtrTo(map[string]interface{}{
"user_comment": "This user is spamming the channel",
}),
UserID: getstream.PtrTo(moderatorId),
})Request parameters
| key | required | type | description |
|---|---|---|---|
| entity_type | true | string | Type of entity being flagged. Examples: stream:chat:v1:message, stream:user, stream:feeds:v2:activity, stream:feeds:v2:reaction, or any custom type. |
| entity_id | true | string | Unique identifier of the entity being flagged. |
| entity_creator_id | false | string | User ID of the entity creator. |
| reason | false | string | Reason for flagging. Use a slug or keyword for easy filtering. |
| custom | false | object | Custom metadata to attach to the flag. |
| moderation_payload | false | object | Content to be reviewed (texts, images, videos). Displayed on the dashboard. |
| user_id | false | string | User ID of the person flagging (required for server-side usage). |
Response
| key | type | description |
|---|---|---|
| item_id | string | ID of the review queue item created by the flag. |
Flag a User
To flag a user rather than a message, set entity_type to stream:user and provide the target user's ID as entity_id.
client.Moderation().Flag(ctx, &getstream.FlagRequest{
EntityType: "stream:user",
EntityID: targetUserId,
Reason: getstream.PtrTo("harassment"),
UserID: getstream.PtrTo(moderatorId),
})Query Moderation Flags
Query flags with filtering, sorting, and pagination. This allows you to retrieve flags matching specific criteria.
response, err := client.Moderation().QueryModerationFlags(ctx, &getstream.QueryModerationFlagsRequest{
Filter: getstream.PtrTo(map[string]interface{}{
"entity_type": "stream:chat:v1:message",
}),
Sort: []getstream.SortParam{
{Field: "created_at", Direction: getstream.PtrTo(-1)},
},
Limit: getstream.PtrTo(20),
})Request parameters
| key | required | type | description |
|---|---|---|---|
| filter | false | object | Filter conditions. |
| sort | false | array | Sort parameters. |
| limit | false | number | Maximum flags to return. |
| next | false | string | Pagination cursor. |
Mute
Mute only takes effect inside Stream Chat, Feeds, and Video. For your own app, store mutes in your own system.
Muting allows users to silence other users. Muted users' messages are still delivered via WebSocket but not via push notifications (APN/Firebase). Implementing UI logic for muted users (hiding or displaying differently) is left to the developer.
Mute User
Mute one or more users. You can optionally set a timeout after which the mute automatically expires.
client.Moderation().Mute(ctx, &getstream.MuteRequest{
TargetIds: []string{"user_to_mute"},
UserID: getstream.PtrTo(mutingUserId),
Timeout: getstream.PtrTo(60),
})Request parameters
| key | required | type | description |
|---|---|---|---|
| target_ids | true | array | List of user IDs to mute. |
| user_id | false | string | User ID of the person performing the mute. |
| timeout | false | number | Mute duration in minutes. If not set, the mute has no expiration. |
Unmute User
Remove a mute from one or more users.
client.Moderation().Unmute(ctx, &getstream.UnmuteRequest{
TargetIds: []string{"user_to_unmute"},
UserID: getstream.PtrTo(unmutingUserId),
})Ban
Banning stops a user from posting. An app-wide ban works for any user: Stream records it on the user (banned, ban_expires) and sends a user.banned webhook. Stream Chat and Feeds enforce it for you; in your own app, read the ban state or handle the webhook and block the user yourself. Stream Chat users can also be banned from a single channel. Bans support timeouts (temporary bans), shadow banning, IP banning, and optional message deletion.
Ban User
Ban a user from the entire app. You can optionally specify a timeout for temporary bans, a reason, and whether to delete the user's messages.
client.Moderation().Ban(ctx, &getstream.BanRequest{
TargetUserID: "user_to_ban",
BannedByID: getstream.PtrTo(moderatorId),
Reason: getstream.PtrTo("Repeated spam"),
Timeout: getstream.PtrTo(1440),
})Request parameters
| key | required | type | description |
|---|---|---|---|
| target_user_id | true | string | User ID to ban. |
| banned_by_id | false | string | User ID of the moderator performing the ban. |
| channel_cid | false | string | Channel CID for channel-scoped bans (e.g., messaging:general). Omit for app-wide bans. |
| reason | false | string | Reason for the ban. |
| timeout | false | number | Ban duration in minutes. If not set, the ban is permanent. |
| shadow | false | boolean | When true, the user is shadow banned. Their messages are only visible to themselves. |
| ip_ban | false | boolean | When true, also bans the user's last known IP address. Automatically applies a 30-day timeout unless overridden. |
| delete_messages | false | string | Delete the user's messages. Values: soft (removes from client, retained on server) or hard (permanently deleted). |
Channel-Scoped Ban
Stream Chat only. Ban a user from a specific channel by providing the channel_cid parameter. The user will still be able to use the rest of the app.
client.Moderation().Ban(ctx, &getstream.BanRequest{
TargetUserID: "user_to_ban",
BannedByID: getstream.PtrTo(moderatorId),
ChannelCID: getstream.PtrTo("messaging:general"),
Reason: getstream.PtrTo("Trolling in this channel"),
Timeout: getstream.PtrTo(60),
})Shadow Ban
Shadow banning allows a user to continue posting, but their messages are only visible to themselves. Other users will not see the shadow-banned user's messages. This is useful for handling disruptive users without alerting them.
client.Moderation().Ban(ctx, &getstream.BanRequest{
TargetUserID: "user_to_shadow_ban",
BannedByID: getstream.PtrTo(moderatorId),
Reason: getstream.PtrTo("Disruptive behavior"),
Shadow: getstream.PtrTo(true),
})Unban User
Remove a ban from a user. For channel-scoped bans, provide the channel_cid parameter.
client.Moderation().Unban(ctx, &getstream.UnbanRequest{
TargetUserID: "user_to_unban",
CreatedBy: getstream.PtrTo(moderatorId),
})Query Banned Users
List bans with the metadata that is intentionally not returned by queryUsers — the moderator who issued the ban (banned_by), the original reason, when it expires, and the channel CID for channel-scoped bans. Use this when you need to render a "who banned this user and why" view, build a moderation dashboard, or audit ban history.
queryUsers returns only the ban state (banned, ban_expires) on the user object — banned_by and reason are not part of the user payload by design. Use queryBannedUsers to retrieve them. This endpoint is part of the Chat product API; bans are app-wide, so it works for any app that has Chat enabled regardless of which product applied the ban.
Client-side the endpoint is restricted to a single channel: a request without a channel_cid filter is rejected, so app-wide bans can only be listed server-side. Both regular and shadow bans are returned, and so are bans that have already expired unless exclude_expired_bans is set.
// Client-side the query is always scoped to a channel. The iOS SDK adds the
// channel_cid filter, so anything you pass narrows down that channel's bans.
let channelId = ChannelId(type: .messaging, id: "general")
let chat = chatClient.makeChat(for: channelId)
// All of the channel's bans
let bans = try await chat.queryBannedUsers()
for ban in bans {
// BannedUser: user, bannedBy, cid, createdAt, expiresAt, reason, isShadowBan
print("\(ban.user.id) banned by \(ban.bannedBy?.id ?? "unknown"): \(ban.reason ?? "no reason")")
}
// Bans a specific moderator created, newest first, expired ones left out.
// Filter keys: userId, bannedById, createdAt, reason. Only createdAt is sortable.
let query = BannedUserListQuery(
filter: .equal(.bannedById, to: moderatorId),
sort: [.init(key: .createdAt, isAscending: false)],
pagination: Pagination(pageSize: 20),
excludeExpiredBans: true
)
let recentBans = try await chat.queryBannedUsers(with: query)
// The same query with controllers
chatClient.channelController(for: channelId).queryBannedUsers(with: query) { result in
// Handle the Result<[BannedUser], Error>
}response, err := client.QueryBannedUsers(ctx, &getstream.QueryBannedUsersRequest{
Payload: &getstream.QueryBannedUsersPayload{
FilterConditions: map[string]any{
"user_id": map[string]any{"$eq": "user_to_ban"},
},
Sort: []getstream.SortParamRequest{
{Field: getstream.PtrTo("created_at"), Direction: getstream.PtrTo(-1)},
},
Limit: getstream.PtrTo(20),
ExcludeExpiredBans: getstream.PtrTo(true),
},
})Request parameters
| key | required | type | description |
|---|---|---|---|
| filter_conditions | true | object | Filter conditions. Supported keys include user_id, banned_by_id, channel_cid, reason, created_at, expires. Operators: $eq, $in, $gt, $gte, $lt, $lte. |
| sort | false | array | Sort parameters (max 1). Typically sort by created_at. |
| limit | false | number | Number of records to return (max 300). |
| offset | false | number | Number of records to skip (max 10000). |
| exclude_expired_bans | false | boolean | When true, omits bans whose expires timestamp is in the past. |
| user_id | false | string | Server-side actor making the request. |
Response
| key | type | description |
|---|---|---|
| bans | array | List of bans. Each entry contains the fields described below. |
Each entry in bans:
| key | type | description |
|---|---|---|
| user | object | The banned user. |
| banned_by | object | The moderator who applied the ban (the answer to "who banned this user?"). |
| reason | string | Reason supplied when the ban was created. |
| expires | string | Expiration timestamp for temporary bans. Absent for permanent bans. |
| shadow | bool | true for shadow bans. |
| channel | object | Present for channel-scoped bans; contains the channel CID and metadata. |
| created_at | string | When the ban was applied. |
Block
Blocking only takes effect inside Stream Chat, Feeds, and Video, and it is not a moderation method. Call blockUsers on the common client (client.blockUsers in Node, client.block_users in Python, client.common.block_users in Ruby, client.BlockUsers in Go, client.blockUsers in Java and PHP). The body is blocked_user_id and user_id.
client.BlockUsers(ctx, &getstream.BlockUsersRequest{
BlockedUserID: "user_to_block",
UserID: getstream.PtrTo(blockingUserId),
})Bypass Moderation
Enable or disable moderation bypass for a specific user, allowing them to skip content moderation checks. This is a server-side only operation intended for trusted users whose content should not be subject to automated moderation. Bypass applies to the user's Stream Chat and Feeds content and to Check calls for any entity type, where entity_creator_id is the user. See Bypass Moderation for permission-based bypass in Stream Chat.
client.Moderation().Bypass(ctx, &getstream.BypassRequest{
TargetUserID: "trusted_user_id",
Enabled: true,
})Request parameters
| key | required | type | description |
|---|---|---|---|
| target_user_id | true | string | User ID to enable or disable bypass for. |
| enabled | true | boolean | true to enable bypass, false to disable. |