await client.moderation.appeal({
appealReason: "I believe my message was wrongfully blocked",
entityID: contentEntityId,
entityType: "stream:chat:v1:message",
attachments: [
"imageurl",
],
}, { user_id: userId });Appeal API
Overview
The Appeal API allows users to submit appeals against moderation decisions and enables moderators to review and decide on those appeals. Appeals can be submitted against review queue items (such as deleted messages, activities, or reactions) or against user bans (both global and channel-specific bans).
Submit Appeal
This endpoint allows users to submit an appeal against a moderation decision. You can appeal against:
- Deleted Content
- Blocked content
- User Bans: Appeals against user bans (both global bans and channel-specific bans)
For each appeal, there must be an associated review queue item or a user ban. When appealing against content, only the user who created the content can submit an appeal. When appealing against a user ban, only the banned user can submit an appeal.
client.moderation.appeal(
appeal_reason: "I believe my message was wrongfully blocked",
entity_id: content_entity_id,
entity_type: "stream:chat:v1:message",
attachments: [
"imageurl",
],
user_id: user_id
)Request Parameters
| key | required | type | description |
|---|---|---|---|
| entity_id | true | string | Unique identifier of the entity being appealed. For user bans, this should be the user ID. For content, this should be the message/activity/reaction ID. |
| entity_type | true | string | Type of entity being appealed. Examples: stream:chat:v1:message, stream:feeds:v2:activity, stream:feeds:v2:reaction, stream:user (for user bans) |
| appeal_reason | true | string | Explanation for why the content is being appealed. Maximum 500 characters. |
| attachments | false | array | Array of attachment URLs (e.g., images) providing evidence for the appeal. Maximum 10 attachments. |
Response
| key | type | description |
|---|---|---|
| appeal_id | string | Unique identifier of the created appeal item. |
Appeal Rules and Restrictions
Appeals Against Content (Messages, Activities, Reactions, Comments)
- Only the user who created the entity can appeal against actions taken on it
- You cannot appeal if the item is unreviewed and just flagged (unless the recommended action is “remove”)
- You cannot appeal against shadow-banned content
- You cannot appeal if the content is not deleted (for reviewed items)
- You cannot appeal if an appeal already exists for the item
Appeals Against User Bans
- Only the banned user can appeal against their own ban
- The user must be banned (globally or in at least one channel) to submit an appeal
- You cannot submit a new appeal if a submitted appeal already exists for the user
Example: Appeal Against User Ban
await client.moderation.appeal({
appealReason: "Appeal to unban me",
entityID: userId,
entityType: "stream:user",
attachments: [
"imageurl",
],
}, { user_id: userId });client.moderation.appeal(
appeal_reason: "Appeal to unban me",
entity_id: user_id,
entity_type: "stream:user",
attachments: [
"imageurl",
],
user_id: user_id
)Query Appeals
This endpoint allows you to query appeals with filters, sorting, and pagination. When used from the client-side, querying appeals is automatically limited to appeals created by the authenticated user.
await client.moderation.queryAppeals({
filter: {
id: { $eq: "appeal_id" },
},
sort: [{ field: "created_at", direction: -1 }],
limit: 20,
});client.moderation.query_appeals(
filter: { id: { $eq: "appeal_id" } },
sort: { created_at: -1 },
limit: 20
)Request Parameters
| key | required | type | description |
|---|---|---|---|
| filter | false | object | Filter conditions for appeals (see below) |
| sort | false | array | Sorting parameters for appeals |
| limit | false | number | Maximum number of appeals to return |
| next | false | string | Cursor for pagination (from previous request) |
Supported Filters
You can filter appeals by the following fields:
| field | type | description |
|---|---|---|
| id | string | Filter by specific appeal ID |
| user_id | string | Filter by user ID who created the appeal |
| entity_type | string | Filter by entity type (e.g., stream:chat:v1:message) |
| entity_id | string | Filter by entity ID |
| status | string | Filter by appeal status: submitted, accepted, rejected |
| created_at | datetime | Filter by creation date |
| updated_at | datetime | Filter by last update date |
Response
| key | type | description |
|---|---|---|
| items | array | List of appeal items |
| next | string | Next cursor for pagination (if any) |
Get Appeal
This endpoint allows you to retrieve a specific appeal item by its ID. When used from the client-side, users can only retrieve their own appeals.
await client.moderation.getAppeal("appealId");client.moderation.get_appeal("appeal_id")Request Parameters
| key | required | type | description |
|---|---|---|---|
| id | true | string | Unique identifier of the appeal |
Response
| key | type | description |
|---|---|---|
| item | object | Appeal item with full details |
The appeal item includes:
id: Appeal IDuser: User who created the appealentity_type: Type of entity being appealedentity_id: ID of the entity being appealedappeal_reason: Appeal Reason/explanationstatus: Appeal status (submitted,accepted,rejected)decision_reason: Reason provided by moderator (if decided)attachments: Array of attachment URLscreated_at: When the appeal was createdupdated_at: When the appeal was last updated
Moderation Actions and Appeals
The Submit Action endpoint can be used to accept or reject appeals when taking moderation actions. Most moderation actions automatically accept an appeal if one exists for the review queue item.
Accepting Appeals
The following actions automatically accept an appeal if one exists:
unban: Unbanning a usermark_reviewed: Marking an item as reviewedrestore: Restoring deleted contentunblock: Unblocking blocked content
When performing these actions, you can optionally provide a decision_reason to explain why the appeal was accepted:
// Approve appeal while unbanning user
await client.moderation.submitAction({
action_type: "unban",
appeal_id: "appeal_id",
unban: {
decision_reason: "I am unbanning a user",
// channel_cids: ["cid1", "cid2"], // optional: for channel-specific unbans
},
}, { user_id: moderatorId });
// Approve appeal while unblocking content
await client.moderation.submitAction({
action_type: "unblock",
item_id: "item_id",
unblock: {},
decision_reason: "I am unblocking content",
}, { user_id: moderatorId });# Approve appeal while unbanning user
client.moderation.submit_action(
action_type: "unban",
appeal_id: "appeal_id",
unban: {
decision_reason: "I am unbanning a user",
# channel_cids: ["cid1", "cid2"], # optional: for channel-specific unbans
},
user_id: moderator_id
)
# Approve appeal while unblocking content
client.moderation.submit_action(
action_type: "unblock",
item_id: "item_id",
unblock: {},
decision_reason: "I am unblocking content",
user_id: moderator_id
)Rejecting Appeals
To reject an appeal, use the reject_appeal action type:
await client.moderation.submitAction({
action_type: "reject_appeal",
appeal_id: "appeal_id",
reject_appeal: {
decision_reason: "I am rejecting your appeal",
},
}, { user_id: moderatorId });client.moderation.submit_action(
action_type: "reject_appeal",
appeal_id: "appeal_id",
reject_appeal: {
decision_reason: "I am rejecting your appeal",
},
user_id: moderator_id
)Request Parameters for Appeal Actions
| key | required | type | description |
|---|---|---|---|
| action_type | true | string | Type of action: unban, restore, unblock, mark_reviewed, or reject_appeal |
| item_id | false | string | Review queue item ID (required for most actions, except reject_appeal and unban without review queue item) |
| appeal_id | false | string | Appeal ID (required for reject_appeal, optional for other actions - will be inferred from item_id if not provided) |
| decision_reason | false | string | Reason for the decision (shown to the user who submitted the appeal) |
| action_params | false | object | Action-specific parameters (e.g., unban, restore, unblock, reject_appeal) |
Appeal Status Values
Appeals can have the following statuses:
submitted: The appeal has been submitted and is awaiting reviewaccepted: The appeal has been accepted by a moderatorrejected: The appeal has been rejected by a moderator
The decided_by field is automatically populated with the moderator’s user ID when an appeal is accepted or rejected. This field is not shown to users but is tracked internally for audit purposes.
Filter by Appeal in Query Review Queue
The Query Review Queue endpoint supports filtering by appeal-related fields. This allows you to:
- Filter review queue items that have appeals
- Filter by appeal status
- Filter by specific appeal ID
This is particularly useful for implementing moderation dashboards where you want to show items with pending appeals separately from other items.
Appeal Filters in Query Review Queue
| filter | type | description |
|---|---|---|
| appeal | boolean | Filter items that have an appeal (true) or don’t have an appeal (false) |
| appeal_status | string | Filter items by appeal status: submitted, accepted, rejected |
| appeal_id | string | Filter items by specific appeal ID |
Example: Query Review Queue Items with Appeals
// Query items with submitted appeals
await client.moderation.queryReviewQueue(
{
appeal: true,
appeal_status: "submitted",
},
[{ field: "created_at", direction: -1 }],
{ limit: 20 }
);
// Query items with a specific appeal
await client.moderation.queryReviewQueue(
{
appeal_id: { $eq: "appeal_id" },
},
[{ field: "created_at", direction: -1 }],
{ limit: 20 }
);# Query items with submitted appeals
client.moderation.query_review_queue(
{ appeal: true, appeal_status: "submitted" },
{ created_at: -1 },
limit: 20
)
# Query items with a specific appeal
client.moderation.query_review_queue(
{ appeal_id: { $eq: "appeal_id" } },
{ created_at: -1 },
limit: 20
)Instead of moving review queue items from “Reviewed” to “Inbox” or vice versa, you can query review queue items with appeal status filters. This approach avoids potential problems that could arise from changing the status of review queue items and provides a more flexible way to organize your moderation workflow.
Complete Workflow Example
Here’s a complete example of the appeal workflow:
- User submits an appeal against deleted content:
const appealResponse = await client.moderation.appeal({
appealReason: "This message was deleted by mistake. Please restore it.",
entityID: "message_id",
entityType: "stream:chat:v1:message",
attachments: [],
}, { user_id: userId });
console.log("Appeal ID:", appealResponse.appeal_id);- Moderator queries appeals to see pending appeals:
const appeals = await client.moderation.queryAppeals({
filter: {
status: "submitted",
},
sort: [{ field: "created_at", direction: -1 }],
limit: 50,
});- Moderator reviews the appeal and decides to accept it by restoring the content:
await client.moderation.submitAction({
action_type: "restore",
item_id: "review_queue_item_id",
restore: {},
decision_reason: "The content was deleted by mistake. Appeal accepted.",
}, { user_id: moderatorId });- User queries their appeals to see the decision:
const myAppeals = await client.moderation.queryAppeals({
filter: {},
sort: [{ field: "created_at", direction: -1 }],
limit: 20,
}, { user_id: userId });- User gets specific appeal details:
const appeal = await client.moderation.getAppeal(appealResponse.appeal_id, { user_id: userId });
console.log("Appeal status:", appeal.item.status);
console.log("Decision reason:", appeal.item.decision_reason);Best Practices
Appeal Reason: Provide clear and concise explanations in the appeal text (max 500 characters). This helps moderators understand the context quickly.
Attachments: Use attachments to provide evidence supporting your appeal (e.g., screenshots, context about why content was flagged incorrectly).
Decision Reasons: When moderators accept or reject appeals, always provide a
decision_reasonto help users understand the decision.Filtering: Use appeal status filters in Query Review Queue to organize your moderation dashboard effectively, separating items with pending appeals from other items.
Client-Side Restrictions: Remember that client-side requests automatically filter appeals to only show appeals created by the authenticated user. Use server-side requests for moderator dashboards that need to see all appeals.