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

# Add a creator

> Add an OnlyFans creator through the API, send her setup link and poll until she is connected: the three connect methods, the OnlyX Login app, the face check, and Telegram creators.

Adding an OnlyFans creator through the API takes two parts:

1. **You** (or your code, or your AI assistant) create the creator in OnlyX and get her one-time **setup link**. It is the same link the dashboard copies with **Copy setup link**.
2. **The creator** opens the link in a browser on her iPhone, Mac or Windows computer and signs in to OnlyFans herself in the **OnlyX Login** app: password, two-factor code, captcha, and OnlyFans' **face (selfie) check** with the camera of that device.

Then you poll the connection until it says `connected`, and OnlyX syncs her inbox, fans, vault and earnings.

This is the dashboard's **Connect with App** method, the only one the API can start on its own. The dashboard has two more, described below. The step-by-step help your team follows in the dashboard is in the Help Center's [Connecting OnlyFans accounts](/connecting) section, and the pages written for the creator herself are in [For creators](/for-creators).

<Info>
  **The API never accepts OnlyFans passwords, two-factor codes or selfies. This is by design.** With the setup link she types her login into OnlyFans' own sign-in page, inside the app on her own device, and the app hands OnlyX the signed-in session, not her password. Do not collect her login, and do not ask an AI assistant to collect it.
</Info>

```mermaid theme={"system"}
sequenceDiagram
  participant You as You or your integration
  participant API as OnlyX API
  participant Her as The creator (her iPhone, Mac or Windows computer)
  participant OF as OnlyFans
  You->>API: POST /v1/creators (Idempotency-Key)
  API-->>You: 201 creator, status disconnected
  You->>API: POST /v1/creators/{creatorId}/connect-link
  API-->>You: 201 url (her setup link), expiresAt (24 hours)
  You->>Her: Send the URL (SMS, WhatsApp, email)
  Her->>Her: Open it in a browser, install OnlyX Login, press Open in OnlyX Login
  Her->>OF: Password, 2FA or e-mail code, captcha, face check (in the app)
  OF-->>Her: Signed in
  loop every 10 seconds or slower
    You->>API: GET /v1/creators/{creatorId}/connection
  end
  API-->>You: status connected, then sync progress
```

## Three ways to connect

In the dashboard, the sign-in window (on the **Creators** page: **Add creator** → **Connect**, or the **Sign in** / **Reconnect** button on her card) has three tabs. Only owners, admins and supervisors can open it. The API gives you the link two of them use, but every sign-in happens on a person's screen:

| Method | Who signs in, and where | Can pass OnlyFans' face check | What the API does |
| - | - | - | - |
| **Connect here** | Someone on your team, in a browser streamed into the dashboard window, typing her email, password and 2FA code | No: the streamed browser has no camera | Nothing to call. There is no field for her login; poll the connection to see the result. |
| **Connect with App** | The creator, in the OnlyX Login app on her iPhone (a TestFlight beta), Mac or Windows computer | Yes, with the camera of that device | `POST /v1/creators/{creatorId}/connect-link` returns her setup link. This guide. |
| **Connect with proxy** | Your team, with her login, from **Connect here**, once her phone (iPhone or Android) is on the account's network through the free Happ app | Yes: she opens OnlyFans' verification link on that phone | Her proxy link is the setup link with `/proxy` on the end (the dashboard's **Copy proxy link**). The sign-in itself needs the dashboard. |

Which one to use:

* **She has an iPhone, a Mac or a Windows computer:** **Connect with App**, as in this guide. Nobody on your team needs her password.
* **You hold her login and OnlyFans will not ask for her face:** **Connect here** is quickest, but it exists only in the dashboard.
* **Her only device is an Android phone** (OnlyX Login does not exist for Android or iPad): **Connect with proxy**, or she opens the setup link on any Mac or Windows computer.

Whichever method connects her, her link is used up and `GET /v1/creators/{creatorId}/connection` reports `connected`, so the polling in step 5 works for all three. The Help Center compares the methods in more detail in [Connecting OnlyFans accounts](/connecting).

<Note>
  **Telegram creators connect differently.** They have no OnlyFans sign-in and no link to send: your team adds them in the dashboard and connects either a ready managed Telegram account or her own number, with a code Telegram sends her. The API lists them with `platform: "telegram"`, but `POST /v1/creators` adds only OnlyFans creators, and a connect link for a Telegram creator is refused with `409 CREATOR_NOT_CONNECTABLE`. See [Telegram creators](/telegram) in the Help Center.
</Note>

## Before you start

* **Scopes**: `creators:write` (create the creator and the link) and `creators:read` (poll the connection).
* **The creator needs**:
  * an **iPhone**, a **Mac** or a **Windows** computer to run OnlyX Login. The iPhone app is a TestFlight beta, installed through Apple's free TestFlight app. On an Android phone, an iPad or a Linux computer her page shows **Sign in from one of these** and asks her to open the link on one of those three instead. If her only device is an Android phone, use **Connect with proxy**;
  * her OnlyFans email and password;
  * access to her two-factor method (authenticator app, SMS or email);
  * to be the **account owner**, in person, for OnlyFans' face check;
  * a few quiet minutes, somewhere with good light.
* **She does not need an OnlyX account.** The link is all she needs.

## Step 1: Create the creator

`POST /v1/creators` with an `Idempotency-Key` header (required). If the request times out, send it again with the **same** key: you get the same creator back instead of a second one.

| Field | Required | Rules | Meaning |
| - | - | - | - |
| `displayName` | Yes | 1 to 120 characters | Your team's label for her. Never changed by OnlyFans. |
| `handle` | No | Her OnlyFans username, without `@`, up to 80 characters | A hint. After she connects, her real OnlyFans username replaces it. Without it, OnlyX derives a handle from `displayName`. Handles are unique within the workspace. |
| `proxyCountry` | No | Two letters, ISO 3166-1 (for example `US`, `GB`, `ES`) | The country OnlyX should run her OnlyFans session from (the dashboard calls it **Preferred location**). Use the country she usually signs in from. A preference, applied when possible. |

Any other field is refused with `400 VALIDATION_ERROR`. The credential must have access to every creator in the workspace: a key limited to some creators cannot add creators (`403 INSUFFICIENT_SCOPE`).

<CodeGroup>
  ```bash cURL theme={"system"}
  curl -X POST https://api.onlyx.ai/v1/creators \
    -H "Authorization: Bearer $ONLYX_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: add-creator-miarose-2026-09-26" \
    -d '{"displayName": "Mia Rose", "handle": "miarose", "proxyCountry": "US"}'
  ```

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

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

  r = requests.post(
      f"{API}/creators",
      headers={**HEADERS, "Idempotency-Key": str(uuid.uuid4())},
      json={"displayName": "Mia Rose", "handle": "miarose", "proxyCountry": "US"},
      timeout=30,
  )
  r.raise_for_status()  # 201 Created
  creator = r.json()
  print(creator["id"], creator["connection"]["status"])  # cre_... disconnected
  ```

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

  const res = await fetch(`${API}/creators`, {
    method: "POST",
    headers: { ...headers, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID() },
    body: JSON.stringify({ displayName: "Mia Rose", handle: "miarose", proxyCountry: "US" }),
  });
  if (res.status !== 201) throw new Error(`${res.status} ${await res.text()}`);
  const creator = await res.json();
  console.log(creator.id, creator.connection.status); // cre_... disconnected
  ```
</CodeGroup>

```json Response 201 theme={"system"}
{
  "id": "cre_5f2d9a1c7b3e4f60a2d1",
  "displayName": "Mia Rose",
  "handle": "miarose",
  "publicName": null,
  "avatarUrl": null,
  "platform": "onlyfans",
  "aiEnabled": true,
  "connection": { "status": "disconnected", "connected": false },
  "onlyfans": null,
  "stats": {
    "fans": 0,
    "conversations": 0,
    "unreadConversations": 0,
    "openHandoffs": 0,
    "revenueCents": 0,
    "pendingCents": 0,
    "revenueKnown": false
  },
  "contentConsentRequired": null,
  "createdAt": "2026-09-26T10:02:11.000Z"
}
```

| Error | When | What to do |
| - | - | - |
| `400 IDEMPOTENCY_KEY_REQUIRED` | No `Idempotency-Key` header | Add one (8 to 64 letters, digits, `-` or `_`). |
| `400 VALIDATION_ERROR` | A field breaks the rules above | Fix the field named in `message`. |
| `403 INSUFFICIENT_SCOPE` | The key lacks `creators:write`, or it is limited to some creators | Use a key with `creators:write` and access to every creator. |
| `409 CONFLICT` | A creator with this handle already exists in the workspace | Use the existing creator (`GET /v1/creators`), or send a different `handle`. |
| `409 IDEMPOTENCY_KEY_REUSED` | The same key was used for a different body | Use a new key for a new creator. |
| `409 CAPACITY_UNAVAILABLE` | OnlyX cannot take a new creator this minute | Wait a few minutes and retry with the same key. |
| `503 CAPACITY_UNAVAILABLE` | Adding creators is briefly unavailable (with `Retry-After`) | Wait `Retry-After` seconds and retry with the same key. |
| `429 RATE_LIMITED` | More than 10 creators added through the API in 24 hours for this workspace | Wait for `Retry-After`, or add her in the dashboard. |

The AI is on by default (`aiEnabled: true`) but does nothing until she is connected. You can set up her [AI persona](/developers/guides/ai-persona) and [AI content](/developers/guides/ai-content) while you wait. Your team also sees her on the dashboard's **Creators** page from now on, and can connect her from there with any of the three methods.

## Step 2: Get her setup link

`POST /v1/creators/{creatorId}/connect-link` returns her one-time **setup link**. No body is needed.

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

  ```python Python theme={"system"}
  link = requests.post(f"{API}/creators/{creator['id']}/connect-link", headers=HEADERS, timeout=30)
  link.raise_for_status()
  link = link.json()
  print(link["url"], "valid until", link["expiresAt"])
  ```

  ```javascript JavaScript theme={"system"}
  const linkRes = await fetch(`${API}/creators/${creator.id}/connect-link`, { method: "POST", headers });
  if (!linkRes.ok) throw new Error(`${linkRes.status} ${await linkRes.text()}`);
  const link = await linkRes.json();
  console.log(link.url, "valid until", link.expiresAt);
  ```
</CodeGroup>

```json Response 201 theme={"system"}
{
  "status": "active",
  "url": "https://app.onlyx.ai/connect/Qm9vX3RoaXNfaXNfYW5fZXhhbXBsZV90b2tlbl9vbmx5",
  "expiresAt": "2026-09-27T10:03:40.000Z",
  "createdAt": "2026-09-26T10:03:40.000Z",
  "opens": 0,
  "lastOpenedAt": null
}
```

The `url` is the link the dashboard copies with **Copy setup link** (in the ⋯ menu on her card on the **Creators** page, or on the **Connect with App** tab of the sign-in window). The API and the dashboard share it: whichever asks first creates it, and the other gets the same link back.

### Three links, one token

Her sign-in involves up to three links. Only the first comes from the API:

| Link | Looks like | Where it comes from | Lasts | What it is for |
| - | - | - | - | - |
| **Setup link** | `https://app.onlyx.ai/connect/…` | `url` from this call, or **Copy setup link** in the dashboard | 24 hours, and used up the moment the account connects | **Connect with App.** She opens it in a browser; the page walks her through the app. |
| **Proxy link** | The setup link with `/proxy` on the end | **Copy proxy link** in the dashboard, or append `/proxy` to `url` | The same token, so the same 24 hours | **Connect with proxy.** She opens it on her phone to get it onto the account's network. |
| **App sign-in link** | `onlyx-connect://open?c=…` | Made by her setup page as it loads (behind **Open in OnlyX Login** and **Copy app sign-in link**), or by the dashboard's **Connect with App** tab | Works once, and for 15 minutes; each new one replaces the last | Opens the OnlyX Login app. Not available through the API. |

Send her the setup link, not an app sign-in link: it lasts a day, and her page makes a fresh app sign-in link whenever she needs one. The app accepts only app sign-in links, so the setup link's web address never works inside the app.

How the setup link behaves:

* It is valid for **24 hours** (`expiresAt`).
* There is **one live link per creator**. Calling `POST` again while a link is active returns **the same link**, and so does **Copy setup link** in the dashboard, so re-sending it is safe and never breaks the one she already has.
* It is **used up** the moment her account connects, whichever of the three methods connected it (`status: "used"`).
* You can **revoke** it at any time with `DELETE /v1/creators/{creatorId}/connect-link` (`204`); that revokes every unused link for the creator. Then `POST` again for a fresh one. The dashboard has no button to cancel a link, so this is the only way to kill one before it expires.
* `opens` counts visits and hand-outs, and `lastOpenedAt` records the latest. Each load of her page counts, and so does each app sign-in link or proxy connection the page hands out, so one visit on an iPhone, Mac or Windows computer usually adds 2 (the page prepares an app sign-in link as it loads). The page's own status checks while she waits do not count. If `opens` rises before you sent the link, or while she says she has not opened it, someone else has it: revoke it and send a new one.
* You can create up to 20 connect links per hour per workspace.
* A creator that cannot be connected through a link gets `409 CREATOR_NOT_CONNECTABLE`, and no link is created: a Telegram creator or a test creator (no OnlyFans account behind her), or one whose status is final (`not_a_creator` or `duplicate`).

<Warning>
  Treat the URL like a password. Anyone who has it can sign an OnlyFans account into this creator. Send it only to her, do not post it in shared channels, and do not log it. The API marks the response `cache-control: no-store`.
</Warning>

`GET /v1/creators/{creatorId}/connect-link` shows the current link, with `status: "none"` (and `url`, `expiresAt`, `createdAt` and `lastOpenedAt` all `null`, `opens` 0) if none was ever created. `status` is one of `active`, `used`, `expired`, `revoked`, `none`; `url` is `null` unless the link is `active`.

## Step 3: Send her the link

Send the URL any way you normally reach her: SMS, WhatsApp, Telegram, Instagram DM or email. A message that works:

> Hi Mia! Here is your secure link to connect your OnlyFans to our team's inbox tool: `<link>`. Open it in the web browser on your iPhone, Mac or Windows computer (not inside the OnlyX Login app). The page walks you through installing OnlyX Login and signing in to OnlyFans yourself. We never see your password. Have your 2FA ready, and do it somewhere with good light, because OnlyFans may ask for a quick selfie check. The link works for 24 hours.

The Help Center's [For creators](/for-creators) section is written for the creator herself (the OnlyX Login app on each device, and the Happ steps for the proxy method); you can send her its pages along with the link.

If her only device is an Android phone, send her the proxy link instead (`url` with `/proxy` on the end, or **Copy proxy link** in the dashboard), and have someone on your team follow the proxy steps in [Connecting OnlyFans accounts](/connecting): they sign her in from the dashboard's **Connect here** tab once her page shows her phone is on the network.

## Step 4: She signs in (her side)

This is what she sees and does with the setup link. Share it with her if she gets stuck.

<Steps>
  <Step title="She opens the link in a browser">
    On an iPhone, a Mac or a Windows computer, the page is headed **Sign in to OnlyFans**, with her @handle and how long the link has left, and offers the OnlyX Login app for that device. Any other device (an Android phone, an iPad, a Linux computer) shows **Sign in from one of these** and asks her to open the same link on an iPhone, a Mac or a Windows computer.
  </Step>

  <Step title="She installs OnlyX Login, the first time only">
    * **Mac or Windows**: she presses **Download for Mac** or **Download for Windows** and follows the short install steps on the page (on Windows, if "Windows protected your PC" appears, she presses **More info**, then **Run anyway**). Then she opens OnlyX Login once.
    * **iPhone**: the app is a TestFlight beta. She installs Apple's free TestFlight app, taps **Get the app (TestFlight)** on the page, then **Accept** and **Install** in TestFlight, and opens OnlyX Login once. Its first screen says "Waiting for your link", which is expected.
  </Step>

  <Step title="She opens her sign-in in the app">
    Back on the page, she presses **Open in OnlyX Login** and chooses **Open** when her browser or iPhone asks.

    * **Mac or Windows**: this button is the only way in. The desktop app has no "Paste your link" button, even though the page mentions one. If nothing opens, she opens OnlyX Login once from Applications (Mac) or the Start menu (Windows), then presses **Open in OnlyX Login** again.
    * **iPhone only**: chat apps often refuse to open app links. If tapping does nothing, she taps **Copy app sign-in link** on the page, opens OnlyX Login and taps **Paste your link**.

    The app sign-in link the button uses works once, and for 15 minutes. If the app says the link has expired, she goes back to her page (the setup link lasts 24 hours) and presses **Get a new app sign-in link**.
  </Step>

  <Step title="She signs in to OnlyFans">
    OnlyFans' own sign-in page opens inside the app. She enters her OnlyFans email and password there. The app hands OnlyX the signed-in session, not her password.
  </Step>

  <Step title="She completes two-factor authentication">
    If her account uses two-factor authentication (2FA), she enters the code from her authenticator app or SMS. If OnlyFans emails her a code, she opens her email and types it in.
  </Step>

  <Step title="She solves any captcha">
    OnlyFans sometimes shows a captcha ("verify you are human"). She solves it in the app.
  </Step>

  <Step title="She completes OnlyFans' face (selfie) check">
    OnlyFans may ask her to prove she is the account owner with a **selfie or face scan**. She allows camera access when asked, and follows OnlyFans' on-screen steps inside the app, with the camera of the phone or computer she is signing in on. It has to be her, the person OnlyFans verified when she opened the account. Nobody can do this step for her.
  </Step>

  <Step title="The app says Connected">
    Once OnlyFans lets her in, the app hands the signed-in OnlyFans session (not her password) to OnlyX and shows **Connected**, and her page switches to "You're connected". She can close the app; nothing keeps running on her device. Keeping the app installed makes a future sign-in quicker.
  </Step>
</Steps>

A smooth sign-in takes a few minutes. Email codes, a captcha and a retried face check can stretch it much longer, so do not give up on her too early. The same steps, written for her, are in the Help Center's [For creators](/for-creators) section.

## Step 5: Poll the connection

`GET /v1/creators/{creatorId}/connection` tells you where she is. Poll it **no more than once every 10 seconds** while she is signing in (the API allows one call per 5 seconds per creator per key, and answers faster calls with `429`). If she is not actively signing in, poll every few minutes instead.

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

  ```python Python theme={"system"}
  conn = requests.get(f"{API}/creators/{creator['id']}/connection", headers=HEADERS, timeout=30).json()
  print(conn["status"], conn["nextStep"], conn["message"])
  ```

  ```javascript JavaScript theme={"system"}
  const conn = await (await fetch(`${API}/creators/${creator.id}/connection`, { headers })).json();
  console.log(conn.status, conn.nextStep, conn.message);
  ```
</CodeGroup>

```json Response 200 (while she signs in) theme={"system"}
{
  "creatorId": "cre_5f2d9a1c7b3e4f60a2d1",
  "status": "connecting",
  "connected": false,
  "verificationRequired": false,
  "message": "The creator is signing in, or OnlyX is waiting for her to sign in again. This can take a few minutes.",
  "nextStep": "wait",
  "duplicateOfCreatorId": null,
  "sync": { "phase": null, "progress": null, "steps": [] },
  "checkedAt": "2026-09-26T10:14:52.000Z"
}
```

```json Response 200 (connected, syncing) theme={"system"}
{
  "creatorId": "cre_5f2d9a1c7b3e4f60a2d1",
  "status": "connected",
  "connected": true,
  "verificationRequired": false,
  "message": "Connected. Her inbox is syncing.",
  "nextStep": "none",
  "duplicateOfCreatorId": null,
  "sync": {
    "phase": "initial",
    "progress": 35,
    "steps": [
      { "key": "conversations", "label": "Recent conversations", "status": "done" },
      { "key": "waiting", "label": "Fans waiting for a reply", "status": "running" },
      { "key": "vault", "label": "Vault media", "status": "pending" }
    ]
  },
  "checkedAt": "2026-09-26T10:21:07.000Z"
}
```

`message` is a human-readable sentence you can show to your team (for example `Connected.` once she is connected and the sync is complete). Branch your code on `status` and `nextStep`, never on `message`:

| `status` | Typical `nextStep` | What it means | What to do |
| - | - | - | - |
| `disconnected` | `send_connect_link` | No sign-in is in progress: none has started, the last one was abandoned, or someone pressed **Disconnect** in the dashboard. The dashboard shows **Disconnected**. | If you have not sent a link, do steps 2 and 3. If you have, check the link: `opens: 0` means she has not opened it yet; `expired` means create a new one. |
| `connecting` | `wait` | A sign-in is set up or in progress, or OnlyFans signed her out and OnlyX is waiting for her to sign in again. The dashboard shows **Signed out**. | Keep polling. This status can appear before she opens the link, so check `opens` to see whether she has. If it lasts more than 30 minutes, ask her where she is stuck (see Troubleshooting). If there is no active link, send a new one. |
| `verification` | `complete_verification_in_app` | She is signed in, but OnlyFans wants a check on the account before it lets OnlyX work. `verificationRequired` is `true`. The dashboard shows **Verification required**. | Someone on your team (an owner, admin or supervisor) presses **Verify on OnlyFans** in [app.onlyx.ai](https://app.onlyx.ai) and completes what OnlyFans shows. If OnlyFans wants her face, that streamed browser cannot pass it: send her the setup link (`POST` still works in this state) or the proxy link. The API cannot complete the check. |
| `connected` | `none` | Working. The link is now used up. The dashboard shows **Connected**. | Stop polling the status. Watch `sync` if you want to know when her history is complete. |
| `not_a_creator` | `delete_and_readd` | Someone signed in with an OnlyFans **fan** account, not a creator account. Final for this creator. The dashboard shows **Not a creator account**. | Delete this creator in the dashboard, add her again, and ask her to sign in with her **creator** account. |
| `duplicate` | `use_duplicate` | This OnlyFans account is already connected as another creator in your workspace. Final. The dashboard shows **Already connected**. | Use the creator in `duplicateOfCreatorId` (it is `null` if your key cannot see that creator), and delete this copy in the dashboard (**Delete this copy**). Do not sign in again on this copy. |

Stop polling on `connected`, `not_a_creator` or `duplicate`, and when the link has expired without her opening it. The statuses are the same whichever method she connects with, so this loop also sees a sign-in your team does in the dashboard.

## Step 6: Sync

After `connected`, OnlyX reads her OnlyFans data in the background. `sync.phase` moves through:

| `sync.phase` | What is being read | What already works |
| - | - | - |
| `initial` | Recent conversations, fans waiting for a reply, the newest vault files, recent earnings | The inbox, sending, and the AI on new messages |
| `collections` | The rest of her fans, fan lists, vault and earnings | Everything above, with growing fan and vault lists |
| `paid_history` | Older message history with fans who have paid | Everything; the AI knows more about big spenders |
| `all_history` | Older message history with everyone else | Everything |
| `complete` | Done. New activity keeps syncing continuously. | Everything |

`sync.progress` is a rough 0 to 100 percentage (or `null`), and `sync.steps` lists what is running now in plain labels (`status` is `pending`, `running`, `done`, `failed` or `skipped`). Step keys can change as OnlyX improves the sync, so show `label` rather than branching on `key`. Before she connects, `sync` is `{"phase": null, "progress": null, "steps": []}`. A large account can take hours to reach `complete`; you do not need to wait for it to start working.

## Full script: create and wait

This script adds a creator, prints the setup link to send, and waits until she is connected (by any of the three methods), then reports sync progress. It retries safely: the `Idempotency-Key` is generated once, and `429` responses are honored.

<CodeGroup>
  ```python Python theme={"system"}
  """Add an OnlyFans creator to OnlyX and wait for her to connect."""
  import os
  import time
  import uuid
  from datetime import datetime, timezone

  import requests

  API = "https://api.onlyx.ai/v1"
  S = requests.Session()
  S.headers["Authorization"] = f"Bearer {os.environ['ONLYX_API_KEY']}"


  def call(method, path, **kw):
      for attempt in range(6):
          r = S.request(method, f"{API}{path}", timeout=30, **kw)
          if r.status_code == 429:
              time.sleep(int(r.headers.get("Retry-After", "10")))
              continue
          if r.status_code >= 500:
              time.sleep(2 ** attempt)
              continue
          if not r.ok:
              err = r.json().get("error", {})
              raise SystemExit(f"{r.status_code} {err.get('code')}: {err.get('message')} (request {err.get('requestId')})")
          return r.json() if r.content else None
      raise SystemExit("OnlyX did not answer after several retries")


  # 1. Create the creator. Reusing this key on a retry returns the same creator.
  creator = call(
      "POST",
      "/creators",
      headers={"Idempotency-Key": str(uuid.uuid4())},
      json={"displayName": "Mia Rose", "handle": "miarose", "proxyCountry": "US"},
  )
  cid = creator["id"]
  print(f"Created {cid}")

  # 2. Get her setup link (returns the live one if it already exists).
  link = call("POST", f"/creators/{cid}/connect-link")
  print(f"Send this link to the creator (valid until {link['expiresAt']}):\n  {link['url']}")
  link_expires = datetime.fromisoformat(link["expiresAt"].replace("Z", "+00:00"))

  # 3. Wait for her to sign in (at most until the link expires).
  FINAL = {"connected", "not_a_creator", "duplicate"}
  last = None
  while True:
      if datetime.now(timezone.utc) > link_expires:
          raise SystemExit("The link expired before she connected. Create a new link and send it again.")
      conn = call("GET", f"/creators/{cid}/connection")
      if conn["status"] != last:
          print(f"[{conn['checkedAt']}] {conn['status']}: {conn['message']} (next step: {conn['nextStep']})")
          last = conn["status"]
      if conn["status"] in FINAL:
          break
      if conn["status"] == "disconnected":
          current = call("GET", f"/creators/{cid}/connect-link")
          if current["status"] in ("expired", "revoked", "none"):
              raise SystemExit("The link is no longer active and she has not connected. Create a new link.")
      time.sleep(15)

  if conn["status"] == "not_a_creator":
      raise SystemExit("She signed in with a fan account. Delete this creator in the dashboard and add her again.")
  if conn["status"] == "duplicate":
      raise SystemExit(f"Already connected as {conn['duplicateOfCreatorId']}. Use that creator.")

  # 4. Report sync progress until the first phase is done (checks for up to an hour).
  for _ in range(60):
      sync = call("GET", f"/creators/{cid}/connection")["sync"]
      print(f"sync: {sync['phase']} {sync['progress']}%")
      if sync["phase"] not in (None, "initial"):
          break
      time.sleep(60)
  print("Connected. Inbox, sending and the AI are working; older history keeps syncing.")
  ```

  ```javascript JavaScript theme={"system"}
  // Add an OnlyFans creator to OnlyX and wait for her to connect. Node.js 18+.
  const API = "https://api.onlyx.ai/v1";
  const auth = { Authorization: `Bearer ${process.env.ONLYX_API_KEY}` };
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

  async function call(method, path, { body, headers = {} } = {}) {
    for (let attempt = 0; attempt < 6; attempt++) {
      const res = await fetch(`${API}${path}`, {
        method,
        headers: { ...auth, ...headers, ...(body ? { "Content-Type": "application/json" } : {}) },
        body: body ? JSON.stringify(body) : undefined,
      });
      if (res.status === 429) { await sleep(Number(res.headers.get("Retry-After") ?? 10) * 1000); continue; }
      if (res.status >= 500) { await sleep(2 ** attempt * 1000); continue; }
      const text = await res.text();
      const data = text ? JSON.parse(text) : null;
      if (!res.ok) throw new Error(`${res.status} ${data?.error?.code}: ${data?.error?.message} (request ${data?.error?.requestId})`);
      return data;
    }
    throw new Error("OnlyX did not answer after several retries");
  }

  // 1. Create the creator. Reusing this key on a retry returns the same creator.
  const creator = await call("POST", "/creators", {
    headers: { "Idempotency-Key": crypto.randomUUID() },
    body: { displayName: "Mia Rose", handle: "miarose", proxyCountry: "US" },
  });
  console.log(`Created ${creator.id}`);

  // 2. Get her setup link (returns the live one if it already exists).
  const link = await call("POST", `/creators/${creator.id}/connect-link`);
  console.log(`Send this link to the creator (valid until ${link.expiresAt}):\n  ${link.url}`);
  const linkExpires = Date.parse(link.expiresAt);

  // 3. Wait for her to sign in (at most until the link expires).
  const FINAL = new Set(["connected", "not_a_creator", "duplicate"]);
  let conn, last;
  for (;;) {
    if (Date.now() > linkExpires) throw new Error("The link expired before she connected. Create a new link and send it again.");
    conn = await call("GET", `/creators/${creator.id}/connection`);
    if (conn.status !== last) {
      console.log(`[${conn.checkedAt}] ${conn.status}: ${conn.message} (next step: ${conn.nextStep})`);
      last = conn.status;
    }
    if (FINAL.has(conn.status)) break;
    if (conn.status === "disconnected") {
      const current = await call("GET", `/creators/${creator.id}/connect-link`);
      if (["expired", "revoked", "none"].includes(current.status)) {
        throw new Error("The link is no longer active and she has not connected. Create a new link.");
      }
    }
    await sleep(15_000);
  }

  if (conn.status === "not_a_creator") throw new Error("She signed in with a fan account. Delete this creator in the dashboard and add her again.");
  if (conn.status === "duplicate") throw new Error(`Already connected as ${conn.duplicateOfCreatorId}. Use that creator.`);

  // 4. Report sync progress until the first phase is done (checks for up to an hour).
  for (let i = 0; i < 60; i++) {
    const { sync } = await call("GET", `/creators/${creator.id}/connection`);
    console.log(`sync: ${sync.phase} ${sync.progress}%`);
    if (sync.phase && sync.phase !== "initial") break;
    await sleep(60_000);
  }
  console.log("Connected. Inbox, sending and the AI are working; older history keeps syncing.");
  ```
</CodeGroup>

## The face check

OnlyFans sometimes asks the person signing in to prove she is the account owner with a selfie or face scan. It is the step most likely to go wrong, and only two of the three methods can pass it:

* **Connect with App** (the setup link): she does it inside OnlyX Login, with the camera of the iPhone, Mac or Windows computer she is signing in on. The iPhone app is a TestFlight beta; if an iPhone sign-in stalls at the face check, a Mac or Windows computer, or the proxy method, is the way round it.
* **Connect with proxy**: your team forwards the verification link or QR code that OnlyFans shows in the dashboard's streamed browser, and she opens it in Safari or Chrome on the phone that has Happ turned on. It can pass there because her phone and the account's browser then share one internet address.

**Connect here** cannot pass it: the browser streamed into the dashboard has no camera. If a face check appears there, switch the same sign-in window to **Connect with App** or **Connect with proxy**. The Help Center explains the check for your team in [Connecting OnlyFans accounts](/connecting) and for her in [For creators](/for-creators).

Share these with her before she starts:

* **Good, even light on her face.** Face a window or a lamp; do not sit with a bright window behind her.
* **Nothing covering her face**: no sunglasses, hat, mask or heavy filter. Hold the phone at eye level and follow the prompts slowly.
* **It must be the account owner**, the same person OnlyFans verified when the account was opened. A manager, assistant or friend cannot do it for her.
* **Finish it where it started.** With the setup link, the check has to happen inside OnlyX Login: do not open OnlyFans' verification page in a separate browser or on another device, and keep the app open in the foreground until it says **Connected**. With the proxy link, she keeps Happ on until the check is done.
* **Do not retry again and again.** OnlyFans may limit how many face checks an account can start. If it fails twice, stop and try again later or the next day.
* **Allow camera access** when the app or her device asks. If she refused it by mistake on an iPhone, she switches it on in Settings › OnlyX Login › Camera and opens her link again. OnlyX does not record the check.

## What the API cannot do

These are deliberate limits, so that the API never handles an OnlyFans login:

* It cannot sign in for her. There is no field for an OnlyFans password, a two-factor code, an email code, a captcha answer or a selfie, and there never will be.
* It cannot run **Connect here** or the proxy sign-in. Both happen in the dashboard's sign-in window, by a person.
* It cannot hand out an app sign-in link. Her setup page makes those, and so does the dashboard's **Connect with App** tab.
* It cannot complete OnlyFans' face check or captcha. She does both, on her device.
* It cannot resolve the `verification` state. Someone on your team does that in the dashboard (**Verify on OnlyFans**), or she signs in again through her setup link if OnlyFans wants her face.
* It cannot add or connect Telegram creators. Your team does that in the dashboard (see [Telegram creators](/telegram)).
* It cannot delete or disconnect a creator in v1. Use the dashboard.

## Troubleshooting

<AccordionGroup>
  <Accordion title="Her page says Link no longer works">
    The setup link itself is dead. Check `GET /v1/creators/{creatorId}/connect-link`: if `status` is `expired` or `revoked`, call `POST` for a new link and send it. If it is `used`, the account is already connected (her page then says "You're connected" instead): check the connection status.
  </Accordion>

  <Accordion title="The app says This link has expired">
    That is the app sign-in link, which works once and for 15 minutes, not her 24-hour setup link. She goes back to her setup page and presses **Get a new app sign-in link**, then **Open in OnlyX Login**. Only if her page also says "Link no longer works" do you need a new link from the API.
  </Accordion>

  <Accordion title="The iPhone app says nothing on the clipboard looks like a connect link">
    She copied the setup link from your chat. The app accepts only app sign-in links (`onlyx-connect://…`), and the setup link is a web page. She opens the setup link in Safari instead, taps **Copy app sign-in link** there, then **Paste your link** in the app. (The Mac and Windows app has no paste button at all: on a computer she uses **Open in OnlyX Login**.)
  </Accordion>

  <Accordion title="The Open in OnlyX Login button does nothing">
    The app is not installed yet, has never been opened, or her browser or chat app blocked the hand-off. On a Mac or Windows computer she installs the app from the page, opens OnlyX Login once from Applications (Mac) or the Start menu (Windows), and presses **Open in OnlyX Login** again. On an iPhone she taps **Copy app sign-in link** on the page, opens OnlyX Login and taps **Paste your link**.
  </Accordion>

  <Accordion title="She only has an Android phone or an iPad">
    OnlyX Login runs on iPhone, Mac and Windows only, so her page shows **Sign in from one of these**. She can open the same link on any Mac or Windows computer, including a friend's, as long as she signs in herself. Or use **Connect with proxy**: send her the proxy link (`url` with `/proxy` on the end) to open on her phone, and your team signs her in from the dashboard.
  </Accordion>

  <Accordion title="She signed in to the wrong OnlyFans account">
    In the app, she signs out inside the sign-in window and signs in again with the right account; she does not need a new link. If a fan account or an account that is already a creator in your workspace got through, the status says so (`not_a_creator` or `duplicate`).
  </Accordion>

  <Accordion title="OnlyFans says the face check failed or there were too many attempts">
    Stop retrying. Improve the light, remove anything covering her face, and make sure the account owner is doing it inside OnlyX Login. If OnlyFans says there were too many attempts, wait until the next day. Her link lasts 24 hours; if it expires meanwhile, create a new one.
  </Accordion>

  <Accordion title="The status stays connecting for a long time">
    Check `opens` on her connect link first: `0` means she has not opened it yet. If she has, she may be stuck on an email code, a captcha or the face check: ask her what the app shows. If she gave up, she can open the same link again while it is active.
  </Accordion>

  <Accordion title="The status is verification">
    OnlyFans wants a check on the account before it lets OnlyX work. Someone on your team opens the creator in [app.onlyx.ai](https://app.onlyx.ai), presses **Verify on OnlyFans** and completes what OnlyFans shows. If OnlyFans wants her face, that streamed browser cannot pass it: send her the setup link so she signs in again on her own device, or use the proxy method. When it is done, the status returns to `connected`.
  </Accordion>

  <Accordion title="The status is not_a_creator">
    Someone signed in with an OnlyFans fan account. Delete the creator in the dashboard, add her again, and remind her to use the account she posts from.
  </Accordion>

  <Accordion title="The status is duplicate">
    That OnlyFans account is already a creator in your workspace (`duplicateOfCreatorId`). Keep working with that one and delete the duplicate in the dashboard with **Delete this copy**. Do not sign in again on the copy.
  </Accordion>

  <Accordion title="A connected creator later goes back to connecting">
    OnlyFans signed the account out, which happens from time to time, and the dashboard shows **Signed out**. Your team can press **Reconnect** in the dashboard and use any of the three methods, or you create a connect link and send it to her; the sign-in is the same as the first time.
  </Accordion>

  <Accordion title="POST connect-link answers 409 CREATOR_NOT_CONNECTABLE">
    The creator has no OnlyFans account to sign in to (a Telegram creator or a test creator), or her status is final (`not_a_creator`, `duplicate`). Telegram creators are connected in the dashboard: see [Telegram creators](/telegram).
  </Accordion>

  <Accordion title="POST /v1/creators answers 409 CAPACITY_UNAVAILABLE">
    OnlyX cannot place a new creator this minute. Wait a few minutes and retry with the same `Idempotency-Key`.
  </Accordion>
</AccordionGroup>

## Doing this from your AI assistant

With the [OnlyX MCP server](/developers/mcp/overview) connected, you can ask: *"Add a new creator called Mia Rose, handle miarose, and give me her connect link."* The assistant calls `add_creator` and `create_connect_link`, and can check back later with `get_connection_status`. It will never ask for her password: the only thing it produces is the setup link you send her. The `onboard_a_creator` prompt runs the whole flow. Like the API, the assistant cannot do a **Connect here** or proxy sign-in; those happen in the dashboard.

## Related

* [Creators](/developers/concepts/creators): statuses (with the dashboard's words for each), readiness and the AI switch.
* [Set up the AI persona](/developers/guides/ai-persona) and [Build the AI content ladder](/developers/guides/ai-content): do these while she connects.
* [Rate limits](/developers/rate-limits): connection polling and link limits.
* Help Center: [Connecting OnlyFans accounts](/connecting) (the three methods, statuses and reconnecting, for your team), [For creators](/for-creators) (pages to send her) and [Telegram creators](/telegram).
