> ## Documentation Index
> Fetch the complete documentation index at: https://api.fanvue.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Build dynamic lists

> Define, preview and save a creator's dynamic lists on the v1 Fanvue API, read their members, and target them in a mass message.

A dynamic list is a saved filter over relationship, spend, engagement and memory dimensions, re-evaluated every time it's read. That makes it different from the built-in smart lists and from custom lists, which the creator fills by hand. By the end of this page you'll have previewed a definition, saved it as a list, read its members and sent a mass message to it.

Available to creators who have dynamic lists enabled; calls from any other account return `403`. You also need an access token with the scopes below.

The endpoints exist on [v1](/docs/v1/api-reference/overview) only, under `/v1/chats/lists/dynamic*`, with a creator-scoped twin under `/v1/creators/{creatorUserUuid}/chats/lists/dynamic*` for agencies. Spend values in a definition are USD cents.

## Scopes

| Endpoints | Scopes |
| - | - |
| Dimensions, memory topics, previews, list, detail, members | `read:chat` and `read:fan` |
| Create, update, delete | `write:chat` |
| Translate | `write:chat` and `read:fan` |
| Any creator-scoped twin | `read:creator` in addition |

## The definition

A definition has `version`, `match` (always `"any"`), one or more `groups` each holding one or more `conditions`, and an optional `exclude` array of conditions. A condition is `{ dimension, operator, value }`, and the dimensions manifest in the next section tells you which dimensions exist and which operators and value types each accepts.

```json theme={null}
{
  "version": 1,
  "match": "any",
  "groups": [
    { "conditions": [{ "dimension": "total_spent", "operator": "gte", "value": 5000 }] }
  ]
}
```

The example matches fans who have spent at least 5000 USD cents.

## Read the dimensions

Fetch the manifest before you build a definition:

```bash theme={null}
curl "https://api.fanvue.com/v1/chats/lists/dynamic/dimensions" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "X-Fanvue-API-Version: 2025-06-26"
```

```json theme={null}
[
  {
    "key": "total_spent",
    "labelKey": "smartLists.dimension.totalSpent",
    "category": "spend",
    "valueType": "number",
    "operators": ["gte", "lte", "between"],
    "store": "postgres"
  }
]
```

## Preview a definition

Check how many fans a definition matches before you save it. `POST /v1/chats/lists/dynamic/preview/count` takes `{ definition }` and returns `{ count, exact }`. `count` is `null` when the evaluation didn't resolve, and `exact` is `false` when the count is a capped estimate.

```bash theme={null}
curl -X POST "https://api.fanvue.com/v1/chats/lists/dynamic/preview/count" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "X-Fanvue-API-Version: 2025-06-26" \
  -H "Content-Type: application/json" \
  -d '{
    "definition": {
      "version": 1,
      "match": "any",
      "groups": [
        { "conditions": [{ "dimension": "total_spent", "operator": "gte", "value": 5000 }] }
      ]
    }
  }'
```

```json theme={null}
{ "count": 128, "exact": true }
```

`POST /v1/chats/lists/dynamic/preview/members` takes the same `definition` plus `limit` and returns `{ members, unavailable }`. `unavailable` is `true` when the preview couldn't be resolved in time, so retry rather than treating the list as empty.

## Translate a description

If you'd rather start from words than conditions, `POST /v1/chats/lists/dynamic/translate` takes `{ description }`, a plain-language audience description, and returns `{ name, spec, notes }`:

* `spec` is a definition you can preview or save as is.
* `name` is a suggested list name, when one could be inferred.
* `notes` lists assumptions made or parts of the description ignored.

The route is capped at 10 requests per minute and 100 per day per creator.

## Create a list

`POST /v1/chats/lists/dynamic` takes `{ name, icon, definition }` and returns `201` with `{ uuid, name }`. `icon` is optional, up to 16 characters. A name the creator already uses returns `409`.

```bash theme={null}
curl -X POST "https://api.fanvue.com/v1/chats/lists/dynamic" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "X-Fanvue-API-Version: 2025-06-26" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Big spenders",
    "definition": {
      "version": 1,
      "match": "any",
      "groups": [
        { "conditions": [{ "dimension": "total_spent", "operator": "gte", "value": 5000 }] }
      ]
    }
  }'
```

```json theme={null}
{ "uuid": "6f1c8e2a-6b3e-4c9b-8f3a-7c1a2b3c4d5e", "name": "Big spenders" }
```

`GET /v1/chats/lists/dynamic` returns each list with `uuid`, `name`, `icon`, `createdAt`, `updatedAt`, `membersCount`, `membersCountExact` and `valid`. `valid` is `false` when the stored definition uses a dimension the account can no longer filter on, so update the definition before you target the list.

## Read the members

`GET /v1/chats/lists/dynamic/{uuid}/members` returns the fans matching the stored definition in a stable order, in a `{ data, nextCursor, unavailable }` envelope. Pagination is keyset with `cursor` only, and there's no `size` parameter; pass the previous `nextCursor` until it is `null`. Each member carries `uuid`, `displayName` and `handle`.

```json theme={null}
{
  "data": [{ "uuid": "6f1c8e2a-6b3e-4c9b-8f3a-7c1a2b3c4d5e", "displayName": "Sarah", "handle": "sarah" }],
  "nextCursor": "k0FQ1m9yZXN0aWdpb3VzLW9wYXF1ZS1jdXJzb3I",
  "unavailable": false
}
```

## Target a dynamic list in a mass message

Mass messages accept dynamic list UUIDs in `includedLists.dynamicSmartListUuids` and `excludedLists.dynamicSmartListUuids`, on v0 and v1. Membership is resolved at send time.

```bash theme={null}
curl -X POST "https://api.fanvue.com/v1/chats/mass-messages" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "X-Fanvue-API-Version: 2025-06-26" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Thank you for your support this month",
    "includedLists": {
      "dynamicSmartListUuids": ["6f1c8e2a-6b3e-4c9b-8f3a-7c1a2b3c4d5e"]
    }
  }'
```

See [Send a mass message](/docs/tutorials/sending-mass-messages) for the rest of the body and the response.

## Endpoints and limits

Every route has its own per-minute cap, counted per caller, on top of the default [rate limit](/docs/authentication/rate-limits#per-endpoint-limits). The self-serve route and its creator-scoped twin share one bucket.

| Method | Path | Purpose | Limit per minute |
| - | - | - | - |
| `GET` | `/v1/chats/lists/dynamic/dimensions` | Manifest of filterable dimensions, operators and value types | 30 |
| `GET` | `/v1/chats/lists/dynamic/memory-topics` | Suggested topics for `has_fact` conditions, with fan counts | 30 |
| `POST` | `/v1/chats/lists/dynamic/preview/count` | Live member count for an unsaved definition | 60 |
| `POST` | `/v1/chats/lists/dynamic/preview/members` | Sample members for an unsaved definition (`limit` 1 to 20, default 8) | 60 |
| `POST` | `/v1/chats/lists/dynamic/translate` | Turns a plain-language audience description into a definition | 10, and 100 per day per creator |
| `POST` | `/v1/chats/lists/dynamic` | Create a list | 20 |
| `GET` | `/v1/chats/lists/dynamic` | List the creator's dynamic lists | 30 |
| `GET` | `/v1/chats/lists/dynamic/{uuid}` | One list with its definition | 30 |
| `PATCH` | `/v1/chats/lists/dynamic/{uuid}` | Update name, icon or definition | 20 |
| `DELETE` | `/v1/chats/lists/dynamic/{uuid}` | Delete a list | 20 |
| `GET` | `/v1/chats/lists/dynamic/{uuid}/members` | Current members, keyset paginated | 60 |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.