# Custom Views

## Overview

A custom view is a saved set of [review queue](https://getstream.io/moderation/docs/go-golang/content-moderation/review-queue/) filters and sort order, stored server-side under a name. Instead of re-applying the same filters at the start of every shift, a moderator opens the view and lands on exactly the slice of the queue they own.

Views come in two kinds:

| Type              | Wire value          | Dashboard label         | Who can see it                                                       |
| ----------------- | ------------------- | ----------------------- | -------------------------------------------------------------------- |
| Personal view     | `personal_view`     | Personal (only you)     | Only the moderator who created it, plus users who can manage queues. |
| Operational queue | `operational_queue` | Shared (all moderators) | Every moderator in the app.                                          |

Personal views serve one moderator's own workflow. A moderator who owns image review keeps a view filtered to `has_image: true` with `category: ai_image`, while someone working appeals keeps one on `appeal_status: submitted`. Neither has to rebuild those filters at the start of a shift.

Operational queues serve team structure. A Trust & Safety lead defines "User-reported harassment" (`reporter_type: user`, `label: harassment`) and "Automod removals to spot-check" (`reporter_type: automod`, `recommended_action: remove`) once, and the whole team works out of the same two surfaces instead of each moderator filtering by hand.

<Admonition type="info">

Custom views are in beta and require moderation v2 to be enabled on your app. The endpoints below are the same ones the Stream dashboard uses.

</Admonition>

## Managing Views in the Dashboard

Most teams never call these endpoints directly. Open the [Stream Dashboard](https://getstream.io/signin/?product=moderation), go to **Moderation > Content Queue**, apply the filters you want, then save them as a view.

The save dialog asks for a name and a visibility: **Personal (only you)** or **Shared (all moderators)**. The shared option is only selectable for users whose role can manage operational queues.

Saved views appear in the sidebar with an item count next to each one.

## Create a View

Creating a view stores the filters, sort, name, and description. The `type` field decides whether the view is private to the caller or shared with the team.

`POST /api/v2/moderation/queues`

```go label="Go"
response, err := client.Moderation().CreateQueue(ctx, &getstream.CreateQueueRequest{
    Name:        "Flagged images",
    Type:        "personal_view",
    Description: getstream.PtrTo("Unreviewed image content flagged by the AI image engine"),
    UserID:      getstream.PtrTo("moderator-1"),
    Filters: getstream.PtrTo(map[string]any{
        "entity_type": "stream:chat:v1:message",
        "has_image":   true,
        "category":    "ai_image",
    }),
    Sort: getstream.PtrTo([]map[string]any{
        {"field": "created_at", "direction": -1},
    }),
})
```

### Request Parameters

| Key         | Required | Type   | Description                                                                                         |
| ----------- | -------- | ------ | --------------------------------------------------------------------------------------------------- |
| name        | true     | string | Display name for the view. Max 255 characters.                                                      |
| type        | true     | string | `personal_view` or `operational_queue`.                                                             |
| description | false    | string | Free-text description shown alongside the view. Max 512 characters.                                 |
| filters     | false    | object | Review queue filter conditions. Accepts the same keys as the `filter` object on Query Review Queue. |
| sort        | false    | array  | Sort parameters. Supported fields: `id`, `created_at`, `updated_at`.                                |
| user_id     | false    | string | The acting moderator. Required on server-side requests, ignored on client-side requests.            |

### Response

| Key   | Type   | Description                                     |
| ----- | ------ | ----------------------------------------------- |
| queue | object | The created view, including its generated `id`. |

## List Views

Returns every view visible to the calling moderator: their own personal views, plus all operational queues. Users whose role can manage operational queues additionally see other moderators' personal views, so views left behind by a departed moderator stay manageable.

`GET /api/v2/moderation/queues`

```go label="Go"
response, err := client.Moderation().ListQueues(ctx, &getstream.ListQueuesRequest{
    UserID: getstream.PtrTo("moderator-1"),
})
```

### Request Parameters

| Key     | Required | Type   | Description                                                                          |
| ------- | -------- | ------ | ------------------------------------------------------------------------------------ |
| user_id | false    | string | The acting moderator, passed as a query parameter. Required on server-side requests. |

### Response

| Key    | Type  | Description                                                         |
| ------ | ----- | ------------------------------------------------------------------- |
| queues | array | Views visible to the calling moderator, each with its `item_count`. |

## Get a View

`GET /api/v2/moderation/queues/{id}`

```go label="Go"
response, err := client.Moderation().GetQueue(ctx, "queue-id", &getstream.GetQueueRequest{
    UserID: getstream.PtrTo("moderator-1"),
})
```

### Request Parameters

| Key     | Required | Type   | Description                                        |
| ------- | -------- | ------ | -------------------------------------------------- |
| id      | true     | string | The view ID, in the path.                          |
| user_id | false    | string | The acting moderator, passed as a query parameter. |

## Update a View

Updates are partial: omitted fields are left unchanged. The `type` field cannot be changed after creation. To move a personal view to a shared queue, create a new operational queue with the same filters and delete the old one.

`PATCH /api/v2/moderation/queues/{id}`

```go label="Go"
response, err := client.Moderation().UpdateQueue(ctx, "queue-id", &getstream.UpdateQueueRequest{
    Name:   getstream.PtrTo("Flagged images (EU)"),
    UserID: getstream.PtrTo("moderator-1"),
    Filters: getstream.PtrTo(map[string]any{
        "entity_type": "stream:chat:v1:message",
        "has_image":   true,
        "category":    "ai_image",
        "label":       "nudity",
    }),
})
```

### Request Parameters

| Key         | Required | Type   | Description                                                        |
| ----------- | -------- | ------ | ------------------------------------------------------------------ |
| id          | true     | string | The view ID, in the path.                                          |
| name        | false    | string | New display name. Max 255 characters.                              |
| description | false    | string | New description. Max 512 characters.                               |
| filters     | false    | object | Replaces the stored filters entirely. This is not a per-key merge. |
| sort        | false    | array  | Replaces the stored sort order entirely.                           |
| user_id     | false    | string | The acting moderator. Required on server-side requests.            |

A personal view can only be updated by the moderator who created it, or by a user whose role can manage operational queues.

## Delete a View

Deleting a view is a soft delete: the view stops appearing in listings. Review queue items are never touched, because a view is only a saved filter over them.

Note that delete is a `POST`, not an HTTP `DELETE`.

`POST /api/v2/moderation/queues/{id}/delete`

```go label="Go"
response, err := client.Moderation().DeleteQueue(ctx, "queue-id", &getstream.DeleteQueueRequest{
    UserID: getstream.PtrTo("moderator-1"),
})
```

### Request Parameters

| Key     | Required | Type   | Description                                             |
| ------- | -------- | ------ | ------------------------------------------------------- |
| id      | true     | string | The view ID, in the path.                               |
| user_id | false    | string | The acting moderator. Required on server-side requests. |

As with updates, a personal view can only be deleted by its creator or by a user who can manage operational queues.

## Filters

The `filters` object accepts the same keys as the `filter` object on [Query Review Queue](https://getstream.io/moderation/docs/go-golang/content-moderation/review-queue/), including `entity_type`, `entity_creator_id`, `category`, `label`, `recommended_action`, `reporter_type`, `has_image`, and `appeal_status`. See that page for the full list.

Note the field name is `filters` here, plural, while Query Review Queue takes a singular `filter`.

Three keys are dropped when a view is saved, even if you send them:

| Key           | Why it is dropped                                                                                  |
| ------------- | -------------------------------------------------------------------------------------------------- |
| `reviewed`    | Controlled at read time by the Inbox and Reviewed tabs, so it has no meaning as stored state.      |
| `archived_at` | Also a read-time toggle, for the same reason.                                                      |
| `date_range`  | A saved absolute date range goes stale immediately. Pick the range when you open the view instead. |

This is why a view saved from the **Reviewed** tab still opens on **Inbox**. The tab is not part of what gets stored.

## Item Counts

Each view carries an `item_count`. Counts are computed by a background task and served from cache, so a freshly created view returns a count computed inline on first read, while an existing view's count can lag behind the live queue by a short interval.

Treat `item_count` as an indicator of queue depth, not as a precise number to reconcile against.

## Limits and Permissions

| Constraint                            | Value                                                                                                |
| ------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| Views per app                         | 10 by default, counting personal views and operational queues together. Contact support to raise it. |
| Create or read a personal view        | Requires the `QueryModerationReviewQueue` permission.                                                |
| Create, update, delete a shared queue | Requires the `ManageOperationalQueues` permission.                                                   |

Creating a view past the per-app cap returns a `400` naming the limit. Attempting to modify someone else's personal view without permission to manage queues returns a `403`.

---

This page was last updated at 2026-09-02T15:00:34.798Z.

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