# Appeals

## 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.

<codetabs>

<codetabs-item value="js" label="Node">

```js
await client.moderation.appeal({
  appeal_reason: "I believe my message was wrongfully blocked",
  entity_id: contentEntityId,
  entity_type: "stream:chat:v1:message",
  attachments: ["imageurl"],
  user_id: userId,
});
```

</codetabs-item>

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

```python
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,
)
```

</codetabs-item>

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

```go
client.Moderation().Appeal(ctx, &getstream.AppealRequest{
    AppealReason: "I believe my message was wrongfully blocked",
    EntityID:     contentEntityId,
    EntityType:   "stream:chat:v1:message",
    Attachments:  []string{"imageurl"},
    UserID:       getstream.PtrTo(userId),
})
```

</codetabs-item>

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

```java
client.moderation().appeal(AppealRequest.builder()
    .appealReason("I believe my message was wrongfully blocked")
    .entityId(contentEntityId)
    .entityType("stream:chat:v1:message")
    .attachments(List.of("imageurl"))
    .userId(userId)
    .build()).execute();
```

</codetabs-item>

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

```php
$client->moderation()->appeal(new AppealRequest(
    appealReason: 'I believe my message was wrongfully blocked',
    entityId: $contentEntityId,
    entityType: 'stream:chat:v1:message',
    attachments: ['imageurl'],
    userId: $userId,
));
```

</codetabs-item>

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

```ruby
client.moderation.appeal(GetStream::Generated::Models::AppealRequest.new(
  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
))
```

</codetabs-item>

<codetabs-item value="dotnet" label=".NET">

```csharp
await client.Moderation.AppealAsync(new AppealRequest
{
    AppealReason = "I believe my message was wrongfully blocked",
    EntityId = contentEntityId,
    EntityType = "stream:chat:v1:message",
    Attachments = new List<string> { "imageurl" },
    UserId = userId,
});
```

</codetabs-item>

</codetabs>

### 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

<codetabs>

<codetabs-item value="js" label="Node">

```js
await client.moderation.appeal({
  appeal_reason: "Appeal to unban me",
  entity_id: userId,
  entity_type: "stream:user",
  attachments: ["imageurl"],
  user_id: userId,
});
```

</codetabs-item>

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

```python
client.moderation().appeal(
    appeal_reason="Appeal to unban me",
    entity_id=user_id,
    entity_type="stream:user",
    attachments=["imageurl"],
    user_id=user_id,
)
```

</codetabs-item>

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

```go
client.Moderation().Appeal(ctx, &getstream.AppealRequest{
    AppealReason: "Appeal to unban me",
    EntityID:     userId,
    EntityType:   "stream:user",
    Attachments:  []string{"imageurl"},
    UserID:       getstream.PtrTo(userId),
})
```

</codetabs-item>

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

```java
client.moderation().appeal(AppealRequest.builder()
    .appealReason("Appeal to unban me")
    .entityId(userId)
    .entityType("stream:user")
    .attachments(List.of("imageurl"))
    .userId(userId)
    .build()).execute();
```

</codetabs-item>

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

```php
$client->moderation()->appeal(new AppealRequest(
    appealReason: 'Appeal to unban me',
    entityId: $userId,
    entityType: 'stream:user',
    attachments: ['imageurl'],
    userId: $userId,
));
```

</codetabs-item>

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

```ruby
client.moderation.appeal(GetStream::Generated::Models::AppealRequest.new(
  appeal_reason: "Appeal to unban me",
  entity_id: user_id,
  entity_type: "stream:user",
  attachments: ["imageurl"],
  user_id: user_id
))
```

</codetabs-item>

<codetabs-item value="dotnet" label=".NET">

```csharp
await client.Moderation.AppealAsync(new AppealRequest
{
    AppealReason = "Appeal to unban me",
    EntityId = userId,
    EntityType = "stream:user",
    Attachments = new List<string> { "imageurl" },
    UserId = userId,
});
```

</codetabs-item>

</codetabs>

## 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.

<codetabs>

<codetabs-item value="js" label="Node">

```js
await client.moderation.queryAppeals({
  filter: {
    id: { $eq: "appeal_id" },
  },
  sort: [{ field: "created_at", direction: -1 }],
  limit: 20,
});
```

</codetabs-item>

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

```python
client.moderation().query_appeals(
    filter={"id": {"$eq": "appeal_id"}},
    sort=[{"field": "created_at", "direction": -1}],
    limit=20,
)
```

</codetabs-item>

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

```go
client.Moderation().QueryAppeals(ctx, &getstream.QueryAppealsRequest{
    Filter: map[string]interface{}{
        "id": map[string]interface{}{"$eq": "appeal_id"},
    },
    Sort: []getstream.SortParam{
        {Field: "created_at", Direction: getstream.PtrTo(-1)},
    },
    Limit: getstream.PtrTo(20),
})
```

</codetabs-item>

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

```java
client.moderation().queryAppeals(QueryAppealsRequest.builder()
    .filter(Map.of("id", Map.of("$eq", "appeal_id")))
    .sort(List.of(SortParam.builder().field("created_at").direction(-1).build()))
    .limit(20)
    .build()).execute();
```

</codetabs-item>

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

```php
$client->moderation()->queryAppeals(new QueryAppealsRequest(
    filter: ['id' => ['$eq' => 'appeal_id']],
    sort: [new SortParam(field: 'created_at', direction: -1)],
    limit: 20,
));
```

</codetabs-item>

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

```ruby
client.moderation.query_appeals(GetStream::Generated::Models::QueryAppealsRequest.new(
  filter: { "id" => { "$eq" => "appeal_id" } },
  sort: [GetStream::Generated::Models::SortParam.new(field: "created_at", direction: -1)],
  limit: 20
))
```

</codetabs-item>

<codetabs-item value="dotnet" label=".NET">

```csharp
await client.Moderation.QueryAppealsAsync(new QueryAppealsRequest
{
    Filter = new Dictionary<string, object>
    {
        { "id", new Dictionary<string, object> { { "$eq", "appeal_id" } } }
    },
    Sort = new List<SortParam>
    {
        new SortParam { Field = "created_at", Direction = -1 }
    },
    Limit = 20,
});
```

</codetabs-item>

</codetabs>

### 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.

<codetabs>

<codetabs-item value="js" label="Node">

```js
await client.moderation.getAppeal({ id: "appeal_id" });
```

</codetabs-item>

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

```python
client.moderation().get_appeal("appeal_id")
```

</codetabs-item>

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

```go
client.Moderation().GetAppeal(ctx, "appeal_id", &getstream.GetAppealRequest{})
```

</codetabs-item>

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

```java
client.moderation().getAppeal("appeal_id").execute();
```

</codetabs-item>

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

```php
$client->moderation()->getAppeal('appeal_id');
```

</codetabs-item>

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

```ruby
client.moderation.get_appeal("appeal_id")
```

</codetabs-item>

<codetabs-item value="dotnet" label=".NET">

```csharp
await client.Moderation.GetAppealAsync("appeal_id");
```

</codetabs-item>

</codetabs>

### 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 ID
- `user`: User who created the appeal
- `entity_type`: Type of entity being appealed
- `entity_id`: ID of the entity being appealed
- `appeal_reason`: Appeal Reason/explanation
- `status`: Appeal status (`submitted`, `accepted`, `rejected`)
- `decision_reason`: Reason provided by moderator (if decided)
- `attachments`: Array of attachment URLs
- `created_at`: When the appeal was created
- `updated_at`: When the appeal was last updated

## Moderation Actions and Appeals

The [Submit Action](/moderation/docs/<framework>/content-moderation/review-queue/#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 user
- `mark_reviewed`: Marking an item as reviewed
- `restore`: Restoring deleted content
- `unblock`: Unblocking blocked content

When performing these actions, you can optionally provide a `decision_reason` to explain why the appeal was accepted:

<codetabs>

<codetabs-item value="js" label="Node">

```js
// 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,
});
```

</codetabs-item>

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

```python
# Approve appeal while unbanning user
client.moderation().submit_action(
    action_type="unban",
    appeal_id="appeal_id",
    unban={"decision_reason": "I am unbanning a user"},
    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,
)
```

</codetabs-item>

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

```go
// Approve appeal while unbanning user
client.Moderation().SubmitAction(ctx, &getstream.SubmitActionRequest{
    ActionType: "unban",
    AppealID:   getstream.PtrTo("appeal_id"),
    Unban: &getstream.UnbanActionRequest{
        DecisionReason: getstream.PtrTo("I am unbanning a user"),
    },
    UserID: getstream.PtrTo(moderatorId),
})

// Approve appeal while unblocking content
client.Moderation().SubmitAction(ctx, &getstream.SubmitActionRequest{
    ActionType:     "unblock",
    ItemID:         getstream.PtrTo("item_id"),
    Unblock:        &getstream.UnblockActionRequest{},
    DecisionReason: getstream.PtrTo("I am unblocking content"),
    UserID:         getstream.PtrTo(moderatorId),
})
```

</codetabs-item>

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

```java
// Approve appeal while unbanning user
client.moderation().submitAction(SubmitActionRequest.builder()
    .actionType("unban")
    .appealId("appeal_id")
    .unban(UnbanActionRequest.builder()
        .decisionReason("I am unbanning a user")
        .build())
    .userId(moderatorId)
    .build()).execute();

// Approve appeal while unblocking content
client.moderation().submitAction(SubmitActionRequest.builder()
    .actionType("unblock")
    .itemId("item_id")
    .unblock(UnblockActionRequest.builder().build())
    .decisionReason("I am unblocking content")
    .userId(moderatorId)
    .build()).execute();
```

</codetabs-item>

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

```php
// Approve appeal while unbanning user
$client->moderation()->submitAction(new SubmitActionRequest(
    actionType: 'unban',
    appealId: 'appeal_id',
    unban: new UnbanActionRequest(
        decisionReason: 'I am unbanning a user',
    ),
    userId: $moderatorId,
));

// Approve appeal while unblocking content
$client->moderation()->submitAction(new SubmitActionRequest(
    actionType: 'unblock',
    itemId: 'item_id',
    unblock: new UnblockActionRequest(),
    decisionReason: 'I am unblocking content',
    userId: $moderatorId,
));
```

</codetabs-item>

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

```ruby
# Approve appeal while unbanning user
client.moderation.submit_action(GetStream::Generated::Models::SubmitActionRequest.new(
  action_type: "unban",
  appeal_id: "appeal_id",
  unban: GetStream::Generated::Models::UnbanActionRequest.new(
    decision_reason: "I am unbanning a user"
  ),
  user_id: moderator_id
))

# Approve appeal while unblocking content
client.moderation.submit_action(GetStream::Generated::Models::SubmitActionRequest.new(
  action_type: "unblock",
  item_id: "item_id",
  unblock: GetStream::Generated::Models::UnblockActionRequest.new,
  decision_reason: "I am unblocking content",
  user_id: moderator_id
))
```

</codetabs-item>

<codetabs-item value="dotnet" label=".NET">

```csharp
// Approve appeal while unbanning user
await client.Moderation.SubmitActionAsync(new SubmitActionRequest
{
    ActionType = "unban",
    AppealId = "appeal_id",
    Unban = new UnbanActionRequest
    {
        DecisionReason = "I am unbanning a user",
    },
    UserId = moderatorId,
});

// Approve appeal while unblocking content
await client.Moderation.SubmitActionAsync(new SubmitActionRequest
{
    ActionType = "unblock",
    ItemId = "item_id",
    Unblock = new UnblockActionRequest(),
    DecisionReason = "I am unblocking content",
    UserId = moderatorId,
});
```

</codetabs-item>

</codetabs>

### Rejecting Appeals

To reject an appeal, use the `reject_appeal` action type:

<codetabs>

<codetabs-item value="js" label="Node">

```js
await client.moderation.submitAction({
  action_type: "reject_appeal",
  appeal_id: "appeal_id",
  reject_appeal: {
    decision_reason: "I am rejecting your appeal",
  },
  user_id: moderatorId,
});
```

</codetabs-item>

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

```python
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,
)
```

</codetabs-item>

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

```go
client.Moderation().SubmitAction(ctx, &getstream.SubmitActionRequest{
    ActionType: "reject_appeal",
    AppealID:   getstream.PtrTo("appeal_id"),
    RejectAppeal: &getstream.RejectAppealActionRequest{
        DecisionReason: getstream.PtrTo("I am rejecting your appeal"),
    },
    UserID: getstream.PtrTo(moderatorId),
})
```

</codetabs-item>

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

```java
client.moderation().submitAction(SubmitActionRequest.builder()
    .actionType("reject_appeal")
    .appealId("appeal_id")
    .rejectAppeal(RejectAppealActionRequest.builder()
        .decisionReason("I am rejecting your appeal")
        .build())
    .userId(moderatorId)
    .build()).execute();
```

</codetabs-item>

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

```php
$client->moderation()->submitAction(new SubmitActionRequest(
    actionType: 'reject_appeal',
    appealId: 'appeal_id',
    rejectAppeal: new RejectAppealActionRequest(
        decisionReason: 'I am rejecting your appeal',
    ),
    userId: $moderatorId,
));
```

</codetabs-item>

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

```ruby
client.moderation.submit_action(GetStream::Generated::Models::SubmitActionRequest.new(
  action_type: "reject_appeal",
  appeal_id: "appeal_id",
  reject_appeal: GetStream::Generated::Models::RejectAppealActionRequest.new(
    decision_reason: "I am rejecting your appeal"
  ),
  user_id: moderator_id
))
```

</codetabs-item>

<codetabs-item value="dotnet" label=".NET">

```csharp
await client.Moderation.SubmitActionAsync(new SubmitActionRequest
{
    ActionType = "reject_appeal",
    AppealId = "appeal_id",
    RejectAppeal = new RejectAppealActionRequest
    {
        DecisionReason = "I am rejecting your appeal",
    },
    UserId = moderatorId,
});
```

</codetabs-item>

</codetabs>

### 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 review
- `accepted`: The appeal has been accepted by a moderator
- `rejected`: 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](/moderation/docs/<framework>/content-moderation/review-queue/#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

<codetabs>

<codetabs-item value="js" label="Node">

```js
// Query items with submitted appeals
await client.moderation.queryReviewQueue({
  filter: {
    appeal: true,
    appeal_status: "submitted",
  },
  sort: [{ field: "created_at", direction: -1 }],
  limit: 20,
});

// Query items with a specific appeal
await client.moderation.queryReviewQueue({
  filter: {
    appeal_id: { $eq: "appeal_id" },
  },
  sort: [{ field: "created_at", direction: -1 }],
  limit: 20,
});
```

</codetabs-item>

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

```python
# Query items with submitted appeals
client.moderation().query_review_queue(
    filter={"appeal": True, "appeal_status": "submitted"},
    sort=[{"field": "created_at", "direction": -1}],
    limit=20,
)

# Query items with a specific appeal
client.moderation().query_review_queue(
    filter={"appeal_id": {"$eq": "appeal_id"}},
    sort=[{"field": "created_at", "direction": -1}],
    limit=20,
)
```

</codetabs-item>

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

```go
// Query items with submitted appeals
client.Moderation().QueryReviewQueue(ctx, &getstream.QueryReviewQueueRequest{
    Filter: map[string]interface{}{
        "appeal":        true,
        "appeal_status": "submitted",
    },
    Sort: []getstream.SortParam{
        {Field: "created_at", Direction: getstream.PtrTo(-1)},
    },
    Limit: getstream.PtrTo(20),
})

// Query items with a specific appeal
client.Moderation().QueryReviewQueue(ctx, &getstream.QueryReviewQueueRequest{
    Filter: map[string]interface{}{
        "appeal_id": map[string]interface{}{"$eq": "appeal_id"},
    },
    Sort: []getstream.SortParam{
        {Field: "created_at", Direction: getstream.PtrTo(-1)},
    },
    Limit: getstream.PtrTo(20),
})
```

</codetabs-item>

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

```java
// Query items with submitted appeals
client.moderation().queryReviewQueue(QueryReviewQueueRequest.builder()
    .filter(Map.of("appeal", true, "appeal_status", "submitted"))
    .sort(List.of(SortParam.builder().field("created_at").direction(-1).build()))
    .limit(20)
    .build()).execute();

// Query items with a specific appeal
client.moderation().queryReviewQueue(QueryReviewQueueRequest.builder()
    .filter(Map.of("appeal_id", Map.of("$eq", "appeal_id")))
    .sort(List.of(SortParam.builder().field("created_at").direction(-1).build()))
    .limit(20)
    .build()).execute();
```

</codetabs-item>

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

```php
// Query items with submitted appeals
$client->moderation()->queryReviewQueue(new QueryReviewQueueRequest(
    filter: ['appeal' => true, 'appeal_status' => 'submitted'],
    sort: [new SortParam(field: 'created_at', direction: -1)],
    limit: 20,
));

// Query items with a specific appeal
$client->moderation()->queryReviewQueue(new QueryReviewQueueRequest(
    filter: ['appeal_id' => ['$eq' => 'appeal_id']],
    sort: [new SortParam(field: 'created_at', direction: -1)],
    limit: 20,
));
```

</codetabs-item>

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

```ruby
# Query items with submitted appeals
client.moderation.query_review_queue(GetStream::Generated::Models::QueryReviewQueueRequest.new(
  filter: { "appeal" => true, "appeal_status" => "submitted" },
  sort: [GetStream::Generated::Models::SortParam.new(field: "created_at", direction: -1)],
  limit: 20
))

# Query items with a specific appeal
client.moderation.query_review_queue(GetStream::Generated::Models::QueryReviewQueueRequest.new(
  filter: { "appeal_id" => { "$eq" => "appeal_id" } },
  sort: [GetStream::Generated::Models::SortParam.new(field: "created_at", direction: -1)],
  limit: 20
))
```

</codetabs-item>

<codetabs-item value="dotnet" label=".NET">

```csharp
// Query items with submitted appeals
await client.Moderation.QueryReviewQueueAsync(new QueryReviewQueueRequest
{
    Filter = new Dictionary<string, object>
    {
        { "appeal", true },
        { "appeal_status", "submitted" }
    },
    Sort = new List<SortParam>
    {
        new SortParam { Field = "created_at", Direction = -1 }
    },
    Limit = 20,
});

// Query items with a specific appeal
await client.Moderation.QueryReviewQueueAsync(new QueryReviewQueueRequest
{
    Filter = new Dictionary<string, object>
    {
        { "appeal_id", new Dictionary<string, object> { { "$eq", "appeal_id" } } }
    },
    Sort = new List<SortParam>
    {
        new SortParam { Field = "created_at", Direction = -1 }
    },
    Limit = 20,
});
```

</codetabs-item>

</codetabs>

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:

1. **User submits an appeal** against deleted content:

```jsx
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);
```

2. **Moderator queries appeals** to see pending appeals:

```jsx
const appeals = await client.moderation.queryAppeals({
  filter: {
    status: "submitted",
  },
  sort: [{ field: "created_at", direction: -1 }],
  limit: 50,
});
```

3. **Moderator reviews the appeal** and decides to accept it by restoring the content:

```jsx
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 },
);
```

4. **User queries their appeals** to see the decision:

```jsx
const myAppeals = await client.moderation.queryAppeals(
  {
    filter: {},
    sort: [{ field: "created_at", direction: -1 }],
    limit: 20,
  },
  { user_id: userId },
);
```

5. **User gets specific appeal details**:

```jsx
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

1. **Appeal Reason**: Provide clear and concise explanations in the appeal text (max 500 characters). This helps moderators understand the context quickly.

2. **Attachments**: Use attachments to provide evidence supporting your appeal (e.g., screenshots, context about why content was flagged incorrectly).

3. **Decision Reasons**: When moderators accept or reject appeals, always provide a `decision_reason` to help users understand the decision.

4. **Filtering**: Use appeal status filters in Query Review Queue to organize your moderation dashboard effectively, separating items with pending appeals from other items.

5. **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.


---

This page was last updated at 2026-04-16T18:29:44.203Z.

For the most recent version of this documentation, visit [https://getstream.io/moderation/docs/go-golang/content-moderation/appeals/](https://getstream.io/moderation/docs/go-golang/content-moderation/appeals/).