> ## Documentation Index
> Fetch the complete documentation index at: https://help.onlyx.ai/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> You are reading OnlyX Help: the OnlyX Help Center and the OnlyX developer documentation. OnlyX is an AI chatting and CRM platform for OnlyFans agencies. Its AI chatter is called Hugo in the app.
> Pages at the site root (for example /inbox/..., /hugo/..., /billing/...) are Help Center articles for agency owners, admins, chatters and creators who use the app at app.onlyx.ai. Words in bold are the exact button, menu and label names the app shows; keep them exactly as written. When these pages do not answer a question, the person can email support at support@onlyx.ai.
> Pages under /developers are the developer documentation. REST API base URL: https://api.onlyx.ai/v1 (authenticate with `Authorization: Bearer <API key>`; keys start with onx_sk_ and are created in app.onlyx.ai under Settings > API & MCP). MCP server: https://mcp.onlyx.ai/mcp (OAuth, or a Bearer API key). In the API the AI chatter is `ai` on the wire. Money is integer US cents in fields ending in Cents; timestamps are UTC ISO-8601.
> Rules for assistants acting on a user's behalf: discover ids with list calls and never invent them; before any call that reaches a real fan or the live OnlyFans account (sending a message, releasing a chat to the AI, turning AI on for a chat, resolving a hand-off, turning review mode off, changing the welcome message, creating a tracking link) show the user the exact content and get explicit confirmation; send every POST with an Idempotency-Key and reuse it on retry; never resend a message whose delivery status is unconfirmed; never ask a creator for her OnlyFans password or codes - she signs in herself through a connect link and the OnlyX Login app.

# Work with fans

> Find fans by segment, spend and subscription status, read a fan's purchases, and keep your team's notes, custom names and mute flags.

A **fan** is one subscriber (current or past) of one creator. The same person subscribed to two of your creators is two fans, with two ids. Fans come from OnlyFans and update continuously while the creator is connected.

**Scopes**: `fans:read` to list and read; `fans:write` to set custom names, notes and mute.

## The Fan object

```json Fan theme={"system"}
{
  "id": "fan_3b7e9d2a1c5f48e06b92",
  "creatorId": "cre_5f2d9a1c7b3e4f60a2d1",
  "conversationId": "cnv_9f2c41d7a8b35e06c1f4",
  "displayName": "Jake",
  "customName": "Jake (Texas, trucks)",
  "username": "jake_91",
  "avatarUrl": "https://...",
  "subscribed": true,
  "subscribedAt": "2026-03-14T19:22:05.000Z",
  "subscriptionExpiresAt": "2026-10-14T19:22:05.000Z",
  "subscriptionPriceCents": 999,
  "renewOn": true,
  "totalSpentCents": 48500,
  "tipsCents": 12000,
  "paidMessagesCents": 31500,
  "lastActiveAt": "2026-09-26T13:58:12.000Z",
  "lastPurchaseAt": "2026-09-25T22:41:09.000Z",
  "muted": false,
  "notes": "Likes lace sets. Birthday Oct 3.",
  "salesOptOut": false,
  "lists": [{ "id": "lst_7c9e1a3b5d7f9a1c3e5b", "name": "VIPs" }],
  "createdAt": "2026-03-14T19:22:05.000Z"
}
```

| Field | Meaning |
| - | - |
| `conversationId` | The fan's chat with this creator, or `null` if they have never messaged |
| `displayName`, `username` | Their OnlyFans name and username |
| `customName` | The name your team gave them in OnlyX. Only your team sees it. |
| `subscribed` | Whether they are subscribed right now |
| `subscribedAt`, `subscriptionExpiresAt` | When the current or last subscription started and when it ends |
| `subscriptionPriceCents` | What they pay per subscription period (`0` on a free page) |
| `renewOn` | Whether auto-renew is on. `false` means they will lapse at `subscriptionExpiresAt`. `null` when unknown. |
| `totalSpentCents` | Everything they have spent with this creator: subscriptions, paid messages, tips |
| `tipsCents`, `paidMessagesCents` | The tips and paid-message parts of that total |
| `lastActiveAt`, `lastPurchaseAt` | Their latest activity and latest purchase |
| `muted` | An OnlyX flag your team sets. It does not mute or block the fan on OnlyFans. |
| `notes` | Your team's free-text notes |
| `salesOptOut` | `true` when the fan asked not to be sold to. The AI stops offering paid content, and paid sends return `409 SALES_OPTED_OUT`. |
| `lists` | The creator's fan lists this fan is on |
| `createdAt` | When OnlyX first saw this fan (`null` if unknown) |

Timestamps are `null` when OnlyFans has not reported them.

## List and filter fans

`GET /v1/fans` returns fans across the creators the key can see, biggest spenders first by default.

| Parameter | Values | Meaning |
| - | - | - |
| `creatorId` | `cre_...` | One creator's fans. A creator the key cannot see is `404 CREATOR_NOT_FOUND`, never an empty list. |
| `q` | text, up to 100 characters | Search by name (their OnlyFans name or your custom name) |
| `segment` | see below | A ready-made audience |
| `status` | `active`, `expired` | Currently subscribed, or lapsed |
| `sort` | `spend` (default), `subscribed`, `last_active`, `last_purchase`, `name` | Sort key |
| `order` | `asc`, `desc` | Sort direction. Default `desc`, except `asc` for `name`. |
| `limit` | 1 to 100 (default 25) | Page size |
| `cursor` | from `nextCursor` | Next page |

**Segments**

| `segment` | Who is in it |
| - | - |
| `whales` | Fans who have spent \$200 or more |
| `spenders` | Fans who have spent anything |
| `never_spent` | Subscribed fans who have never bought anything |
| `new` | Fans who subscribed in the last 7 days |
| `recent_buyer` | Fans who bought something in the last 30 days |
| `lapsing` | Auto-renew off, and the subscription ends within 7 days |
| `winback` | No longer subscribed; the subscription ended in the last 30 days |

<CodeGroup>
  ```bash cURL theme={"system"}
  curl "https://api.onlyx.ai/v1/fans?creatorId=cre_5f2d9a1c7b3e4f60a2d1&segment=lapsing&sort=spend&order=desc&limit=50" \
    -H "Authorization: Bearer $ONLYX_API_KEY"
  ```

  ```python Python theme={"system"}
  import os, requests

  API = "https://api.onlyx.ai/v1"
  HEADERS = {"Authorization": f"Bearer {os.environ['ONLYX_API_KEY']}"}

  def all_fans(**filters):
      cursor = None
      while True:
          params = {**filters, "limit": 100, **({"cursor": cursor} if cursor else {})}
          page = requests.get(f"{API}/fans", headers=HEADERS, params=params, timeout=30).json()
          yield from page["data"]
          if not page["hasMore"]:
              return
          cursor = page["nextCursor"]

  for fan in all_fans(creatorId="cre_5f2d9a1c7b3e4f60a2d1", segment="lapsing", sort="spend", order="desc"):
      print(fan["displayName"], fan["totalSpentCents"] / 100, fan["subscriptionExpiresAt"])
  ```

  ```javascript JavaScript theme={"system"}
  const API = "https://api.onlyx.ai/v1";
  const headers = { Authorization: `Bearer ${process.env.ONLYX_API_KEY}` };

  async function* allFans(filters) {
    let cursor;
    for (;;) {
      const q = new URLSearchParams({ ...filters, limit: "100", ...(cursor ? { cursor } : {}) });
      const page = await (await fetch(`${API}/fans?${q}`, { headers })).json();
      yield* page.data;
      if (!page.hasMore) return;
      cursor = page.nextCursor;
    }
  }

  for await (const fan of allFans({ creatorId: "cre_5f2d9a1c7b3e4f60a2d1", segment: "lapsing", sort: "spend", order: "desc" })) {
    console.log(fan.displayName, fan.totalSpentCents / 100, fan.subscriptionExpiresAt);
  }
  ```
</CodeGroup>

```json Response 200 theme={"system"}
{
  "data": [
    {
      "id": "fan_3b7e9d2a1c5f48e06b92",
      "creatorId": "cre_5f2d9a1c7b3e4f60a2d1",
      "conversationId": "cnv_9f2c41d7a8b35e06c1f4",
      "displayName": "Jake",
      "customName": null,
      "username": "jake_91",
      "avatarUrl": "https://...",
      "subscribed": true,
      "subscribedAt": "2026-03-14T19:22:05.000Z",
      "subscriptionExpiresAt": "2026-09-30T19:22:05.000Z",
      "subscriptionPriceCents": 999,
      "renewOn": false,
      "totalSpentCents": 48500,
      "tipsCents": 12000,
      "paidMessagesCents": 31500,
      "lastActiveAt": "2026-09-26T13:58:12.000Z",
      "lastPurchaseAt": "2026-09-25T22:41:09.000Z",
      "muted": false,
      "notes": null,
      "salesOptOut": false,
      "lists": [],
      "createdAt": "2026-03-14T19:22:05.000Z"
    }
  ],
  "hasMore": true,
  "nextCursor": "eyJvIjo1MH0"
}
```

`GET /v1/fans/{fanId}` returns one fan. A fan id you cannot see returns `404 FAN_NOT_FOUND`.

## Purchase history

`GET /v1/fans/{fanId}/purchases` lists what the fan bought from this creator, newest first: paid-message unlocks and tips. Sales the AI made, sales your team made and unlocks bought on OnlyFans directly are all included; refunded sales are not. Subscription payments are not in this list: find them in the creator's transaction ledger (`GET /v1/creators/{creatorId}/transactions`, scope `money:read`; see [Pull stats](/developers/guides/pull-stats)). Page with `limit` (1 to 100, default 25) and `cursor`.

| Field | Meaning |
| - | - |
| `type` | `ppv` (a paid message) or `tip` today; `subscription` and `other` are reserved, so handle them if they appear |
| `amountCents` | What the fan paid |
| `at` | When |
| `messageId` | The paid message that was bought, when there is one |

```json Response 200 theme={"system"}
{
  "data": [
    { "type": "tip", "amountCents": 500, "at": "2026-09-26T13:58:12.000Z", "messageId": null },
    { "type": "ppv", "amountCents": 1500, "at": "2026-09-25T22:41:09.000Z", "messageId": "msg_1f3a5c7e9b2d4f60a8c1" }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

## Notes, custom names and mute

`PATCH /v1/fans/{fanId}` (scope `fans:write`) changes only the fields you send. Send an empty string or `null` to clear a text field. Any other field is refused with `400 VALIDATION_ERROR`. Send an optional `Idempotency-Key` header to make retries safe.

| Field | Type | Meaning |
| - | - | - |
| `customName` | string, up to 160 characters | A name your team recognizes, shown instead of the OnlyFans name in OnlyX |
| `notes` | string, up to 4,000 characters | Free-text notes for your team: preferences, birthdays, what they bought |
| `muted` | boolean | Your team's mute flag. It never mutes or blocks the fan on OnlyFans. |

<CodeGroup>
  ```bash cURL theme={"system"}
  curl -X PATCH https://api.onlyx.ai/v1/fans/fan_3b7e9d2a1c5f48e06b92 \
    -H "Authorization: Bearer $ONLYX_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"customName": "Jake (Texas, trucks)", "notes": "Likes lace sets. Birthday Oct 3."}'
  ```

  ```python Python theme={"system"}
  fan = requests.patch(
      f"{API}/fans/fan_3b7e9d2a1c5f48e06b92",
      headers=HEADERS,
      json={"customName": "Jake (Texas, trucks)", "notes": "Likes lace sets. Birthday Oct 3."},
      timeout=30,
  ).json()
  ```

  ```javascript JavaScript theme={"system"}
  const fan = await (await fetch(`${API}/fans/fan_3b7e9d2a1c5f48e06b92`, {
    method: "PATCH",
    headers: { ...headers, "Content-Type": "application/json" },
    body: JSON.stringify({ customName: "Jake (Texas, trucks)", notes: "Likes lace sets. Birthday Oct 3." }),
  })).json();
  ```
</CodeGroup>

The response is the updated Fan. Nothing here is visible to the fan.

## Fan lists

`GET /v1/fan-lists` (optionally `creatorId`; `limit` 1 to 100, default 25, and `cursor`) returns the creators' OnlyFans lists with their size. Lists are mirrored from OnlyFans; managing them is not available through the API yet.

```json Response 200 theme={"system"}
{
  "data": [
    { "id": "lst_1b3d5f7a9c1e3b5d7f9a", "name": "Fans", "kind": "subscribers", "creatorId": "cre_5f2d9a1c7b3e4f60a2d1", "count": 2210 },
    { "id": "lst_2c4e6a8b0d2f4a6c8e0b", "name": "Renew off", "kind": "rebill_off", "creatorId": "cre_5f2d9a1c7b3e4f60a2d1", "count": 318 },
    { "id": "lst_7c9e1a3b5d7f9a1c3e5b", "name": "VIPs", "kind": "custom", "creatorId": "cre_5f2d9a1c7b3e4f60a2d1", "count": 42 }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

`kind` is the kind of list as OnlyFans reports it, for example `subscribers`, `rebill_off` (renew off) or `custom` for lists the creator made herself. It is a plain string, so treat values you do not recognize as system lists. Each Fan's `lists` shows which lists it is on.

## Recipes

* **Lapsing whales to save this week**: `segment=lapsing&sort=spend&order=desc`, then have your team (or the AI) reach out personally.
* **Win-back campaign**: `segment=winback`, sorted by `spend`. OnlyFans often refuses messages to fans who are no longer subscribed (`SEND_REFUSED`), so pair this list with offers outside the inbox, such as a discounted trial link from [Tracking links](/developers/guides/tracking-links).
* **Who to thank**: `segment=recent_buyer&sort=last_purchase&order=desc`.
* **Enrich your CRM**: page through `GET /v1/fans` nightly and store `id`, `totalSpentCents`, `lastPurchaseAt` and `renewOn`.

## Related

* [Read and send messages](/developers/guides/read-and-send-messages): message a fan through their `conversationId`.
* [Pull stats](/developers/guides/pull-stats): audience totals and spend bands.
* [Pagination](/developers/pagination): cursors and page sizes.
