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

# Tune AI settings

> Turn the AI on or off per creator, configure follow-ups and review mode (turning it off releases waiting drafts), and read or replace the OnlyFans welcome message with dry runs.

Each creator has a small set of switches that control how the AI chatter works for her, plus the OnlyFans welcome message every new subscriber receives.

**Scopes**: `ai:read` to read; `ai:write` to change. Turning review mode **off** also needs `messages:send`, because it releases waiting replies to fans.

## Read the settings

<CodeGroup>
  ```bash cURL theme={"system"}
  curl https://api.onlyx.ai/v1/creators/cre_5f2d9a1c7b3e4f60a2d1/ai-settings \
    -H "Authorization: Bearer $ONLYX_API_KEY"
  ```

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

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

  settings = requests.get(f"{API}/creators/{CREATOR}/ai-settings", headers=HEADERS, timeout=30).json()
  ```

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

  const settings = await (await fetch(`${API}/creators/${CREATOR}/ai-settings`, { headers })).json();
  ```
</CodeGroup>

```json Response 200 theme={"system"}
{
  "aiEnabled": true,
  "followUps": {
    "enabled": true,
    "effective": true,
    "maxPerFanPerDay": 2,
    "unansweredAfterHours": 24,
    "plannedToday": 37,
    "sentToday": 21
  },
  "review": {
    "enabled": false,
    "reviewMinutes": 5,
    "whenUnreviewed": "send",
    "waitingNow": 0
  },
  "massMessages": {
    "enabled": false,
    "effective": false,
    "perDay": 1,
    "nextSendAt": null
  }
}
```

| Field | Meaning |
| - | - |
| `aiEnabled` | The creator's master AI switch (the same value as on the [Creator](/developers/concepts/creators)) |
| `followUps.enabled` | Your follow-up setting |
| `followUps.effective` | Whether follow-ups actually go out now: `enabled` **and** the creator's AI is on |
| `followUps.maxPerFanPerDay` | At most this many follow-ups per fan per day (1 to 4) |
| `followUps.unansweredAfterHours` | How long a fan stays silent before a follow-up is considered |
| `followUps.plannedToday`, `sentToday` | Today's follow-ups (in the creator's time zone), planned and sent |
| `review.enabled` | Whether every AI reply waits for a person |
| `review.reviewMinutes` | How long a reply waits for review (1 to 60) |
| `review.whenUnreviewed` | What happens when nobody reviews in time: `send` or `hold` |
| `review.waitingNow` | Replies waiting for review right now |
| `massMessages` | The AI's scheduled messages to fan lists: `enabled` (your setting), `effective` (whether they actually go out now), `perDay` and `nextSendAt` (or `null`). Read-only in the API; configure them in the dashboard. |

## Change settings

`PATCH /v1/creators/{creatorId}/ai-settings` changes only what you send and returns the full settings. Any other field is refused with `400 VALIDATION_ERROR`. Send an `Idempotency-Key` header to make retries safe.

| Field | Values |
| - | - |
| `aiEnabled` | `true` or `false` |
| `followUps.enabled` | `true` or `false` |
| `followUps.maxPerFanPerDay` | 1 to 4 |
| `review.enabled` | `true` or `false` |
| `review.reviewMinutes` | 1 to 60 |
| `review.whenUnreviewed` | `send` or `hold` |

<Warning>
  These switches decide whether the AI messages real fans. Turning the AI or follow-ups **on** lets it start writing to fans; turning review mode **off** sends the waiting replies right away. Confirm with the person responsible for the creator first.
</Warning>

### Follow-ups

Follow-ups are short, in-character nudges to fans who went quiet. They only go out in chats the AI is handling, and they are cancelled in a chat as soon as your team sends a message there.

<CodeGroup>
  ```bash cURL theme={"system"}
  curl -X PATCH https://api.onlyx.ai/v1/creators/cre_5f2d9a1c7b3e4f60a2d1/ai-settings \
    -H "Authorization: Bearer $ONLYX_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"followUps": {"enabled": true, "maxPerFanPerDay": 1}}'
  ```

  ```python Python theme={"system"}
  s = requests.patch(
      f"{API}/creators/{CREATOR}/ai-settings",
      headers=HEADERS,
      json={"followUps": {"enabled": True, "maxPerFanPerDay": 1}},
      timeout=30,
  ).json()
  print(s["followUps"])
  ```

  ```javascript JavaScript theme={"system"}
  const s = await (await fetch(`${API}/creators/${CREATOR}/ai-settings`, {
    method: "PATCH",
    headers: { ...headers, "Content-Type": "application/json" },
    body: JSON.stringify({ followUps: { enabled: true, maxPerFanPerDay: 1 } }),
  })).json();
  ```
</CodeGroup>

Send only the fields you want to change. If you echo back `followUps.effective` as `enabled`, you may switch follow-ups off without meaning to while the creator's AI is paused.

### Review mode

With review mode on, each reply the AI writes waits in the dashboard's review queue for `reviewMinutes`. A person approves, edits or discards it there; when time runs out, `whenUnreviewed` decides (`send` or `hold`). Reviewing drafts one by one happens in the dashboard, not the API.

Turn it on for a new creator's first days:

```bash cURL theme={"system"}
curl -X PATCH https://api.onlyx.ai/v1/creators/cre_5f2d9a1c7b3e4f60a2d1/ai-settings \
  -H "Authorization: Bearer $ONLYX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"review": {"enabled": true, "reviewMinutes": 10, "whenUnreviewed": "hold"}}'
```

**Turning review mode off releases the waiting drafts to fans immediately.** Drafts that have waited more than about ten minutes are discarded instead of sent (the AI writes a fresh reply to those fans). Because it sends messages, switching `review.enabled` from on to off:

* needs both `ai:write` and `messages:send` (otherwise `403 INSUFFICIENT_SCOPE` naming `messages:send`);
* is limited to 6 per hour per creator (`429 RATE_LIMITED` with `Retry-After` beyond that).

Sending `"enabled": false` while review mode is already off changes nothing, needs no extra scope and does not count against that limit.

Check `review.waitingNow` first so you know how many replies will go out:

```python Python theme={"system"}
current = requests.get(f"{API}/creators/{CREATOR}/ai-settings", headers=HEADERS, timeout=30).json()
waiting = current["review"]["waitingNow"]
if input(f"{waiting} replies will be sent to fans now. Type yes to continue: ") == "yes":
    requests.patch(f"{API}/creators/{CREATOR}/ai-settings", headers=HEADERS,
                   json={"review": {"enabled": False}}, timeout=30).raise_for_status()
```

## Welcome message

The welcome message is OnlyFans' automatic message to **every new subscriber** of the creator. It can carry text, free media, and paid media with a price. OnlyX reads it from OnlyFans and can replace it for you.

### Read it

<CodeGroup>
  ```bash cURL theme={"system"}
  curl https://api.onlyx.ai/v1/creators/cre_5f2d9a1c7b3e4f60a2d1/welcome-message \
    -H "Authorization: Bearer $ONLYX_API_KEY"
  ```

  ```python Python theme={"system"}
  wm = requests.get(f"{API}/creators/{CREATOR}/welcome-message", headers=HEADERS, timeout=30).json()
  ```

  ```javascript JavaScript theme={"system"}
  const wm = await (await fetch(`${API}/creators/${CREATOR}/welcome-message`, { headers })).json();
  ```
</CodeGroup>

```json Response 200 theme={"system"}
{
  "connected": true,
  "enabled": true,
  "text": "hiii welcome to my page 💕 so happy you're here! check your messages for a little gift",
  "priceCents": 0,
  "media": [{ "id": "4012345601", "type": "photo", "preview": true }],
  "pending": false,
  "lastUpdate": { "status": "done", "at": "2026-09-20T09:14:02.000Z", "error": null }
}
```

| Field | Meaning |
| - | - |
| `connected` | `false` when the creator is not connected; the other fields are then empty |
| `enabled` | Whether OnlyFans sends the welcome message at all |
| `text`, `priceCents`, `media` | What new subscribers receive. `preview: false` media is locked behind `priceCents`. |
| `pending` | `true` while a change you made is being applied on OnlyFans |
| `lastUpdate` | Your latest change: `status` (`queued`, `done` or `failed`), `at` (when it reached that status), and a readable `error` sentence when it failed. `null` if the welcome message was never changed through OnlyX. |

### Replace it

`PUT /v1/creators/{creatorId}/welcome-message` (scope `ai:write`) **replaces the whole welcome message** on the creator's live OnlyFans account. It answers `202` with an `updateId`; the change is applied within a minute or two.

| Field | Rules |
| - | - |
| `text` | Required. Up to 2,000 characters. |
| `previewMediaIds` | Optional. Up to 20 vault ids sent free |
| `paidMediaIds` | Optional. Up to 20 vault ids locked behind the price |
| `priceCents` | `0` to `1000000`. A price above 0 needs `paidMediaIds`, and `paidMediaIds` need a price. |
| `enabled` | Optional. `true` or `false`; leave it out to keep the current switch. |
| `dryRun` | Optional. `true` checks the change on OnlyFans without saving it. |

<Warning>
  This changes what **every new subscriber** receives and pays for, starting with the next one. Do a dry run first and confirm the exact text, media and price with the person responsible. You can change it at most 5 times per hour per creator, dry runs included.
</Warning>

<CodeGroup>
  ```bash cURL theme={"system"}
  # 1. Dry run: checked on OnlyFans, not saved
  curl -X PUT https://api.onlyx.ai/v1/creators/cre_5f2d9a1c7b3e4f60a2d1/welcome-message \
    -H "Authorization: Bearer $ONLYX_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: wm-miarose-2026-09-26-dry" \
    -d '{"text": "hiii welcome 💕 I made you something, check below", "previewMediaIds": ["4012345601"], "paidMediaIds": ["4012345611","4012345612"], "priceCents": 800, "dryRun": true}'

  # 2. The real change, once the dry run is done
  curl -X PUT https://api.onlyx.ai/v1/creators/cre_5f2d9a1c7b3e4f60a2d1/welcome-message \
    -H "Authorization: Bearer $ONLYX_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: wm-miarose-2026-09-26" \
    -d '{"text": "hiii welcome 💕 I made you something, check below", "previewMediaIds": ["4012345601"], "paidMediaIds": ["4012345611","4012345612"], "priceCents": 800, "enabled": true}'
  ```

  ```python Python theme={"system"}
  body = {
      "text": "hiii welcome 💕 I made you something, check below",
      "previewMediaIds": ["4012345601"],
      "paidMediaIds": ["4012345611", "4012345612"],
      "priceCents": 800,
  }

  def put_welcome(dry_run):
      r = requests.put(
          f"{API}/creators/{CREATOR}/welcome-message",
          headers={**HEADERS, "Idempotency-Key": str(uuid.uuid4())},
          json={**body, "dryRun": dry_run} if dry_run else {**body, "enabled": True},
          timeout=30,
      )
      r.raise_for_status()  # 202 {"queued": true, "updateId": "..."}
      while True:  # wait until OnlyFans has applied (or checked) it
          time.sleep(10)
          wm = requests.get(f"{API}/creators/{CREATOR}/welcome-message", headers=HEADERS, timeout=30).json()
          if not wm["pending"] and wm["lastUpdate"] and wm["lastUpdate"]["status"] != "queued":
              return wm["lastUpdate"]

  check = put_welcome(dry_run=True)
  if check["status"] == "failed":
      raise SystemExit(f"OnlyFans would refuse it: {check['error']}")
  print(put_welcome(dry_run=False))
  ```

  ```javascript JavaScript theme={"system"}
  const body = {
    text: "hiii welcome 💕 I made you something, check below",
    previewMediaIds: ["4012345601"],
    paidMediaIds: ["4012345611", "4012345612"],
    priceCents: 800,
  };

  async function putWelcome(dryRun) {
    const res = await fetch(`${API}/creators/${CREATOR}/welcome-message`, {
      method: "PUT",
      headers: { ...headers, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID() },
      body: JSON.stringify(dryRun ? { ...body, dryRun: true } : { ...body, enabled: true }),
    });
    if (res.status !== 202) throw new Error(`${res.status} ${await res.text()}`);
    for (;;) {
      await new Promise((r) => setTimeout(r, 10_000));
      const wm = await (await fetch(`${API}/creators/${CREATOR}/welcome-message`, { headers })).json();
      if (!wm.pending && wm.lastUpdate && wm.lastUpdate.status !== "queued") return wm.lastUpdate;
    }
  }

  const check = await putWelcome(true);
  if (check.status === "failed") throw new Error(`OnlyFans would refuse it: ${check.error}`);
  console.log(await putWelcome(false));
  ```
</CodeGroup>

```json Response 202 theme={"system"}
{ "queued": true, "updateId": "wmu_5f0c2a9e41b7d3c8a6e1f024" }
```

| Status | Code | When |
| - | - | - |
| 400 | `VALIDATION_ERROR` | Text too long, a price without paid media (or paid media without a price), a media id that is not digits, the same media in both lists, a price OnlyFans does not accept for her account, or a message OnlyFans refuses |
| 409 | `CREATOR_NOT_CONNECTED` | The creator is not connected |
| 409 | `CONFLICT` | Another welcome message change is still being applied; wait for `pending: false` |
| 409 | `IDEMPOTENCY_KEY_REUSED` / `IDEMPOTENCY_IN_PROGRESS` | The key was used for a different request, or the first request is still running |
| 429 | `RATE_LIMITED` | More than 5 changes (dry runs included) in an hour for this creator |
| 503 | `SEND_UNAVAILABLE` | The change could not be queued right now. Nothing was changed; try again in a few minutes. |

## From your AI assistant

With the [MCP server](/developers/mcp/overview) connected: *"Is review mode on for Mia? How many replies are waiting?"* uses `get_ai_settings`. Changes go through `update_ai_settings` and `update_welcome_message`; the assistant shows you the exact change and asks for confirmation first, because both can reach fans.

## Related

* [Hugo, the AI chatter](/developers/concepts/hugo-ai): what follow-ups and review mode do.
* [Creators](/developers/concepts/creators): the `aiEnabled` switch.
* [Vault media](/developers/guides/vault-media): media ids for the welcome message.
