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

# Set up the AI persona

> Read and update a creator's AI persona with GET and PATCH: partial updates, null to clear, validation limits, a complete example, and readiness.

The AI persona tells the AI chatter who the creator is and how she talks. This guide shows the calls; [AI persona](/developers/concepts/ai-persona) explains every field with good and bad examples.

**Scopes**: `ai:read` to read, `ai:write` to change. Changes apply to the AI's next reply; nothing is sent to fans when you edit the persona.

## Read the persona

<CodeGroup>
  ```bash cURL theme={"system"}
  curl https://api.onlyx.ai/v1/creators/cre_5f2d9a1c7b3e4f60a2d1/persona \
    -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']}"}
  CREATOR = "cre_5f2d9a1c7b3e4f60a2d1"

  persona = requests.get(f"{API}/creators/{CREATOR}/persona", headers=HEADERS, timeout=30).json()
  print(persona["readyForAi"], persona["completeness"], persona["requiredMissing"])
  ```

  ```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 persona = await (await fetch(`${API}/creators/${CREATOR}/persona`, { headers })).json();
  console.log(persona.readyForAi, persona.completeness, persona.requiredMissing);
  ```
</CodeGroup>

A new creator's persona is mostly empty, with defaults for the style fields. Reading it never creates anything, so `updatedAt` stays `null` until the first change:

```json Response 200 (new creator) theme={"system"}
{
  "personaName": null,
  "nickname": null,
  "age": null,
  "city": "",
  "origin": null,
  "occupation": null,
  "archetype": "girl_next_door",
  "lore": "",
  "accountContext": "",
  "physicalDescription": "",
  "interests": [],
  "contentMenu": [],
  "contentNotes": "",
  "boundaries": [],
  "hardLimits": [],
  "exemplars": [],
  "primaryLanguage": "en",
  "additionalLanguages": [],
  "timezone": "UTC",
  "capitalization": "normal",
  "punctuation": "relaxed",
  "messageSplitting": "sometimes",
  "typingSpeed": "natural",
  "emojiPolicy": "some",
  "allowTypos": false,
  "customContentEnabled": false,
  "priceCustomPhotoCents": null,
  "priceCustomVideoPerMinCents": null,
  "priceCustomVideoMinCents": null,
  "customPhotoDeliveryDays": null,
  "customVideoDeliveryDays": null,
  "aiDisclosureEnabled": false,
  "aiDisclosureText": "",
  "handoffKinds": null,
  "handoffEffective": ["welfare.self_harm", "welfare.immediate_danger", "prohibited.underage", "payment.refund_demand"],
  "handoffCatalogue": [
    { "key": "welfare.self_harm", "reason": "welfare", "label": "Suicidal or self-harm language", "default": true, "locked": true },
    { "key": "real_world.meet_request", "reason": "real_world", "label": "Asks to meet", "default": false, "locked": false }
  ],
  "completeness": 8,
  "readyForAi": false,
  "requiredMissing": ["personaName", "age", "city", "occupation", "lore"],
  "updatedAt": null
}
```

`handoffEffective` and `handoffCatalogue` are shortened here; the real lists cover every hand-off kind.

## Update it: PATCH semantics

`PATCH /v1/creators/{creatorId}/persona` is a true partial update:

| You send | Result |
| - | - |
| A field with a value | That field is replaced |
| A field left out | Unchanged |
| A field set to `null` | Cleared: text becomes `""`, lists become `[]`, numbers become `null`. `archetype`, `primaryLanguage` and `timezone` go back to their defaults (`girl_next_door`, `en`, `UTC`), the delivery days to 3 and 7, and `handoffKinds` to the recommended set. |
| `null` on a style choice or switch | `400 VALIDATION_ERROR`: `capitalization`, `punctuation`, `messageSplitting`, `typingSpeed`, `emojiPolicy`, `allowTypos`, `customContentEnabled` and `aiDisclosureEnabled` can only be set, not cleared |
| A field that does not exist | `400 VALIDATION_ERROR`, nothing is saved |

Lists are replaced as a whole. To add one exemplar, read the list, append, and send the full list back.

The response is `200` with the full, updated persona. Send an `Idempotency-Key` header if you want retries of the same change to be answered from the first result.

## A complete persona

This body fills every field for a real-world-ready creator. Send it in one call.

<CodeGroup>
  ```bash cURL theme={"system"}
  curl -X PATCH https://api.onlyx.ai/v1/creators/cre_5f2d9a1c7b3e4f60a2d1/persona \
    -H "Authorization: Bearer $ONLYX_API_KEY" \
    -H "Content-Type: application/json" \
    -d @persona.json
  ```

  ```python Python theme={"system"}
  import json

  body = json.load(open("persona.json"))
  r = requests.patch(f"{API}/creators/{CREATOR}/persona", headers=HEADERS, json=body, timeout=30)
  if r.status_code == 400:
      print(r.json()["error"]["message"])  # names the field that failed
  r.raise_for_status()
  persona = r.json()
  print(persona["readyForAi"], persona["completeness"])
  ```

  ```javascript JavaScript theme={"system"}
  import { readFile } from "node:fs/promises";

  const body = JSON.parse(await readFile("persona.json", "utf8"));
  const res = await fetch(`${API}/creators/${CREATOR}/persona`, {
    method: "PATCH",
    headers: { ...headers, "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
  const persona = await res.json();
  if (!res.ok) throw new Error(persona.error.message);
  console.log(persona.readyForAi, persona.completeness);
  ```
</CodeGroup>

```json persona.json theme={"system"}
{
  "personaName": "Mia",
  "nickname": "Mimi",
  "age": 24,
  "city": "Austin, Texas",
  "origin": "Outside Denver, Colorado",
  "occupation": "Nursing student, weekend shifts at a coffee shop",
  "archetype": "girl_next_door",
  "lore": "Mia grew up outside Denver with two older brothers and moved to Austin for nursing school three years ago. She lives with her cat Pickle in a small apartment near campus, works Saturday and Sunday mornings at a coffee shop, and studies most evenings. She loves hiking Barton Creek, trying every taco truck in town, and rewatching Gilmore Girls. She is flirty and teasing but gets shy when fans compliment her smile. She started OnlyFans last year to pay for school.",
  "accountContext": "Free page. The wall has teasing lingerie photos; every explicit set is sold in paid messages. She posts daily around 8 pm Austin time.",
  "physicalDescription": "5'4\", long brown hair, green eyes, freckles, small rose tattoo on her left wrist.",
  "interests": ["hiking", "tacos", "Gilmore Girls", "her cat Pickle", "true crime podcasts"],
  "contentMenu": ["lingerie photo sets", "bath and shower videos", "custom photos", "custom videos"],
  "contentNotes": "Face shown in all photos. Videos are 2 to 6 minutes. No content with other people.",
  "boundaries": ["Does not talk about her ex", "Changes the subject if asked about her family's jobs"],
  "hardLimits": ["No meetups or video calls", "Never shares her last name, school or workplace", "No content with other people"],
  "exemplars": [
    "omg stop you're making me blush 🙈 what are you up to tonight?",
    "ok but that shirt?? where did u get it",
    "just got back from the gym, legs are dead lol",
    "hmm maybe I have something you'd like… want a peek?"
  ],
  "primaryLanguage": "en",
  "additionalLanguages": ["es"],
  "timezone": "America/Chicago",
  "capitalization": "casual_mix",
  "punctuation": "relaxed",
  "messageSplitting": "often",
  "typingSpeed": "natural",
  "emojiPolicy": "some",
  "allowTypos": true,
  "customContentEnabled": true,
  "priceCustomPhotoCents": 3000,
  "priceCustomVideoPerMinCents": 1500,
  "priceCustomVideoMinCents": 5000,
  "customPhotoDeliveryDays": 2,
  "customVideoDeliveryDays": 5,
  "aiDisclosureEnabled": false,
  "handoffKinds": null
}
```

After this call `readyForAi` is `true` and `requiredMissing` is `[]`.

## Small updates

Change one thing without touching the rest:

<CodeGroup>
  ```bash cURL theme={"system"}
  # She moved: update the city only
  curl -X PATCH https://api.onlyx.ai/v1/creators/cre_5f2d9a1c7b3e4f60a2d1/persona \
    -H "Authorization: Bearer $ONLYX_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"city": "San Diego, California", "timezone": "America/Los_Angeles"}'

  # Clear her nickname and her boundaries list
  curl -X PATCH https://api.onlyx.ai/v1/creators/cre_5f2d9a1c7b3e4f60a2d1/persona \
    -H "Authorization: Bearer $ONLYX_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"nickname": null, "boundaries": null}'
  ```

  ```python Python theme={"system"}
  # Append one exemplar (lists are replaced as a whole)
  current = requests.get(f"{API}/creators/{CREATOR}/persona", headers=HEADERS, timeout=30).json()
  exemplars = current["exemplars"] + ["wait you actually remembered that?? cute"]
  requests.patch(f"{API}/creators/{CREATOR}/persona", headers=HEADERS,
                 json={"exemplars": exemplars}, timeout=30).raise_for_status()
  ```

  ```javascript JavaScript theme={"system"}
  // Make "Asks to meet" hand off, on top of the recommended set
  const current = await (await fetch(`${API}/creators/${CREATOR}/persona`, { headers })).json();
  const kinds = new Set(current.handoffEffective);
  kinds.add("real_world.meet_request");
  await fetch(`${API}/creators/${CREATOR}/persona`, {
    method: "PATCH",
    headers: { ...headers, "Content-Type": "application/json" },
    body: JSON.stringify({ handoffKinds: [...kinds] }),
  });
  ```
</CodeGroup>

## Validation limits

A value outside these limits returns `400 VALIDATION_ERROR` whose message starts with the field name, and nothing is saved. The message never repeats the value you sent.

| Field | Limit |
| - | - |
| `personaName` | 120 characters |
| `nickname` | 80 |
| `archetype` | 60 |
| `city`, `origin` | 120 |
| `occupation` | 160 |
| `lore` | 4,000 |
| `accountContext`, `physicalDescription`, `contentNotes` | 1,000 each |
| `aiDisclosureText` | 500 |
| `age` | 18 to 99 |
| `primaryLanguage` | A BCP-47 language tag, up to 20 characters (`en`, `es`, `pt-BR`) |
| `timezone` | A valid IANA timezone, up to 64 characters (`America/Chicago`) |
| `boundaries`, `hardLimits`, `exemplars`, `interests`, `contentMenu` | Up to 50 items, each up to 500 characters |
| `additionalLanguages` | Up to 50 BCP-47 tags, each 2 to 20 characters |
| `capitalization` | `normal`, `lowercase`, `casual_mix` |
| `punctuation` | `proper`, `relaxed`, `minimal` |
| `messageSplitting` | `never`, `sometimes`, `often` |
| `typingSpeed` | `fast`, `natural`, `slow` |
| `emojiPolicy` | `none`, `rare`, `some`, `heavy` |
| `priceCustomPhotoCents`, `priceCustomVideoPerMinCents`, `priceCustomVideoMinCents` | 0 to 1,000,000 |
| `customPhotoDeliveryDays`, `customVideoDeliveryDays` | 1 to 60 |
| `handoffKinds` | Up to 100 keys, each a key from `handoffCatalogue` |

```json Response 400 theme={"system"}
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "timezone: Value error, must be a valid IANA time zone, for example `America/Chicago`",
    "requestId": "req-3f9a1c2b7d4e5f60a1b2c3d4"
  }
}
```

## Readiness

Two places tell you whether the persona is good enough:

* On the persona: `readyForAi` (all five required fields filled: `personaName`, `age`, `city`, `occupation`, `lore`), `requiredMissing`, and `completeness` (0 to 100).
* On the creator (`GET /v1/creators/{creatorId}`): the `readiness` checks `persona` (age and backstory present) and `limits` (at least one hard limit). See [Creators](/developers/concepts/creators#readiness).

Aim for `readyForAi: true`, at least three hard limits, and five or more exemplars before you let the AI chat for a new creator. Turning on [review mode](/developers/guides/ai-settings#review-mode) for the first days lets your team check the AI's replies before fans see them.

## Build a persona with your AI assistant

With the [MCP server](/developers/mcp/overview) connected, the `build_ai_persona` prompt interviews you (or reads notes you paste), drafts every field, shows you the result, and saves it with `update_persona` once you approve. For example: *"Here are my notes about Mia, turn them into her OnlyX persona."*

## Related

* [AI persona](/developers/concepts/ai-persona): every field explained.
* [Hugo, the AI chatter](/developers/concepts/hugo-ai): how the persona is used.
* [Build the AI content ladder](/developers/guides/ai-content): what the AI sells.
