{"openapi":"3.1.0","info":{"title":"The Only API","version":"1.0.0","summary":"OnlyFans and Fansly API for agencies and developers — 200+ endpoints across a signed OnlyFans passthrough and a normalized, Fansly-aware CRM layer.","description":"# The Only API — OnlyFans & Fansly API\n\n**200+ endpoints** for agencies and developers: CRM routes plus OnlyFans passthrough routes. One API key, two platforms — **OnlyFans and Fansly via one normalized API**.\n\nYour CRM panel is the tenant boundary. Each panel manages a set of connected creator accounts (one \"slot\" per account), each with its own persisted session and proxy.\n\n## Platform support: OnlyFans and Fansly\n\nEvery connected account has a `platform` of either `onlyfans` or `fansly`. All three connection routes — `POST /accounts/login`, `POST /accounts/login/cookies`, `POST /accounts/login/verify-otp` — accept a `platform` field in the request body (`\"onlyfans\" | \"fansly\"`, default `\"onlyfans\"`).\n\nOnce connected, the account id is returned as `of_user_id` regardless of platform. That same value is the `{of_user_id}` path parameter and the `user-id` header everywhere in this spec.\n\n**Which surface supports which platform:**\n\n| Surface | OnlyFans | Fansly |\n| --- | --- | --- |\n| CRM data routes (`/accounts/...`, `/fans`, `/events`, `/webhooks`, `/automations`, `/exports`) | yes | yes |\n| Transparent passthrough (`/api2/v2/*`) | yes | **no — rejected** |\n| `POST /accounts/{of_user_id}/payout-requests`, `/campaigns/{campaign_id}/claimers` | yes | **no — rejected** |\n\nRequests to `/api2/v2/*` carrying a `user-id` that belongs to a Fansly account are rejected with an `unsupported` error. Use the platform-neutral CRM data routes instead (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...), which are Fansly-aware.\n\n## Two surfaces\n\n### 1. Transparent passthrough — `/api2/v2/*` (OnlyFans only)\n\nA thin, signed proxy. OnlyFans' own response body is passed through unchanged, but always wrapped in a small envelope: every `/api2/v2/*` response is `{ success, status_code, data }` (plus `relogin: true` when the session was refreshed mid-request). Check `success` first, then read the OnlyFans payload -- including `list`/`hasMore` on paginated endpoints -- from `data`. On error, `success` is false, `status_code` carries the upstream HTTP status, and `data` holds an `error` object with `code` and `message`. Throughout this spec, each `/api2/v2/*` response schema describes the shape of `data` (see the `OFPassthroughEnvelope` schema, and the vault endpoints for worked examples that show the full envelope).\n\nOnlyFans' API requires **signed headers** (`sign`, `time`, `app-token`) that rotate constantly, plus a live session cookie, plus a residential/datacenter proxy. We handle all of that. You hit our endpoints with a single `X-API-Key` + a `user-id` header, and we:\n\n1. Load the saved session for that account (cookies, `x-bc`, `x-hash`)\n2. Route the request through the account's proxy (so OF sees the same IP that logged in)\n3. Generate fresh signed headers server-side using the current OF signing algorithm\n4. Forward to `onlyfans.com/api2/v2/...`\n5. Return the raw JSON response\n\n### 2. Normalized CRM layer (OnlyFans + Fansly)\n\nOn this surface responses **are** normalized — OnlyFans and Fansly rows are projected to the same shape, and several routes are server-side aggregations with no platform equivalent to pass through:\n\n- **Cached reads that cost zero platform requests** — `/subscribers/cached`, `/subscribers/new`, `/subscribers/stats`, `/transactions/cached`, `/fans/{fan_id}/transactions/cached`, `/campaigns/{campaign_id}/claimers/cached`\n- **Async refresh + backfill jobs** — `POST /subscribers/refresh`, `POST /transactions/refresh`, `POST /campaigns/refresh`, `POST /backfill`. Each returns `202`; poll the matching `/refresh/status` route or listen on `GET /events/stream`.\n- **Cross-account earnings aggregation** — `GET /earnings/summary`\n- **Fan CRM** — `GET /fans` with tags, notes, and per-fan cached transactions\n- **Messaging** — `/chats`, `/chats/{with_user_id}/messages`, `POST /messages/mass` (mass DMs), `/ppv-stats`\n- **Data export jobs** — `/exports`, producing a downloadable ZIP\n- **Webhooks** — `/webhooks` with a delivery log and test fire\n- **Automations** — event-triggered actions, `/run-now`, run history\n- **Real-time events** — `GET /events` (poll) and `GET /events/stream` (SSE)\n- **Hosted MCP server** — `GET /api/whoami` resolves an API key to a panel; `PATCH /api/crm/{crm_id}/mcp/unsafe-proxy` toggles whether the MCP server may issue non-GET requests (off by default)\n\nAlso included: session management with automatic re-login, per-account proxy routing, and server-side request signing — the combination behind our zero-ban track record.\n\n## Connecting an account\n\n### (A) Session paste — recommended\n\n**OnlyFans** — grab the session cookies (`sess`, `auth_id`, optionally `fp`) from a logged-in browser (**DevTools → Application → Cookies → onlyfans.com**), send them once with a proxy, and we store the session server-side. You never pass cookies again.\n\n```\nPOST /accounts/login/cookies\nX-API-Key: <your 43-char key>\nX-Proxy: http://user:pass@host:port      (required for OnlyFans)\n\n{\n  \"platform\": \"onlyfans\",\n  \"sess\": \"<sess cookie value>\",\n  \"auth_id\": \"<your OF user id>\",\n  \"fp\": \"<optional fingerprint cookie>\"\n}\n```\n\n**Fansly** — paste an auth token instead of cookies. `X-Proxy` is optional here.\n\n```\nPOST /accounts/login/cookies\nX-API-Key: <your 43-char key>\n\n{\n  \"platform\": \"fansly\",\n  \"auth_token\": \"<Fansly bearer token>\",\n  \"fansly_session_id\": \"<Fansly session id>\",\n  \"fansly_client_id\": \"<optional device/client id>\"\n}\n```\n\nProxies can be HTTP or SOCKS5 — `socks5://user:pass@host:port` (or `socks5h://` for proxy-side DNS) works anywhere a proxy is accepted.\n\n### (B) Credentials (email/username + password)\n\nWe handle the full login flow: Cloudflare init, Turnstile solve, signed login request, 2FA prompt if required.\n\n```\nPOST /accounts/login\nX-API-Key: <your 43-char key>\nX-Proxy: http://user:pass@host:port      (required for OnlyFans, optional for Fansly)\n\n{ \"platform\": \"onlyfans\", \"email\": \"...\", \"password\": \"...\", \"use_captcha\": true }\n```\n\nFor `platform: \"fansly\"` the `email` field accepts a **username or an email**.\n\nIf the platform demands 2FA, the response includes `requires_2fa: true` (Fansly additionally returns `twofa_type`). Submit the code via `POST /accounts/login/verify-otp`, passing the same `platform` and the same identifier you logged in with, plus an `X-Proxy` header.\n\n## Using the passthrough after connection\n\nOnce an OnlyFans account is connected, every `/api2/v2/*` request needs:\n\n- `X-API-Key: <your CRM key>` — **required**\n- `user-id: <of_user_id>` — **required**; which account to act as. Omitting it returns `400 user-id header is required`.\n- `X-Proxy: <proxy>` — optional; overrides the proxy saved at login time\n\nThe URL path mirrors the OnlyFans endpoint:\n\n```\nGET /api/crm/{crm_id}/api2/v2/users/me\nGET /api/crm/{crm_id}/api2/v2/subscriptions/subscribers?limit=10&type=active\nPOST /api/crm/{crm_id}/api2/v2/chats/{fan_id}/messages   (body: { \"text\": \"hi\" })\n```\n\nFull working example:\n\n```bash\ncurl \"https://theonlyapi.com/api/crm/crm_abc123/api2/v2/users/me\" \\\n  -H \"X-API-Key: <your 43-char key>\" \\\n  -H \"user-id: 482687148\"\n```\n\nThat's it. No cookie header, no signing, no proxy rotation — the saved session does the work. There is **no** cookie-bootstrap on this surface: the account must already be connected via `POST /accounts/login` or `POST /accounts/login/cookies`, otherwise the call returns `403 Account not found or does not belong to this CRM panel`.\n\n## Picking an account from your panel\n\n`GET /accounts` lists connected accounts and their ids. Add `?include_session=true` and each entry also carries a `session` block with `sess`, `auth_id`, and `proxy`. Either use those values directly, or just pass the `of_user_id` as the `user-id` header and let us resolve the rest.\n\n`DELETE /accounts/{of_user_id}` disconnects an account and frees its slot.\n\n## Session lifecycle\n\nSessions are persisted per CRM panel. If the platform invalidates a session (password changed, suspicious-activity lockout), we auto-retry with stored credentials when possible. If re-login fails, the endpoint returns `relogin_failed` — reconnect the account.\n\n## Authentication & API keys\n\nSend `X-API-Key` on every request. Keys are tied to your CRM panel — losing one is like losing a password.\n\nA panel has one **primary** key (named `Default`) plus unlimited **secondary** keys:\n\n- `GET /api-keys` — list all keys (active + revoked) with usage counts. Full key bodies are never returned.\n- `POST /api-keys` — mint a secondary key. **Primary key only.** The full key is returned **exactly once**, in this response, and is never retrievable again.\n- `DELETE /api-keys/{key_id}` — revoke a secondary key. **Primary key only.** The primary `Default` key cannot be revoked — rotate it instead.\n- `GET /api-keys/{key_id}/usage` — per-key usage detail (30/90-day series, month + all-time totals, endpoint breakdown).\n- `POST /rotate-key` — rotate the caller's own key. The old key dies immediately; the new key is returned once in the response body.\n\nCalling `POST /api-keys` or `DELETE /api-keys/{key_id}` with a secondary key returns `403`.\n\n`GET /api/whoami` resolves an API key to its `crm_id` — this is how the hosted MCP server turns a bearer token into a tenant.\n\n## Webhook signature verification\n\nEvery webhook delivery is a `POST` to your URL carrying these headers:\n\n| Header | Value |\n| --- | --- |\n| `X-OnlyAPI-Signature` | `sha256=<hex digest>` |\n| `X-OnlyAPI-Timestamp` | Unix timestamp in seconds — the same value that was signed |\n| `X-OnlyAPI-Event` | The event type, e.g. `new_tip` |\n| `X-OnlyAPI-Delivery-Id` | Unique id for this delivery attempt |\n\nThe signed message is **not** the raw body on its own. It is the timestamp, a literal `.`, then the raw request body:\n\n```\nmessage   = \"{X-OnlyAPI-Timestamp}.\" + raw_body\nsignature = \"sha256=\" + HMAC_SHA256(webhook_secret, message).hexdigest()\n```\n\nVerify it against the `X-OnlyAPI-Signature` header using a constant-time comparison:\n\n```python\nimport hmac, hashlib\n\ndef verify(secret: str, timestamp: str, raw_body: bytes, received: str) -> bool:\n    message = f\"{timestamp}.\".encode() + raw_body\n    expected = \"sha256=\" + hmac.new(secret.encode(), message, hashlib.sha256).hexdigest()\n    return hmac.compare_digest(expected, received)\n\n# timestamp = request.headers[\"X-OnlyAPI-Timestamp\"]\n# received  = request.headers[\"X-OnlyAPI-Signature\"]\n# raw_body  = request.get_data()   # bytes, before any JSON parsing\n```\n\nSign the **raw** bytes exactly as received — re-serializing the parsed JSON can change the byte sequence and the signature will not match.\n\n## Base URL\n\n`https://theonlyapi.com/api/crm/{crm_id}`\n\n`{crm_id}` is your panel ID, found in the dashboard → Settings. Routes that are not scoped to a panel (`GET /health`, `GET /api/whoami`, `POST /api/auth/*`, `POST /api/crm/register`) live at `https://theonlyapi.com` instead and are documented here with their full path.\n\n## Plans & pricing\n\n- **Free** — $0/mo. 1,000 API calls/month. Read `GET /usage` for your real `accounts_limit` rather than assuming a slot count.\n- **Slots** — $20 per slot per month ($15 per slot at 15+ slots). 1 slot = 1 connected account. Unlimited API calls.\n- **Enterprise** — custom pricing, custom limits, dedicated support.\n\nCheck current consumption with `GET /usage` (returns `plan`, `api_calls_used`, `api_calls_limit`, `accounts_used`, `accounts_limit`). An `api_calls_limit` of `-1` means unlimited.\n\n## Rate limits\n\nPer-minute HTTP rate limits apply on every plan as anti-flood protection, independently of the monthly call quota:\n\n- **Default:** 1000 requests/minute\n- **Sensitive routes** (writes, key management, mass DMs, exports): 100 requests/minute\n- **Login routes** (`/accounts/login`, `/accounts/login/cookies`, `/accounts/login/verify-otp`): 20 requests/minute\n- **`POST /api/auth/login`:** 6 requests/minute\n- **`GET /health`** and **`GET /events/stream`** are exempt\n\nExceeding a limit returns `429`. Platform-side rate limits still apply per account (roughly 1 req/sec bursts are fine, sustained >5 req/sec will get an account flagged) — we don't enforce those, so pace your live calls and prefer the `/cached` routes, which cost zero platform requests.","contact":{"name":"The Only API Support","url":"https://theonlyapi.com","email":"support@theonlyapi.com"}},"servers":[{"url":"{baseUrl}/api/crm/{crmId}","description":"CRM API Server","variables":{"baseUrl":{"default":"https://theonlyapi.com","description":"Backend server URL"},"crmId":{"default":"your-crm-id","description":"Your CRM panel ID"}}}],"tags":[{"name":"Panel & Usage","description":"Panel signup, health, key→panel resolution, and plan/usage counters. Works for OnlyFans and Fansly panels alike."},{"name":"API Keys","description":"Primary + secondary API key management. `POST /api-keys` and `DELETE /api-keys/{key_id}` are PRIMARY-KEY ONLY (a secondary key gets 403). The primary `Default` key cannot be revoked — rotate it via `POST /rotate-key`."},{"name":"MCP","description":"Hosted MCP server support. `GET /api/whoami` turns a bearer token into a tenant; `PATCH /mcp/unsafe-proxy` toggles non-GET tool access (off by default)."},{"name":"Auth","description":"Connect a creator account (OnlyFans or Fansly) and complete 2FA. All three routes accept `platform: \"onlyfans\" | \"fansly\"` and are rate limited to 10 requests/minute."},{"name":"Accounts","description":"Manage connected creator accounts (OnlyFans and Fansly), their polling settings, proxies, and slots."},{"name":"Earnings","description":"Revenue & earnings data, including the cross-account `GET /earnings/summary` aggregation. Fansly-aware."},{"name":"Subscribers","description":"Subscriber reads. Live routes hit the platform; `/subscribers/cached`, `/subscribers/new`, and `/subscribers/stats` cost zero platform requests and normalize OnlyFans + Fansly rows to the same shape."},{"name":"Cache & Sync","description":"Async refresh, backfill, and job-status routes that populate the local cache. Refresh routes return 202 — poll the matching `/refresh/status` route or listen on `GET /events/stream`."},{"name":"Fans","description":"Fan CRM across all accounts in the panel — tags, notes, per-fan cached transactions, profile refresh. Fansly-aware."},{"name":"Messaging","description":"Chats, single DMs/PPVs, mass DMs, and PPV performance stats. Fansly-aware."},{"name":"Content","description":"Media upload. Runs OnlyFans' four-stage signed-S3 pipeline server-side and returns a reference you attach to a post, message or story via `mediaFiles`. OnlyFans only."},{"name":"Campaigns","description":"Tracking link campaigns, claimers, campaign tags, and the tracked-campaign allowlist."},{"name":"Transactions","description":"Payout & purchase transactions, live and cached."},{"name":"Notifications","description":"Account notifications. Fansly-aware."},{"name":"Settings","description":"Account settings, subscription price & proxy management"},{"name":"Payouts","description":"Withdrawal requests and payout account details. Creating a withdrawal is OnlyFans only."},{"name":"Referrals","description":"OnlyFans referral programme — who the account referred, what it earned, and referral payout history. **OnlyFans only**: Fansly accounts get `501 platform_not_supported` (`feature: \"referrals\"`). Live reads, no cache; one quota call per request. OnlyFans' referral response bodies are passed through with minimal normalization — only the `list`/`hasMore`/`marker` envelope keys are read, and the raw body is echoed under `data`. Fields *inside* a referral row are unverified: do not depend on them without checking against a live account."},{"name":"Exports","description":"Async data export jobs producing a downloadable ZIP. Progress streams over `GET /events/stream`."},{"name":"Events & Streaming","description":"Real-time event feed — `GET /events` for polling, `GET /events/stream` for Server-Sent Events (rate-limit exempt)."},{"name":"Webhooks","description":"Outbound webhook subscriptions, test fires, and delivery history."},{"name":"Automations","description":"Event-triggered automations, manual runs, and run history."},{"name":"Bulk Import","description":"Connect many creator accounts from one pasted list. `POST /import/preview` validates without side effects; `POST /import/jobs` runs the import in the background and streams `import.progress` / `import.complete` over `GET /events/stream`. Rows that hit a 2FA prompt park as `needs_2fa` until you supply a code."},{"name":"Integrations","description":"Third-party integrations. One Telegram channel per panel: pair it once, then matching events are delivered to that chat. The bot token is encrypted at rest and is never returned by any route."},{"name":"Proxy","description":"Generic authenticated OnlyFans API proxy (`POST /accounts/{of_user_id}/request`). OnlyFans only."},{"name":"OF API — User","description":"Direct proxy to OnlyFans user profile & settings endpoints. **OnlyFans only.** Requires `X-API-Key` + `user-id` headers."},{"name":"OF API — Subscribers","description":"OnlyFans subscriber endpoints (via proxy). **OnlyFans only** — Fansly accounts are rejected on this surface."},{"name":"OF API — Subscriptions","description":"OnlyFans subscription management (via proxy). **OnlyFans only** — Fansly accounts are rejected on this surface."},{"name":"OF API — Messaging","description":"OnlyFans chats & messages (via proxy). **OnlyFans only** — Fansly accounts are rejected on this surface."},{"name":"OF API — Content","description":"OnlyFans posts, vault, labels (via proxy). **OnlyFans only** — Fansly accounts are rejected on this surface."},{"name":"OF API — Stories","description":"OnlyFans stories (via proxy). **OnlyFans only** — Fansly accounts are rejected on this surface."},{"name":"OF API — Streams","description":"OnlyFans live streams (via proxy). **OnlyFans only** — Fansly accounts are rejected on this surface."},{"name":"OF API — Campaigns","description":"OnlyFans campaign endpoints (via proxy). **OnlyFans only** — Fansly accounts are rejected on this surface."},{"name":"OF API — Payouts","description":"OnlyFans payout & payment endpoints (via proxy). **OnlyFans only** — Fansly accounts are rejected on this surface."},{"name":"OF API — Notifications","description":"OnlyFans notification endpoints (via proxy). **OnlyFans only** — Fansly accounts are rejected on this surface."},{"name":"OF API — Lists","description":"OnlyFans user lists (via proxy). **OnlyFans only** — Fansly accounts are rejected on this surface."},{"name":"OF API — Promotions","description":"OnlyFans promotions & trials (via proxy). **OnlyFans only** — Fansly accounts are rejected on this surface."},{"name":"OF API — Helpers","description":"OnlyFans helper/manager accounts (via proxy). **OnlyFans only** — Fansly accounts are rejected on this surface."},{"name":"OF API — Misc","description":"Other OnlyFans endpoints. **OnlyFans only** — Fansly accounts are rejected on this surface."}],"components":{"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Your CRM panel API key"}},"schemas":{"Error":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string"}}},"ImportJobRow":{"type":"object","properties":{"id":{"type":"integer","description":"Row ID — this is the `{row_id}` for the OTP and retry routes."},"job_id":{"type":"string"},"row_index":{"type":"integer","description":"Zero-based position within the paste."},"source_line":{"type":"integer","nullable":true,"description":"Line number in the original paste, for error display."},"lane":{"type":"string","enum":["cookie","password"],"description":"How this row connects: pasted session (`cookie`) or credentials (`password`)."},"platform":{"type":"string","enum":["onlyfans","fansly"]},"email":{"type":"string","nullable":true},"label":{"type":"string","nullable":true,"description":"Optional per-row label from the paste."},"proxy":{"type":"string","nullable":true},"status":{"type":"string","enum":["pending","running","success","needs_2fa","needs_2fa_expired","failed","canceled","invalid","skipped","slot_exhausted"]},"has_password":{"type":"boolean"},"has_totp_secret":{"type":"boolean"},"two_fa_expires_at":{"type":"string","nullable":true,"description":"When a parked 2FA challenge stops being answerable."},"two_fa_remaining_seconds":{"type":"integer","nullable":true,"description":"Seconds left on the 2FA window; `null` unless the row is parked."},"of_user_id":{"type":"string","nullable":true,"description":"Set once the account connects."},"username":{"type":"string","nullable":true},"error":{"type":"string","nullable":true},"error_reason":{"type":"string","nullable":true,"description":"Machine-readable failure cause; match on this, not `error`."},"permanent":{"type":"boolean","description":"True when retrying cannot help."},"attempts":{"type":"integer"},"started_at":{"type":"string","nullable":true},"finished_at":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string","nullable":true}}},"SuccessResponse":{"type":"object","properties":{"success":{"type":"boolean","example":true}}},"OFMessage":{"type":"object","description":"An OnlyFans chat message object.","properties":{"id":{"type":"integer"},"text":{"type":"string"},"price":{"type":"number","nullable":true},"isFromQueue":{"type":"boolean"},"isOpened":{"type":"boolean"},"isNew":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"changedAt":{"type":"string","format":"date-time"},"isFree":{"type":"boolean"},"media":{"type":"array","items":{"$ref":"#/components/schemas/OFMedia"}},"fromUser":{"type":"object"}}},"OFMedia":{"type":"object","description":"Media item (photo/video/audio/gif). Returned by the vault endpoints and embedded in posts, messages, and stories.\n\n**No source fingerprint exists.** OnlyFans does NOT expose an original filename, content hash, upload source ID, or any immutable content fingerprint on this object — `id` is the only stable identifier. To correlate a local file with an existing vault item, use `GET /api2/v2/vault/media/hash?hash={md5}`. `files.*.size` is frequently `0` and must never be used as a fingerprint.","properties":{"id":{"type":"integer","description":"Stable OnlyFans media ID — the only persistent identifier for the item."},"type":{"type":"string","enum":["photo","video","audio","gif"]},"createdAt":{"type":"string","format":"date-time"},"duration":{"type":"integer","description":"Duration in seconds (video/audio; 0 for photos)."},"convertedToVideo":{"type":"boolean"},"canView":{"type":"boolean"},"isReady":{"type":"boolean","description":"False while OnlyFans is still processing/transcoding the upload."},"hasError":{"type":"boolean"},"hasPosts":{"type":"boolean","description":"Whether the media is attached to any post."},"hasCustomPreview":{"type":"boolean"},"counters":{"type":"object","description":"Engagement counters (present on the vault view).","properties":{"buyersCount":{"type":"integer"},"likesCount":{"type":"integer"},"tipsSumm":{"type":"number"}}},"listStates":{"type":"array","description":"Which vault lists (folders) this media belongs to.","items":{"type":"object","properties":{"id":{"type":"integer","description":"Vault list ID."},"name":{"type":"string","description":"Vault list name."},"hasMedia":{"type":"boolean"},"canAddMedia":{"type":"boolean"}}}},"files":{"type":"object","description":"Signed, time-limited CDN URLs at several resolutions. `size` is frequently `0` — do not rely on it.","properties":{"full":{"type":"object","properties":{"url":{"type":"string"},"width":{"type":"integer"},"height":{"type":"integer"},"size":{"type":"integer","description":"Frequently 0 — not a reliable fingerprint."}}},"preview":{"type":"object","properties":{"url":{"type":"string"},"width":{"type":"integer"},"height":{"type":"integer"}}},"thumb":{"type":"object","properties":{"url":{"type":"string"},"width":{"type":"integer"},"height":{"type":"integer"}}},"squarePreview":{"type":"object","properties":{"url":{"type":"string"},"width":{"type":"integer"},"height":{"type":"integer"}}}}},"videoSources":{"type":"object","description":"Per-resolution video URLs (e.g. `240`, `720`); entries are null when not applicable.","additionalProperties":{"type":"string","nullable":true}}}},"OFPost":{"type":"object","description":"An OnlyFans post object.","properties":{"id":{"type":"integer"},"text":{"type":"string"},"rawText":{"type":"string"},"price":{"type":"number","nullable":true},"isOpened":{"type":"boolean"},"isPinned":{"type":"boolean"},"postedAt":{"type":"string","format":"date-time"},"postedAtPrecise":{"type":"string"},"expiredAt":{"type":"string","format":"date-time","nullable":true},"commentsCount":{"type":"integer"},"favoritesCount":{"type":"integer"},"tipsAmount":{"type":"number"},"media":{"type":"array","items":{"$ref":"#/components/schemas/OFMedia"}},"author":{"type":"object"},"canComment":{"type":"boolean"},"canEdit":{"type":"boolean"},"hasVoting":{"type":"boolean"},"voting":{"type":"array","nullable":true,"items":{"type":"object","properties":{"id":{"type":"integer"},"text":{"type":"string"},"votesCount":{"type":"integer"},"isSelected":{"type":"boolean"}}}}}},"OFStream":{"type":"object","description":"A live stream object.","properties":{"id":{"type":"integer"},"title":{"type":"string"},"description":{"type":"string"},"isActive":{"type":"boolean"},"startedAt":{"type":"string","format":"date-time"},"room":{"type":"string"},"thumbUrl":{"type":"string"},"viewersCount":{"type":"integer"},"likesCount":{"type":"integer"},"tipsAmount":{"type":"number"}}},"OFStory":{"type":"object","description":"A story object.","properties":{"id":{"type":"integer"},"userId":{"type":"integer"},"createdAt":{"type":"string","format":"date-time"},"expiredAt":{"type":"string","format":"date-time"},"isReady":{"type":"boolean"},"isWatched":{"type":"boolean"},"media":{"type":"array","items":{"$ref":"#/components/schemas/OFMedia"}},"question":{"type":"string","nullable":true}}},"OFUserProfile":{"type":"object","description":"Full OnlyFans user profile object.","properties":{"id":{"type":"integer"},"name":{"type":"string"},"username":{"type":"string"},"displayName":{"type":"string","nullable":true},"about":{"type":"string"},"avatar":{"type":"string","description":"Avatar URL"},"avatarThumbs":{"type":"object","properties":{"c50":{"type":"string"},"c144":{"type":"string"}}},"header":{"type":"string","description":"Profile header/banner URL"},"email":{"type":"string"},"isVerified":{"type":"boolean"},"isPerformer":{"type":"boolean"},"canEarn":{"type":"boolean"},"tipsEnabled":{"type":"boolean"},"subscribedBy":{"type":"boolean","description":"Whether you are subscribed to this user"},"subscribersCount":{"type":"integer"},"subscribesCount":{"type":"integer"},"postsCount":{"type":"integer"},"photosCount":{"type":"integer"},"videosCount":{"type":"integer"},"audiosCount":{"type":"integer"},"friendsCount":{"type":"integer"},"joinDate":{"type":"string","format":"date-time"},"firstPublishedPostDate":{"type":"string","format":"date-time","nullable":true},"subscribePrice":{"type":"number"},"chatMessagesCount":{"type":"integer","description":"Unread chat messages"},"countPriorityChat":{"type":"integer"},"countPinnedChat":{"type":"integer"},"hasPurchasedPosts":{"type":"boolean"},"paidFeed":{"type":"boolean"},"openseaInfo":{"type":"object","nullable":true}}},"OFUserSettings":{"type":"object","description":"Account settings object.","properties":{"needUpdateBanking":{"type":"boolean","description":"Whether banking info needs updating"},"canReceiveManualPayout":{"type":"boolean","description":"Manual payout eligibility"},"isVerifiedReason":{"type":"string","nullable":true,"description":"Verification status reason code"},"needVerifyPayoutData":{"type":"boolean","description":"Whether payout data verification is needed"}}},"OFUserStat":{"type":"object","description":"User statistics / analytics.","properties":{"earnings":{"type":"number"},"revenue":{"type":"number"},"views":{"type":"integer"},"purchases":{"type":"integer"},"tips":{"type":"number"},"comments":{"type":"integer"},"likes":{"type":"integer"}}},"OFChat":{"type":"object","description":"A chat thread object.","properties":{"id":{"type":"integer"},"withUser":{"$ref":"#/components/schemas/OFUserProfile"},"lastMessage":{"$ref":"#/components/schemas/OFMessage"},"unreadMessagesCount":{"type":"integer"}}},"OFMessageTemplate":{"type":"object","description":"A saved message template.","properties":{"id":{"type":"integer"},"name":{"type":"string"},"content":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"OFVaultList":{"type":"object","description":"A vault (media library) list/folder.","properties":{"id":{"type":"integer"},"name":{"type":"string"},"photosCount":{"type":"integer"},"videosCount":{"type":"integer"},"gifsCount":{"type":"integer"},"audiosCount":{"type":"integer"},"hasMedia":{"type":"boolean"},"canUpdate":{"type":"boolean"},"canDelete":{"type":"boolean"},"medias":{"type":"array","description":"A few thumbnail previews (type + url) used as the folder cover.","items":{"type":"object","properties":{"type":{"type":"string"},"url":{"type":"string"}}}}}},"OFLabel":{"type":"object","description":"A user-created label/category for organizing content.","properties":{"id":{"type":"integer"},"name":{"type":"string"},"postsCount":{"type":"integer"},"type":{"type":"string"}}},"OFList":{"type":"object","description":"A user list (fans, bookmarks, custom).","properties":{"id":{"type":"integer"},"name":{"type":"string"},"type":{"type":"string","description":"List type (e.g. 'archived', 'private_archived', or standard)"},"postsCount":{"type":"integer"},"usersCount":{"type":"integer"},"customOrderEnabled":{"type":"boolean"}}},"OFCampaign":{"type":"object","description":"A tracking-link campaign with click/subscriber stats.","properties":{"id":{"type":"integer","example":12345},"campaignName":{"type":"string","example":"My Campaign"},"campaignCode":{"type":"string","description":"Short code / slug used in the tracking URL"},"countTransitions":{"description":"Number of link clicks. May be an integer or `{ count, date }` object when stats=true.","oneOf":[{"type":"integer"},{"type":"object","properties":{"count":{"type":"integer"},"date":{"type":"string"}}}]},"countSubscribers":{"description":"Number of subscribers gained through this campaign. May be an integer or `{ count, date }` object.","oneOf":[{"type":"integer"},{"type":"object","properties":{"count":{"type":"integer"},"date":{"type":"string"}}}]},"createdAt":{"type":"string","format":"date-time"},"sharedWith":{"type":"array","description":"Users this campaign link is shared with","items":{"$ref":"#/components/schemas/OFUserProfile"}}}},"OFPromotion":{"type":"object","description":"A subscription promotion (a discounted or free-trial offer to your list).","properties":{"id":{"type":"integer"},"type":{"type":"string","description":"Promotion type, e.g. 'promotion' or 'free_trial'."},"message":{"type":"string"},"rawMessage":{"type":"string"},"price":{"type":"number","description":"Discounted subscription price."},"subscribeDays":{"type":"integer","description":"Duration the promo subscription lasts."},"subscribeCounts":{"type":"integer"},"claimsCount":{"type":"integer"},"canClaim":{"type":"boolean"},"hasRelatedPromo":{"type":"boolean"},"isFinished":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"finishedAt":{"type":"string","format":"date-time","nullable":true}}},"OFTrial":{"type":"object","description":"A free trial link.","properties":{"id":{"type":"integer"},"code":{"type":"string","description":"Trial link code"},"isFinished":{"type":"boolean"},"sharedWith":{"type":"array","items":{"type":"object"}}}},"OFHelper":{"type":"object","description":"A helper/manager account with delegated access.","properties":{"userId":{"type":"integer"},"user":{"$ref":"#/components/schemas/OFUserProfile"}}},"OFBookmarkCategory":{"type":"object","description":"A bookmark collection/category.","properties":{"id":{"type":"string","description":"'all' for all bookmarks, or numeric ID"},"name":{"type":"string"}}},"OFInitPayload":{"type":"object","description":"App initialization data — config, feature flags, and authenticated user state.","properties":{"user":{"$ref":"#/components/schemas/OFUserProfile"},"chatMessagesCount":{"type":"integer"},"countPriorityChat":{"type":"integer"},"countPinnedChat":{"type":"integer"},"hasPurchasedPosts":{"type":"boolean"},"paidFeed":{"type":"boolean"}}},"PaginatedList":{"type":"object","description":"Standard paginated response wrapper used by most OF API list endpoints.","properties":{"list":{"type":"array","items":{"type":"object"}},"hasMore":{"type":"boolean"}}},"OFPassthroughEnvelope":{"type":"object","description":"Standard wrapper around EVERY `/api2/v2/*` passthrough response. OnlyFans' raw body is nested under `data` — check `success` first, then read `data` (which itself usually contains `list`/`hasMore` or the OF object). On an error, `success` is `false`, `status_code` carries the upstream HTTP status, and `data` typically holds `{ error: { code, message } }`. A `relogin: true` field is added when the session was refreshed mid-request.","properties":{"success":{"type":"boolean","description":"True when OnlyFans returned a 2xx status."},"status_code":{"type":"integer","description":"The upstream OnlyFans HTTP status code."},"data":{"description":"The raw OnlyFans response body (object or array)."},"relogin":{"type":"boolean","description":"Present and true when the session was refreshed mid-request."}},"required":["success","status_code","data"]}},"parameters":{"ofUserId":{"name":"of_user_id","in":"path","required":true,"schema":{"type":"string"},"description":"Creator account ID of the connected account (OnlyFans or Fansly). Use `GET /accounts` to list connected accounts and their IDs."},"ofUserIdHeader":{"name":"user-id","in":"header","required":true,"schema":{"type":"string"},"description":"Creator account ID of the account to act as (e.g. `509955039`). **Required** — omitting it returns `400 user-id header is required`. Use `GET /accounts` to list your connected accounts. On `/api2/v2/*` the account must be an OnlyFans account; Fansly accounts are rejected there.","example":"509955039"},"proxyHeader":{"name":"X-Proxy","in":"header","required":false,"schema":{"type":"string"},"description":"Proxy URL — HTTP or SOCKS5 (e.g. http://user:pass@host:port or socks5://user:pass@host:port). Optional on post-connection routes, where it overrides the proxy saved at login time. **Required on the connection routes** (`POST /accounts/login`, `POST /accounts/login/cookies`, `POST /accounts/login/verify-otp`) for OnlyFans accounts; optional for Fansly."},"fanId":{"name":"fan_id","in":"path","required":true,"schema":{"type":"string"},"description":"Fan's platform user ID."},"fanOfUserId":{"name":"fan_of_user_id","in":"path","required":true,"schema":{"type":"string"},"description":"Fan's platform user ID — the same value returned as `of_user_id` on a row from `GET /fans`, not an internal database id."},"importJobId":{"name":"job_id","in":"path","required":true,"schema":{"type":"string"},"description":"Import job ID (32-character hex) returned by `POST /import/jobs`."},"importRowId":{"name":"row_id","in":"path","required":true,"schema":{"type":"integer"},"description":"Row ID within the import job — the `id` field of a row from `GET /import/jobs/{job_id}`, not its `row_index`."},"refreshKind":{"name":"kind","in":"path","required":true,"schema":{"type":"string","enum":["subs","tx","campaigns"]},"description":"Which refresh job to act on: `subs` (subscribers), `tx` (transactions), or `campaigns` (campaign claimers)."},"campaignId":{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string"},"description":"Tracking-link campaign ID."},"limitParam":{"name":"limit","in":"query","schema":{"type":"integer","default":100},"description":"Maximum number of rows to return."},"offsetParam":{"name":"offset","in":"query","schema":{"type":"integer","default":0},"description":"Row offset for pagination."}}},"security":[{"apiKey":[]}],"paths":{"/api2/v2/accepted-cookies":{"post":{"tags":["OF API — User"],"summary":"Record cookie consent","description":"Stores the user's accepted cookie/consent preferences. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible (cookie consent payload)"}}},"/api2/v2/address":{"post":{"tags":["OF API — Payouts"],"summary":"Save address","description":"Submits/saves an address for the user; appears alongside payout/Stripe and country endpoints, suggesting a payout or billing address. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; address fields not statically visible"}}},"/api2/v2/address/stat":{"post":{"tags":["OF API — Misc"],"summary":"Record address statistics","description":"Posts address-related statistics data. Appears alongside GDPR, clicks-stats and accepted-cookies analytics calls. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/age-verifier/start":{"post":{"tags":["OF API — User"],"summary":"Start age verification","description":"Initiates the age verification flow. Grouped with iv/start and face-id/start identity checks. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/alternative-payment-methods":{"delete":{"tags":["OF API — Payouts"],"summary":"Delete alternative payment method","description":"Removes a saved alternative payment method, identified by id in the request body. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"get":{"tags":["OF API — Payouts"],"summary":"List alternative payment methods","description":"Returns the available alternative (non-card) payment methods for the user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"array"}}}}}}}}},"/api2/v2/alternative-payment-methods/form":{"post":{"tags":["OF API — Payouts"],"summary":"Submit alternative payment method form","description":"Submits the form for an alternative payment method. A GET on the same path retrieves the form definition. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/alternative-payment-methods/pay":{"post":{"tags":["OF API — Payouts"],"summary":"Pay via alternative method","description":"Submits a payment through an alternative payment method (e.g. PayPal). Related GET/POST endpoints handle the alternative-payment-methods form and listing. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; payment fields not statically visible"}}},"/api2/v2/alternative-payment-methods/paypal":{"get":{"tags":["OF API — Payouts"],"summary":"Get PayPal payment method info","description":"Returns the current PayPal alternative payment method configuration/status for the user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"paypalStatus":{"type":"array"}}}}}}}}}}},"/api2/v2/av/email-check":{"post":{"tags":["OF API — User"],"summary":"Check account email","description":"Triggers an email check for the account (av module). Called with no arguments. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/campaigns/transition":{"post":{"tags":["OF API — Promotions"],"summary":"Transition a campaign state","description":"Transitions a promotional campaign to a new state/status. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; transition fields not statically visible"}}},"/api2/v2/campaigns/{campaign_id}":{"delete":{"tags":["OF API — Promotions"],"summary":"Delete campaign","description":"Deletes a promotional campaign by id. Grouped with campaign share-access, claimers and trials endpoints. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the campaign to delete"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/chats/mark-as-read":{"post":{"tags":["OF API — Messaging"],"summary":"Mark chats as read","description":"Marks one or more chats as read for the current user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; likely chat/user ids, not statically visible"}}},"/api2/v2/chats/{user_id}/messages/{message_id}":{"get":{"tags":["OF API — Messaging"],"summary":"Get single chat message","description":"Retrieves a specific message within the chat with a given user. Called as getMessage({userId, groupId}). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the user the chat is with"},{"name":"message_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the message (passed as groupId in the caller)"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/comments/{comment_id}":{"delete":{"tags":["OF API — Content"],"summary":"Delete a comment","description":"Deletes a specific comment by its id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the comment to delete"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/comments/{comment_id}/like":{"delete":{"tags":["OF API — Content"],"summary":"Unlike a comment","description":"Removes the current user's like from a comment (POST on the same path adds a like). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the comment to unlike"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"post":{"tags":["OF API — Content"],"summary":"Like a comment","description":"Adds a like to the specified comment. Paired with a DELETE on the same path to unlike, plus comment pin/delete calls. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the comment to like"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/comments/{comment_id}/pin":{"delete":{"tags":["OF API — Content"],"summary":"Unpin a comment","description":"Removes the pinned status from a comment. The paired POST /comments/{id}/pin pins it. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the comment to unpin"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"post":{"tags":["OF API — Content"],"summary":"Pin a comment","description":"Pins the specified comment. Paired with a DELETE on the same path to unpin. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the comment to pin"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/consent-form":{"post":{"tags":["OF API — Misc"],"summary":"Submit consent form","description":"Submits/creates a consent form. Grouped with release-form and release-form-proof endpoints for content compliance. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/countries/payouts":{"get":{"tags":["OF API — Payouts"],"summary":"List payout-supported countries","description":"Returns the list of countries supported/available for creator payouts. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"code":{"type":"string"},"name":{"type":"string"},"hasStates":{"type":"boolean"},"hasZip":{"type":"boolean"},"canPay":{"type":"boolean"},"canHasW9Form":{"type":"boolean"}}}}}}}}}}}},"/api2/v2/countries/{country_id}/address/expand":{"post":{"tags":["OF API — Misc"],"summary":"Expand address for country","description":"Expands/resolves a partial address (by its hash) for a given country, optionally in Latin transliteration. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"country_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the country for address expansion"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"hash":{"type":"string","description":"Address identifier/hash to expand (addressId)"},"isLatin":{"type":"boolean","description":"Whether to return Latin-script address"}}}}}}}},"/api2/v2/countries/{country_id}/states":{"get":{"tags":["OF API — Misc"],"summary":"List states for a country","description":"Returns the list of states/regions for the specified country. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"country_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the country whose states are requested"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/earnings/chart":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Payouts"],"summary":"Get earnings chart","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":true,"schema":{"type":"string"},"example":"2025-01-01 00:00:00"},{"name":"endDate","in":"query","schema":{"type":"string"},"example":"2025-12-31 23:59:59"},{"name":"withTotal","in":"query","schema":{"type":"string","default":"true"}}],"responses":{"200":{"description":"Earnings chart data","content":{"application/json":{"schema":{"type":"object","properties":{"total":{"type":"object","properties":{"total":{"type":"number","description":"Total net earnings"},"gross":{"type":"number","description":"Total gross earnings"},"chartAmount":{"type":"array","items":{"type":"number"},"description":"Earnings per time bucket"},"chartCount":{"type":"array","items":{"type":"number"},"description":"Transaction count per time bucket"}}}}}}}}}}},"/api2/v2/emails/change":{"delete":{"tags":["OF API — User"],"summary":"Cancel pending email change","description":"Cancels a pending email-change request (POST on the same path initiates one). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"post":{"tags":["OF API — User"],"summary":"Request email address change","description":"Initiates a change of the account's email address. A DELETE on the same path cancels a pending change. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/emails/resend":{"post":{"tags":["OF API — User"],"summary":"Resend confirmation email","description":"Resends the account confirmation/verification email. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/face-id/postpone":{"post":{"tags":["OF API — User"],"summary":"Postpone face-ID verification","description":"Postpones the required Face ID identity verification step. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/face-id/start":{"post":{"tags":["OF API — User"],"summary":"Start Face ID verification","description":"Starts a Face ID (biometric identity) verification session. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object (optional, defaults to {})"}}},"/api2/v2/guests/{guest_id}":{"get":{"tags":["OF API — Misc"],"summary":"Get guest details","description":"Retrieves a guest record by id. Guests appear alongside release-form and guest-assign endpoints (people tagged in content). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"guest_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the guest"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/guests/{guest_id}/assign":{"post":{"tags":["OF API — Misc"],"summary":"Assign a guest","description":"Assigns a guest (co-performer/guest record) identified by guest_id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"guest_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the guest to assign"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/helpers/{helper_id}":{"delete":{"tags":["OF API — User"],"summary":"Remove account helper","description":"Removes a helper (delegated team member) from the account. Related calls manage helper permissions and login-as-helper. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"helper_id","in":"path","required":true,"schema":{"type":"string"},"description":"User ID of the helper to remove"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/helpers/{user_id}":{"post":{"tags":["OF API — User"],"summary":"Add or update account helper","description":"Grants or updates an account helper (team member) identified by user_id, assigning the given permission set. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"User id of the helper being granted permissions"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"permissions":{"type":"array","description":"List of permission keys to grant the helper (defaults to empty array)"}}}}}}}},"/api2/v2/ip":{"get":{"tags":["OF API — Misc"],"summary":"Get client IP address","description":"Returns the caller's IP address as seen by the API. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"ip":{"type":"string"},"geo":{"type":"object","properties":{"connectionType":{"type":"string"},"userType":{"type":"string"},"subdivisionConfidence":{"type":"string"},"postalConfidence":{"type":"string"},"isp":{"type":"string"},"domain":{"type":"string"},"countryConfidence":{"type":"string"},"cityConfidence":{"type":"string"},"legitimateProxy":{"type":"string"},"regionName":{"type":"string"},"region":{"type":"string"},"regionGeonameid":{"type":"string"},"registeredCountryInEu":{"type":"string"},"registeredCountryName":{"type":"string"},"registeredCountryIso":{"type":"string"},"registeredCountryGeonameid":{"type":"string"},"postalCode":{"type":"string"},"locationTimezone":{"type":"string"},"locationMetrocode":{"type":"string"},"longitude":{"type":"string"},"latitude":{"type":"string"},"locationAccuracyRadius":{"type":"string"},"countryInEu":{"type":"string"},"countryName":{"type":"string"},"countryCode":{"type":"string"},"countryGeonameid":{"type":"string"},"continentName":{"type":"string"},"continentGeonameid":{"type":"string"},"continentCode":{"type":"string"},"cityGeonameid":{"type":"string"},"city":{"type":"string"},"cityBuildDate":{"type":"string"}}},"isEurope":{"type":"boolean"}}}}}}}}}}},"/api2/v2/iso/countries/{country_id}/states":{"get":{"tags":["OF API — Misc"],"summary":"List states for country","description":"Returns the ISO list of states/regions for a given country. Reference/lookup data. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"country_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the country"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/issues/login":{"post":{"tags":["OF API — User"],"summary":"Report login issue","description":"Submits a login issue report (support). Called with an optional payload object. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/iv/start":{"post":{"tags":["OF API — User"],"summary":"Start identity verification","description":"Starts an identity-verification (IV) flow for the user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object (defaults to {}); fields not statically visible"}}},"/api2/v2/iv/yoti-face-id-url/{id}/{token}":{"get":{"tags":["OF API — User"],"summary":"Get Yoti face-ID verification URL","description":"Returns a Yoti face-ID identity-verification URL for the given identifiers. Part of the /iv identity-verification module (also yoti-redirect-url). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"First path identifier (uncertain; likely verification/session id)"},{"name":"token","in":"path","required":true,"schema":{"type":"string"},"description":"Second path identifier (uncertain; likely a token/type/hash)"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/iv/yoti-redirect-url/{verification_id}":{"get":{"tags":["OF API — User"],"summary":"Get Yoti identity verification URL","description":"Retrieves a Yoti identity-verification redirect URL for the identity verification (iv) flow. Sibling call fetches a Yoti face-ID URL. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"verification_id","in":"path","required":true,"schema":{"type":"string"},"description":"Identifier for the Yoti identity verification session (exact meaning uncertain)"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/labels/sort":{"post":{"tags":["OF API — Lists"],"summary":"Sort labels","description":"Reorders the user's labels according to the supplied order. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; likely an ordered list of label ids"}}},"/api2/v2/labels/{label_id}":{"delete":{"tags":["OF API — Lists"],"summary":"Delete a label","description":"Deletes a label identified by label id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"label_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the label to delete"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"get":{"tags":["OF API — Lists"],"summary":"Get a label by ID","description":"Retrieves a single content label by its ID. Sibling calls create, rename, sort and delete labels. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"label_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the label to retrieve"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"patch":{"tags":["OF API — Lists"],"summary":"Rename a label","description":"Renames the label identified by label_id to the provided name. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"label_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the label to rename"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"New label name"}}}}},"description":"Payload is {name: t}"}}},"/api2/v2/labels/{label_id}/post/{post_id}":{"delete":{"tags":["OF API — Lists"],"summary":"Remove post from label","description":"Removes a single post from the specified label. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"label_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the label"},{"name":"post_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the post to remove from the label"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/labels/{label_id}/posts":{"delete":{"tags":["OF API — Lists"],"summary":"Remove all posts from label","description":"Removes all posts from the specified label. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"label_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the label to clear"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"post":{"tags":["OF API — Lists"],"summary":"Add posts to label","description":"Adds one or more posts to a label. Called as (labelId, posts[]). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"label_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the label"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"posts":{"type":"array","description":"IDs of the posts to add to the label"}}}}}}}},"/api2/v2/legal-inquiry":{"post":{"tags":["OF API — Misc"],"summary":"Submit a legal inquiry","description":"Creates/submits a new legal inquiry (takedown/legal request). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; legal inquiry submission payload"}}},"/api2/v2/legal-inquiry/by-counsel":{"post":{"tags":["OF API — Misc"],"summary":"Submit legal inquiry by counsel","description":"Submits a legal inquiry on behalf of legal counsel. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; inquiry fields not statically visible"}}},"/api2/v2/legal-inquiry/change-status/{inquiry_id}":{"post":{"tags":["OF API — Misc"],"summary":"Change legal inquiry status","description":"Changes the status of a legal inquiry. Part of the legal-inquiry (static-law) module. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"inquiry_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the legal inquiry"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object (status data); fields not statically visible"}}},"/api2/v2/legal-inquiry/params":{"get":{"tags":["OF API — Misc"],"summary":"Get legal inquiry form params","description":"Returns the parameter/option definitions used to build the legal inquiry form. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"newReportsCount":{"type":"integer"},"fileAllowedExtensions":{"type":"array","items":{"type":"string"}},"legalInquiry":{"type":"object","properties":{"matterTypes":{"type":"array","items":{"type":"object"}}}},"additional":{"type":"object","properties":{"category":{"type":"array","items":{"type":"object"}},"subject":{"type":"array","items":{"type":"object"}}}},"gdprOptions":{"type":"object","properties":{"groups":{"type":"object","properties":{"sdt-group":{"type":"object"},"sw-group":{"type":"object"},"content-group":{"type":"object"},"no-content-group":{"type":"object"},"civil_subpoena-group":{"type":"object"}}},"reports":{"type":"object","properties":{"personal_data":{"type":"object"},"payment_billing":{"type":"object"},"access_links":{"type":"object"},"correspondence":{"type":"object"},"posts_deleted_posts":{"type":"object"},"vaults":{"type":"object"},"audit":{"type":"object"}}}}},"highPrioritySubjects":{"type":"array","items":{"type":"string"}},"success":{"type":"boolean"}}}}}}}}}}},"/api2/v2/legal-inquiry/send-notification/{inquiry_id}":{"post":{"tags":["OF API — Misc"],"summary":"Send legal inquiry notification","description":"Sends a notification for the specified legal inquiry. No request body is sent. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"inquiry_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the legal inquiry"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/legal-inquiry/{inquiry_id}":{"get":{"tags":["OF API — Misc"],"summary":"Get legal inquiry","description":"Retrieves a legal inquiry (e.g. legal/DMCA request) by ID. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"inquiry_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the legal inquiry"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"post":{"tags":["OF API — Misc"],"summary":"Submit legal inquiry response","description":"Submits data/response for a specific legal inquiry. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"inquiry_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the legal inquiry"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque `data` object; fields not statically visible"}}},"/api2/v2/legal-inquiry/{inquiry_id}/history":{"get":{"tags":["OF API — Misc"],"summary":"Get legal inquiry history","description":"Returns the history/audit trail for a legal inquiry by id (from the legal/law module). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"inquiry_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the legal inquiry"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/legal-inquiry/{inquiry_id}/update/{hash}":{"get":{"tags":["OF API — Misc"],"summary":"Get legal inquiry update","description":"Retrieves a specific update of a legal inquiry identified by inquiry_id, addressed by an update hash. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"inquiry_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the legal inquiry"},{"name":"hash","in":"path","required":true,"schema":{"type":"string"},"description":"Hash identifying the specific inquiry update"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"post":{"tags":["OF API — Misc"],"summary":"Update a legal inquiry","description":"Submits an update to a legal inquiry identified by its ID and hash. Part of the legal-inquiry module (search, history, change-status). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"inquiry_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the legal inquiry"},{"name":"hash","in":"path","required":true,"schema":{"type":"string"},"description":"Update hash/token authorizing the update"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object (caller 'data'); fields not statically visible"}}},"/api2/v2/lists/check/{list_id}/{user_id}":{"get":{"tags":["OF API — Lists"],"summary":"Check list membership","description":"Checks whether a given user belongs to a specific list. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the list"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the user to check"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/lists/sort":{"post":{"tags":["OF API — Lists"],"summary":"Sort user lists","description":"Persists a new ordering of the user's lists. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object (ordering payload); fields not statically visible"}}},"/api2/v2/lists/users":{"post":{"tags":["OF API — Lists"],"summary":"Add users to lists","description":"Adds users to one or more lists in bulk. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; likely userIds + listIds, not statically visible"}}},"/api2/v2/lists/{list_id}":{"delete":{"tags":["OF API — Lists"],"summary":"Delete a list","description":"Deletes a user list by id (GET retrieves it, PATCH renames/updates it). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the list to delete"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"get":{"tags":["OF API — Lists"],"summary":"Get a user list","description":"Retrieves a single user list identified by list_id. Enclosing fn is getUsersList. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the list to retrieve"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"patch":{"tags":["OF API — Lists"],"summary":"Update a list","description":"Updates the given user list (getUsersList/updateList module). Sibling calls create, delete and sort lists. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the list to update"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object (caller 'data'); fields not statically visible"}}},"/api2/v2/lists/{list_id}/sort":{"post":{"tags":["OF API — Lists"],"summary":"Sort users in list","description":"Sorts the users within a list. Called as sortListUsers({listId, data}). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the list"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object (sort data); fields not statically visible"}}},"/api2/v2/lists/{list_id}/users/awards/{year}/{month}":{"post":{"tags":["OF API — Lists"],"summary":"Add award-winning users to list","description":"Adds users who received awards in the given year/month to the specified list. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the list"},{"name":"year","in":"path","required":true,"schema":{"type":"string"},"description":"Year of the awards period"},{"name":"month","in":"path","required":true,"schema":{"type":"string"},"description":"Month of the awards period"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/lists/{list_id}/users/campaign/{campaign_id}/claimers":{"post":{"tags":["OF API — Lists"],"summary":"Add campaign claimers to list","description":"Adds the users who claimed a campaign to a custom list. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the list"},{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the campaign whose claimers are added"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/lists/{list_id}/users/media/{media_id}/buyers":{"post":{"tags":["OF API — Lists"],"summary":"Add media buyers to list","description":"Adds users who purchased a given media item to a list (bulk add by media). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the target list"},{"name":"media_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the media whose buyers are added"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/lists/{list_id}/users/pinned/sort":{"post":{"tags":["OF API — Lists"],"summary":"Sort pinned list users","description":"Reorders the pinned users within a list identified by list_id, per the provided order. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the list whose pinned users are sorted"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"order":{"type":"array","description":"Desired ordering of pinned user IDs"}}}}},"description":"Payload is {order: t}"}}},"/api2/v2/lists/{list_id}/users/queue/{queue_id}/buyers":{"post":{"tags":["OF API — Lists"],"summary":"Add queue buyers to list","description":"Adds users who bought from a given queue to a list (bulk add by queue). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the target list"},{"name":"queue_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the queue whose buyers are added"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/lists/{list_id}/users/story/{story_id}/{type}":{"post":{"tags":["OF API — Lists"],"summary":"Add story viewers to list","description":"Adds users who interacted with a given story (by interaction type) to the specified list. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the target list"},{"name":"story_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the story"},{"name":"type","in":"path","required":true,"schema":{"type":"string"},"description":"Interaction type used to select users"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/lists/{list_id}/users/stream/{stream_id}/{type}":{"delete":{"tags":["OF API — Lists"],"summary":"Remove stream users from list","description":"Removes stream-derived users (e.g. viewers who tipped or subscribed over a threshold) of a given type from a list. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the list"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream"},{"name":"type","in":"path","required":true,"schema":{"type":"string"},"description":"User selection type (passed as the trailing path segment)"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"post":{"tags":["OF API — Lists"],"summary":"Add stream audience to list","description":"Adds users from a live stream (matching the given type/criteria) to a list, optionally filtered by tip/subscription thresholds. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the target list"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the stream whose audience is added"},{"name":"type","in":"path","required":true,"schema":{"type":"string"},"description":"Audience segment/type key (e.g. viewers/tippers)"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"tippedOver":{"type":"number","description":"Only include users who tipped over this amount"},"subscribedOver":{"type":"number","description":"Only include users subscribed over this threshold"}}}}}}}},"/api2/v2/lists/{list_id}/users/subscribers":{"post":{"tags":["OF API — Lists"],"summary":"Add subscribers to list","description":"Adds subscribers to the list identified by list_id. Enclosing fn is addSubscribersToList. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the list to add subscribers to"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object (rest of args); fields not statically visible"}}},"/api2/v2/lists/{list_id}/users/top-subscribers":{"post":{"tags":["OF API — Lists"],"summary":"Add top subscribers to list","description":"Adds the account's top subscribers to the given list (addTopSubscribersToList). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the list to add top subscribers to"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; spread of caller-provided fields, not statically visible"}}},"/api2/v2/lists/{list_id}/users/{type}/{id}/claims":{"post":{"tags":["OF API — Lists"],"summary":"Add claimers to list","description":"Adds users who claimed a given entity (identified by type and id) to the specified list. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the target list"},{"name":"type","in":"path","required":true,"schema":{"type":"string"},"description":"Claim entity type"},{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the entity of the given type"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/lists/{list_id}/users/{user_id}/pin":{"post":{"tags":["OF API — Lists"],"summary":"Pin user in list","description":"Pins a user to the top of a custom list. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the list"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the user to pin"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/log":{"post":{"tags":["OF API — Misc"],"summary":"Submit client-side log entry","description":"Sends a client log message with optional context data, logger name and level to the server-side logging endpoint. The context is wrapped with an `onlyfans.<logger>` logger name and a timestamp. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Log message text"},"context":{"type":"object","description":"Arbitrary log data merged with logger name and timestamp"},"level":{"type":"string","description":"Log level, e.g. debug/info/error"}}}}},"description":"Wrapper helpers force level to 'debug'"}}},"/api2/v2/logins":{"get":{"tags":["OF API — User"],"summary":"List login sessions","description":"Returns the account's login sessions/history. Paired with DELETE /logins/{id} to revoke a session. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"login":{"type":"string"},"date":{"type":"string"},"isPersistent":{"type":"boolean"}}}},"hasMore":{"type":"boolean"}}}}}}}}}}},"/api2/v2/logins/{login_id}":{"delete":{"tags":["OF API — User"],"summary":"Revoke a login session","description":"Revokes/removes an active login session identified by login id. Paired with GET /logins which lists active sessions. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"login_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the login/session to revoke"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/messages/queue/chart":{"get":{"tags":["OF API — Misc"],"summary":"Get messages earnings chart","description":"Returns time-series earnings/statistics for queued messages. The dynamic path segment is a built querystring. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":true,"schema":{"type":"string"},"description":"Start of the date range"},{"name":"endDate","in":"query","required":true,"schema":{"type":"string"},"description":"End of the date range"},{"name":"withTotal","in":"query","required":false,"schema":{"type":"string"},"description":"Include totals in the response"},{"name":"filter","in":"query","required":false,"schema":{"type":"string"},"description":"Metric filter (built from 'by' and 'by2' arguments)"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"purchases":{"type":"object","properties":{"chart":{"type":"array","items":{"type":"object"}}}},"messages":{"type":"object","properties":{"chart":{"type":"array","items":{"type":"object"}}}}}}}}}}}}}},"/api2/v2/messages/queue/{queue_id}":{"delete":{"tags":["OF API — Messaging"],"summary":"Delete queued message","description":"Deletes a queued (scheduled) mass message by its queue ID. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"queue_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the queued/scheduled message"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"put":{"tags":["OF API — Messaging"],"summary":"Update queued message","description":"Updates a queued/scheduled message identified by its queue id with new message data. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"queue_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the queued/scheduled message"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; scheduled message payload passed as first argument"}}},"/api2/v2/messages/templates/reply_on_subscribe":{"post":{"tags":["OF API — Messaging"],"summary":"Set reply-on-subscribe template","description":"Creates or updates the automatic welcome message template sent when a fan subscribes. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; template payload not statically visible"}}},"/api2/v2/messages/templates/{template_id}":{"delete":{"tags":["OF API — Messaging"],"summary":"Delete message template","description":"Deletes a saved message template. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"template_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the message template to delete"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/messages/{message_id}/hide":{"put":{"tags":["OF API — Messaging"],"summary":"Hide a message","description":"Hides a chat message identified by message_id. The request body carries hide options. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"message_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the message to hide"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object (2nd arg); fields not statically visible"}}},"/api2/v2/pages/contacts":{"post":{"tags":["OF API — Misc"],"summary":"Submit contact form","description":"Submits the contact page form data. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; contact form fields"}}},"/api2/v2/payments/all/has-transactions":{"get":{"tags":["OF API — Payouts"],"summary":"Check if any transactions exist","description":"Returns whether the account has any payment transactions across all payment sources. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"hasTransactions":{"type":"boolean"}}}}}}}}}}},"/api2/v2/payments/cards":{"get":{"tags":["OF API — Payouts"],"summary":"List saved payment cards","description":"Returns the fan's saved payment cards. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"array"}}}}}}}}},"/api2/v2/payments/cards/{card_id}":{"delete":{"tags":["OF API — Payouts"],"summary":"Delete a payment card","description":"Removes a saved payment card from the user's account. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"card_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the saved payment card to delete"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"put":{"tags":["OF API — Payouts"],"summary":"Update a saved payment card","description":"Updates the details of a saved payment card identified by card_id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"card_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the saved payment card to update"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object (card fields); not statically visible"}}},"/api2/v2/payments/cards/{card_id}/default":{"put":{"tags":["OF API — Payouts"],"summary":"Set default payment card","description":"Marks the specified saved payment card as the account's default card. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"card_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the saved payment card"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/payments/cards/{card_id}/verify":{"post":{"tags":["OF API — Payouts"],"summary":"Verify a saved card","description":"Verifies a stored payment card identified by card id, optionally with verification data. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"card_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the saved payment card"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object (optional, defaults to {}); verification data"}}},"/api2/v2/payments/pay":{"post":{"tags":["OF API — Payouts"],"summary":"Submit a payment","description":"Processes/submits a payment. Part of the payments module alongside 3ds-js and cards endpoints. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; payment fields not statically visible"}}},"/api2/v2/payouts/bank":{"delete":{"tags":["OF API — Payouts"],"summary":"Delete bank payout method","description":"Removes the creator's configured bank account used for payouts. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"patch":{"tags":["OF API — Payouts"],"summary":"Update bank payout details","description":"Partially updates the creator's bank account details used for payouts. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; bank detail fields not statically visible"}},"post":{"tags":["OF API — Payouts"],"summary":"Add payout bank account","description":"Creates/saves a bank account for payouts. GET on the same path retrieves the current bank details. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; bank account fields not statically visible"}}},"/api2/v2/payouts/can-add-vat-documents":{"get":{"tags":["OF API — Payouts"],"summary":"Check if VAT documents allowed","description":"Returns whether the creator is currently allowed to add VAT documents to their payouts. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"success":{"type":"boolean"},"errorMessage":{"type":"string"}}}}}}}}}}},"/api2/v2/payouts/chargebacks/chart":{"get":{"tags":["OF API — Payouts"],"summary":"Get chargebacks chart data","description":"Time-series chart data for chargebacks with totals and delta.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"endDate","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"withTotal","in":"query","schema":{"type":"boolean","default":true}}],"responses":{"200":{"description":"Chargebacks chart","content":{"application/json":{"schema":{"type":"object","properties":{"total":{"type":"number","description":"Total chargeback amount for the period"},"delta":{"type":"number","description":"Change vs previous period"},"chartAmount":{"type":"array","items":{"type":"number"},"description":"Amount per time bucket"},"chartCount":{"type":"array","items":{"type":"number"},"description":"Count per time bucket"}}}}}}}}},"/api2/v2/payouts/chart":{"get":{"tags":["OF API — Misc"],"summary":"Get payouts chart stats","description":"Returns payouts chart statistics (amount/count) over a date range. The dynamic path segment is the serialized querystring. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":true,"schema":{"type":"string"},"description":"Range start date"},{"name":"endDate","in":"query","required":true,"schema":{"type":"string"},"description":"Range end date"},{"name":"withTotal","in":"query","required":false,"schema":{"type":"string"},"description":"Include totals (set true)"},{"name":"withChart","in":"query","required":false,"schema":{"type":"string"},"description":"Include chart series (set true)"},{"name":"filter","in":"query","required":false,"schema":{"type":"string"},"description":"Chart amount/count filter"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"array"}}}}}}}}},"/api2/v2/payouts/check-receive":{"get":{"tags":["OF API — Payouts"],"summary":"Check payout receive eligibility","description":"Checks whether the creator is able to receive payouts. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"isVerifiedReason":{"type":"boolean"},"canReceiveManualPayout":{"type":"boolean"},"needUpdateBanking":{"type":"boolean"}}}}}}}}}}},"/api2/v2/payouts/dac7":{"get":{"tags":["OF API — Payouts"],"summary":"Get DAC7 tax info","description":"Retrieves the creator's stored DAC7 (EU platform reporting) tax information for payouts. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"status":{"type":"string"},"type":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"address":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"zip":{"type":"string"},"countryId":{"type":"integer"},"taxId":{"type":"string"},"issuingCountryId":{"type":"integer"},"vatNumber":{"type":"string"},"DOB":{"type":"string"},"cityOfBirth":{"type":"string"},"countryOfBirthId":{"type":"integer"},"countryOfResidenceId":{"type":"integer"}}}}}}}}}},"post":{"tags":["OF API — Payouts"],"summary":"Submit DAC7 tax information","description":"Submits the creator's DAC7 (EU tax reporting) information. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque payload object; fields not statically visible"}}},"/api2/v2/payouts/download/vat/{vat_document_id}":{"get":{"tags":["OF API — Payouts"],"summary":"Download VAT document","description":"Downloads a specific VAT document/invoice for payouts by its id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"vat_document_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the VAT document to download"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/payouts/legal":{"post":{"tags":["OF API — Payouts"],"summary":"Submit payout legal information","description":"Submits the creator's legal/identity information required for payouts. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque payload object; fields not statically visible"}}},"/api2/v2/payouts/legal-form":{"get":{"tags":["OF API — Payouts"],"summary":"Get payout legal form","description":"Retrieves the legal form data required for payouts (tax/identity legal form). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"realFirstName":{"type":"string"},"realLastName":{"type":"string"},"realBusinessName":{"type":"string"},"realAddress":{"type":"string"},"realCity":{"type":"string"},"realState":{"type":"string"},"realPostal":{"type":"string"},"realTwitter":{"type":"null"},"realInstagram":{"type":"string"},"privateWebsite":{"type":"null"},"dateOfBirth":{"type":"string"},"documentType":{"type":"object","properties":{"values":{"type":"array","items":{"type":"object"}}}},"isAllowedDL":{"type":"boolean"},"isAllowedCustomW9Address":{"type":"boolean"}}}}}}}}}}},"/api2/v2/payouts/legal-info":{"get":{"tags":["OF API — Payouts"],"summary":"Get payout legal info","description":"Retrieves the creator's payout legal information. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"isXXX":{"type":"boolean"},"isW9Required":{"type":"boolean"},"isW9Exist":{"type":"boolean"},"isRealIdImage":{"type":"boolean"},"canChangePayoutType":{"type":"boolean"},"ivStatus":{"type":"string"},"ivFailReason":{"type":"null"},"showIvButton":{"type":"boolean"},"canShowLegalForm":{"type":"boolean"},"payoutLegalApproveRejectReason":{"type":"null"},"hideBanking":{"type":"boolean"},"isCompany":{"type":"boolean"},"DAC7":{"type":"object","properties":{"required":{"type":"boolean"},"type":{"type":"string"},"state":{"type":"string"},"error":{"type":"null"},"countryIds":{"type":"array","items":{"type":"integer"}}}},"DPR":{"type":"array","items":{"type":"string"}}}}}}}}}}}},"/api2/v2/payouts/legal/instagram":{"post":{"tags":["OF API — Payouts"],"summary":"Submit Instagram legal verification","description":"Submits legal/identity verification information via an Instagram account for payouts. Sibling of payouts/legal and payouts/legal/twitter. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/payouts/legal/twitter":{"post":{"tags":["OF API — Payouts"],"summary":"Submit Twitter legal info","description":"Submits Twitter/X account legal verification information for the payouts legal flow. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/payouts/qst":{"post":{"tags":["OF API — Payouts"],"summary":"Submit QST tax information","description":"Submits the creator's QST (Quebec sales tax) information. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque payload object; fields not statically visible"}}},"/api2/v2/payouts/referrals/chart":{"get":{"tags":["OF API — Payouts"],"summary":"Get referrals chart data","description":"Time-series chart data for referral earnings with totals and delta.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"endDate","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"withTotal","in":"query","schema":{"type":"integer","default":1}}],"responses":{"200":{"description":"Referrals chart","content":{"application/json":{"schema":{"type":"object","properties":{"total":{"type":"number","description":"Total referral earnings for the period"},"delta":{"type":"number","description":"Change vs previous period"},"chartAmount":{"type":"array","items":{"type":"number"},"description":"Earnings per time bucket"},"chartCount":{"type":"array","items":{"type":"number"},"description":"Referral count per time bucket"}}}}}}}}},"/api2/v2/payouts/requests":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Payouts"],"summary":"List payout requests","responses":{"200":{"description":"Payout request history","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"type":"object","properties":{"state":{"type":"string","description":"e.g. 'new', 'completed', 'rejected'"},"rejectReason":{"type":"string","nullable":true},"amount":{"type":"number"},"createdAt":{"type":"string","format":"date-time"}}}}}}}}}}},"post":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Payouts"],"summary":"Create payout request","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"withdrawalAmount":{"type":"number","example":100}}}}}},"responses":{"200":{"description":"Payout requested"}}}},"/api2/v2/payouts/requests/referral":{"get":{"tags":["OF API — Payouts"],"summary":"List referral payout requests","description":"Paginated list of referral payout transactions. Uses marker-based pagination.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"endDate","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}},{"name":"marker","in":"query","schema":{"type":"number","nullable":true},"description":"Pagination marker from previous response"}],"responses":{"200":{"description":"Referral payout list","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"type":"object"}},"marker":{"type":"number","nullable":true},"hasMore":{"type":"boolean"}}}}}}}}},"/api2/v2/payouts/requests/stripe":{"get":{"tags":["OF API — Payouts"],"summary":"List Stripe payout requests","description":"Returns the creator's Stripe payout requests, paginated by lastPayoutId. The dynamic path segment is actually the querystring. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","required":false,"schema":{"type":"string"},"description":"Max number of results"},{"name":"lastPayoutId","in":"query","required":false,"schema":{"type":"string"},"description":"Cursor: return requests after this payout ID"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"list":{"type":"array"},"marker":{"type":"integer"}}}}}}}}}}},"/api2/v2/payouts/requests/vat/{request_id}":{"get":{"tags":["OF API — Payouts"],"summary":"Get VAT info for payout request","description":"Returns VAT details associated with a specific payout request. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"request_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the payout/VAT request"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/payouts/stripe":{"get":{"tags":["OF API — Payouts"],"summary":"Get Stripe payout info","description":"Retrieves the creator's Stripe payout account information. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/payouts/stripe/account":{"post":{"tags":["OF API — Payouts"],"summary":"Create or update Stripe payout account","description":"Submits Stripe connected-account details used for creator payouts. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; Stripe account fields not statically visible"}}},"/api2/v2/payouts/stripe/legal":{"get":{"tags":["OF API — Payouts"],"summary":"Get Stripe payout legal info","description":"Retrieves the Stripe payout legal/agreement information for the creator. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"post":{"tags":["OF API — Payouts"],"summary":"Submit Stripe payout legal info","description":"Submits legal information for a Stripe-based payout account. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/payouts/tax":{"post":{"tags":["OF API — Payouts"],"summary":"Submit payout tax info","description":"Submits creator tax information for the payouts flow. Body is passed as the payload property. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque payload object; fields not statically visible"}}},"/api2/v2/payouts/tin":{"post":{"tags":["OF API — Payouts"],"summary":"Submit taxpayer identification number","description":"Submits the creator's taxpayer identification number (TIN) for payout tax compliance. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"tin":{"type":"string","description":"Taxpayer identification number"}}}}}}}},"/api2/v2/payouts/transactions":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Payouts"],"summary":"Get payout transactions","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","default":50}}],"responses":{"200":{"description":"Transaction list","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"type":"object"}},"hasMore":{"type":"boolean"},"marker":{"type":"string","nullable":true},"nextMarker":{"type":"string","nullable":true}}}}}}}}},"/api2/v2/payouts/uk-company-data":{"get":{"tags":["OF API — Payouts"],"summary":"Get UK company payout data","description":"Returns the creator's stored UK company details used for payouts/tax. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"post":{"tags":["OF API — Payouts"],"summary":"Submit UK company payout data","description":"Submits UK company data (for company/business payout accounts) as part of payout onboarding. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; UK company fields not statically visible"}}},"/api2/v2/payouts/vat":{"delete":{"tags":["OF API — Payouts"],"summary":"Delete VAT number","description":"Removes the creator's VAT registration number. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"get":{"tags":["OF API — Payouts"],"summary":"Get payout VAT info","description":"Retrieves the user's VAT information used for payouts (POST on the same path submits the VAT number). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"realFirstName":{"type":"string"},"realLastName":{"type":"string"},"creatorCompanyAddress":{"type":"null"},"creatorCompanyName":{"type":"null"},"creatorVatNumber":{"type":"string"},"creatorCountry":{"type":"null"},"creatorCountryCode":{"type":"null"}}}}}}}}}},"post":{"tags":["OF API — Payouts"],"summary":"Submit VAT number","description":"Submits the creator's VAT registration number. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"vat":{"type":"string","description":"VAT registration number"}}}}},"description":"Body literal is {vat:e}"}}},"/api2/v2/payouts/vat-requests":{"post":{"tags":["OF API — Payouts"],"summary":"Create a VAT request","description":"Creates a VAT (value-added tax) request for payouts. Part of the payouts tax module (vat, qst, tax, dac7). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; spread of caller-provided fields, not statically visible"}}},"/api2/v2/payouts/vat/chart":{"get":{"tags":["OF API — Misc"],"summary":"Get VAT payouts chart stats","description":"Returns VAT payouts chart statistics (amount/count) over a date range. The dynamic path segment is the serialized querystring. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":true,"schema":{"type":"string"},"description":"Range start date"},{"name":"endDate","in":"query","required":true,"schema":{"type":"string"},"description":"Range end date"},{"name":"withTotal","in":"query","required":false,"schema":{"type":"string"},"description":"Include totals (defaults true)"},{"name":"withChart","in":"query","required":false,"schema":{"type":"string"},"description":"Include chart series (set true)"},{"name":"filter","in":"query","required":false,"schema":{"type":"string"},"description":"Chart amount/count filter"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"array"}}}}}}}}},"/api2/v2/payouts/vats":{"get":{"tags":["OF API — Payouts"],"summary":"List payout VAT records","description":"Retrieves the list of VAT records associated with the creator's payouts. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"list":{"type":"array"}}}}}}}}}}},"/api2/v2/payouts/w9":{"post":{"tags":["OF API — Payouts"],"summary":"Submit W-9 tax form","description":"Submits the creator's IRS W-9 tax form data for payouts. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; W-9 form data passed as {data:e}"}}},"/api2/v2/payouts/w9/address":{"post":{"tags":["OF API — Payouts"],"summary":"Submit W-9 address","description":"Submits the address associated with the creator's W-9 tax form. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; address fields not statically visible"}}},"/api2/v2/payouts/w9/tincheck":{"post":{"tags":["OF API — Payouts"],"summary":"Verify W9 TIN","description":"Runs a TIN (taxpayer ID) verification check for W9 payout tax information. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; TIN check fields not statically visible"}}},"/api2/v2/phones/change":{"delete":{"tags":["OF API — User"],"summary":"Cancel pending phone change","description":"Cancels a pending phone-number change request (POST on the same path initiates one). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"post":{"tags":["OF API — User"],"summary":"Request phone number change","description":"Initiates a change of the account's phone number. A DELETE on the same path cancels a pending change. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/posts/bookmarks/categories/sort":{"post":{"tags":["OF API — Content"],"summary":"Sort bookmark categories","description":"Sets the sort order of post bookmark categories. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; category ordering payload"}}},"/api2/v2/posts/bookmarks/categories/{category_id}":{"delete":{"tags":["OF API — Content"],"summary":"Delete a bookmark category","description":"Deletes a post-bookmark category by id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"category_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the bookmark category to delete"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"patch":{"tags":["OF API — Content"],"summary":"Rename bookmark category","description":"Renames a post-bookmark category (DELETE removes it, POST creates one). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"category_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the bookmark category"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"New category name"}}}}}}}},"/api2/v2/posts/bookmarks/categories/{category_id}/{post_id}":{"post":{"tags":["OF API — Content"],"summary":"Add post to bookmark category","description":"Adds a post to a bookmark category. Called as ({categoryId, postId}). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"category_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the bookmark category"},{"name":"post_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the post to add"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/posts/chart":{"get":{"tags":["OF API — Misc"],"summary":"Get posts earnings chart","description":"Returns time-series earnings/statistics for posts. The dynamic path segment is a built querystring, not an id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":true,"schema":{"type":"string"},"description":"Start of the date range"},{"name":"endDate","in":"query","required":true,"schema":{"type":"string"},"description":"End of the date range"},{"name":"withTotal","in":"query","required":false,"schema":{"type":"string"},"description":"Include totals in the response"},{"name":"filter","in":"query","required":false,"schema":{"type":"string"},"description":"Metric filter (built from the 'by' argument plus posts)"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"posts":{"type":"object","properties":{"chart":{"type":"array","items":{"type":"object"}}}},"purchases":{"type":"object","properties":{"chart":{"type":"array","items":{"type":"object"}}}}}}}}}}}}}},"/api2/v2/posts/paid/pin/sort":{"post":{"tags":["OF API — Content"],"summary":"Sort pinned paid posts","description":"Reorders the creator's pinned paid posts. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; likely ordered ids, not statically visible"}}},"/api2/v2/posts/top":{"get":{"tags":["OF API — Misc"],"summary":"Get top posts stats","description":"Returns top-performing posts statistics over a date range. The dynamic path segment is the serialized querystring. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":true,"schema":{"type":"string"},"description":"Range start date"},{"name":"endDate","in":"query","required":true,"schema":{"type":"string"},"description":"Range end date"},{"name":"by","in":"query","required":false,"schema":{"type":"string"},"description":"Metric to sort/group by"},{"name":"offset","in":"query","required":false,"schema":{"type":"string"},"description":"Pagination offset"},{"name":"skip_users","in":"query","required":false,"schema":{"type":"string"},"description":"Skip embedding user objects (set to all)"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/posts/{post_id}/bookmarks":{"delete":{"tags":["OF API — Content"],"summary":"Remove post from bookmarks","description":"Removes the given post from the user's bookmarks; an optional chat_group_id can scope the removal to a specific bookmark group. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"post_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the post to un-bookmark"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"post":{"tags":["OF API — Content"],"summary":"Bookmark a post","description":"Adds a post to bookmarks; an optional chat_group_id body targets a specific bookmark group. DELETE on the same path removes the bookmark. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"post_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the post to bookmark"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"chat_group_id":{"type":"string","description":"Optional bookmark/chat group id; basic call sends no body"}}}}},"description":"Body optional"}}},"/api2/v2/posts/{post_id}/favorites/{author_id}":{"post":{"tags":["OF API — Content"],"summary":"Add post to favorites","description":"Marks the given post (by the specified author) as a favorite for the current user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"post_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the post to favorite"},{"name":"author_id","in":"path","required":true,"schema":{"type":"string"},"description":"User ID of the post's author"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/posts/{post_id}/fund-raising-contributors/count":{"get":{"tags":["OF API — Content"],"summary":"Count fundraising contributors","description":"Returns the number of contributors to a post's fundraising campaign. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"post_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the post"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/posts/{post_id}/hide":{"put":{"tags":["OF API — Content"],"summary":"Hide a post","description":"Hides the specified post. Defined alongside post pin, favorite, vote and delete calls. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"post_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the post to hide"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/promotions":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Promotions"],"summary":"Get promotions","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":10}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Promotions list, wrapped in the passthrough envelope. NOTE: the array is under `data.items` (NOT `data.list`). OnlyFans ignores `limit` here — page with `offset` and watch `hasMore`.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/OFPromotion"}},"hasMore":{"type":"boolean"}}}}}}}}}}},"/api2/v2/promotions/chart":{"get":{"tags":["OF API — Misc"],"summary":"Get promotions statistics chart","description":"Returns chart/statistics data for promotions over a date range. Path segment is static; the trailing token is a serialized query string. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":false,"schema":{"type":"string"},"description":"Start of the date range"},{"name":"endDate","in":"query","required":false,"schema":{"type":"string"},"description":"End of the date range"},{"name":"stats","in":"query","required":false,"schema":{"type":"string"},"description":"Stats flag (set to 1)"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"claims":{"type":"object","properties":{"chart":{"type":"array","items":{"type":"object"}},"total":{"type":"integer"}}},"offers":{"type":"object","properties":{"chart":{"type":"array","items":{"type":"object"}},"total":{"type":"integer"}}}}}}}}}}}}},"/api2/v2/promotions/claim":{"post":{"tags":["OF API — Promotions"],"summary":"Claim a promotion by code","description":"Claims a promotional offer using a promo code. Sends the code with a strictAuthCheck flag. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"Promotion code to claim"},"strictAuthCheck":{"type":"integer","description":"Auth-check flag, sent as 1"}}}}},"description":"Enclosing fn claims a promotion offer code"}}},"/api2/v2/promotions/invite":{"post":{"tags":["OF API — Promotions"],"summary":"Send promotion invite","description":"Sends an invitation for a promotion/promo offer. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/promotions/offer/{offer_id}":{"delete":{"tags":["OF API — Promotions"],"summary":"Delete a promotion offer","description":"Deletes a single promotion offer identified by offer_id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"offer_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the promotion offer to delete"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"get":{"tags":["OF API — Promotions"],"summary":"Get promotion offer by ID","description":"Fetches a single promotion offer by its ID. Sibling calls in the module handle claiming trials/promotions and confirming emails. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"offer_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the promotion offer to retrieve"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/promotions/offers/hide":{"post":{"tags":["OF API — Promotions"],"summary":"Hide promotion offers","description":"Hides the current promotion offers from view. Defined alongside promotions/offers list and delete calls. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/promotions/{promotion_id}":{"delete":{"tags":["OF API — Promotions"],"summary":"Delete promotion","description":"Deletes a subscription promotion by its ID. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"promotion_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the promotion to delete"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/promotions/{promotion_id}/finish":{"post":{"tags":["OF API — Promotions"],"summary":"Finish a promotion","description":"Ends/finishes an active promotion campaign identified by promotion id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"promotion_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the promotion to finish"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/release-form-documents":{"post":{"tags":["OF API — Misc"],"summary":"Upload release form document","description":"Uploads a release-form document (co-performer consent / model release). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/release-form-links":{"post":{"tags":["OF API — Content"],"summary":"Create release form link","description":"Creates a release-form link (content consent document link) from the supplied data. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/release-form-links/{link_id}/start":{"post":{"tags":["OF API — Misc"],"summary":"Start release form link","description":"Starts the flow for a release-form (consent) link identified by its id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"link_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the release-form link"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/release-form-links/{link_id}/url":{"post":{"tags":["OF API — Misc"],"summary":"Generate release form link URL","description":"Generates/returns a shareable URL for a release form link identified by link_id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"link_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the release form link"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/release-form-proof":{"post":{"tags":["OF API — Misc"],"summary":"Submit release form proof","description":"Submits proof for a content release form (consent documentation). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/release-forms/attach":{"post":{"tags":["OF API — Content"],"summary":"Attach release form","description":"Attaches a release form (content consent document) to content. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/release-forms/partner/{partner_id}":{"get":{"tags":["OF API — Misc"],"summary":"Get partner release forms","description":"Retrieves release forms associated with a partner identified by partner id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"partner_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the partner whose release forms are fetched"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/release-forms/rename":{"patch":{"tags":["OF API — Misc"],"summary":"Rename a release form","description":"Renames a content release form. Part of the release-forms module (attach, links, documents, toggle-show). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/release-forms/toggle-show":{"patch":{"tags":["OF API — Misc"],"summary":"Toggle release form visibility","description":"Toggles the show/visibility state of release forms. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; toggle payload not statically visible"}}},"/api2/v2/reports/reasons":{"get":{"tags":["OF API — Misc"],"summary":"List content report reasons","description":"Returns the list of available reasons for reporting content or users. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"code":{"type":"string"},"requiresIssue":{"type":"boolean"},"subReasons":{"type":"array","items":{"type":"object"}},"involves":{"type":"array"},"issues":{"type":"object","properties":{"revenge_porn":{"type":"string"},"expose":{"type":"string"},"impersonation":{"type":"string"},"underage":{"type":"string"},"tm":{"type":"string"},"spam":{"type":"string"},"prostitution":{"type":"string"},"weapons":{"type":"string"},"drugs":{"type":"string"},"other":{"type":"string"}}}}}}}}}}}}}},"/api2/v2/reports/reasons/{reason_id}/details-options":{"get":{"tags":["OF API — Misc"],"summary":"Get report reason detail options","description":"Retrieves the detail options available for a specific report reason. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"reason_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the report reason"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/schedules/{schedule_id}/publish":{"put":{"tags":["OF API — Content"],"summary":"Publish a scheduled item","description":"Publishes a scheduled entity (e.g. queued post/stream) immediately by its schedule id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"schedule_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the scheduled item to publish"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/sessions":{"delete":{"tags":["OF API — User"],"summary":"Revoke all sessions","description":"Terminates the user's active login sessions (used in the sessions settings screen). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"get":{"tags":["OF API — User"],"summary":"List active sessions","description":"Retrieves the current user's active login sessions. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"array","items":{"type":"object","properties":{"isCurrent":{"type":"boolean"},"lastActivity":{"type":"integer"},"ipAddress":{"type":"string"},"countryName":{"type":"string"},"client":{"type":"string"},"os":{"type":"string"},"brand":{"type":"string"},"loginMessage":{"type":"null"}}}}}}}}}}}},"/api2/v2/shopify/stores/{store_id}":{"delete":{"tags":["OF API — Misc"],"summary":"Delete Shopify store","description":"Disconnects/removes a linked Shopify store by ID. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"store_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the linked Shopify store"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/stories":{"post":{"tags":["OF API — Stories"],"summary":"Create story","description":"Creates a new story for the current user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; story payload not statically visible"}}},"/api2/v2/stories/answer/{answer_id}":{"delete":{"tags":["OF API — Stories"],"summary":"Delete a story answer","description":"Deletes a viewer's answer/reply to a story (e.g. story question sticker) identified by answer id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"answer_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the story answer to delete"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/stories/chart":{"get":{"tags":["OF API — Misc"],"summary":"Get stories statistics chart","description":"Returns time-series chart data for stories earnings/activity over a date range. The `{id}` in path_raw is actually the querystring appended by the Zq helper. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":true,"schema":{"type":"string"},"description":"Range start date"},{"name":"endDate","in":"query","required":true,"schema":{"type":"string"},"description":"Range end date"},{"name":"withTotal","in":"query","required":false,"schema":{"type":"string"},"description":"Whether to include totals"},{"name":"by","in":"query","required":false,"schema":{"type":"string"},"description":"Grouping/breakdown key"},{"name":"filter","in":"query","required":false,"schema":{"type":"string"},"description":"Filter object (e.g. {stories:'stories'})"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"tips":{"type":"object","properties":{"chart":{"type":"array","items":{"type":"object"}}}},"stories":{"type":"object","properties":{"chart":{"type":"array","items":{"type":"object"}}}}}}}}}}}}}},"/api2/v2/stories/top":{"get":{"tags":["OF API — Misc"],"summary":"Get top stories stats","description":"Returns top-performing stories statistics for a date range, optionally grouped. The dynamic suffix in path_raw is the querystring. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":false,"schema":{"type":"string"},"description":"Range start date"},{"name":"endDate","in":"query","required":false,"schema":{"type":"string"},"description":"Range end date"},{"name":"by","in":"query","required":false,"schema":{"type":"string"},"description":"Grouping/metric field"},{"name":"offset","in":"query","required":false,"schema":{"type":"string"},"description":"Pagination offset"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/stories/users/blocked":{"get":{"tags":["OF API — Stories"],"summary":"List story-blocked users","description":"Returns users blocked from viewing the current user's stories. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"array"}}}}}}}}},"/api2/v2/stories/users/{user_id}/block":{"delete":{"tags":["OF API — Stories"],"summary":"Unblock user from stories","description":"Removes a user from the story block list (unblocks them from viewing stories). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the user to unblock"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"post":{"tags":["OF API — Stories"],"summary":"Block user from stories","description":"Blocks the specified user from viewing the creator's stories. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the user to block from stories"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/stories/{story_id}/like":{"delete":{"tags":["OF API — Stories"],"summary":"Unlike a story","description":"Removes a like from the specified story. Paired with a POST on the same path to like it. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"story_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the story to unlike"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"post":{"tags":["OF API — Stories"],"summary":"Like a story","description":"Likes the story identified by story_id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"story_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the story to like"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/stories/{story_id}/viewers":{"get":{"tags":["OF API — Stories"],"summary":"List story viewers","description":"Returns the list of viewers for a story, with pagination; a variant filters to only viewers who tipped. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"story_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the story"},{"name":"limit","in":"query","required":false,"schema":{"type":"string"},"description":"Page size"},{"name":"offset","in":"query","required":false,"schema":{"type":"string"},"description":"Pagination offset"},{"name":"onlyWithTips","in":"query","required":false,"schema":{"type":"string"},"description":"Return only viewers who tipped (used by one variant)"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/stories/{story_id}/watched":{"put":{"tags":["OF API — Stories"],"summary":"Mark story as watched","description":"Marks a story as watched/seen by the current user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"story_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the story to mark watched"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streaks":{"get":{"tags":["OF API — Misc"],"summary":"Get streaks over date range","description":"Retrieves subscriber/engagement streak statistics over a date range. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":false,"schema":{"type":"string"},"description":"Start of date range"},{"name":"endDate","in":"query","required":false,"schema":{"type":"string"},"description":"End of date range"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"list":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"startDate":{"type":"string"},"endDate":{"type":"string"},"isActive":{"type":"boolean"},"postsCount":{"type":"integer"},"daysCount":{"type":"integer"},"streamsDuration":{"type":"integer"},"storiesCount":{"type":"integer"},"chatsCount":{"type":"integer"},"frozenDays":{"type":"array"}}}},"hasMore":{"type":"boolean"}}}}}}}}}}},"/api2/v2/streaks/top":{"get":{"tags":["OF API — Misc"],"summary":"Get top streaks","description":"Returns the top fan streaks for the creator. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"id":{"type":"integer"},"startDate":{"type":"string"},"endDate":{"type":"string"},"isActive":{"type":"boolean"},"postsCount":{"type":"integer"},"daysCount":{"type":"integer"},"streamsDuration":{"type":"integer"},"storiesCount":{"type":"integer"},"chatsCount":{"type":"integer"},"frozenDays":{"type":"array","items":{"type":"string"}}}}}}}}}}}},"/api2/v2/streams/chart":{"get":{"tags":["OF API — Misc"],"summary":"Get streams stats chart","description":"Returns time-series chart data for live-stream statistics over a date range. The dynamic path segment is actually the querystring. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":true,"schema":{"type":"string"},"description":"Start of the date range"},{"name":"endDate","in":"query","required":true,"schema":{"type":"string"},"description":"End of the date range"},{"name":"withTotal","in":"query","required":false,"schema":{"type":"string"},"description":"Whether to include totals"},{"name":"filter","in":"query","required":false,"schema":{"type":"string"},"description":"Filter object (keyed by the 'by' field, plus duration)"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"duration":{"type":"object","properties":{"chart":{"type":"array","items":{"type":"object"}}}},"purchases":{"type":"object","properties":{"chart":{"type":"array","items":{"type":"object"}}}}}}}}}}}}}},"/api2/v2/streams/top":{"get":{"tags":["OF API — Misc"],"summary":"Get top streams stats","description":"Returns top-performing live streams over a date range, ranked (default by purchases) with pagination. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":true,"schema":{"type":"string"},"description":"Range start date"},{"name":"endDate","in":"query","required":true,"schema":{"type":"string"},"description":"Range end date"},{"name":"limit","in":"query","required":false,"schema":{"type":"string"},"description":"Page size (default 10)"},{"name":"offset","in":"query","required":false,"schema":{"type":"string"},"description":"Pagination offset (default 0)"},{"name":"by","in":"query","required":false,"schema":{"type":"string"},"description":"Ranking metric (default 'purchases')"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/users/{user_id}/block":{"delete":{"tags":["OF API — Streams"],"summary":"Unblock stream viewer","description":"Unblocks a previously blocked live-stream viewer by user ID. Enclosing fn unblockStreamViewerByUserId. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the viewer to unblock"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/users/{username}/block":{"post":{"tags":["OF API — Streams"],"summary":"Block stream viewer by name","description":"Blocks a live-stream viewer identified by their username (blockStreamViewerByName). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"username","in":"path","required":true,"schema":{"type":"string"},"description":"Username of the stream viewer to block"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/{stream_id}":{"delete":{"tags":["OF API — Streams"],"summary":"Delete a stream","description":"Deletes a live stream identified by stream id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream to delete"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"get":{"tags":["OF API — Streams"],"summary":"Get stream details","description":"Retrieves a live stream by its id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"patch":{"tags":["OF API — Streams"],"summary":"Update a stream","description":"Updates properties of an existing live stream. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream (taken from the body's id field)"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Stream id (also used in the path)"}}}}},"description":"full stream object; remaining fields not statically visible"}}},"/api2/v2/streams/{stream_id}/accept":{"post":{"tags":["OF API — Streams"],"summary":"Accept dual-stream invite","description":"Accepts an invitation to join a dual (co-host) live stream identified by stream id (acceptDualStreamInvite). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream whose invite is accepted"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/{stream_id}/active":{"get":{"tags":["OF API — Streams"],"summary":"Check if stream active","description":"Checks whether the specified live stream is currently active. Enclosing fn checkStreamActive. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the live stream"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/{stream_id}/block":{"post":{"tags":["OF API — Streams"],"summary":"Block stream viewer","description":"Blocks a viewer in a live stream, optionally permanently. Called as blockStreamViewer({streamId, userId, isPermanent}). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string|number","description":"ID of the viewer to block"},"isPermanent":{"type":"boolean","description":"Whether the block is permanent (default false)"}}}}}}}},"/api2/v2/streams/{stream_id}/cancel":{"post":{"tags":["OF API — Streams"],"summary":"Cancel dual-stream request","description":"Cancels a pending dual-stream (co-streaming) request for the given stream (enclosing fn cancelDualStreamRequest). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the stream whose dual-stream request is cancelled"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/{stream_id}/comments/{comment_id}":{"delete":{"tags":["OF API — Streams"],"summary":"Delete stream comment","description":"Removes a comment from a live stream. Called as removeComment(streamId, commentId). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream"},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the comment to remove"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"get":{"tags":["OF API — Streams"],"summary":"Get a single stream comment","description":"Fetches a single comment on a live stream by stream and comment id (enclosing fn getStreamComment). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the stream"},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the stream comment"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/{stream_id}/comments/{comment_id}/pin":{"delete":{"tags":["OF API — Streams"],"summary":"Unpin a stream comment","description":"Removes the pin from a comment on a live stream. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the stream"},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the pinned comment to unpin"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"post":{"tags":["OF API — Streams"],"summary":"Pin a stream comment","description":"Pins a comment within a live stream (DELETE on the same path unpins it). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream"},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the comment to pin"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/{stream_id}/cover":{"put":{"tags":["OF API — Streams"],"summary":"Save live stream cover","description":"Saves/updates the cover image for a live stream (enclosing fn saveStreamCover). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the stream whose cover is set"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object (cover selection/data); fields not statically visible"}}},"/api2/v2/streams/{stream_id}/covers":{"get":{"tags":["OF API — Streams"],"summary":"Fetch stream covers","description":"Fetches available cover images for a stream (fetchStreamCovers). Retries on HTTP 400. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/{stream_id}/decline":{"post":{"tags":["OF API — Streams"],"summary":"Decline dual-stream invite","description":"Declines an invitation to join a dual (co-host) live stream. Enclosing fn declineDualStreamInvite. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream whose invite is declined"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/{stream_id}/finish":{"put":{"tags":["OF API — Streams"],"summary":"Finish a live stream","description":"Ends the live stream identified by stream_id. Enclosing fn is finishStream. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream to finish"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object (2nd arg); fields not statically visible"}}},"/api2/v2/streams/{stream_id}/hide":{"post":{"tags":["OF API — Streams"],"summary":"Hide stream","description":"Hides the specified live stream from the feed. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the live stream to hide"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/{stream_id}/is-viewer":{"get":{"tags":["OF API — Streams"],"summary":"Check if current user is viewer","description":"Checks whether the current user is a viewer of the given stream (checkViewer). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/{stream_id}/join":{"post":{"tags":["OF API — Streams"],"summary":"Join a dual stream","description":"Requests to join a dual (co-host) live stream identified by stream_id. Enclosing fn is joinDualStreamRequest. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream to join"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/{stream_id}/likes":{"post":{"tags":["OF API — Streams"],"summary":"Add likes to stream","description":"Adds a number of likes to a live stream (streamLikes). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"likes":{"type":"number","description":"Number of likes to add"}}}}}}}},"/api2/v2/streams/{stream_id}/look":{"delete":{"tags":["OF API — Streams"],"summary":"Stop looking at a stream","description":"Removes the current viewer's 'look' marker on a stream (streamUnlook). Paired with a POST on the same path (streamLook). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"post":{"tags":["OF API — Streams"],"summary":"Mark viewing a stream","description":"Registers the current user as actively looking at the stream. Enclosing fn is streamLook. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream being viewed"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/{stream_id}/make-post":{"put":{"tags":["OF API — Streams"],"summary":"Save stream as a post","description":"Saves a finished live stream as a feed post (saveStreamAsPost). Requires the stream ID and additional post fields. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream to save as a post"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Stream ID (echoed into the body)"}}}}},"description":"Additional post fields are spread from caller and not statically visible."}}},"/api2/v2/streams/{stream_id}/reminder":{"delete":{"tags":["OF API — Streams"],"summary":"Remove stream reminder","description":"Removes the reminder the user set for the scheduled stream identified by stream_id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream to remove the reminder for"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"post":{"tags":["OF API — Streams"],"summary":"Set stream reminder","description":"Sets a reminder for an upcoming/scheduled live stream. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/{stream_id}/tweet":{"post":{"tags":["OF API — Streams"],"summary":"Share stream to Twitter","description":"Tweets/shares the live stream identified by stream_id with preview options. Enclosing fn is sendStreamTweet. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream to tweet"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"tweetWithPreview":{"type":"boolean","description":"Include stream preview in tweet"},"tweetWithStillPreview":{"type":"boolean","description":"Include still-image preview in tweet"}}}}}}}},"/api2/v2/streams/{stream_id}/user/{user_id}/comments":{"delete":{"tags":["OF API — Streams"],"summary":"Remove a user's stream comments","description":"Removes all comments from a specific user within a live stream (removeComment by user). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the live stream"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the user whose comments are removed"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/{stream_id}/users/{user_id}/accept":{"post":{"tags":["OF API — Streams"],"summary":"Accept dual-stream request","description":"Accepts a user's request to join a dual/co-stream (acceptDualStreamRequest). Sibling calls decline, cancel or invite users. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the user whose dual-stream request is accepted"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/{stream_id}/users/{user_id}/cancel":{"post":{"tags":["OF API — Streams"],"summary":"Cancel dual-stream invite","description":"Cancels a pending dual-stream (co-stream) invite for a user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the invited user"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/{stream_id}/users/{user_id}/decline":{"post":{"tags":["OF API — Streams"],"summary":"Decline dual-stream request","description":"Declines a dual-stream (co-stream) request from a user (declineDualStreamRequest). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the requesting user"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/{stream_id}/users/{user_id}/helper":{"delete":{"tags":["OF API — Streams"],"summary":"Remove stream helper","description":"Removes a user's helper (moderator) role on a live stream. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the helper user"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"put":{"tags":["OF API — Streams"],"summary":"Add stream helper","description":"Grants a user helper (moderator) permissions on a live stream. Enclosing fn addStreamHelper. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the live stream"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the user to grant helper role"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/{stream_id}/users/{user_id}/invite":{"post":{"tags":["OF API — Streams"],"summary":"Invite user to dual stream","description":"Invites a user to join a dual/co-stream. Called as inviteDualStream({streamId, userId}). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the user to invite"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/streams/{stream_id}/viewers":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Streams"],"summary":"Get stream viewers","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Viewer list","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"$ref":"#/components/schemas/OFUserProfile"}},"hasMore":{"type":"boolean"}}}}}}}}},"/api2/v2/streams/{stream_id}/viewers/{user_id}":{"get":{"tags":["OF API — Streams"],"summary":"Get a stream viewer","description":"Retrieves details for a single viewer of a live stream. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the stream"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the viewer user"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/subscriptions/bundles/{bundle_id}":{"delete":{"tags":["OF API — Subscriptions"],"summary":"Delete subscription bundle","description":"Deletes a subscription bundle offer. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"bundle_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the subscription bundle to delete"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"put":{"tags":["OF API — Subscriptions"],"summary":"Update subscription bundle","description":"Updates a subscription bundle (discounted multi-month offer) by ID. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"bundle_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the subscription bundle (from body e.id)"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; full bundle object including id"}}},"/api2/v2/subscriptions/subscribers":{"get":{"tags":["OF API — Subscribers"],"summary":"List subscribers","description":"Full subscriber list with total-spent data.\n\n`data` is a **bare array** of subscriber (fan) profiles — no `{list, hasMore}` wrapper — page it with `limit`/`offset`. For a bounded active/expired roster on very large accounts prefer the CRM route `GET /accounts/{of_user_id}/subscribers/refresh` + `/subscribers/cached`.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":10}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}},{"name":"type","in":"query","schema":{"type":"string","enum":["all","active","expired"],"default":"all"}},{"name":"format","in":"query","schema":{"type":"string","default":"infinite"}},{"name":"filter[total_spent]","in":"query","schema":{"type":"integer","default":1}},{"name":"more","in":"query","schema":{"type":"boolean","default":true}}],"responses":{"200":{"description":"Subscriber list, wrapped in the passthrough envelope. Each item is an OFUserProfile plus subscription-relationship fields (`subscribedBy`, `subscribedByExpireDate`, `subscribedOnData`, `currentSubscribePrice`, `listsStates`, tip limits).","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"array","items":{"$ref":"#/components/schemas/OFUserProfile"}}}}}}}}}},"/api2/v2/subscriptions/subscribers/chart":{"get":{"tags":["OF API — Misc"],"summary":"Get subscribers statistics chart","description":"Returns chart/statistics data for subscribers over a date range, groupable via 'by'. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":false,"schema":{"type":"string"},"description":"Start of the date range"},{"name":"endDate","in":"query","required":false,"schema":{"type":"string"},"description":"End of the date range"},{"name":"by","in":"query","required":false,"schema":{"type":"string"},"description":"Grouping/breakdown dimension"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"earnings":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"count":{"type":"integer"}}}},"subscribes":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"count":{"type":"integer"}}}},"total":{"type":"integer"},"subscribers":{"type":"integer"}}}}}}}}}}},"/api2/v2/subscriptions/subscribers/latest":{"get":{"tags":["OF API — Misc"],"summary":"Get latest subscribers","description":"Returns the latest subscribers within a date range, grouped by the given field. The dynamic path segment is actually the querystring. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":true,"schema":{"type":"string"},"description":"Start of the date range"},{"name":"endDate","in":"query","required":true,"schema":{"type":"string"},"description":"End of the date range"},{"name":"by","in":"query","required":false,"schema":{"type":"string"},"description":"Grouping/aggregation field"},{"name":"offset","in":"query","required":false,"schema":{"type":"string"},"description":"Pagination offset"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"users":{"type":"array","items":{"type":"object","properties":{"view":{"type":"string"},"avatar":{"type":"null"},"avatarThumbs":{"type":"null"},"header":{"type":"null"},"headerSize":{"type":"null"},"headerThumbs":{"type":"null"},"id":{"type":"integer"},"name":{"type":"string"},"username":{"type":"string"},"canLookStory":{"type":"boolean"},"canCommentStory":{"type":"boolean"},"hasNotViewedStory":{"type":"boolean"},"isVerified":{"type":"boolean"},"canPayInternal":{"type":"boolean"},"hasScheduledStream":{"type":"boolean"},"hasStream":{"type":"boolean"},"hasStories":{"type":"boolean"},"tipsEnabled":{"type":"boolean"},"tipsTextEnabled":{"type":"boolean"},"tipsMin":{"type":"integer"},"tipsMinInternal":{"type":"integer"},"tipsMax":{"type":"integer"},"canEarn":{"type":"boolean"},"canAddSubscriber":{"type":"boolean"},"subscribePrice":{"type":"integer"},"displayName":{"type":"string"},"notice":{"type":"string"},"isActive":{"type":"boolean"},"isRestricted":{"type":"boolean"},"canRestrict":{"type":"boolean"},"subscribedBy":{"type":"boolean"},"subscribedByExpire":{"type":"boolean"},"subscribedByExpireDate":{"type":"string"},"subscribedByAutoprolong":{"type":"boolean"},"subscribedIsExpiredNow":{"type":"boolean"},"currentSubscribePrice":{"type":"integer"},"subscribedOn":{"type":"boolean"},"subscribedOnExpire":{"type":"boolean"},"subscribedOnExpiredNow":{"type":"boolean"},"subscribedOnDuration":{"type":"string"},"listsStates":{"type":"array"},"canReport":{"type":"boolean"},"canReceiveChatMessage":{"type":"boolean"},"hideChat":{"type":"boolean"},"lastSeen":{"type":"string"},"isPerformer":{"type":"boolean"},"isRealPerformer":{"type":"boolean"},"subscribedByData":{"type":"object"},"subscribedOnData":{"type":"object"},"canTrialSend":{"type":"boolean"},"isBlocked":{"type":"boolean"},"canUnsubscribe":{"type":"boolean"},"isPendingAutoprolong":{"type":"boolean"}}}},"offset":{"type":"integer"},"hasMore":{"type":"boolean"}}}}}}}}}}},"/api2/v2/subscriptions/subscribers/top":{"get":{"tags":["OF API — Misc"],"summary":"Get top subscribers stats","description":"Retrieves top subscribers over a date range (statistics context). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":true,"schema":{"type":"string"},"description":"Start of date range"},{"name":"endDate","in":"query","required":true,"schema":{"type":"string"},"description":"End of date range"},{"name":"by","in":"query","required":false,"schema":{"type":"string"},"description":"Metric to rank by"},{"name":"offset","in":"query","required":false,"schema":{"type":"string"},"description":"Pagination offset"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"users":{"type":"array","items":{"type":"object","properties":{"view":{"type":"string"},"avatar":{"type":"null"},"avatarThumbs":{"type":"null"},"header":{"type":"null"},"headerSize":{"type":"null"},"headerThumbs":{"type":"null"},"id":{"type":"integer"},"name":{"type":"string"},"username":{"type":"string"},"canLookStory":{"type":"boolean"},"canCommentStory":{"type":"boolean"},"hasNotViewedStory":{"type":"boolean"},"isVerified":{"type":"boolean"},"canPayInternal":{"type":"boolean"},"hasScheduledStream":{"type":"boolean"},"hasStream":{"type":"boolean"},"hasStories":{"type":"boolean"},"tipsEnabled":{"type":"boolean"},"tipsTextEnabled":{"type":"boolean"},"tipsMin":{"type":"integer"},"tipsMinInternal":{"type":"integer"},"tipsMax":{"type":"integer"},"canEarn":{"type":"boolean"},"canAddSubscriber":{"type":"boolean"},"subscribePrice":{"type":"integer"},"displayName":{"type":"string"},"notice":{"type":"string"},"isActive":{"type":"boolean"},"isRestricted":{"type":"boolean"},"canRestrict":{"type":"boolean"},"subscribedBy":{"type":"boolean"},"subscribedByExpire":{"type":"boolean"},"subscribedByExpireDate":{"type":"string"},"subscribedByAutoprolong":{"type":"boolean"},"subscribedIsExpiredNow":{"type":"boolean"},"currentSubscribePrice":{"type":"integer"},"subscribedOn":{"type":"null"},"subscribedOnExpire":{"type":"boolean"},"subscribedOnExpiredNow":{"type":"boolean"},"subscribedOnDuration":{"type":"string"},"listsStates":{"type":"array"},"canReport":{"type":"boolean"},"canReceiveChatMessage":{"type":"boolean"},"hideChat":{"type":"boolean"},"lastSeen":{"type":"string"},"isPerformer":{"type":"boolean"},"isRealPerformer":{"type":"boolean"},"subscribedByData":{"type":"object"},"subscribedOnData":{"type":"object"},"canTrialSend":{"type":"boolean"},"isBlocked":{"type":"boolean"},"canUnsubscribe":{"type":"boolean"},"isPendingAutoprolong":{"type":"boolean"}}}},"hasMore":{"type":"boolean"}}}}}}}}}}},"/api2/v2/subscriptions/{subscription_id}":{"put":{"tags":["OF API — Subscriptions"],"summary":"Update a subscription","description":"Updates settings for the given subscription. Sibling calls manage autoprolong, hide-posts and price-change hints. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"subscription_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the subscription to update"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/subscriptions/{subscription_id}/attention":{"delete":{"tags":["OF API — Subscriptions"],"summary":"Dismiss subscription attention flag","description":"Clears/dismisses the 'attention' flag on a subscription. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"subscription_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the subscription whose attention flag is cleared"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/subscriptions/{subscription_id}/hide-posts":{"delete":{"tags":["OF API — Subscriptions"],"summary":"Unhide subscription posts","description":"Re-shows posts from a subscription previously hidden (PUT on the same path hides them). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"subscription_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the subscription"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"put":{"tags":["OF API — Subscriptions"],"summary":"Hide posts from subscription","description":"Hides posts from the given subscription in the user's feed. Paired with a DELETE on the same path to unhide. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"subscription_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the subscription (subscribed user) to hide posts from"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/subscriptions/{subscription_id}/price-change-hint":{"delete":{"tags":["OF API — Subscriptions"],"summary":"Dismiss price-change hint","description":"Dismisses the price-change hint/notice for a subscription. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"subscription_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the subscription"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/texts/search":{"post":{"tags":["OF API — Misc"],"summary":"Search localization texts","description":"Searches localization strings by code and languages. Part of the i18n text system (texts.onlyfans.com). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"Text/translation key to search"},"languages":{"type":"array","description":"Languages to search within"}}}}}}}},"/api2/v2/texts/{code}":{"put":{"tags":["OF API — Misc"],"summary":"Update a localization text","description":"Updates the localized text string for a given text code. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Localization/text string code"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","description":"New text content for the code"}}}}}}}},"/api2/v2/trials/chart":{"get":{"tags":["OF API — Misc"],"summary":"Get trials chart stats","description":"Returns trial statistics chart data over a date range. The dynamic path segment is the serialized querystring, not a path param. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":true,"schema":{"type":"string"},"description":"Range start date"},{"name":"endDate","in":"query","required":true,"schema":{"type":"string"},"description":"Range end date"},{"name":"stats","in":"query","required":false,"schema":{"type":"string"},"description":"Include stats flag (set to 1)"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"claims":{"type":"object","properties":{"chart":{"type":"array","items":{"type":"object"}},"total":{"type":"integer"}}},"offers":{"type":"object","properties":{"chart":{"type":"array","items":{"type":"object"}},"total":{"type":"integer"}}}}}}}}}}}}},"/api2/v2/trials/check":{"post":{"tags":["OF API — Promotions"],"summary":"Check and reserve trial code","description":"Validates a free-trial link code and reserves it for the current user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"Trial link code to validate"},"reserve":{"type":"boolean","description":"Reserve the trial (sent as true)"}}}}}}}},"/api2/v2/trials/claim":{"post":{"tags":["OF API — Promotions"],"summary":"Claim free trial by code","description":"Claims a free-trial subscription offer using a trial code. The code is sent in the request body. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"Trial/promo code to claim"}}}}},"description":"Body literal is {code:e}"}}},"/api2/v2/trials/share-access":{"delete":{"tags":["OF API — Promotions"],"summary":"Revoke trial share access","description":"Revokes shared free-trial access. The request carries a body via the delete data option. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"post":{"tags":["OF API — Promotions"],"summary":"Share trial access","description":"Creates a shared free-trial access grant. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/trials/stats":{"get":{"tags":["OF API — Misc"],"summary":"Get trial link statistics","description":"Returns statistics for free-trial links over a date range with pagination. The dynamic suffix in path_raw is the querystring. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"offset","in":"query","required":false,"schema":{"type":"string"},"description":"Pagination offset"},{"name":"startDate","in":"query","required":false,"schema":{"type":"string"},"description":"Range start date"},{"name":"endDate","in":"query","required":false,"schema":{"type":"string"},"description":"Range end date"},{"name":"limit","in":"query","required":false,"schema":{"type":"string"},"description":"Max results"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"hasMore":{"type":"boolean"},"items":{"type":"array"}}}}}}}}}}},"/api2/v2/trials/{trial_id}":{"delete":{"tags":["OF API — Promotions"],"summary":"Delete a trial link","description":"Deletes a free-trial subscription link/campaign identified by trial id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"trial_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the trial to delete"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"put":{"tags":["OF API — Promotions"],"summary":"Update trial link","description":"Updates a trial-subscription link by ID (e.g. finishes/deactivates it). No request body is sent. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"trial_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the trial link"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/trust":{"post":{"tags":["OF API — User"],"summary":"Mark device as trusted","description":"Marks the current session/device as trusted. Found next to webauthn, oauth/confirm and logout auth calls. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/unsubscribe/reasons":{"get":{"tags":["OF API — Subscriptions"],"summary":"List unsubscribe reasons","description":"Returns the selectable reasons offered when a fan unsubscribes. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"}}}}}}}}}}}},"/api2/v2/upload/signed/create":{"post":{"tags":["OF API — Content"],"summary":"Create signed media upload","description":"Stage 1 of OnlyFans' media upload. Returns pre-signed S3 URL(s) that you then PUT the bytes to.\n\n**Body** (verified live 2026-08-06):\n```json\n{ \"key\": \"upload/{uuid4}/{nonce}/{url-encoded-lowercased-filename}\", \"parts\": 1, \"contentType\": \"image/jpeg\", \"secure\": false }\n```\nThe `key` prefix comes from `GET /api2/v2/init` → `upload.s3.uploadPath` (`upload/`, or `upload/secure/` when `secure` is true). `parts` is `floor(size / 5242880) + 1` for files ≥ 5 MiB, else `1`.\n\n**A wrong or missing `key` is what produces `400 {\"error\":{\"message\":\"Bad key\"}}`** — the field really is named `key`, and it must be a path under the account's upload prefix.\n\nResponse: `{ keys: [{putUrl}], uploadId, putUrl, getUrl }`. Multipart when `keys[]` is populated and the file is ≥ 5 MiB; otherwise PUT the whole file to `putUrl`.\n\n**You normally don't call this directly** — `POST /accounts/{of_user_id}/media` runs all four stages for you.\n\n**OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["key","parts","contentType"],"properties":{"key":{"type":"string","description":"Storage PATH, not a filename — `{uploadPath}{uuid4}/{nonce}/{url-encoded lowercased filename}`. `uploadPath` comes from GET /api2/v2/init → `upload.s3.uploadPath` (`upload/`, or `upload/secure/` when secure=true). A missing or malformed value is what returns `400 Bad key`.","example":"upload/0e683d4d-1726-4506-a04d-2a5d05f5b9e1/1031326954680/photo.jpg"},"parts":{"type":"integer","description":"Number of 5 MiB S3 parts: `floor(size / 5242880) + 1` for files ≥ 5 MiB, otherwise 1.","minimum":1,"example":1},"contentType":{"type":"string","description":"MIME type of the file. `.heic` must be sent as `image/heic`.","example":"image/jpeg"},"secure":{"type":"boolean","description":"Use the secure (DRM) upload prefix instead of the standard one.","default":false}}},"example":{"key":"upload/0e683d4d-1726-4506-a04d-2a5d05f5b9e1/1031326954680/photo.jpg","parts":1,"contentType":"image/jpeg","secure":false}}},"description":"Upload descriptor. Prefer POST /accounts/{of_user_id}/media, which runs all four stages for you."}}},"/api2/v2/upload/signed/finish":{"post":{"tags":["OF API — Content"],"summary":"Finish signed upload","description":"Stage 3 of OnlyFans' media upload — completes an S3 **multipart** upload after every part has been PUT.\n\n**Body** (verified live 2026-08-06):\n```json\n{ \"key\": \"<same key as create>\", \"parts\": [{ \"ETag\": \"\\\"abc…\\\"\", \"PartNumber\": 1 }], \"uploadId\": \"<from create>\", \"secure\": false }\n```\nReturns `{ \"ETag\": \"…\" }` for the assembled object.\n\nSingle-part uploads (< 5 MiB) **skip this call** — the ETag comes straight off the S3 PUT response.\n\nNote that neither this nor `create` puts anything in the vault; a fourth stage hands the S3 descriptor to OnlyFans' converter host (`GET /api2/v2/init` → `upload.geoUploadHosts`). `POST /accounts/{of_user_id}/media` does all of it for you.\n\n**OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["key","parts","uploadId"],"properties":{"key":{"type":"string","description":"The same `key` passed to upload/signed/create.","example":"upload/0e683d4d-1726-4506-a04d-2a5d05f5b9e1/1031326954680/photo.jpg"},"parts":{"type":"array","description":"One entry per uploaded S3 part, in order. `ETag` is the quoted value from that part's PUT response header.","items":{"type":"object","required":["ETag","PartNumber"],"properties":{"ETag":{"type":"string","example":"\"5623a10f3404a04d201a8e38aea195d6\""},"PartNumber":{"type":"integer","minimum":1,"example":1}}}},"uploadId":{"type":"string","description":"The `uploadId` returned by upload/signed/create."},"secure":{"type":"boolean","description":"Must match the value used on create.","default":false}}},"example":{"key":"upload/0e683d4d-1726-4506-a04d-2a5d05f5b9e1/1031326954680/photo.jpg","parts":[{"ETag":"\"5623a10f3404a04d201a8e38aea195d6\"","PartNumber":1}],"uploadId":"2~abc123","secure":false}}},"description":"Multipart completion. Single-part uploads (< 5 MiB) skip this call entirely — the ETag comes off the PUT response. Prefer POST /accounts/{of_user_id}/media."}}},"/api2/v2/users/alert":{"get":{"tags":["OF API — Notifications"],"summary":"Get user alerts","description":"Returns the current user's alert(s) banner data. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"array"}}}}}}}}},"/api2/v2/users/alert/{alert_id}":{"delete":{"tags":["OF API — Notifications"],"summary":"Delete user alert","description":"Deletes a user alert by id. Related to users/mass-alert endpoints (creator alert broadcasts). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"alert_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the alert to delete"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/appeal":{"post":{"tags":["OF API — User"],"summary":"Submit account appeal","description":"Submits an appeal, e.g. against an account restriction or moderation action. Defined near reports/reasons and unsubscribe/reasons calls. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/users/change-password":{"post":{"tags":["OF API — User"],"summary":"Change password","description":"Changes the current user's account password. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; likely current + new password, not statically visible"}}},"/api2/v2/users/clicks-stats":{"post":{"tags":["OF API — Misc"],"summary":"Report user click statistics","description":"Sends a batch of user click/interaction statistics for tracking. The response is fire-and-forget (errors are swallowed via .catch). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible (click-tracking payload)"}}},"/api2/v2/users/connect":{"post":{"tags":["OF API — User"],"summary":"Connect a linked account","description":"Connects/links another account to the current user (used for multi-account switching). Paired with DELETE /users/connect/{id}. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; connection/credentials payload"}}},"/api2/v2/users/connect/{account_id}":{"delete":{"tags":["OF API — User"],"summary":"Disconnect a linked account","description":"Disconnects a linked/connected account identified by account_id. Enclosing fn is a disconnect helper. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"account_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the connected account to disconnect"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/delete/request":{"delete":{"tags":["OF API — User"],"summary":"Cancel account deletion request","description":"Cancels a previously submitted account-deletion request. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"post":{"tags":["OF API — User"],"summary":"Request account deletion","description":"Submits a request to delete the current user's account. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"captchaCode":{"type":"string","description":"Captcha verification code"}}}}}}}},"/api2/v2/users/exists":{"post":{"tags":["OF API — User"],"summary":"Check if username exists","description":"Checks whether a username is already taken. Body carries the username to check. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"username":{"type":"string","description":"Username to check for existence"}}}}},"description":"Uses skip429Alert retry option"}}},"/api2/v2/users/forgot-password":{"post":{"tags":["OF API — User"],"summary":"Request password reset","description":"Initiates a forgot-password / password-reset request. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; likely contains email"}}},"/api2/v2/users/get-auth-token":{"post":{"tags":["OF API — User"],"summary":"Get auth token","description":"Retrieves an authentication token for the user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/users/get-otp-token":{"post":{"tags":["OF API — User"],"summary":"Get OTP token","description":"Exchanges credentials for a one-time-password token. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/users/helper-logout":{"post":{"tags":["OF API — User"],"summary":"Log out helper session","description":"Ends a helper (login-as-helper) session and returns to the primary account. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/hints/{hint_id}":{"delete":{"tags":["OF API — User"],"summary":"Dismiss user hint","description":"Dismisses a UI hint/recommendation by ID for the current user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"hint_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the hint to dismiss"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/license_form":{"post":{"tags":["OF API — Misc"],"summary":"Submit license form","description":"Submits a license form for the current user (identity/creator license documentation). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; license form data"}}},"/api2/v2/users/links":{"get":{"tags":["OF API — User"],"summary":"Get user links","description":"Retrieves the current user's configured profile links. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"array"}}}}}}}},"post":{"tags":["OF API — User"],"summary":"Add profile link","description":"Adds a custom link to the current user's profile. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object (defaults to {}); fields not statically visible"}},"put":{"tags":["OF API — User"],"summary":"Update a user link","description":"Updates one of the user's external profile links. Sibling calls list, create and delete links. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"link":{"type":"string|object","description":"The link value to update"}}}}}}}},"/api2/v2/users/links/{link_id}":{"delete":{"tags":["OF API — User"],"summary":"Delete a user link","description":"Deletes one of the user's profile links by id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"link_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the link to delete"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/login":{"post":{"tags":["OF API — User"],"summary":"Log in user","description":"Authenticates a user and starts a session. Accepts login credentials in the request body. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible (likely email/username + password)"}}},"/api2/v2/users/login-as-helper/{helper_id}":{"post":{"tags":["OF API — User"],"summary":"Log in as a helper","description":"Impersonates/switches into a helper (team member) account identified by helper id. Paired with POST /users/helper-logout. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"helper_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the helper account to log in as"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/logout":{"post":{"tags":["OF API — User"],"summary":"Log out current user","description":"Logs out the currently authenticated user session. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/mass-alert/{alert_id}":{"delete":{"tags":["OF API — Notifications"],"summary":"Dismiss a mass alert","description":"Dismisses/deletes a mass alert notification identified by alert id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"alert_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the mass alert to dismiss"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/mass-hints/{hint_id}":{"delete":{"tags":["OF API — User"],"summary":"Delete mass message hint","description":"Deletes a mass-message hint for the current user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"hint_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the mass-message hint to delete"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/me/auth-token":{"get":{"tags":["OF API — User"],"summary":"Get current auth token","description":"Retrieves an auth token for the current user (used for authenticated sub-flows). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"token":{"type":"string"}}}}}}}}}}},"/api2/v2/users/me/id":{"get":{"tags":["OF API — User"],"summary":"Get current user ID","description":"Returns the ID of the currently authenticated user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"id":{"type":"integer"}}}}}}}}}}},"/api2/v2/users/me/referrals":{"get":{"tags":["OF API — Misc"],"summary":"List referrals","description":"Returns the current user's referrals over a date range with pagination. The dynamic path segment is a built querystring. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":false,"schema":{"type":"string"},"description":"Start of the date range"},{"name":"endDate","in":"query","required":false,"schema":{"type":"string"},"description":"End of the date range"},{"name":"offset","in":"query","required":false,"schema":{"type":"string"},"description":"Pagination offset"},{"name":"marker","in":"query","required":false,"schema":{"type":"string"},"description":"Pagination marker"},{"name":"onlyPerformers","in":"query","required":false,"schema":{"type":"string"},"description":"Only referred performers/creators"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"hasMore":{"type":"boolean"},"list":{"type":"array"}}}}}}}}}}},"/api2/v2/users/me/settings/messages":{"patch":{"tags":["OF API — User"],"summary":"Update message settings","description":"Updates the current user's messaging settings. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; message-settings fields not statically visible"}}},"/api2/v2/users/me/settings/{section}":{"put":{"tags":["OF API — User"],"summary":"Update user settings section","description":"Replaces the settings for a specific settings section of the current user. The section key is the path param and the settings payload is the body. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"section","in":"path","required":true,"schema":{"type":"string"},"description":"Settings section/type key (e.g. messages, story, streams)"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; settings payload for the section"}}},"/api2/v2/users/me/settings/{settings_section}":{"patch":{"tags":["OF API — User"],"summary":"Update user settings section","description":"Partially updates a named section of the current user's settings. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"settings_section","in":"path","required":true,"schema":{"type":"string"},"description":"Name of the settings section to update"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; settings fields not statically visible"}}},"/api2/v2/users/me/start-date-model":{"get":{"tags":["OF API — User"],"summary":"Get creator start date","description":"Returns the current user's creator/model start-date information. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"startDate":{"type":"string"}}}}}}}}}}},"/api2/v2/users/me/stats/messages/{type}":{"get":{"tags":["OF API — Misc"],"summary":"Get messages statistics by type","description":"Returns the current user's messaging statistics for a given type (default 'all') over a date range, paginated and searchable. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"type","in":"path","required":true,"schema":{"type":"string"},"description":"Message stats type/segment (default 'all')"},{"name":"startDate","in":"query","required":false,"schema":{"type":"string"},"description":"Start of the date range"},{"name":"endDate","in":"query","required":false,"schema":{"type":"string"},"description":"End of the date range"},{"name":"limit","in":"query","required":false,"schema":{"type":"string"},"description":"Max number of rows"},{"name":"query","in":"query","required":false,"schema":{"type":"string"},"description":"Search term"},{"name":"offset","in":"query","required":false,"schema":{"type":"string"},"description":"Pagination offset"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/me/stats/top/fan":{"get":{"tags":["OF API — Misc"],"summary":"Get top fans stats","description":"Returns the current user's top-fan statistics over a date range. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":true,"schema":{"type":"string"},"description":"Range start date"},{"name":"endDate","in":"query","required":true,"schema":{"type":"string"},"description":"Range end date"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"total":{"type":"object","properties":{"view":{"type":"string"},"avatar":{"type":"null"},"avatarThumbs":{"type":"null"},"header":{"type":"null"},"headerSize":{"type":"null"},"headerThumbs":{"type":"null"},"id":{"type":"integer"},"name":{"type":"string"},"username":{"type":"string"},"canLookStory":{"type":"boolean"},"canCommentStory":{"type":"boolean"},"hasNotViewedStory":{"type":"boolean"},"isVerified":{"type":"boolean"},"canPayInternal":{"type":"boolean"},"hasScheduledStream":{"type":"boolean"},"hasStream":{"type":"boolean"},"hasStories":{"type":"boolean"},"tipsEnabled":{"type":"boolean"},"tipsTextEnabled":{"type":"boolean"},"tipsMin":{"type":"integer"},"tipsMinInternal":{"type":"integer"},"tipsMax":{"type":"integer"},"canEarn":{"type":"boolean"},"canAddSubscriber":{"type":"boolean"},"subscribePrice":{"type":"integer"},"displayName":{"type":"string"},"notice":{"type":"string"},"isActive":{"type":"boolean"},"isRestricted":{"type":"boolean"},"canRestrict":{"type":"boolean"},"subscribedBy":{"type":"boolean"},"subscribedByExpire":{"type":"boolean"},"subscribedByExpireDate":{"type":"string"},"subscribedByAutoprolong":{"type":"boolean"},"subscribedIsExpiredNow":{"type":"boolean"},"currentSubscribePrice":{"type":"integer"},"subscribedOn":{"type":"null"},"subscribedOnExpire":{"type":"boolean"},"subscribedOnExpiredNow":{"type":"boolean"},"subscribedOnDuration":{"type":"string"},"listsStates":{"type":"array","items":{"type":"object"}},"canReport":{"type":"boolean"},"canReceiveChatMessage":{"type":"boolean"},"hideChat":{"type":"boolean"},"lastSeen":{"type":"string"},"isPerformer":{"type":"boolean"},"isRealPerformer":{"type":"boolean"},"subscribedByData":{"type":"object","properties":{"price":{"type":"integer"},"newPrice":{"type":"integer"},"regularPrice":{"type":"integer"},"subscribePrice":{"type":"integer"},"discountPercent":{"type":"integer"},"discountPeriod":{"type":"integer"},"subscribeAt":{"type":"string"},"expiredAt":{"type":"string"},"renewedAt":{"type":"string"},"discountFinishedAt":{"type":"null"},"discountStartedAt":{"type":"null"},"status":{"type":"null"},"isMuted":{"type":"boolean"},"unsubscribeReason":{"type":"string"},"duration":{"type":"string"},"showPostsInFeed":{"type":"boolean"},"subscribes":{"type":"array"},"hasActivePaidSubscriptions":{"type":"boolean"}}},"subscribedOnData":{"type":"object","properties":{"price":{"type":"integer"},"newPrice":{"type":"integer"},"regularPrice":{"type":"integer"},"subscribePrice":{"type":"integer"},"discountPercent":{"type":"integer"},"discountPeriod":{"type":"integer"},"subscribeAt":{"type":"string"},"expiredAt":{"type":"string"},"renewedAt":{"type":"null"},"discountFinishedAt":{"type":"null"},"discountStartedAt":{"type":"null"},"status":{"type":"null"},"isMuted":{"type":"boolean"},"unsubscribeReason":{"type":"string"},"duration":{"type":"string"},"tipsSumm":{"type":"integer"},"subscribesSumm":{"type":"integer"},"messagesSumm":{"type":"number"},"postsSumm":{"type":"integer"},"streamsSumm":{"type":"integer"},"totalSumm":{"type":"number"},"subscribes":{"type":"array"},"hasActivePaidSubscriptions":{"type":"boolean"}}},"canTrialSend":{"type":"boolean"},"isBlocked":{"type":"boolean"},"canUnsubscribe":{"type":"boolean"},"isPendingAutoprolong":{"type":"boolean"}}},"subscriptions":{"type":"object","properties":{"view":{"type":"string"},"avatar":{"type":"null"},"avatarThumbs":{"type":"null"},"header":{"type":"null"},"headerSize":{"type":"null"},"headerThumbs":{"type":"null"},"id":{"type":"integer"},"name":{"type":"string"},"username":{"type":"string"},"canLookStory":{"type":"boolean"},"canCommentStory":{"type":"boolean"},"hasNotViewedStory":{"type":"boolean"},"isVerified":{"type":"boolean"},"canPayInternal":{"type":"boolean"},"hasScheduledStream":{"type":"boolean"},"hasStream":{"type":"boolean"},"hasStories":{"type":"boolean"},"tipsEnabled":{"type":"boolean"},"tipsTextEnabled":{"type":"boolean"},"tipsMin":{"type":"integer"},"tipsMinInternal":{"type":"integer"},"tipsMax":{"type":"integer"},"canEarn":{"type":"boolean"},"canAddSubscriber":{"type":"boolean"},"subscribePrice":{"type":"integer"},"displayName":{"type":"string"},"notice":{"type":"string"},"isActive":{"type":"boolean"},"isRestricted":{"type":"boolean"},"canRestrict":{"type":"boolean"},"subscribedBy":{"type":"boolean"},"subscribedByExpire":{"type":"boolean"},"subscribedByExpireDate":{"type":"string"},"subscribedByAutoprolong":{"type":"boolean"},"subscribedIsExpiredNow":{"type":"boolean"},"currentSubscribePrice":{"type":"integer"},"subscribedOn":{"type":"null"},"subscribedOnExpire":{"type":"boolean"},"subscribedOnExpiredNow":{"type":"boolean"},"subscribedOnDuration":{"type":"string"},"listsStates":{"type":"array","items":{"type":"object"}},"canReport":{"type":"boolean"},"canReceiveChatMessage":{"type":"boolean"},"hideChat":{"type":"boolean"},"lastSeen":{"type":"string"},"isPerformer":{"type":"boolean"},"isRealPerformer":{"type":"boolean"},"subscribedByData":{"type":"object","properties":{"price":{"type":"integer"},"newPrice":{"type":"integer"},"regularPrice":{"type":"integer"},"subscribePrice":{"type":"integer"},"discountPercent":{"type":"integer"},"discountPeriod":{"type":"integer"},"subscribeAt":{"type":"string"},"expiredAt":{"type":"string"},"renewedAt":{"type":"string"},"discountFinishedAt":{"type":"null"},"discountStartedAt":{"type":"null"},"status":{"type":"null"},"isMuted":{"type":"boolean"},"unsubscribeReason":{"type":"string"},"duration":{"type":"string"},"showPostsInFeed":{"type":"boolean"},"subscribes":{"type":"array"},"hasActivePaidSubscriptions":{"type":"boolean"}}},"subscribedOnData":{"type":"object","properties":{"price":{"type":"integer"},"newPrice":{"type":"integer"},"regularPrice":{"type":"integer"},"subscribePrice":{"type":"integer"},"discountPercent":{"type":"integer"},"discountPeriod":{"type":"integer"},"subscribeAt":{"type":"string"},"expiredAt":{"type":"string"},"renewedAt":{"type":"null"},"discountFinishedAt":{"type":"null"},"discountStartedAt":{"type":"null"},"status":{"type":"null"},"isMuted":{"type":"boolean"},"unsubscribeReason":{"type":"string"},"duration":{"type":"string"},"tipsSumm":{"type":"integer"},"subscribesSumm":{"type":"integer"},"messagesSumm":{"type":"number"},"postsSumm":{"type":"integer"},"streamsSumm":{"type":"integer"},"totalSumm":{"type":"number"},"subscribes":{"type":"array"},"hasActivePaidSubscriptions":{"type":"boolean"}}},"canTrialSend":{"type":"boolean"},"isBlocked":{"type":"boolean"},"canUnsubscribe":{"type":"boolean"},"isPendingAutoprolong":{"type":"boolean"}}},"tips":{"type":"object","properties":{"view":{"type":"string"},"avatar":{"type":"null"},"avatarThumbs":{"type":"null"},"header":{"type":"null"},"headerSize":{"type":"null"},"headerThumbs":{"type":"null"},"id":{"type":"integer"},"name":{"type":"string"},"username":{"type":"string"},"canLookStory":{"type":"boolean"},"canCommentStory":{"type":"boolean"},"hasNotViewedStory":{"type":"boolean"},"isVerified":{"type":"boolean"},"canPayInternal":{"type":"boolean"},"hasScheduledStream":{"type":"boolean"},"hasStream":{"type":"boolean"},"hasStories":{"type":"boolean"},"tipsEnabled":{"type":"boolean"},"tipsTextEnabled":{"type":"boolean"},"tipsMin":{"type":"integer"},"tipsMinInternal":{"type":"integer"},"tipsMax":{"type":"integer"},"canEarn":{"type":"boolean"},"canAddSubscriber":{"type":"boolean"},"subscribePrice":{"type":"integer"},"displayName":{"type":"string"},"notice":{"type":"string"},"isActive":{"type":"boolean"},"isRestricted":{"type":"boolean"},"canRestrict":{"type":"boolean"},"subscribedBy":{"type":"boolean"},"subscribedByExpire":{"type":"boolean"},"subscribedByExpireDate":{"type":"string"},"subscribedByAutoprolong":{"type":"boolean"},"subscribedIsExpiredNow":{"type":"boolean"},"currentSubscribePrice":{"type":"integer"},"subscribedOn":{"type":"boolean"},"subscribedOnExpire":{"type":"boolean"},"subscribedOnExpiredNow":{"type":"boolean"},"subscribedOnDuration":{"type":"string"},"listsStates":{"type":"array","items":{"type":"object"}},"canReport":{"type":"boolean"},"canReceiveChatMessage":{"type":"boolean"},"hideChat":{"type":"boolean"},"lastSeen":{"type":"string"},"isPerformer":{"type":"boolean"},"isRealPerformer":{"type":"boolean"},"subscribedByData":{"type":"object","properties":{"price":{"type":"integer"},"newPrice":{"type":"integer"},"regularPrice":{"type":"integer"},"subscribePrice":{"type":"integer"},"discountPercent":{"type":"integer"},"discountPeriod":{"type":"integer"},"subscribeAt":{"type":"string"},"expiredAt":{"type":"string"},"renewedAt":{"type":"string"},"discountFinishedAt":{"type":"null"},"discountStartedAt":{"type":"null"},"status":{"type":"null"},"isMuted":{"type":"boolean"},"unsubscribeReason":{"type":"string"},"duration":{"type":"string"},"showPostsInFeed":{"type":"boolean"},"subscribes":{"type":"array"},"hasActivePaidSubscriptions":{"type":"boolean"}}},"subscribedOnData":{"type":"object","properties":{"price":{"type":"integer"},"newPrice":{"type":"integer"},"regularPrice":{"type":"integer"},"subscribePrice":{"type":"integer"},"discountPercent":{"type":"integer"},"discountPeriod":{"type":"integer"},"subscribeAt":{"type":"string"},"expiredAt":{"type":"string"},"renewedAt":{"type":"string"},"discountFinishedAt":{"type":"null"},"discountStartedAt":{"type":"null"},"status":{"type":"null"},"isMuted":{"type":"boolean"},"unsubscribeReason":{"type":"string"},"duration":{"type":"string"},"tipsSumm":{"type":"number"},"subscribesSumm":{"type":"integer"},"messagesSumm":{"type":"number"},"postsSumm":{"type":"integer"},"streamsSumm":{"type":"integer"},"totalSumm":{"type":"number"},"subscribes":{"type":"array"},"hasActivePaidSubscriptions":{"type":"boolean"}}},"canTrialSend":{"type":"boolean"},"isBlocked":{"type":"boolean"},"canUnsubscribe":{"type":"boolean"},"isPendingAutoprolong":{"type":"boolean"}}},"messages":{"type":"object","properties":{"view":{"type":"string"},"avatar":{"type":"null"},"avatarThumbs":{"type":"null"},"header":{"type":"null"},"headerSize":{"type":"null"},"headerThumbs":{"type":"null"},"id":{"type":"integer"},"name":{"type":"string"},"username":{"type":"string"},"canLookStory":{"type":"boolean"},"canCommentStory":{"type":"boolean"},"hasNotViewedStory":{"type":"boolean"},"isVerified":{"type":"boolean"},"canPayInternal":{"type":"boolean"},"hasScheduledStream":{"type":"boolean"},"hasStream":{"type":"boolean"},"hasStories":{"type":"boolean"},"tipsEnabled":{"type":"boolean"},"tipsTextEnabled":{"type":"boolean"},"tipsMin":{"type":"integer"},"tipsMinInternal":{"type":"integer"},"tipsMax":{"type":"integer"},"canEarn":{"type":"boolean"},"canAddSubscriber":{"type":"boolean"},"subscribePrice":{"type":"integer"},"displayName":{"type":"string"},"notice":{"type":"string"},"isActive":{"type":"boolean"},"isRestricted":{"type":"boolean"},"canRestrict":{"type":"boolean"},"subscribedBy":{"type":"boolean"},"subscribedByExpire":{"type":"boolean"},"subscribedByExpireDate":{"type":"string"},"subscribedByAutoprolong":{"type":"boolean"},"subscribedIsExpiredNow":{"type":"boolean"},"currentSubscribePrice":{"type":"integer"},"subscribedOn":{"type":"null"},"subscribedOnExpire":{"type":"boolean"},"subscribedOnExpiredNow":{"type":"boolean"},"subscribedOnDuration":{"type":"string"},"listsStates":{"type":"array","items":{"type":"object"}},"canReport":{"type":"boolean"},"canReceiveChatMessage":{"type":"boolean"},"hideChat":{"type":"boolean"},"lastSeen":{"type":"string"},"isPerformer":{"type":"boolean"},"isRealPerformer":{"type":"boolean"},"subscribedByData":{"type":"object","properties":{"price":{"type":"integer"},"newPrice":{"type":"integer"},"regularPrice":{"type":"integer"},"subscribePrice":{"type":"integer"},"discountPercent":{"type":"integer"},"discountPeriod":{"type":"integer"},"subscribeAt":{"type":"string"},"expiredAt":{"type":"string"},"renewedAt":{"type":"string"},"discountFinishedAt":{"type":"null"},"discountStartedAt":{"type":"null"},"status":{"type":"null"},"isMuted":{"type":"boolean"},"unsubscribeReason":{"type":"string"},"duration":{"type":"string"},"showPostsInFeed":{"type":"boolean"},"subscribes":{"type":"array"},"hasActivePaidSubscriptions":{"type":"boolean"}}},"subscribedOnData":{"type":"object","properties":{"price":{"type":"integer"},"newPrice":{"type":"integer"},"regularPrice":{"type":"integer"},"subscribePrice":{"type":"integer"},"discountPercent":{"type":"integer"},"discountPeriod":{"type":"integer"},"subscribeAt":{"type":"string"},"expiredAt":{"type":"string"},"renewedAt":{"type":"null"},"discountFinishedAt":{"type":"null"},"discountStartedAt":{"type":"null"},"status":{"type":"null"},"isMuted":{"type":"boolean"},"unsubscribeReason":{"type":"string"},"duration":{"type":"string"},"tipsSumm":{"type":"integer"},"subscribesSumm":{"type":"integer"},"messagesSumm":{"type":"number"},"postsSumm":{"type":"integer"},"streamsSumm":{"type":"integer"},"totalSumm":{"type":"number"},"subscribes":{"type":"array"},"hasActivePaidSubscriptions":{"type":"boolean"}}},"canTrialSend":{"type":"boolean"},"isBlocked":{"type":"boolean"},"canUnsubscribe":{"type":"boolean"},"isPendingAutoprolong":{"type":"boolean"}}},"streams":{"type":"null"}}}}}}}}}}},"/api2/v2/users/me/stats/top/message":{"get":{"tags":["OF API — Misc"],"summary":"Get top messages stats","description":"Returns statistics on the current user's top-performing messages over a date range. The `{id}` in path_raw is the appended querystring. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":true,"schema":{"type":"string"},"description":"Range start date"},{"name":"endDate","in":"query","required":true,"schema":{"type":"string"},"description":"Range end date"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"purchases":{"type":"object","properties":{"id":{"type":"integer"},"date":{"type":"string"},"responseType":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"},"giphyId":{"type":"null"},"isFree":{"type":"boolean"},"isMediaReady":{"type":"boolean"},"mediaCount":{"type":"integer"},"media":{"type":"array","items":{"type":"object"}},"previews":{"type":"array"},"isTip":{"type":"boolean"},"isReportedByMe":{"type":"boolean"},"viewedCount":{"type":"integer"},"sentCount":{"type":"integer"},"isCanceled":{"type":"boolean"},"template":{"type":"string"},"canUnsend":{"type":"boolean"},"unsendSeconds":{"type":"integer"},"price":{"type":"string"},"purchasedCount":{"type":"integer"},"canSendMessageToBuyers":{"type":"boolean"}}}}}}}}}}}}},"/api2/v2/users/me/stats/top/post":{"get":{"tags":["OF API — Misc"],"summary":"Get top posts statistics","description":"Returns the current user's top-performing posts statistics for a date range. Sibling calls fetch top story/stream/stream stats. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":false,"schema":{"type":"string"},"description":"Start of the date range"},{"name":"endDate","in":"query","required":false,"schema":{"type":"string"},"description":"End of the date range"},{"name":"skip_users","in":"query","required":false,"schema":{"type":"string"},"description":"Whether to omit expanded user objects (set to 'all')"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"purchases":{"type":"null"},"tips":{"type":"object","properties":{"author":{"type":"object","properties":{"view":{"type":"string"},"avatar":{"type":"string"},"avatarThumbs":{"type":"object"},"header":{"type":"string"},"headerSize":{"type":"object"},"headerThumbs":{"type":"object"},"id":{"type":"integer"},"name":{"type":"string"},"username":{"type":"string"},"canLookStory":{"type":"boolean"},"canCommentStory":{"type":"boolean"},"hasNotViewedStory":{"type":"boolean"},"isVerified":{"type":"boolean"},"canPayInternal":{"type":"boolean"},"hasScheduledStream":{"type":"boolean"},"hasStream":{"type":"boolean"},"hasStories":{"type":"boolean"},"tipsEnabled":{"type":"boolean"},"tipsTextEnabled":{"type":"boolean"},"tipsMin":{"type":"integer"},"tipsMinInternal":{"type":"integer"},"tipsMax":{"type":"integer"},"canEarn":{"type":"boolean"},"canAddSubscriber":{"type":"boolean"},"subscribePrice":{"type":"integer"},"hasStripe":{"type":"boolean"},"isStripeExist":{"type":"boolean"},"subscriptionBundles":{"type":"array"},"canSendChatToAll":{"type":"boolean"},"creditsMin":{"type":"integer"},"creditsMax":{"type":"integer"},"isPaywallPassed":{"type":"boolean"},"creditsMinAlternatives":{"type":"integer"},"payPalStatus":{"type":"array"},"canCreateLists":{"type":"boolean"},"showMediaCount":{"type":"boolean"},"showPostsInFeed":{"type":"boolean"},"subscribedBy":{"type":"boolean"},"canTrialSend":{"type":"boolean"}}},"responseType":{"type":"string"},"id":{"type":"integer"},"postedAt":{"type":"string"},"postedAtPrecise":{"type":"string"},"text":{"type":"string"},"isMarkdownDisabled":{"type":"boolean"},"canDelete":{"type":"boolean"},"canComment":{"type":"boolean"},"canEdit":{"type":"boolean"},"favoritesCount":{"type":"integer"},"mediaCount":{"type":"integer"},"isMediaReady":{"type":"boolean"},"isOpened":{"type":"boolean"},"canToggleFavorite":{"type":"boolean"},"stats":{"type":"object","properties":{"isAvailable":{"type":"boolean"},"hasStats":{"type":"boolean"},"hasVideo":{"type":"boolean"},"hasAudio":{"type":"boolean"},"lookCount":{"type":"integer"},"uniqueLookCount":{"type":"integer"},"uniqueLookChart":{"type":"array"},"lookChart":{"type":"array"},"lookDuration":{"type":"integer"},"lookDurationAverage":{"type":"number"},"likeCount":{"type":"integer"},"likeChart":{"type":"array"},"commentCount":{"type":"integer"},"commentChart":{"type":"array"},"tipCount":{"type":"integer"},"tipChart":{"type":"array"},"tipSum":{"type":"number"},"purchasedCount":{"type":"integer"},"purchasedSumm":{"type":"integer"},"videoPlayCount":{"type":"integer"},"videoPlayDuration":{"type":"integer"},"videoPlayDurationAverage":{"type":"number"}}},"commentsCount":{"type":"integer"},"tipsAmount":{"type":"string"},"rawText":{"type":"string"},"tweetSend":{"type":"boolean"},"media":{"type":"array","items":{"type":"object"}},"canViewMedia":{"type":"boolean"},"labelStates":{"type":"array","items":{"type":"object"}}}},"views":{"type":"object","properties":{"author":{"type":"object","properties":{"view":{"type":"string"},"avatar":{"type":"string"},"avatarThumbs":{"type":"object"},"header":{"type":"string"},"headerSize":{"type":"object"},"headerThumbs":{"type":"object"},"id":{"type":"integer"},"name":{"type":"string"},"username":{"type":"string"},"canLookStory":{"type":"boolean"},"canCommentStory":{"type":"boolean"},"hasNotViewedStory":{"type":"boolean"},"isVerified":{"type":"boolean"},"canPayInternal":{"type":"boolean"},"hasScheduledStream":{"type":"boolean"},"hasStream":{"type":"boolean"},"hasStories":{"type":"boolean"},"tipsEnabled":{"type":"boolean"},"tipsTextEnabled":{"type":"boolean"},"tipsMin":{"type":"integer"},"tipsMinInternal":{"type":"integer"},"tipsMax":{"type":"integer"},"canEarn":{"type":"boolean"},"canAddSubscriber":{"type":"boolean"},"subscribePrice":{"type":"integer"},"hasStripe":{"type":"boolean"},"isStripeExist":{"type":"boolean"},"subscriptionBundles":{"type":"array"},"canSendChatToAll":{"type":"boolean"},"creditsMin":{"type":"integer"},"creditsMax":{"type":"integer"},"isPaywallPassed":{"type":"boolean"},"creditsMinAlternatives":{"type":"integer"},"payPalStatus":{"type":"array"},"canCreateLists":{"type":"boolean"},"showMediaCount":{"type":"boolean"},"showPostsInFeed":{"type":"boolean"},"subscribedBy":{"type":"boolean"},"canTrialSend":{"type":"boolean"}}},"responseType":{"type":"string"},"id":{"type":"integer"},"postedAt":{"type":"string"},"postedAtPrecise":{"type":"string"},"text":{"type":"string"},"isMarkdownDisabled":{"type":"boolean"},"canDelete":{"type":"boolean"},"canComment":{"type":"boolean"},"canEdit":{"type":"boolean"},"isPinned":{"type":"boolean"},"favoritesCount":{"type":"integer"},"mediaCount":{"type":"integer"},"isMediaReady":{"type":"boolean"},"isOpened":{"type":"boolean"},"canToggleFavorite":{"type":"boolean"},"stats":{"type":"object","properties":{"isAvailable":{"type":"boolean"},"hasStats":{"type":"boolean"},"hasVideo":{"type":"boolean"},"hasAudio":{"type":"boolean"},"lookCount":{"type":"integer"},"uniqueLookCount":{"type":"integer"},"uniqueLookChart":{"type":"array"},"lookChart":{"type":"array"},"lookDuration":{"type":"integer"},"lookDurationAverage":{"type":"number"},"likeCount":{"type":"integer"},"likeChart":{"type":"array"},"commentCount":{"type":"integer"},"commentChart":{"type":"array"},"tipCount":{"type":"integer"},"tipChart":{"type":"array"},"tipSum":{"type":"number"},"purchasedCount":{"type":"integer"},"purchasedSumm":{"type":"integer"},"videoPlayCount":{"type":"integer"},"videoPlayDuration":{"type":"integer"},"videoPlayDurationAverage":{"type":"number"}}},"commentsCount":{"type":"integer"},"tipsAmount":{"type":"string"},"rawText":{"type":"string"},"tweetSend":{"type":"boolean"},"media":{"type":"array","items":{"type":"object"}},"canViewMedia":{"type":"boolean"},"labelStates":{"type":"array","items":{"type":"object"}}}},"likes":{"type":"object","properties":{"author":{"type":"object","properties":{"view":{"type":"string"},"avatar":{"type":"string"},"avatarThumbs":{"type":"object"},"header":{"type":"string"},"headerSize":{"type":"object"},"headerThumbs":{"type":"object"},"id":{"type":"integer"},"name":{"type":"string"},"username":{"type":"string"},"canLookStory":{"type":"boolean"},"canCommentStory":{"type":"boolean"},"hasNotViewedStory":{"type":"boolean"},"isVerified":{"type":"boolean"},"canPayInternal":{"type":"boolean"},"hasScheduledStream":{"type":"boolean"},"hasStream":{"type":"boolean"},"hasStories":{"type":"boolean"},"tipsEnabled":{"type":"boolean"},"tipsTextEnabled":{"type":"boolean"},"tipsMin":{"type":"integer"},"tipsMinInternal":{"type":"integer"},"tipsMax":{"type":"integer"},"canEarn":{"type":"boolean"},"canAddSubscriber":{"type":"boolean"},"subscribePrice":{"type":"integer"},"hasStripe":{"type":"boolean"},"isStripeExist":{"type":"boolean"},"subscriptionBundles":{"type":"array"},"canSendChatToAll":{"type":"boolean"},"creditsMin":{"type":"integer"},"creditsMax":{"type":"integer"},"isPaywallPassed":{"type":"boolean"},"creditsMinAlternatives":{"type":"integer"},"payPalStatus":{"type":"array"},"canCreateLists":{"type":"boolean"},"showMediaCount":{"type":"boolean"},"showPostsInFeed":{"type":"boolean"},"subscribedBy":{"type":"boolean"},"canTrialSend":{"type":"boolean"}}},"responseType":{"type":"string"},"id":{"type":"integer"},"postedAt":{"type":"string"},"postedAtPrecise":{"type":"string"},"text":{"type":"string"},"isMarkdownDisabled":{"type":"boolean"},"canDelete":{"type":"boolean"},"canComment":{"type":"boolean"},"canEdit":{"type":"boolean"},"isPinned":{"type":"boolean"},"favoritesCount":{"type":"integer"},"mediaCount":{"type":"integer"},"isMediaReady":{"type":"boolean"},"isOpened":{"type":"boolean"},"canToggleFavorite":{"type":"boolean"},"stats":{"type":"object","properties":{"isAvailable":{"type":"boolean"},"hasStats":{"type":"boolean"},"hasVideo":{"type":"boolean"},"hasAudio":{"type":"boolean"},"lookCount":{"type":"integer"},"uniqueLookCount":{"type":"integer"},"uniqueLookChart":{"type":"array"},"lookChart":{"type":"array"},"lookDuration":{"type":"integer"},"lookDurationAverage":{"type":"number"},"likeCount":{"type":"integer"},"likeChart":{"type":"array"},"commentCount":{"type":"integer"},"commentChart":{"type":"array"},"tipCount":{"type":"integer"},"tipChart":{"type":"array"},"tipSum":{"type":"number"},"purchasedCount":{"type":"integer"},"purchasedSumm":{"type":"integer"},"videoPlayCount":{"type":"integer"},"videoPlayDuration":{"type":"integer"},"videoPlayDurationAverage":{"type":"number"}}},"commentsCount":{"type":"integer"},"tipsAmount":{"type":"string"},"rawText":{"type":"string"},"tweetSend":{"type":"boolean"},"media":{"type":"array","items":{"type":"object"}},"canViewMedia":{"type":"boolean"},"labelStates":{"type":"array","items":{"type":"object"}}}},"comments":{"type":"object","properties":{"author":{"type":"object","properties":{"view":{"type":"string"},"avatar":{"type":"string"},"avatarThumbs":{"type":"object"},"header":{"type":"string"},"headerSize":{"type":"object"},"headerThumbs":{"type":"object"},"id":{"type":"integer"},"name":{"type":"string"},"username":{"type":"string"},"canLookStory":{"type":"boolean"},"canCommentStory":{"type":"boolean"},"hasNotViewedStory":{"type":"boolean"},"isVerified":{"type":"boolean"},"canPayInternal":{"type":"boolean"},"hasScheduledStream":{"type":"boolean"},"hasStream":{"type":"boolean"},"hasStories":{"type":"boolean"},"tipsEnabled":{"type":"boolean"},"tipsTextEnabled":{"type":"boolean"},"tipsMin":{"type":"integer"},"tipsMinInternal":{"type":"integer"},"tipsMax":{"type":"integer"},"canEarn":{"type":"boolean"},"canAddSubscriber":{"type":"boolean"},"subscribePrice":{"type":"integer"},"hasStripe":{"type":"boolean"},"isStripeExist":{"type":"boolean"},"subscriptionBundles":{"type":"array"},"canSendChatToAll":{"type":"boolean"},"creditsMin":{"type":"integer"},"creditsMax":{"type":"integer"},"isPaywallPassed":{"type":"boolean"},"creditsMinAlternatives":{"type":"integer"},"payPalStatus":{"type":"array"},"canCreateLists":{"type":"boolean"},"showMediaCount":{"type":"boolean"},"showPostsInFeed":{"type":"boolean"},"subscribedBy":{"type":"boolean"},"canTrialSend":{"type":"boolean"}}},"responseType":{"type":"string"},"id":{"type":"integer"},"postedAt":{"type":"string"},"postedAtPrecise":{"type":"string"},"isMarkdownDisabled":{"type":"boolean"},"canDelete":{"type":"boolean"},"canComment":{"type":"boolean"},"canEdit":{"type":"boolean"},"favoritesCount":{"type":"integer"},"mediaCount":{"type":"integer"},"isMediaReady":{"type":"boolean"},"isOpened":{"type":"boolean"},"canToggleFavorite":{"type":"boolean"},"stats":{"type":"object","properties":{"isAvailable":{"type":"boolean"},"hasStats":{"type":"boolean"},"hasVideo":{"type":"boolean"},"hasAudio":{"type":"boolean"},"lookCount":{"type":"integer"},"uniqueLookCount":{"type":"integer"},"uniqueLookChart":{"type":"array"},"lookChart":{"type":"array"},"lookDuration":{"type":"integer"},"lookDurationAverage":{"type":"number"},"likeCount":{"type":"integer"},"likeChart":{"type":"array"},"commentCount":{"type":"integer"},"commentChart":{"type":"array"},"tipCount":{"type":"integer"},"tipChart":{"type":"array"},"tipSum":{"type":"integer"},"purchasedCount":{"type":"integer"},"purchasedSumm":{"type":"integer"}}},"commentsCount":{"type":"integer"},"tipsAmount":{"type":"string"},"tweetSend":{"type":"boolean"},"media":{"type":"array","items":{"type":"object"}},"canViewMedia":{"type":"boolean"}}}}}}}}}}}}},"/api2/v2/users/me/stats/top/story":{"get":{"tags":["OF API — Misc"],"summary":"Get top stories stats","description":"Retrieves top-performing story statistics over a date range. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":true,"schema":{"type":"string"},"description":"Start of date range"},{"name":"endDate","in":"query","required":true,"schema":{"type":"string"},"description":"End of date range"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"tips":{"type":"null"},"views":{"type":"object","properties":{"id":{"type":"integer"},"userId":{"type":"integer"},"isReady":{"type":"boolean"},"hasPost":{"type":"boolean"},"isWatched":{"type":"boolean"},"media":{"type":"array","items":{"type":"object"}},"createdAt":{"type":"string"},"canvasHeight":{"type":"integer"},"canvasWidth":{"type":"integer"},"question":{"type":"object","properties":{"entity":{"type":"object"},"type":{"type":"string"},"positions":{"type":"object"}}},"viewersCount":{"type":"integer"},"viewers":{"type":"array","items":{"type":"object"}},"commentsCount":{"type":"integer"},"canDelete":{"type":"boolean"},"isHighlightCover":{"type":"boolean"},"isLastInHighlight":{"type":"boolean"},"tipsAmount":{"type":"string"},"tipsAmountRaw":{"type":"integer"},"tipsCount":{"type":"integer"},"likesCount":{"type":"integer"},"releaseForms":{"type":"array"}}},"likes":{"type":"object","properties":{"id":{"type":"integer"},"userId":{"type":"integer"},"isReady":{"type":"boolean"},"hasPost":{"type":"boolean"},"isWatched":{"type":"boolean"},"media":{"type":"array","items":{"type":"object"}},"createdAt":{"type":"string"},"canvasHeight":{"type":"integer"},"canvasWidth":{"type":"integer"},"question":{"type":"object","properties":{"entity":{"type":"object"},"type":{"type":"string"},"positions":{"type":"object"}}},"viewersCount":{"type":"integer"},"viewers":{"type":"array","items":{"type":"object"}},"commentsCount":{"type":"integer"},"canDelete":{"type":"boolean"},"isHighlightCover":{"type":"boolean"},"isLastInHighlight":{"type":"boolean"},"tipsAmount":{"type":"string"},"tipsAmountRaw":{"type":"integer"},"tipsCount":{"type":"integer"},"likesCount":{"type":"integer"},"releaseForms":{"type":"array"}}},"comments":{"type":"object","properties":{"id":{"type":"integer"},"userId":{"type":"integer"},"isReady":{"type":"boolean"},"hasPost":{"type":"boolean"},"isWatched":{"type":"boolean"},"media":{"type":"array","items":{"type":"object"}},"createdAt":{"type":"string"},"canvasHeight":{"type":"integer"},"canvasWidth":{"type":"integer"},"question":{"type":"object","properties":{"entity":{"type":"object"},"type":{"type":"string"},"positions":{"type":"object"}}},"viewersCount":{"type":"integer"},"viewers":{"type":"array","items":{"type":"object"}},"commentsCount":{"type":"integer"},"canDelete":{"type":"boolean"},"isHighlightCover":{"type":"boolean"},"isLastInHighlight":{"type":"boolean"},"tipsAmount":{"type":"string"},"tipsAmountRaw":{"type":"integer"},"tipsCount":{"type":"integer"},"likesCount":{"type":"integer"},"releaseForms":{"type":"array"}}}}}}}}}}}}},"/api2/v2/users/me/stats/top/stream":{"get":{"tags":["OF API — Misc"],"summary":"Get top streams stats","description":"Returns the current user's top-performing streams statistics for a date range. The dynamic suffix in path_raw is the querystring. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":false,"schema":{"type":"string"},"description":"Range start date"},{"name":"endDate","in":"query","required":false,"schema":{"type":"string"},"description":"Range end date"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"purchases":{"type":"null"},"tips":{"type":"null"},"views":{"type":"null"},"likes":{"type":"null"},"comments":{"type":"null"}}}}}}}}}}},"/api2/v2/users/me/strong_otp_codes":{"get":{"tags":["OF API — User"],"summary":"Get OTP backup codes","description":"Returns the current user's strong OTP (backup/recovery) codes. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/me/validate-data":{"post":{"tags":["OF API — User"],"summary":"Validate current user data","description":"Validates submitted profile/account data for the current user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/users/media/drm/certificate":{"get":{"tags":["OF API — Content"],"summary":"Get DRM certificate","description":"Retrieves the DRM certificate used for protected media playback. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"string"}}}}}}}}},"/api2/v2/users/notifications/settings/tabs-order":{"get":{"tags":["OF API — Notifications"],"summary":"Get notification tabs order","description":"Returns the user's configured ordering of notification tabs. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"array","items":{"type":"string"}}}}}}}}},"post":{"tags":["OF API — Notifications"],"summary":"Save notification tabs order","description":"Saves the ordering of the notification settings tabs. A GET on the same path retrieves the current order. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; tab order payload, not statically visible"}}},"/api2/v2/users/notifications/{notification_id}/read":{"post":{"tags":["OF API — Notifications"],"summary":"Mark notification as read","description":"Marks a single notification as read by its id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"notification_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the notification to mark read"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/opensea/nft":{"put":{"tags":["OF API — User"],"summary":"Set OpenSea NFT profile item","description":"Updates the OpenSea NFT associated with the user's profile. Sibling calls manage the linked OpenSea wallet and asset listings. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/users/opensea/wallet":{"delete":{"tags":["OF API — User"],"summary":"Disconnect OpenSea wallet","description":"Removes the connected OpenSea crypto wallet from the current user account. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"post":{"tags":["OF API — User"],"summary":"Connect OpenSea wallet","description":"Links an OpenSea (crypto) wallet to the current user account. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; wallet details, not statically visible"}}},"/api2/v2/users/otp":{"delete":{"tags":["OF API — User"],"summary":"Disable two-factor OTP","description":"Disables two-factor authentication (OTP) for the account using a verification code. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"put":{"tags":["OF API — User"],"summary":"Confirm OTP code","description":"Confirms/enables one-time-password (2FA) by submitting the verification code. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"OTP verification code"}}}}}}}},"/api2/v2/users/otp/alternative":{"put":{"tags":["OF API — User"],"summary":"Request alternative OTP method","description":"Requests an alternative one-time-password (2FA) delivery method. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/otp/check":{"post":{"tags":["OF API — User"],"summary":"Verify OTP code","description":"Verifies a one-time password code. Grouped with users/otp code/phone/alternative endpoints. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible (likely {code})"}}},"/api2/v2/users/otp/code":{"get":{"tags":["OF API — User"],"summary":"Request OTP code","description":"Requests/retrieves a one-time password (OTP) code for the current user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/otp/phone":{"put":{"tags":["OF API — User"],"summary":"Enable phone OTP","description":"Enables/requests one-time-password (2FA) delivery via phone for the current user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/password":{"delete":{"tags":["OF API — User"],"summary":"Remove account password","description":"Deletes the current user's password (e.g. for social-login-only accounts). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/posts/on-this-day":{"get":{"tags":["OF API — Content"],"summary":"Get 'on this day' posts","description":"Retrieves the current user's posts from this date in previous years ('on this day' memories). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"array"}}}}}}}}},"/api2/v2/users/profile/view":{"post":{"tags":["OF API — User"],"summary":"Record profile view","description":"Records that the current user viewed a profile (paired with users/profile/visit). Retries once on failure. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; likely target user id"}}},"/api2/v2/users/profile/visit":{"post":{"tags":["OF API — User"],"summary":"Record a profile visit","description":"Records a profile visit event; a sibling POST /users/profile/view records profile views. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; visit payload not statically visible"}}},"/api2/v2/users/promotions":{"get":{"tags":["OF API — Promotions"],"summary":"Get user promotions","description":"Retrieves promotions available to the current user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"array","items":{"type":"object","properties":{"imageSrc":{"type":"string"},"url":{"type":"string"}}}}}}}}}}}},"/api2/v2/users/push-token/pwa":{"put":{"tags":["OF API — Notifications"],"summary":"Register PWA push token","description":"Registers or updates the web-push (PWA) push notification token for the current user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; push subscription/token payload"}}},"/api2/v2/users/recommends/{user_id}":{"delete":{"tags":["OF API — User"],"summary":"Dismiss a recommended user","description":"Removes/dismisses a suggested (recommended) user identified by user_id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the recommended user to dismiss"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/register":{"post":{"tags":["OF API — User"],"summary":"Register a new user account","description":"Registers a new user account. The request body carries the registration form fields. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; registration fields not statically visible"}}},"/api2/v2/users/restore-access":{"post":{"tags":["OF API — User"],"summary":"Restore account access with code","description":"Restores access to a user account using a provided restore/verification code. Sent with a skip429Alert retry config. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"Restore-access code"}}}}}}}},"/api2/v2/users/settings/notifications":{"patch":{"tags":["OF API — User"],"summary":"Update notification settings","description":"Updates the account's notification settings. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/users/settings/notifications/transports":{"get":{"tags":["OF API — User"],"summary":"Get notification transport settings","description":"Returns the available/enabled notification transport channels (email, push, etc.) for the user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"array","items":{"type":"string"}}}}}}}}}},"/api2/v2/users/social/buttons":{"get":{"tags":["OF API — User"],"summary":"Get social buttons","description":"Retrieves the current user's configured social media buttons/links. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"array"}}}}}}}},"post":{"tags":["OF API — User"],"summary":"Add social buttons","description":"Adds social/link buttons to the user's profile using the provided button IDs. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"buttonIds":{"type":"array","description":"IDs of the social buttons to add"}}}}},"description":"Payload is {buttonIds: e}"}},"put":{"tags":["OF API — User"],"summary":"Update social profile buttons","description":"Updates the configuration of the user's social profile buttons. Sibling calls add, reorder, delete and track clicks on these buttons. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/users/social/buttons/{button_id}":{"delete":{"tags":["OF API — User"],"summary":"Delete a social button","description":"Deletes a profile social/link button identified by button_id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"button_id","in":"path","required":true,"schema":{"type":"string"},"description":"Id of the social button to delete"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"put":{"tags":["OF API — User"],"summary":"Update a social button","description":"Updates a single social button by id on the user's profile. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"button_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the social button to update"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; button fields not statically visible"}}},"/api2/v2/users/social/buttons/{button_id}/click":{"post":{"tags":["OF API — User"],"summary":"Register social button click","description":"Records a click on a user's social button. Part of the profile social-buttons feature. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"button_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the social button clicked"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/social/spotify/anthem":{"put":{"tags":["OF API — User"],"summary":"Set Spotify anthem","description":"Sets the user's Spotify profile anthem track. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"anthemId":{"type":"string","description":"Spotify track ID to set as the anthem"}}}}}}}},"/api2/v2/users/social/spotify/artists":{"put":{"tags":["OF API — User"],"summary":"Set top Spotify artists","description":"Sets the user's top Spotify artists shown on their profile. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"topArtistsIds":{"type":"array","description":"IDs of the top Spotify artists"}}}}}}}},"/api2/v2/users/social/spring":{"delete":{"tags":["OF API — User"],"summary":"Disconnect Spring integration","description":"Disconnects/removes the user's linked Spring (merch) social integration. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"post":{"tags":["OF API — User"],"summary":"Connect Spring merch account","description":"Connects/saves the user's Spring (merch) social integration (DELETE unlinks it, GET retrieves it). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; Spring integration fields not statically visible"}}},"/api2/v2/users/social/{network}":{"delete":{"tags":["OF API — User"],"summary":"Disconnect social network","description":"Disconnects/unlinks a social network account (defaults to twitter) from the profile. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"network","in":"path","required":true,"schema":{"type":"string"},"description":"Social network name (e.g. twitter); defaults to twitter"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/switch/{user_id}":{"post":{"tags":["OF API — User"],"summary":"Switch to connected account","description":"Switches the active session to a connected/linked user account. Defined near users/connect and users/get-auth-token. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the connected account to switch to"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/telegram-link":{"get":{"tags":["OF API — User"],"summary":"Get Telegram link info","description":"Returns the Telegram linking information/URL for connecting the account to Telegram. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/terms/confirm":{"post":{"tags":["OF API — Misc"],"summary":"Confirm terms acceptance","description":"Confirms the user's acceptance of updated terms. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/tickets":{"post":{"tags":["OF API — Misc"],"summary":"Create a support ticket","description":"Creates a new support ticket for the current user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; ticket subject/message payload"}}},"/api2/v2/users/tickets/allowed":{"post":{"tags":["OF API — Misc"],"summary":"Check support ticket allowed","description":"Checks whether the user is allowed to create a support ticket (part of the users/tickets support module). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; fields not statically visible"}}},"/api2/v2/users/tickets/form_subjects":{"get":{"tags":["OF API — Misc"],"summary":"Get support ticket subjects","description":"Returns the selectable subject options for the support-ticket creation form. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"title":{"type":"string"},"items":{"type":"array"}}}}}}}}}}}}}},"/api2/v2/users/tickets/{ticket_id}":{"get":{"tags":["OF API — Misc"],"summary":"Get support ticket","description":"Retrieves a support ticket by id. Grouped with ticket reply/comments/solve/reopen endpoints. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"ticket_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the support ticket"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/tickets/{ticket_id}/read":{"put":{"tags":["OF API — Misc"],"summary":"Mark support ticket as read","description":"Marks the given support ticket as read. Part of the users/tickets support module (reply, solve, reopen, comments). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"ticket_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the support ticket"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/tickets/{ticket_id}/reopen":{"put":{"tags":["OF API — Misc"],"summary":"Reopen a support ticket","description":"Reopens a previously closed support ticket identified by ticket_id. Body carries the reopen data. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"ticket_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the support ticket to reopen"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque data object; fields not statically visible"}}},"/api2/v2/users/tickets/{ticket_id}/reply":{"post":{"tags":["OF API — Misc"],"summary":"Reply to support ticket","description":"Posts a reply to the specified support ticket. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"ticket_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the support ticket"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; reply content not statically visible"}}},"/api2/v2/users/tickets/{ticket_id}/solve":{"post":{"tags":["OF API — Misc"],"summary":"Mark support ticket solved","description":"Marks a user support ticket as solved. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"ticket_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the support ticket"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/ws-auth":{"get":{"tags":["OF API — User"],"summary":"Get WebSocket auth token","description":"Returns authentication data/token needed to establish the realtime WebSocket connection. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"wsUrl":{"type":"string"},"wsAuthToken":{"type":"string"}}}}}}}}}}},"/api2/v2/users/{user_id}/friends/pinned":{"get":{"tags":["OF API — User"],"summary":"Get pinned friends","description":"Returns a user's pinned friends. Called as getPinnedFriends({userId}). **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the user whose pinned friends to fetch"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/{user_id}/links":{"get":{"tags":["OF API — User"],"summary":"Get user's links","description":"Retrieves the profile links configured by a user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the user whose links to fetch"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/{user_id}/resubscribe":{"post":{"tags":["OF API — Subscriptions"],"summary":"Resubscribe to a user","description":"Resubscribes to the given user's account. Sibling of users/{id}/subscribe. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the user to resubscribe to"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; optional subscription options, not statically visible"}}},"/api2/v2/users/{user_id}/shopify/stores":{"get":{"tags":["OF API — User"],"summary":"List user's Shopify stores","description":"Retrieves the Shopify stores connected to a given user. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the user whose Shopify stores are listed"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/users/{user_id}/social/buttons":{"get":{"tags":["OF API — User"],"summary":"Get user social buttons","description":"Retrieves the social/link buttons configured on a user's profile. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the user whose social buttons to fetch"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/vault/lists/sort":{"post":{"tags":["OF API — Content"],"summary":"Sort vault lists","description":"Reorders the creator's vault media lists. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"opaque object; likely ordered ids, not statically visible"}}},"/api2/v2/vault/lists/{list_id}":{"delete":{"tags":["OF API — Content"],"summary":"Delete vault list","description":"Deletes a vault media list, optionally also clearing its media. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the vault media list"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"get":{"tags":["OF API — Content"],"summary":"Get vault media list","description":"Retrieves a single vault media list by id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the vault list"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"patch":{"tags":["OF API — Content"],"summary":"Rename a vault list","description":"Renames a media vault list identified by list id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the vault list"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"New name for the vault list"}}}}}}}},"/api2/v2/vault/media/hidden":{"put":{"tags":["OF API — Content"],"summary":"Hide vault media","description":"Marks the given vault media items as hidden. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"mediaIds":{"type":"array","description":"Ids of the vault media to hide"}}}}}}}},"/api2/v2/vault/media/{media_id}/release-forms":{"get":{"tags":["OF API — Content"],"summary":"Get vault media release forms","description":"Returns the release forms attached to a vault media item identified by media_id. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"media_id","in":"path","required":true,"schema":{"type":"string"},"description":"ID of the vault media item"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/api2/v2/webauthn/credentials":{"delete":{"tags":["OF API — User"],"summary":"Delete a WebAuthn credential","description":"Removes a registered WebAuthn (passkey/security key) credential for the current user; the credential identifier is sent in the request body. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}},"get":{"tags":["OF API — User"],"summary":"List WebAuthn credentials","description":"Returns the current user's registered WebAuthn (passkey/security key) credentials. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n✓ **Verified live** against a real OnlyFans account (2026-07-30); the `data` schema below is the real response shape.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"OnlyFans response — verified live; `data` is the real shape.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"array"}}}}}}}}},"/api2/v2/zip/{zip_code}/state":{"get":{"tags":["OF API — Misc"],"summary":"Get state for a ZIP code","description":"Looks up the state/region associated with a given ZIP code, used by payouts address forms. Defined in the payouts/address module. **OnlyFans only** — Fansly accounts are rejected on `/api2/v2/*`.\n\n_Reverse-engineered from the OnlyFans web client (build passthrough) — call shape auto-extracted and may need verification._","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"zip_code","in":"path","required":true,"schema":{"type":"string"},"description":"ZIP/postal code to resolve to a state"}],"responses":{"200":{"description":"OnlyFans response (passthrough envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPassthroughEnvelope"}}}}}}},"/accounts":{"get":{"tags":["Accounts"],"summary":"List connected accounts","description":"Returns every creator account connected to this CRM panel — OnlyFans and Fansly. Each entry carries `of_user_id`, `username`, and `platform` (`\"onlyfans\"` or `\"fansly\"`). Use `of_user_id` as the `{of_user_id}` path parameter and as the `user-id` header elsewhere in this API.","parameters":[{"name":"include_session","in":"query","required":false,"schema":{"type":"boolean","default":false},"description":"Include the per-account session block (`sess`, `auth_id`, `proxy`) in each entry."}],"responses":{"200":{"description":"Account list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"count":{"type":"integer"},"accounts":{"type":"array","items":{"type":"object","properties":{"of_user_id":{"type":"string"},"username":{"type":"string"},"platform":{"type":"string","enum":["onlyfans","fansly"]},"session":{"type":"object","description":"Only present when `include_session=true`.","properties":{"sess":{"type":"string"},"auth_id":{"type":"string"},"proxy":{"type":"string","nullable":true}}}}}}}},"example":{"success":true,"count":2,"accounts":[{"of_user_id":"482687148","username":"creator_one","platform":"onlyfans"},{"of_user_id":"739104882","username":"creator_two","platform":"fansly"}]}}}}}}},"/accounts/{of_user_id}":{"delete":{"tags":["Accounts"],"summary":"Disconnect an account","description":"Delete/disconnect a creator account from this CRM panel, freeing its slot. Works for OnlyFans and Fansly accounts. This is the only way to release a paid slot.","parameters":[{"$ref":"#/components/parameters/ofUserId"}],"responses":{"200":{"description":"Account disconnected and its slot released","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"404":{"description":"Account not found in this panel"}}}},"/accounts/login":{"post":{"tags":["Auth"],"summary":"Connect an account with credentials (OnlyFans or Fansly)","description":"Connect a new creator account via email/username + password. Set `platform` to `\"onlyfans\"` (default) or `\"fansly\"`.\n\n- **X-Proxy header is REQUIRED for OnlyFans**, optional for Fansly. You may pass `proxy` in the body as an alternative.\n- For `platform: \"fansly\"` the `email` field accepts a **username or an email**.\n- Supports 2FA: if the account has 2FA enabled the response includes `requires_2fa: true` (Fansly also returns `twofa_type`), and you must call `POST /accounts/login/verify-otp` next with the same `platform` and identifier.\n\nConsumes one account slot. Rate limited to 10 requests/minute.","parameters":[{"$ref":"#/components/parameters/proxyHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password"],"properties":{"platform":{"type":"string","enum":["onlyfans","fansly"],"default":"onlyfans","description":"Which platform to connect. Defaults to `onlyfans` when omitted."},"email":{"type":"string","example":"user@example.com","description":"Account identifier. An email for OnlyFans; a **username or an email** for Fansly."},"password":{"type":"string","example":"your_password"},"use_captcha":{"type":"boolean","default":true,"description":"Use captcha solver for login"},"proxy":{"type":"string","nullable":true,"example":"http://user:pass@host:port","description":"Proxy for this account — alternative to the `X-Proxy` header. Required for OnlyFans (via header or here), optional for Fansly."}}},"examples":{"onlyfans":{"summary":"OnlyFans (X-Proxy header required)","value":{"platform":"onlyfans","email":"user@example.com","password":"your_password","use_captcha":true}},"fansly":{"summary":"Fansly (username accepted, proxy optional)","value":{"platform":"fansly","email":"creator_username","password":"your_password"}}}}}},"responses":{"200":{"description":"Login successful or 2FA required","content":{"application/json":{"examples":{"success":{"summary":"Successful login","value":{"success":true,"of_user_id":"123456789","username":"example_user","email":"user@example.com","avatar":"https://..."}},"twofa":{"summary":"2FA required","value":{"success":false,"requires_2fa":true,"otp_state":"...","email":"user@example.com"}}}}}},"500":{"description":"Login failed"}}}},"/accounts/login/cookies":{"post":{"tags":["Auth"],"summary":"Connect an account with an existing session (OnlyFans or Fansly)","description":"Connect a creator account using credentials you already hold, rather than a password login.\n\n- **OnlyFans** — session cookie paste. Body: `{ sess, auth_id, fp? }`. `sess` and `auth_id` are **required for OnlyFans only**. **`X-Proxy` is REQUIRED.**\n- **Fansly** — auth-token paste. Body: `{ platform: \"fansly\", auth_token, fansly_session_id, fansly_client_id? }`. `auth_token` and `fansly_session_id` are **required for Fansly**. `X-Proxy` is optional.\n\nWhere to find the OnlyFans cookies: open onlyfans.com while logged in → **DevTools → Application → Cookies → onlyfans.com** and copy `sess` and `auth_id`.\n\nConsumes one account slot. Rate limited to 10 requests/minute.","parameters":[{"$ref":"#/components/parameters/proxyHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Field requirements are conditional on `platform`: OnlyFans requires `sess` + `auth_id`; Fansly requires `auth_token` + `fansly_session_id`.","properties":{"platform":{"type":"string","enum":["onlyfans","fansly"],"default":"onlyfans","description":"Which platform to connect. Defaults to `onlyfans` when omitted."},"sess":{"type":"string","description":"The `sess` cookie value. **Required for OnlyFans**, unused for Fansly."},"auth_id":{"type":"string","description":"The `auth_id` cookie value (OnlyFans user ID). **Required for OnlyFans**, unused for Fansly."},"fp":{"type":"string","description":"The `fp` fingerprint cookie (OnlyFans, optional — used as `x-bc`)."},"auth_token":{"type":"string","description":"Fansly bearer/auth token. **Required for Fansly**, unused for OnlyFans."},"fansly_session_id":{"type":"string","description":"Fansly session ID. **Required for Fansly**, unused for OnlyFans."},"fansly_client_id":{"type":"string","description":"Fansly device/client ID (optional, Fansly only)."}}},"examples":{"onlyfans":{"summary":"OnlyFans — cookie paste (X-Proxy required)","value":{"platform":"onlyfans","sess":"<sess cookie value>","auth_id":"482687148","fp":"<optional fingerprint cookie>"}},"fansly":{"summary":"Fansly — auth-token paste (X-Proxy optional)","value":{"platform":"fansly","auth_token":"<Fansly bearer token>","fansly_session_id":"<Fansly session id>","fansly_client_id":"<optional device id>"}}}}}},"responses":{"200":{"description":"Account connected","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"of_user_id":{"type":"string"},"username":{"type":"string"},"platform":{"type":"string","enum":["onlyfans","fansly"]},"login_method":{"type":"string","example":"cookies"}}}}}},"401":{"description":"Invalid or expired session credentials"}}}},"/accounts/login/verify-otp":{"post":{"tags":["Auth"],"summary":"Verify 2FA OTP code (OnlyFans or Fansly)","description":"Complete a 2FA login started by `POST /accounts/login`.\n\n- Pass the same `platform` and the same identifier you logged in with. For `platform: \"fansly\"` the `email` field may be a **username**.\n- The Fansly 2FA challenge returned by `POST /accounts/login` carries a `twofa_type` field indicating the delivery channel.\n- **The `X-Proxy` header is required.**\n\nRate limited to 10 requests/minute.","parameters":[{"name":"X-Proxy","in":"header","required":true,"schema":{"type":"string"},"description":"Proxy URL — **required** on this route. Use the same proxy the login attempt was made through."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","otp_code"],"properties":{"platform":{"type":"string","enum":["onlyfans","fansly"],"default":"onlyfans","description":"Must match the `platform` used on `POST /accounts/login`."},"email":{"type":"string","example":"user@example.com","description":"The same identifier used on `POST /accounts/login`. An email for OnlyFans; a **username or an email** for Fansly."},"otp_code":{"type":"string","example":"123456","description":"6-digit OTP code"}}}}}},"responses":{"200":{"description":"OTP verified, account connected"},"404":{"description":"2FA session not found or expired — start the login again"}}}},"/accounts/{of_user_id}/media":{"post":{"tags":["Content"],"summary":"Upload media to the vault","description":"Upload a photo, video, gif or audio file to the account's vault, and get back a reference you can attach to a post, message or story.\n\n**Two ways to call it:**\n\n1. `multipart/form-data` with a `file` part — raw bytes.\n2. `application/json` with `{\"source_url\": \"https://…\"}` — we fetch the file server-side. Use this when your media already lives somewhere public (Drive, S3, a CDN); it saves you building a multipart request at all.\n\n**What happens under the hood.** OnlyFans has no single upload endpoint. The web client runs a four-stage pipeline and so do we: `POST /api2/v2/upload/signed/create` → `PUT` the bytes straight to the returned pre-signed S3 URL (5 MiB parts for files ≥ 5 MiB) → `POST /api2/v2/upload/signed/finish` → hand the S3 descriptor to OnlyFans' converter host. You do not have to orchestrate any of that.\n\n**Using the result.** Put the returned `media` object into the post/message's **`mediaFiles`** array — not `media`, which OnlyFans silently ignores for freshly uploaded files. Pass the object through whole; trimming it to just `processId` also silently attaches nothing:\n\n```json\n{ \"text\": \"new set 🔥\",\n  \"mediaFiles\": [ { \"processId\": \"0ifuov…\", \"host\": \"convert4.onlyfans.com\", \"thumbId\": 1, \"name\": \"IMG_2676.HEIC\", \"extra\": \"…\" } ],\n  \"isScheduled\": 1, \"scheduledDate\": \"2026-08-20T12:00:00+00:00\" }\n```\n\n⚠️ Two things that fail *silently*, both confirmed on a live account:\n\n* Using `media` instead of `mediaFiles`, or trimming the object down to `{processId}`, returns `200` with **no media attached**.\n* **`postedAt` does not schedule anything.** In either format (`…Z` or `…+00:00`) OnlyFans ignores it and publishes immediately — verified live. Scheduling requires **two** fields instead:\n\n```json\n{ \"isScheduled\": 1, \"scheduledDate\": \"2026-08-20T12:00:00+00:00\" }\n```\n\nAlways confirm the post actually queued by checking `GET /api2/v2/schedules` for its id — a post that published instead of queueing looks identical in the create response apart from `postedAt`.\n\nMedia already in the vault is still referenced by plain integer id — list those with `GET /api2/v2/vault/media`.\n\n**An upload does not by itself create a vault item, and that matches OnlyFans exactly.** OnlyFans has no upload-to-vault endpoint: its own vault page is browse/organise only (list, hide, folders, attach), and the uploader is wired solely into the post/message composer. The vault entry is created when a post, message or story consumes the `processId`. So immediately after this call the file will *not* appear in `GET /api2/v2/vault/media`, nor in `GET /api2/v2/vault/media/processing`, nor under `GET /api2/v2/vault/media/hash?h={md5}&size={bytes}` — that last one is the lookup OnlyFans' own client uses to decide whether a file is already in the vault, and it answers `404 Media Not Found`. Publish or schedule with the `processId` and the vault row appears. This is the same behaviour you get clicking upload in the OnlyFans web app.\n\n**Working example:** a runnable Python client covering upload, immediate posting and scheduling — including every silent-failure case above — is at [theonlyapi-posting-example.zip](https://docs.theonlyapi.com/theonlyapi-posting-example.zip).\n\n**Requires writes to be enabled** for the account (`PATCH /accounts/{of_user_id}/polling {\"allow_of_write_actions\": true}`), because uploading acts as the creator.\n\n**OnlyFans only.** The Fansly upload pipeline is not wired yet; a Fansly account returns 501.\n\n*(This replaces the long-documented `POST /api2/v2/media`, which never existed.)*","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"The media file to upload."},"secure":{"type":"boolean","description":"Upload to the secure (DRM) path instead of the standard one.","default":false}}}},"application/json":{"schema":{"type":"object","required":["source_url"],"properties":{"source_url":{"type":"string","format":"uri","description":"Publicly reachable http(s) URL of the file. Fetched server-side. Private/loopback/link-local addresses are rejected.\n\nThe URL must return the **file bytes**, not a viewer page. If it serves HTML the request fails at `stage: \"fetch\"` with an explicit message rather than a confusing converter error. For Google Drive that means the file must be shared as \"Anyone with the link\" and the URL must be the direct-download form `https://drive.google.com/uc?export=download&id=FILE_ID` — a normal `/file/d/.../view` link returns a viewer page, and large files return a virus-scan interstitial instead of the bytes.","example":"https://example.com/photos/set-01.jpg"},"filename":{"type":"string","description":"Override the filename. Defaults to the last path segment of source_url."},"secure":{"type":"boolean","description":"Upload to the secure (DRM) path.","default":false}}}}}},"responses":{"200":{"description":"Upload complete","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"media":{"type":"object","description":"Ready to drop into a post/message `media` array.","properties":{"processId":{"type":"string"},"host":{"type":"string"},"name":{"type":"string"},"extra":{"type":"string"}}},"data":{"type":"object","description":"Full converter response — thumbs, sourceUrl, duration, plus the echoed key/etag/contentType/size."}}}}}},"400":{"description":"No file and no source_url, an unsafe source_url, or OnlyFans rejected the upload descriptor."},"403":{"description":"Writes are disabled for this account (`code: WRITES_DISABLED`)."},"413":{"description":"File exceeds MEDIA_UPLOAD_MAX_BYTES."},"501":{"description":"Fansly account — not supported yet."},"502":{"description":"A stage failed upstream (S3 PUT, the converter, or fetching source_url). The `stage` field says which."}}}},"/accounts/{of_user_id}/notifications":{"get":{"tags":["Notifications"],"summary":"Get notifications","description":"Fetch notifications for a connected account.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":20}}],"responses":{"200":{"description":"Notification list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"count":{"type":"integer"},"notifications":{"type":"array","items":{"type":"object"}}}}}}}}}},"/accounts/{of_user_id}/balances":{"get":{"tags":["Earnings"],"summary":"Get account balances","description":"Fetch current payout balance for a connected account.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"Balance data","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"balances":{"type":"object"}}}}}}}}},"/accounts/{of_user_id}/earnings":{"get":{"tags":["Earnings"],"summary":"Get earnings chart data","description":"Fetch earnings data for a date range with chart-ready breakdown.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","required":true,"schema":{"type":"string"},"example":"2025-01-01 00:00:00","description":"Start date (ISO format)"},{"name":"endDate","in":"query","schema":{"type":"string"},"example":"2025-12-31 23:59:59","description":"End date (ISO format)"},{"name":"withTotal","in":"query","schema":{"type":"string","default":"true"}}],"responses":{"200":{"description":"Earnings data with chart arrays","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"earnings":{"type":"object"}}}}}}}}},"/accounts/{of_user_id}/subscribers":{"get":{"tags":["Subscribers"],"summary":"List subscribers","description":"Get subscribers with pagination and type filtering. Includes total spent data.\n\n**Paging:** advance with the `nextOffset` from the response, and stop when `hasMore` is `false` — not when `list` is empty. `offset` does not mean the same thing on both platforms: OnlyFans filters by type server-side so its offset counts rows *returned*, while Fansly filters client-side so its offset counts rows *consumed*, which can exceed `count`. Computing `offset + list.length` yourself silently skips subscribers on Fansly, and a page whose rows were all filtered out comes back with an empty `list` and more still behind it. `nextOffset` + `hasMore` are correct on both.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":10,"maximum":100}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}},{"name":"type","in":"query","schema":{"type":"string","enum":["all","active","expired"],"default":"all"}}],"responses":{"200":{"description":"Subscriber list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"list":{"type":"array","items":{"type":"object"}},"hasMore":{"type":"boolean","description":"The stop signal. An empty `list` is not — a page can filter to zero rows and still have more behind it."},"count":{"type":"integer","description":"Length of `list` after type filtering. A page-size readout, not a cursor."},"offset":{"type":"integer"},"nextOffset":{"type":"integer","description":"The offset to pass for the next page. Always follow this rather than computing offset + list.length."},"limit":{"type":"integer"}}}}}}}}},"/accounts/{of_user_id}/purchases":{"get":{"tags":["Transactions"],"summary":"Get purchase transactions","description":"Fetch payout transactions with marker-based pagination.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","schema":{"type":"string"},"example":"2025-01-01 00:00:00"},{"name":"marker","in":"query","schema":{"type":"string"},"description":"Pagination marker (use nextMarker from response)"},{"name":"limit","in":"query","schema":{"type":"integer","default":100}}],"responses":{"200":{"description":"Transaction list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"purchases":{"type":"array","items":{"type":"object"}},"hasMore":{"type":"boolean"},"nextMarker":{"type":"string","nullable":true}}}}}}}}},"/accounts/{of_user_id}/campaigns":{"get":{"tags":["Campaigns"],"summary":"List campaigns","description":"Get tracking link campaigns with statistics and pagination.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":10}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Campaign list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"campaigns":{"type":"array","items":{"type":"object"}},"hasMore":{"type":"boolean"}}}}}}}},"post":{"tags":["Campaigns"],"summary":"Create campaign","description":"Create a new tracking link campaign.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","example":"My Campaign"}}}}}},"responses":{"200":{"description":"Campaign created"}}}},"/accounts/{of_user_id}/campaigns/{campaign_id}/claimers":{"get":{"tags":["Campaigns"],"summary":"Get campaign claimers (live)","description":"Fetch subscribers who converted through a specific campaign.\n\n**OnlyFans only** — rejected for Fansly accounts. Fetched live from the platform, so it consumes platform requests. For a zero-platform-request read, use `GET /accounts/{of_user_id}/campaigns/{campaign_id}/claimers/cached`.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":10,"maximum":100}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Claimer list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"claimers":{"type":"array","items":{"type":"object"}},"hasMore":{"type":"boolean"},"count":{"type":"integer"}}}}}}}}},"/accounts/{of_user_id}/subscription-price":{"get":{"tags":["Settings"],"summary":"Get subscription price","description":"Read the account's current subscription price live from `/users/me`. Consumes a platform request.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"Current subscription price","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"subscribePrice":{"type":"number","example":9.99}}}}}}}},"patch":{"tags":["Settings"],"summary":"Update subscription price","description":"Change the subscription price for a connected OnlyFans account.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["subscribePrice"],"properties":{"subscribePrice":{"type":"number","example":9.99,"description":"New subscription price in USD"}}}}}},"responses":{"200":{"description":"Price updated"}}}},"/accounts/{of_user_id}/proxy":{"get":{"tags":["Settings"],"summary":"Get account proxy","description":"Get the current proxy configured for an account.","parameters":[{"$ref":"#/components/parameters/ofUserId"}],"responses":{"200":{"description":"Proxy info","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"proxy":{"type":"string","nullable":true}}}}}}}},"patch":{"tags":["Settings"],"summary":"Update account proxy","description":"Update or remove the proxy for an account. Set to `null` to remove.","parameters":[{"$ref":"#/components/parameters/ofUserId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"proxy":{"type":"string","nullable":true,"example":"http://user:pass@host:port"}}}}}},"responses":{"200":{"description":"Proxy updated"}}}},"/accounts/{of_user_id}/payout-requests":{"get":{"tags":["Payouts"],"summary":"List withdrawal requests","description":"Withdrawal request history for the account. For OnlyFans this is read live from the platform; for Fansly it is served from the synced wallet ledger.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"},{"$ref":"#/components/parameters/limitParam"},{"$ref":"#/components/parameters/offsetParam"}],"responses":{"200":{"description":"Withdrawal request history","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"payout_requests":{"type":"array","items":{"type":"object"}},"hasMore":{"type":"boolean"}}}}}}}},"post":{"tags":["Payouts"],"summary":"Create payout request","description":"Create a withdrawal request for a connected account.\n\n**OnlyFans only** — rejected for Fansly accounts. Requires an `X-Proxy` header (or a proxy saved on the account).","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["withdrawal_amount"],"properties":{"withdrawal_amount":{"type":"number","example":100,"description":"Amount to withdraw in USD"}}}}}},"responses":{"200":{"description":"Payout request created"}}}},"/accounts/{of_user_id}/request":{"post":{"tags":["Proxy"],"summary":"Generic OF API proxy","description":"Send any authenticated request to the OnlyFans API. Use this to access any OF endpoint not covered by the dedicated CRM routes. Auto-relogins on session expiry.\n\n**OnlyFans only** — rejected for Fansly accounts. For Fansly, use the platform-neutral CRM data routes.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["path"],"properties":{"path":{"type":"string","example":"/api2/v2/users/me","description":"OnlyFans API path"},"method":{"type":"string","enum":["GET","POST","PUT","PATCH","DELETE"],"default":"GET"},"body":{"type":"object","description":"Request body (for POST/PUT/PATCH)"}}}}}},"responses":{"200":{"description":"Proxied response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object"}}}}}}}}},"/health":{"get":{"tags":["Panel & Usage"],"summary":"Service health check","description":"Liveness probe. Exempt from rate limiting and requires no API key.","servers":[{"url":"{baseUrl}","variables":{"baseUrl":{"default":"https://theonlyapi.com"}}}],"security":[],"responses":{"200":{"description":"Service is healthy"}}}},"/api/whoami":{"get":{"tags":["MCP","Panel & Usage"],"summary":"Resolve an API key to a panel","description":"Resolves the API key sent in `X-API-Key` to its `crm_id`. This is how the hosted MCP server turns a bearer token into a tenant. Returns only public-safe fields (panel id, plan info).","servers":[{"url":"{baseUrl}","variables":{"baseUrl":{"default":"https://theonlyapi.com"}}}],"responses":{"200":{"description":"Panel resolved","content":{"application/json":{"schema":{"type":"object","properties":{"crm_id":{"type":"string"},"plan":{"type":"string","example":"only-api-free"}}}}}},"401":{"description":"Missing or invalid API key"}}}},"/api/auth/register":{"post":{"tags":["Panel & Usage"],"summary":"Register a dashboard user","description":"Create a dashboard account. Follow with `POST /api/crm/register` to create a CRM panel and receive your first API key.","servers":[{"url":"{baseUrl}","variables":{"baseUrl":{"default":"https://theonlyapi.com"}}}],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string"}}}}}},"responses":{"200":{"description":"User registered"},"400":{"description":"Invalid payload or email already in use"}}}},"/api/auth/login":{"post":{"tags":["Panel & Usage"],"summary":"Dashboard user login","description":"Authenticate a dashboard user. Rate limited to **6 requests/minute** — the strictest limit in the API.","servers":[{"url":"{baseUrl}","variables":{"baseUrl":{"default":"https://theonlyapi.com"}}}],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string"}}}}}},"responses":{"200":{"description":"Authenticated"},"401":{"description":"Invalid credentials"},"429":{"description":"Rate limit exceeded (6/minute)"}}}},"/api/auth/start-email-verification":{"post":{"tags":["Panel & Usage"],"summary":"Send an email verification code","servers":[{"url":"{baseUrl}","variables":{"baseUrl":{"default":"https://theonlyapi.com"}}}],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"}}}}}},"responses":{"200":{"description":"Verification email sent"}}}},"/api/auth/verify-email":{"post":{"tags":["Panel & Usage"],"summary":"Verify an email code","servers":[{"url":"{baseUrl}","variables":{"baseUrl":{"default":"https://theonlyapi.com"}}}],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","code"],"properties":{"email":{"type":"string","format":"email"},"code":{"type":"string","example":"123456"}}}}}},"responses":{"200":{"description":"Email verified"},"400":{"description":"Invalid or expired code"}}}},"/api/crm/register":{"post":{"tags":["Panel & Usage"],"summary":"Create a CRM panel","description":"Create a CRM panel and receive its `crm_id` plus the primary `Default` API key. The key is returned once — store it.","servers":[{"url":"{baseUrl}","variables":{"baseUrl":{"default":"https://theonlyapi.com"}}}],"security":[],"responses":{"200":{"description":"Panel created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"crm_id":{"type":"string"},"api_key":{"type":"string"}}}}}}}}},"/usage":{"get":{"tags":["Panel & Usage"],"summary":"Get plan usage","description":"Current plan and consumption for this panel.\n\nPlans: **Free** — $0/mo, 1 connected account, 1,000 API calls/month. **Slots** — $20 per slot per month ($15 per slot at 15+ slots), 1 slot = 1 connected account, unlimited API calls. **Enterprise** — custom pricing and limits.\n\nAn `api_calls_limit` of `-1` means unlimited.","responses":{"200":{"description":"Usage statistics","content":{"application/json":{"schema":{"type":"object","properties":{"crm_id":{"type":"string"},"plan":{"type":"string","example":"only-api-free"},"api_calls_used":{"type":"integer"},"api_calls_limit":{"type":"integer","description":"`1000` on Free, `-1` (unlimited) on paid plans."},"accounts_used":{"type":"integer"},"accounts_limit":{"type":"integer"},"month":{"type":"string","example":"2026-07"}}},"example":{"crm_id":"crm_abc123","plan":"only-api-free","api_calls_used":214,"api_calls_limit":1000,"accounts_used":1,"accounts_limit":1,"month":"2026-07"}}}}}}},"/rotate-key":{"post":{"tags":["API Keys"],"summary":"Rotate your API key","description":"Rotate (regenerate) the caller's own API key. Authenticate with your **current** key. The old key dies immediately and the new key is returned once in the response body — store it before discarding the response.","responses":{"200":{"description":"Key rotated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"api_key":{"type":"string","description":"The new key. Returned only here."}}}}}},"404":{"description":"CRM panel not found"}}}},"/api-keys":{"get":{"tags":["API Keys"],"summary":"List API keys","description":"List all API keys for the panel (active + revoked), each with this-month request count and a 30-day series. Full key bodies are never returned — only a prefix.","responses":{"200":{"description":"Key list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"keys":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"prefix":{"type":"string"},"is_primary":{"type":"boolean"},"created_at":{"type":"string"},"last_used_at":{"type":"string","nullable":true},"revoked_at":{"type":"string","nullable":true}}}}}}}}}}},"post":{"tags":["API Keys"],"summary":"Create a secondary API key","description":"Mint a new secondary API key. **PRIMARY-KEY ONLY** — calling this with a secondary key returns `403`. The full key is returned **exactly once**, in this response, and is never retrievable again.","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":60,"default":"Untitled key","example":"CI pipeline"}}}}}},"responses":{"201":{"description":"Key created — `api_key` is shown only here","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"id":{"type":"integer"},"name":{"type":"string"},"prefix":{"type":"string"},"api_key":{"type":"string"},"created_at":{"type":"string"}}}}}},"403":{"description":"Not the primary key"},"404":{"description":"CRM panel not found"}}}},"/api-keys/{key_id}":{"delete":{"tags":["API Keys"],"summary":"Revoke a secondary API key","description":"Revoke a secondary key. **PRIMARY-KEY ONLY** — calling this with a secondary key returns `403`. The primary `Default` key is non-revocable; rotate it via `POST /rotate-key` instead.","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Key revoked"},"403":{"description":"Not the primary key"},"404":{"description":"Key not found, already revoked, or non-revocable (primary)"}}}},"/api-keys/{key_id}/usage":{"get":{"tags":["API Keys"],"summary":"Per-key usage detail","description":"30/90-day usage series, month and all-time totals, last-used timestamp, and this month's endpoint breakdown for one key.","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"integer"}},{"name":"days","in":"query","schema":{"type":"integer","default":30,"minimum":1,"maximum":90}}],"responses":{"200":{"description":"Usage detail","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"key":{"type":"object"},"totals":{"type":"object"},"series":{"type":"array","items":{"type":"object"}},"breakdown":{"type":"array","items":{"type":"object"}}}}}}},"404":{"description":"Key not found"}}}},"/mcp/unsafe-proxy":{"patch":{"tags":["MCP"],"summary":"Toggle MCP non-GET proxy access","description":"Toggle whether the hosted MCP server may issue non-GET requests through the generic OF proxy tool. **Off by default.** Use with care: enabling it lets a model POST/PATCH/DELETE arbitrary OnlyFans endpoints on your behalf.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean","default":false}}}}}},"responses":{"200":{"description":"Toggle updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"mcp_unsafe_proxy":{"type":"boolean"}}}}}}}}},"/proxy/test":{"post":{"tags":["Settings"],"summary":"Test a proxy","description":"Check that a proxy URL is reachable and usable before connecting an account with it. HTTP and SOCKS5 are both accepted.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["proxy"],"properties":{"proxy":{"type":"string","example":"http://user:pass@host:port"}}}}}},"responses":{"200":{"description":"Proxy test result"}}}},"/accounts/{of_user_id}/polling":{"get":{"tags":["Accounts"],"summary":"Get polling settings","description":"Read the background polling configuration for an account — whether polling is enabled, the interval, and whether write actions are allowed.","parameters":[{"$ref":"#/components/parameters/ofUserId"}],"responses":{"200":{"description":"Polling settings","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"polling":{"type":"object","properties":{"enabled":{"type":"boolean"},"interval_seconds":{"type":"integer"},"allow_of_write_actions":{"type":"boolean"}}}}}}}},"404":{"description":"Account not found"}}},"patch":{"tags":["Accounts"],"summary":"Update polling settings","description":"Enable/disable background polling for an account, set the interval, and control whether automations may perform write actions on the platform.","parameters":[{"$ref":"#/components/parameters/ofUserId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"interval_seconds":{"type":"integer","minimum":60,"maximum":3600,"description":"Polling interval in seconds (60–3600)."},"allow_of_write_actions":{"type":"boolean","description":"Allow automations to send DMs and other writes for this account."}}}}}},"responses":{"200":{"description":"Polling settings updated"},"400":{"description":"interval_seconds out of range (60–3600)"}}}},"/accounts/{of_user_id}/payout-account":{"get":{"tags":["Payouts"],"summary":"Get payout account","description":"Payout/withdrawal account details for a connected account.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"Payout account details"}}}},"/accounts/{of_user_id}/referrals":{"get":{"tags":["Referrals"],"summary":"List referred users","description":"The creators/users this account referred, read live from OnlyFans.\n\n**OnlyFans only** — a Fansly account returns `501` with `code: \"platform_not_supported\"` and `feature: \"referrals\"`.\n\nQuery params are forwarded upstream **only when supplied** — no default date window is invented, because a wrong default silently truncates money figures.\n\nThe row shape inside `referrals` is **unverified** (OnlyFans' item fields were never captured); rows pass through untouched and the raw upstream body is echoed under `data`.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","schema":{"type":"string"},"description":"Range start, `YYYY-MM-DD` or `YYYY-MM-DD HH:MM:SS`"},{"name":"endDate","in":"query","schema":{"type":"string"},"description":"Range end, same format as startDate"},{"name":"offset","in":"query","schema":{"type":"integer"}},{"name":"marker","in":"query","schema":{"type":"string"},"description":"Pagination cursor returned by OnlyFans"},{"name":"onlyPerformers","in":"query","schema":{"type":"string"},"description":"Restrict to referred creators/performers"},{"name":"limit","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"Referred users","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"referrals":{"type":"array","items":{"type":"object"},"description":"Rows exactly as OnlyFans returned them — item fields unverified."},"count":{"type":"integer"},"hasMore":{"type":"boolean"},"data":{"description":"Raw upstream body, verbatim."}}}}}},"400":{"description":"Invalid startDate/endDate format"},"403":{"description":"Account does not belong to this panel"},"501":{"description":"Fansly account — referrals are not available on this platform"}}}},"/accounts/{of_user_id}/referrals/earnings":{"get":{"tags":["Referrals","Earnings"],"summary":"Referral balance + chart","description":"Referral money summary in one response, fanned out to two OnlyFans endpoints (still one quota call).\n\n`balance` and `chart` hold the **raw** upstream bodies — no field mapping. The two documentation sources disagree about the chart body, and the balance endpoint appears in no capture at all, so mapping fields would fabricate a contract.\n\nPartial failure is tolerated: a source that fails comes back `null` and `sources` records its status. If **both** fail, the route returns the upstream status with `success: false` — an expired session must not be indistinguishable from \"no referral earnings\".\n\n**OnlyFans only** — Fansly returns `501`.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","schema":{"type":"string"},"description":"Chart range start (forwarded to the chart call)"},{"name":"endDate","in":"query","schema":{"type":"string"},"description":"Chart range end"},{"name":"withTotal","in":"query","schema":{"type":"string","default":"1"},"description":"Include totals. Defaults to the value the OnlyFans web client sends."},{"name":"withChart","in":"query","schema":{"type":"string","default":"true"},"description":"Include the chart series. Defaults to the value the OnlyFans web client sends."},{"name":"filter","in":"query","schema":{"type":"string"},"description":"Chart filter; forwarded verbatim when supplied."}],"responses":{"200":{"description":"Referral earnings (one source may be null)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"balance":{"nullable":true,"description":"Raw body of the OnlyFans referral balance endpoint, or null if it failed. Shape unverified."},"chart":{"nullable":true,"description":"Raw body of the OnlyFans referral chart endpoint, or null if it failed. Shape unverified."},"sources":{"type":"object","description":"Per-source outcome, so a null above is explainable.","properties":{"balance":{"type":"object","properties":{"ok":{"type":"boolean"},"status":{"type":"integer"}}},"chart":{"type":"object","properties":{"ok":{"type":"boolean"},"status":{"type":"integer"}}}}}}}}}},"400":{"description":"Invalid startDate/endDate format"},"403":{"description":"Account does not belong to this panel"},"501":{"description":"Fansly account — referrals are not available on this platform"}}}},"/accounts/{of_user_id}/referrals/payout-requests":{"get":{"tags":["Referrals","Payouts"],"summary":"List referral payout requests","description":"Referral payout history — the referral twin of `GET /accounts/{of_user_id}/payout-requests`, shaped identically.\n\nThe row shape inside `requests` is **unverified**; rows pass through untouched and the raw upstream body is echoed under `data`.\n\n**OnlyFans only** — Fansly returns `501`.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","schema":{"type":"string"},"description":"Range start, `YYYY-MM-DD` or `YYYY-MM-DD HH:MM:SS`"},{"name":"endDate","in":"query","schema":{"type":"string"},"description":"Range end, same format as startDate"},{"name":"offset","in":"query","schema":{"type":"integer"}},{"name":"marker","in":"query","schema":{"type":"string"},"description":"Pagination cursor returned by OnlyFans"},{"name":"limit","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"Referral payout requests","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"requests":{"type":"array","items":{"type":"object"},"description":"Rows exactly as OnlyFans returned them — item fields unverified."},"count":{"type":"integer"},"marker":{"nullable":true,"description":"Upstream pagination cursor; pass back as `marker` for the next page."},"data":{"description":"Raw upstream body, verbatim."}}}}}},"400":{"description":"Invalid startDate/endDate format"},"403":{"description":"Account does not belong to this panel"},"501":{"description":"Fansly account — referrals are not available on this platform"}}}},"/earnings/summary":{"get":{"tags":["Earnings"],"summary":"Aggregated earnings across all accounts","description":"Server-side earnings aggregation across every account in the panel — OnlyFans and Fansly combined into one normalized response.\n\nThe response is cached in memory per (panel, period) with a 2–15 minute TTL depending on the period, and auto-invalidates when an earnings-changing event (`new_tip`, `new_purchase`, `balance_increased`) fires for any account in the panel.","parameters":[{"name":"period","in":"query","schema":{"type":"string","enum":["today","week","month"],"default":"week"}}],"responses":{"200":{"description":"Aggregated earnings","content":{"application/json":{"schema":{"type":"object","properties":{"total":{"type":"number"},"prev_total":{"type":"number"},"by_category":{"type":"object","properties":{"subscriptions":{"type":"number"},"posts":{"type":"number"},"messages":{"type":"number"},"tips":{"type":"number"},"streams":{"type":"number"},"referrals":{"type":"number"}}},"chart":{"type":"array","items":{"type":"number"},"description":"Daily net values."},"accounts_count":{"type":"integer"},"transactions_counted":{"type":"integer"},"transactions_capped":{"type":"boolean"},"period":{"type":"string"},"cached":{"type":"boolean"},"cached_age_seconds":{"type":"integer"}}}}}}}}},"/accounts/{of_user_id}/subscribers/refresh":{"post":{"tags":["Cache & Sync","Subscribers"],"summary":"Refresh the subscriber cache (async)","description":"Start an async subscriber sync for this account. Returns **202** immediately — poll `GET /accounts/{of_user_id}/subscribers/refresh/status` or listen on `GET /events/stream` for completion. Works for OnlyFans and Fansly.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"202":{"description":"Refresh job accepted and running"},"409":{"description":"A refresh is already in progress"}}}},"/accounts/{of_user_id}/subscribers/refresh/status":{"get":{"tags":["Cache & Sync","Subscribers"],"summary":"Subscriber refresh status","description":"Progress/state of the current or last subscriber refresh job. **Zero platform requests.**","parameters":[{"$ref":"#/components/parameters/ofUserId"}],"responses":{"200":{"description":"Refresh job status"}}}},"/accounts/{of_user_id}/subscribers/cached":{"get":{"tags":["Subscribers","Cache & Sync"],"summary":"List cached subscribers","description":"Read subscribers from the local cache, populated by the background refresh job. **Zero platform requests.**\n\nPlatform-neutral: OnlyFans and Fansly rows are normalized to the same shape. Each row carries the raw platform payload merged with the flat spend/lifecycle projection (total spent, breakdown), which for Fansly is backfilled from the wallet ledger.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"name":"limit","in":"query","schema":{"type":"integer","default":100}},{"$ref":"#/components/parameters/offsetParam"},{"name":"type","in":"query","schema":{"type":"string","enum":["all","active","expired"],"default":"all"}}],"responses":{"200":{"description":"Cached subscriber list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"subscribers":{"type":"array","items":{"type":"object"}},"count":{"type":"integer"},"hasMore":{"type":"boolean"}}}}}}}}},"/accounts/{of_user_id}/subscribers/new":{"get":{"tags":["Subscribers","Cache & Sync"],"summary":"List newly-seen subscribers","description":"Subscribers first seen within a recent window, read from the cache. **Zero platform requests.** Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"name":"since","in":"query","schema":{"type":"string"},"description":"ISO timestamp or date lower bound."},{"$ref":"#/components/parameters/limitParam"}],"responses":{"200":{"description":"New subscriber list"}}}},"/accounts/{of_user_id}/subscribers/stats":{"get":{"tags":["Subscribers","Cache & Sync"],"summary":"Subscriber statistics","description":"Time-bucketed counts of incoming subscriptions, plus the cache summary. **Zero platform requests.** Fansly-aware.\n\nQuery: `granularity` = `hour|day|week|month` (default `day`), `since`/`until` = inclusive ISO-8601 bounds on `subscribed_at`.\n\nBuckets are ascending, contiguous and **zero-filled server-side** — a quiet day comes back as `count: 0`, not as a missing key, so the array plots directly. With both `since` and `until` the axis spans exactly that window even if it holds no subscriptions; otherwise it spans the first to the last bucket with data. Week buckets are Monday-start ISO dates.\n\n`zero_filled` is `false` when the requested span exceeded the server's bucket ceiling (e.g. `granularity=hour` over an unbounded window): the series is then sparse and you must fill the gaps yourself. Narrow the window or coarsen the granularity.","parameters":[{"$ref":"#/components/parameters/ofUserId"}],"responses":{"200":{"description":"Subscriber statistics"}}}},"/accounts/{of_user_id}/transactions/refresh":{"post":{"tags":["Cache & Sync","Transactions"],"summary":"Refresh the transaction cache (async)","description":"Start an async transaction sync. Returns **202** — poll `GET /accounts/{of_user_id}/transactions/refresh/status` or listen on `GET /events/stream`. Works for OnlyFans and Fansly.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"202":{"description":"Refresh job accepted and running"},"409":{"description":"A refresh is already in progress"}}}},"/accounts/{of_user_id}/transactions/refresh/status":{"get":{"tags":["Cache & Sync","Transactions"],"summary":"Transaction refresh status","description":"Progress/state of the current or last transaction refresh job. **Zero platform requests.**","parameters":[{"$ref":"#/components/parameters/ofUserId"}],"responses":{"200":{"description":"Refresh job status"}}}},"/accounts/{of_user_id}/transactions/cached":{"get":{"tags":["Transactions","Cache & Sync"],"summary":"List cached transactions","description":"Read transactions from the local cache. **Zero platform requests.** OnlyFans and Fansly rows are normalized to the same shape.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/limitParam"},{"$ref":"#/components/parameters/offsetParam"},{"name":"since","in":"query","schema":{"type":"string"},"description":"ISO date lower bound."},{"name":"until","in":"query","schema":{"type":"string"},"description":"ISO date upper bound."}],"responses":{"200":{"description":"Cached transaction list"}}}},"/accounts/{of_user_id}/backfill":{"post":{"tags":["Cache & Sync"],"summary":"Backfill historical data (async)","description":"Kick off a historical backfill for this account, walking further back than the routine refresh. Returns **202** — track progress via the refresh-status routes or `GET /events/stream`.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"202":{"description":"Backfill job accepted and running"},"409":{"description":"A job is already in progress"}}}},"/refresh/active":{"get":{"tags":["Cache & Sync"],"summary":"List active refresh jobs","description":"All refresh/backfill jobs currently running across the panel. **Zero platform requests.**","responses":{"200":{"description":"Active job list"}}}},"/accounts/{of_user_id}/campaigns/refresh":{"post":{"tags":["Cache & Sync","Campaigns"],"summary":"Refresh the campaign cache (async)","description":"Start an async campaign + claimer sync. Returns **202** — poll `GET /accounts/{of_user_id}/campaigns/refresh/status` or listen on `GET /events/stream`. Honours the `/tracked-campaigns` allowlist when one is set.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"202":{"description":"Refresh job accepted and running"},"409":{"description":"A refresh is already in progress"}}}},"/accounts/{of_user_id}/campaigns/refresh/status":{"get":{"tags":["Cache & Sync","Campaigns"],"summary":"Campaign refresh status","description":"Progress/state of the current or last campaign refresh job. **Zero platform requests.**","parameters":[{"$ref":"#/components/parameters/ofUserId"}],"responses":{"200":{"description":"Refresh job status"}}}},"/accounts/{of_user_id}/campaigns/earnings":{"get":{"tags":["Campaigns"],"summary":"Campaign earnings breakdown","description":"Earnings attributed to each tracking-link campaign, computed from cached claimers joined with cached transactions. **Zero platform requests.**","parameters":[{"$ref":"#/components/parameters/ofUserId"}],"responses":{"200":{"description":"Per-campaign earnings"}}}},"/accounts/{of_user_id}/campaigns/{campaign_id}/claimers/cached":{"get":{"tags":["Campaigns","Cache & Sync"],"summary":"Get cached campaign claimers","description":"Cached claimers for one campaign, already joined with the subscriber cache so each row carries `total_spent`, its breakdown, and `mapped_spent`. **Zero platform requests.**","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/campaignId"},{"$ref":"#/components/parameters/limitParam"},{"$ref":"#/components/parameters/offsetParam"}],"responses":{"200":{"description":"Cached claimer list"}}}},"/accounts/{of_user_id}/tracked-campaigns":{"get":{"tags":["Campaigns"],"summary":"Get the tracked-campaign allowlist","description":"The allowlist of tracking-link campaigns (names/codes) kept synced for this account. When set, scheduled and backfill claimer syncs walk only these — so a huge link (20k+ subscribers) is never walked on a timer.","parameters":[{"$ref":"#/components/parameters/ofUserId"}],"responses":{"200":{"description":"Tracked campaign allowlist"}}},"put":{"tags":["Campaigns"],"summary":"Set the tracked-campaign allowlist","parameters":[{"$ref":"#/components/parameters/ofUserId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"campaigns":{"type":"array","items":{"type":"string"},"description":"Campaign names or codes to keep synced."}}}}}},"responses":{"200":{"description":"Allowlist updated"}}}},"/accounts/{of_user_id}/campaign-tags":{"get":{"tags":["Campaigns"],"summary":"List campaign tags","description":"All campaign tags in use for this account.","parameters":[{"$ref":"#/components/parameters/ofUserId"}],"responses":{"200":{"description":"Campaign tag list"}}}},"/accounts/{of_user_id}/campaigns/{campaign_id}/tags":{"post":{"tags":["Campaigns"],"summary":"Add a tag to a campaign","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/campaignId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tag"],"properties":{"tag":{"type":"string","example":"reddit"}}}}}},"responses":{"200":{"description":"Tag added"}}}},"/accounts/{of_user_id}/campaigns/{campaign_id}/tags/{tag}":{"delete":{"tags":["Campaigns"],"summary":"Remove a tag from a campaign","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/campaignId"},{"name":"tag","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Tag removed"}}}},"/fans":{"get":{"tags":["Fans"],"summary":"List fans","description":"Fan CRM across the whole panel — every fan seen on any connected account, OnlyFans or Fansly, with tags, notes, spend, and activity counters. **Zero platform requests.**","parameters":[{"name":"of_user_id","in":"query","schema":{"type":"string"},"description":"Restrict to fans of one connected account."},{"name":"limit","in":"query","schema":{"type":"integer","default":100,"minimum":1,"maximum":500}},{"$ref":"#/components/parameters/offsetParam"},{"name":"sort","in":"query","schema":{"type":"string","enum":["last_seen","first_seen","tips","spend","events"],"default":"last_seen"}},{"name":"search","in":"query","schema":{"type":"string","maxLength":80},"description":"Free-text search over fan name/username."},{"name":"tag","in":"query","schema":{"type":"string"},"description":"Filter to fans carrying this tag."}],"responses":{"200":{"description":"Fan list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"fans":{"type":"array","items":{"type":"object"}},"count":{"type":"integer"}}}}}},"400":{"description":"Invalid sort value"}}}},"/fans/{fan_of_user_id}/tags":{"post":{"tags":["Fans"],"summary":"Add a tag to a fan","parameters":[{"$ref":"#/components/parameters/fanOfUserId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tag"],"properties":{"tag":{"type":"string","example":"whale"}}}}}},"responses":{"200":{"description":"Tag added"}}}},"/fans/{fan_of_user_id}/tags/{tag}":{"delete":{"tags":["Fans"],"summary":"Remove a tag from a fan","parameters":[{"$ref":"#/components/parameters/fanOfUserId"},{"name":"tag","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Tag removed"}}}},"/fans/{fan_of_user_id}/note":{"put":{"tags":["Fans"],"summary":"Set a fan note","description":"Replace the free-text CRM note stored against a fan. Send an empty string to clear it.","parameters":[{"$ref":"#/components/parameters/fanOfUserId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"note":{"type":"string","example":"Prefers PPV over tips."}}}}}},"responses":{"200":{"description":"Note saved"}}}},"/accounts/{of_user_id}/fans/{fan_id}/refresh-profile":{"post":{"tags":["Fans"],"summary":"Refresh a fan profile from the platform","description":"Re-fetch one fan's profile from the platform and update the cache. Consumes a platform request.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/fanId"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"Fan profile refreshed"}}}},"/accounts/{of_user_id}/fans/{fan_id}/transactions/cached":{"get":{"tags":["Fans","Cache & Sync"],"summary":"Get a fan's cached transactions","description":"Every cached transaction attributed to one fan on this account. **Zero platform requests.** Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/fanId"},{"$ref":"#/components/parameters/limitParam"},{"$ref":"#/components/parameters/offsetParam"}],"responses":{"200":{"description":"Cached transaction list"}}}},"/accounts/{of_user_id}/chats":{"get":{"tags":["Messaging"],"summary":"List chats","description":"Conversation list for a connected account. Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":100}},{"$ref":"#/components/parameters/offsetParam"}],"responses":{"200":{"description":"Chat list"}}}},"/accounts/{of_user_id}/chats/{with_user_id}/messages":{"get":{"tags":["Messaging"],"summary":"Get message history","description":"Message history for one conversation, newest-first by default. Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"name":"with_user_id","in":"path","required":true,"schema":{"type":"string"},"description":"The fan's user ID."},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":100}}],"responses":{"200":{"description":"Message list"}}},"post":{"tags":["Messaging"],"summary":"Send a DM or PPV","description":"Send a single DM (or PPV) to one fan. Works for OnlyFans and Fansly.\n\n- `price > 0` makes the message a **PPV**.\n- `mediaFiles` locks vault media behind the price.\n- OnlyFans sends are gated per account by the `allow_of_write_actions` polling setting (`PATCH /accounts/{of_user_id}/polling`).\n\nRate limited to 120 requests/minute.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"name":"with_user_id","in":"path","required":true,"schema":{"type":"string"},"description":"The fan's user ID."},{"$ref":"#/components/parameters/proxyHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["text"],"properties":{"text":{"type":"string","example":"Hey! New drop is up 💕"},"price":{"type":"number","default":0,"description":"Above 0 makes this a PPV message."},"mediaFiles":{"type":"array","items":{"type":"string"},"description":"Vault media IDs to attach."}}}}}},"responses":{"200":{"description":"Message sent"},"403":{"description":"Write actions disabled for this account"}}}},"/accounts/{of_user_id}/messages/mass":{"post":{"tags":["Messaging"],"summary":"Send a mass DM","description":"Send one message to many fans at once. Supports PPV pricing and vault media, and can target a subscriber segment. Rate limited to 120 requests/minute. Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/proxyHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["text"],"properties":{"text":{"type":"string","example":"New set just dropped!"},"price":{"type":"number","default":0,"description":"Above 0 makes this a mass PPV."},"mediaFiles":{"type":"array","items":{"type":"string"},"description":"Vault media IDs to attach."},"userLists":{"type":"array","items":{"type":"string"},"description":"Target segment(s), e.g. `[\"active\"]` or specific list IDs."},"excludedLists":{"type":"array","items":{"type":"string"},"description":"Segment(s) to exclude."}}}}}},"responses":{"200":{"description":"Mass DM queued/sent"},"403":{"description":"Write actions disabled for this account"}}}},"/accounts/{of_user_id}/ppv-stats":{"get":{"tags":["Messaging"],"summary":"PPV performance stats","description":"Purchase/unlock statistics for PPV messages sent from this account, computed from cached transactions. **Zero platform requests.** Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"name":"since","in":"query","schema":{"type":"string"},"description":"ISO date lower bound."},{"name":"until","in":"query","schema":{"type":"string"},"description":"ISO date upper bound."}],"responses":{"200":{"description":"PPV statistics"}}}},"/accounts/{of_user_id}/exports":{"post":{"tags":["Exports"],"summary":"Create an export job","description":"Kick off an async data export for one account. Returns **202** with the created job.\n\nProgress streams over `GET /events/stream` as `export.progress`; completion fires `export.complete`. The generated ZIP is downloadable from `GET /accounts/{of_user_id}/exports/{job_id}/download` for the configured retention window. Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data_types":{"type":"array","items":{"type":"string","enum":["subscribers","transactions","fans","earnings","messages","account"]},"example":["subscribers","transactions"]},"since":{"type":"string","nullable":true,"example":"2026-01-01","description":"YYYY-MM-DD lower bound."},"until":{"type":"string","nullable":true,"example":"2026-06-30","description":"YYYY-MM-DD upper bound."},"include_media":{"type":"boolean","default":false,"description":"OnlyFans only in v1."}}}}}},"responses":{"202":{"description":"Export job created"},"400":{"description":"Invalid data_types or date range"}}},"get":{"tags":["Exports"],"summary":"List export jobs","parameters":[{"$ref":"#/components/parameters/ofUserId"}],"responses":{"200":{"description":"Export job list"}}}},"/accounts/{of_user_id}/exports/{job_id}":{"get":{"tags":["Exports"],"summary":"Get export job status","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"name":"job_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Export job"},"404":{"description":"Job not found"}}},"delete":{"tags":["Exports"],"summary":"Delete an export job","description":"Delete the job and its generated ZIP.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"name":"job_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Job deleted"}}}},"/accounts/{of_user_id}/exports/{job_id}/download":{"get":{"tags":["Exports"],"summary":"Download an export ZIP","description":"Download the generated archive. Available until the retention window expires.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"name":"job_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ZIP archive","content":{"application/zip":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"Job not found, not finished, or expired"}}}},"/accounts/{of_user_id}/exports/{job_id}/cancel":{"post":{"tags":["Exports"],"summary":"Cancel a running export job","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"name":"job_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Job cancelled"}}}},"/events":{"get":{"tags":["Events & Streaming"],"summary":"List events","description":"Poll the panel's event feed. **Zero platform requests.** Covers OnlyFans and Fansly accounts alike.","parameters":[{"name":"types","in":"query","schema":{"type":"string"},"description":"Comma-separated event types, e.g. `new_subscriber,new_tip`. An unknown type returns 400.","example":"new_subscriber,new_tip"},{"name":"of_user_id","in":"query","schema":{"type":"string"},"description":"Restrict to one connected account."},{"name":"since","in":"query","schema":{"type":"string"},"description":"ISO timestamp lower bound."},{"name":"until","in":"query","schema":{"type":"string"},"description":"ISO timestamp upper bound."},{"name":"limit","in":"query","schema":{"type":"integer","default":100,"minimum":1,"maximum":500}}],"responses":{"200":{"description":"Event list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"events":{"type":"array","items":{"type":"object"}}}}}}},"400":{"description":"Unknown event type"}}}},"/events/stream":{"get":{"tags":["Events & Streaming"],"summary":"Stream events (SSE)","description":"Long-lived **Server-Sent Events** stream of panel events. **Rate-limit exempt.** Use this instead of polling to observe refresh-job completion and `export.progress` / `export.complete`.\n\nOptional server-side filtering: `?types=new_subscriber,new_tip` (`*` or absent = everything). An unknown type returns 400.","parameters":[{"name":"types","in":"query","schema":{"type":"string"},"description":"Comma-separated event types, or `*` for everything."}],"responses":{"200":{"description":"SSE stream","content":{"text/event-stream":{"schema":{"type":"string"}}}},"400":{"description":"Unknown event type"}}}},"/webhooks":{"get":{"tags":["Webhooks"],"summary":"List webhooks","responses":{"200":{"description":"Webhook list"}}},"post":{"tags":["Webhooks"],"summary":"Create a webhook","description":"Subscribe an HTTPS endpoint to panel events. Rate limited to 120 requests/minute.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","example":"https://example.com/hooks/theonlyapi"},"events":{"type":"array","items":{"type":"string"},"description":"Event types to deliver. Omit or use `*` for everything.","example":["new_subscriber","new_tip"]},"of_user_id":{"type":"string","nullable":true,"description":"Restrict to one connected account."},"enabled":{"type":"boolean","default":true}}}}}},"responses":{"200":{"description":"Webhook created"},"400":{"description":"Invalid URL or event type"}}}},"/webhooks/{webhook_id}":{"get":{"tags":["Webhooks"],"summary":"Get a webhook","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Webhook"},"404":{"description":"Not found"}}},"patch":{"tags":["Webhooks"],"summary":"Update a webhook","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"}},"enabled":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Webhook updated"}}},"delete":{"tags":["Webhooks"],"summary":"Delete a webhook","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Webhook deleted"}}}},"/webhooks/{webhook_id}/test":{"post":{"tags":["Webhooks"],"summary":"Send a test delivery","description":"Fire a synthetic event at the webhook so you can verify the endpoint and signature handling. The delivery carries X-OnlyAPI-Signature (sha256=<hex digest>), X-OnlyAPI-Timestamp, X-OnlyAPI-Event and X-OnlyAPI-Delivery-Id; the digest is HMAC-SHA256 over \"{X-OnlyAPI-Timestamp}.\" + the raw body.","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Test delivery result"}}}},"/webhooks/{webhook_id}/deliveries":{"get":{"tags":["Webhooks"],"summary":"List webhook deliveries","description":"Delivery history with response status codes, for debugging failures.","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"integer"}},{"$ref":"#/components/parameters/limitParam"}],"responses":{"200":{"description":"Delivery history"}}}},"/automations":{"get":{"tags":["Automations"],"summary":"List automations","responses":{"200":{"description":"Automation list"}}},"post":{"tags":["Automations"],"summary":"Create an automation","description":"Run an action whenever a panel event fires. `trigger_event` must be a known event type (not `*`) and `action_type` must be a supported action. Rate limited to 120 requests/minute.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","trigger_event","action_type"],"properties":{"name":{"type":"string","maxLength":100,"example":"Welcome DM"},"trigger_event":{"type":"string","example":"new_subscriber","description":"A known event type. `*` is not allowed."},"action_type":{"type":"string","example":"send_dm","description":"A supported action type."},"conditions":{"type":"array","items":{"type":"object"},"default":[]},"action_params":{"type":"object","default":{}},"of_user_id":{"type":"string","nullable":true,"description":"Restrict to one connected account. Omit to apply panel-wide."}}}}}},"responses":{"200":{"description":"Automation created"},"400":{"description":"Invalid trigger_event or action_type"}}}},"/automations/{automation_id}":{"get":{"tags":["Automations"],"summary":"Get an automation","parameters":[{"name":"automation_id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Automation"},"404":{"description":"Not found"}}},"patch":{"tags":["Automations"],"summary":"Update an automation","parameters":[{"name":"automation_id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"enabled":{"type":"boolean"},"conditions":{"type":"array","items":{"type":"object"}},"action_params":{"type":"object"}}}}}},"responses":{"200":{"description":"Automation updated"}}},"delete":{"tags":["Automations"],"summary":"Delete an automation","parameters":[{"name":"automation_id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Automation deleted"}}}},"/automations/{automation_id}/run-now":{"post":{"tags":["Automations"],"summary":"Run an automation immediately","parameters":[{"name":"automation_id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Run triggered"}}}},"/automations/{automation_id}/runs":{"get":{"tags":["Automations"],"summary":"List automation runs","description":"Execution history for one automation.","parameters":[{"name":"automation_id","in":"path","required":true,"schema":{"type":"integer"}},{"$ref":"#/components/parameters/limitParam"}],"responses":{"200":{"description":"Run history"}}}},"/integrations/telegram/groups":{"post":{"tags":["Integrations"],"summary":"Register Telegram groups","description":"Register Telegram group targets so automations and webhooks can deliver notifications there.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"groups":{"type":"array","items":{"type":"object"},"description":"Telegram group identifiers to register."}}}}}},"responses":{"200":{"description":"Groups registered"}}}},"/api2/v2/users/me":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"tags":["OF API — User"],"summary":"Get current user profile","description":"Returns the authenticated user's full profile including stats, subscription info, and settings. Call via the generic proxy endpoint.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","responses":{"200":{"description":"User profile object","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"username":{"type":"string"},"about":{"type":"string"},"avatar":{"type":"string"},"header":{"type":"string"},"email":{"type":"string"},"isPerformer":{"type":"boolean"},"subscribesCount":{"type":"integer"},"subscribersCount":{"type":"integer"},"postsCount":{"type":"integer"},"photosCount":{"type":"integer"},"videosCount":{"type":"integer"},"audiosCount":{"type":"integer"},"tipsEnabled":{"type":"boolean"},"subscribePrice":{"type":"number"},"canEarn":{"type":"boolean"}}}}}}}},"patch":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"tags":["OF API — User"],"summary":"Update user profile","description":"Update profile fields. Supports display name, about text, subscription price, tip settings, and more. Only include the fields you want to change.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"displayName":{"type":"string","description":"Profile display name","example":"My Creator Name"},"about":{"type":"string","description":"Bio / about text","example":"Welcome to my page! 💕"},"subscribePrice":{"type":"number","description":"Monthly subscription price in USD","example":9.99},"tipsEnabled":{"type":"boolean","description":"Whether tips are enabled on the profile"},"tipsMin":{"type":"number","description":"Minimum tip amount in USD"},"tipsMax":{"type":"number","description":"Maximum tip amount in USD"},"location":{"type":"string","description":"Profile location text"},"website":{"type":"string","description":"Website URL"},"wishlist":{"type":"string","description":"Amazon wishlist URL"},"showPostsTipsSum":{"type":"boolean","description":"Show tip totals on posts"},"showMediaCount":{"type":"boolean","description":"Show photo/video counts on profile"},"showPostsCount":{"type":"boolean","description":"Show total posts count"},"canCommentOnContent":{"type":"boolean","description":"Allow comments on posts"},"isPossibleToReply":{"type":"boolean","description":"Allow DM replies"}}},"examples":{"updatePrice":{"summary":"Update subscription price","value":{"subscribePrice":14.99}},"updateBio":{"summary":"Update bio and display name","value":{"displayName":"My New Name","about":"Welcome to my page! 💕 DM me for custom content."}},"updateSettings":{"summary":"Update profile settings","value":{"tipsEnabled":true,"tipsMin":5,"canCommentOnContent":true}}}}}},"responses":{"200":{"description":"Updated profile"}}}},"/api2/v2/users/me/settings":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"tags":["OF API — User"],"summary":"Get account settings","description":"Returns account-level settings including banking status, payout eligibility, and verification state.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","responses":{"200":{"description":"Settings object","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFUserSettings"}}}}}}},"/api2/v2/users/{user_id}":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — User"],"summary":"Get user public profile","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"User profile","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFUserProfile"}}}}}}},"/api2/v2/users/{user_id}/block":{"post":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — User"],"summary":"Block a user","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"User blocked"}}},"delete":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — User"],"summary":"Unblock a user","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"User unblocked"}}}},"/api2/v2/users/{user_id}/restrict":{"post":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — User"],"summary":"Restrict a user","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"User restricted"}}}},"/api2/v2/users/blocked":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — User"],"summary":"List blocked users","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":10}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Blocked users list","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"$ref":"#/components/schemas/OFUserProfile"}},"hasMore":{"type":"boolean"}}}}}}}}},"/api2/v2/users/me/profile/views/qr":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — User"],"summary":"Get profile QR code","responses":{"200":{"description":"QR code data","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"QR code image URL"}}}}}}}}},"/api2/v2/subscriptions/subscribers/count":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Subscribers"],"summary":"Get subscriber count","responses":{"200":{"description":"Count","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer"}}}}}}}}},"/api2/v2/subscriptions/subscribers/recent-expired":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Subscribers"],"summary":"Recently expired subscribers","responses":{"200":{"description":"Expired subscriber list","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"$ref":"#/components/schemas/OFUserProfile"}},"hasMore":{"type":"boolean"}}}}}}}}},"/api2/v2/subscriptions/subscribers/awards":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Subscribers"],"summary":"Top-spending subscribers","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":10}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Awards list","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"$ref":"#/components/schemas/OFUserProfile"}},"hasMore":{"type":"boolean"}}}}}}}}},"/api2/v2/subscriptions/subscribers/awards/count":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Subscribers"],"summary":"Subscriber awards count","responses":{"200":{"description":"Count","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer"}}}}}}}}},"/api2/v2/subscriptions/subscribes":{"get":{"tags":["OF API — Subscriptions"],"summary":"List your subscriptions","description":"Accounts you are subscribed to.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":10}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}},{"name":"type","in":"query","schema":{"type":"string","default":"active"}},{"name":"format","in":"query","schema":{"type":"string","default":"infinite"}}],"responses":{"200":{"description":"Subscription list","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"$ref":"#/components/schemas/OFUserProfile"}},"hasMore":{"type":"boolean"}}}}}}}}},"/api2/v2/subscriptions/subscribes/count":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Subscriptions"],"summary":"Subscription count","responses":{"200":{"description":"Count","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer"}}}}}}}}},"/api2/v2/subscriptions/{subscription_id}/history":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Subscriptions"],"summary":"Subscription payment history","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"subscription_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Payment history","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"type":"object"}},"hasMore":{"type":"boolean"}}}}}}}}},"/api2/v2/subscriptions/{subscription_id}/discount":{"put":{"tags":["OF API — Subscriptions"],"summary":"Apply subscription discount","description":"Apply a percentage discount to a specific subscriber's subscription. Useful for retention or promotional pricing for individual fans.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"subscription_id","in":"path","required":true,"schema":{"type":"string"},"description":"Subscription ID to apply discount to"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["discount","period"],"properties":{"discount":{"type":"integer","description":"Discount percentage (0-100). 0 removes the discount.","minimum":0,"maximum":100,"example":50},"period":{"type":"integer","description":"Number of months the discount is active (1-12).","minimum":1,"maximum":12,"example":3}}},"examples":{"halfOff":{"summary":"50% off for 3 months","value":{"discount":50,"period":3}},"removeDiscount":{"summary":"Remove discount","value":{"discount":0,"period":1}}}}}},"responses":{"200":{"description":"Discount applied"}}}},"/api2/v2/users/{user_id}/subscribe":{"post":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Subscriptions"],"summary":"Subscribe to a user","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Subscribed"}}}},"/api2/v2/users/{user_id}/unsubscribe":{"delete":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Subscriptions"],"summary":"Unsubscribe from a user","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Unsubscribed"}}}},"/api2/v2/chats":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Messaging"],"summary":"List recent chats","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":10}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}},{"name":"order","in":"query","schema":{"type":"string","default":"recent"}},{"name":"skip_users","in":"query","schema":{"type":"string","default":"all"}}],"responses":{"200":{"description":"Chat list","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"$ref":"#/components/schemas/OFChat"}},"hasMore":{"type":"boolean"}}}}}}}}},"/api2/v2/chats/{user_id}/messages":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Messaging"],"summary":"Get chat messages","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","default":20}},{"name":"order","in":"query","schema":{"type":"string","default":"desc"}},{"name":"id","in":"query","schema":{"type":"integer"},"description":"Message ID cursor for pagination. Returns messages before/after this ID depending on order."},{"name":"skip_users","in":"query","schema":{"type":"string","default":"all"}}],"responses":{"200":{"description":"Message list","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"$ref":"#/components/schemas/OFMessage"}},"hasMore":{"type":"boolean"}}}}}}}},"post":{"tags":["OF API — Messaging"],"summary":"Send a message","description":"Send a direct message to a user. Supports text, media attachments (uploaded via the media upload endpoint first), PPV pricing, and locked text. Media must be uploaded to the vault first, then referenced by ID in the `mediaFiles` array.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","description":"Message text content","example":"Hello! Check out this exclusive content 🔥"},"mediaFiles":{"type":"array","description":"Media to attach. Two accepted forms:\n\n* an integer vault ID of media already in the vault (list them with `GET /api2/v2/vault/media`)\n* the `media` object returned by `POST /accounts/{of_user_id}/media` — a freshly uploaded file is referenced by `processId`, not a vault ID\n\nUpload new files with `POST /accounts/{of_user_id}/media` (raw bytes, or a `source_url` we fetch for you).","items":{"type":"integer"},"example":[12345,67890]},"price":{"type":"number","description":"PPV price in USD. If set, the message content is locked behind a paywall.","example":9.99},"lockedText":{"type":"boolean","description":"If true, the text is hidden until the user pays the PPV price.","default":false},"releaseForms":{"type":"array","description":"Release form IDs for compliance. Required if media contains other people.","items":{"type":"integer"}},"replyToMessage":{"type":"integer","description":"Message ID to reply to (creates a threaded reply)."}}},"examples":{"textOnly":{"summary":"Simple text message","value":{"text":"Hey! How are you?"}},"withMedia":{"summary":"Message with media attachments","value":{"text":"Here's your exclusive content!","mediaFiles":[12345,67890]}},"ppvMessage":{"summary":"Pay-per-view message","value":{"text":"Unlock to see 🔒","mediaFiles":[12345],"price":15,"lockedText":true}}}}}},"responses":{"200":{"description":"Message sent successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFMessage"}}}}}}},"/api2/v2/chats/{user_id}/messages/search":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Messaging"],"summary":"Search messages in chat","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"query","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Search results","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"$ref":"#/components/schemas/OFMessage"}},"hasMore":{"type":"boolean"}}}}}}}}},"/api2/v2/chats/{user_id}/mark-as-read":{"post":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Messaging"],"summary":"Mark chat as read","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Marked as read"}}}},"/api2/v2/messages/queue":{"get":{"tags":["OF API — Messaging"],"summary":"Get queued mass messages","description":"List all scheduled/queued mass messages. These are messages waiting to be sent to multiple users.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":10}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Queue list","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"type":"object"}},"hasMore":{"type":"boolean"}}}}}}}},"post":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"tags":["OF API — Messaging"],"summary":"Create a mass message","description":"Queue a mass message to be sent to multiple subscribers. You can target all subscribers, specific lists, or exclude certain users.\n\n**Attaching media:** `POST /accounts/{of_user_id}/media` (raw bytes or a `source_url` we fetch for you) returns a `media` object — pass it through here. Media already in the vault is referenced by its integer ID from `GET /api2/v2/vault/media`.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","description":"Message text","example":"Special offer just for you! 🎁"},"mediaFiles":{"type":"array","items":{"type":"integer"},"description":"Media IDs from vault"},"price":{"type":"number","nullable":true,"description":"PPV price for locked content"},"lockedText":{"type":"boolean","default":false},"releaseForms":{"type":"array","items":{"type":"integer"}},"queueBuyers":{"type":"array","description":"Target audience filters. Empty array = all subscribers. Can specify list IDs or user groups.","items":{"type":"object"},"example":[]}}},"examples":{"allSubscribers":{"summary":"Mass message to all subscribers","value":{"text":"Happy weekend! Check your DMs for a surprise 🎁","mediaFiles":[12345],"price":5,"queueBuyers":[]}}}}}},"responses":{"200":{"description":"Mass message queued successfully"}}}},"/api2/v2/messages/queue/size":{"post":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"tags":["OF API — Messaging"],"summary":"Calculate mass message audience size","description":"Preview how many users would receive a mass message given the buyer filter criteria.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"queueBuyers":{"type":"array","description":"Same filter array as used in POST /messages/queue. Empty = all subscribers.","items":{"type":"object"},"example":[]}}}}}},"responses":{"200":{"description":"Audience size estimate","content":{"application/json":{"schema":{"type":"object","properties":{"size":{"type":"integer","description":"Number of users who would receive the message"}}}}}}}}},"/api2/v2/messages/{message_id}/like":{"post":{"tags":["OF API — Messaging"],"summary":"Like / react to a message","description":"Send a like reaction to a specific message in a chat.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"message_id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"withUserId":{"type":"integer","description":"The user ID of the chat partner (recipient of the like notification)."}}}}}},"responses":{"200":{"description":"Message liked"}}}},"/api2/v2/messages/templates":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"tags":["OF API — Messaging"],"summary":"Get message templates","description":"Get saved message templates for quick replies.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","responses":{"200":{"description":"Template list","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OFMessageTemplate"}}}}}}}},"/api2/v2/posts":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Content"],"summary":"Get posts feed","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":10}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}},{"name":"format","in":"query","schema":{"type":"string","default":"infinite"}},{"name":"skip_users","in":"query","schema":{"type":"string","default":"all"}}],"responses":{"200":{"description":"Posts list","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"$ref":"#/components/schemas/OFPost"}},"hasMore":{"type":"boolean"}}}}}}}},"post":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"tags":["OF API — Content"],"summary":"Create a new post","description":"Create a new post on your profile. Supports text, media attachments, PPV pricing, polls, scheduled publishing (`isScheduled` + `scheduledDate`), and expiration dates.\n\n**Attaching a freshly uploaded file — use `mediaFiles`, not `media`.** `POST /accounts/{of_user_id}/media` returns a `media` object; put it in a **`mediaFiles`** array here, unchanged and complete.\n\nVerified live 2026-08-06, and the failure mode is silent — OnlyFans returns `200` and simply attaches nothing if you get this wrong:\n\n| body | result |\n|---|---|\n| `mediaFiles: [{processId, host, thumbId, name, extra}]` | media attached ✅ |\n| `media: [{…same object…}]` | post created, **0 media** |\n| `mediaFiles: [{processId}]` only | post created, **0 media** |\n\nSo pass the whole object through — dropping `thumbId` or `name` silently loses the attachment.\n\nThe vault row for the media is created by this call and appears in `GET /api2/v2/vault/media` roughly 10–15 seconds later. It survives deletion of the post.\n\nMedia already in the vault is referenced by its integer ID from `GET /api2/v2/vault/media`.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","description":"Post text / caption","example":"New exclusive content! 🔥"},"rawText":{"type":"string","description":"Raw text without formatting (used internally for search/indexing)"},"price":{"type":"number","nullable":true,"description":"PPV price in USD. If set, the post is locked behind a paywall.","example":15},"lockedText":{"type":"boolean","description":"If true, text content is hidden until PPV is purchased.","default":false},"media":{"type":"array","description":"Media to attach: integer vault IDs (from `GET /api2/v2/vault/media`), and/or the `media` object returned by `POST /accounts/{of_user_id}/media` for a file you just uploaded.","items":{"type":"integer"},"example":[12345,67890]},"preview":{"type":"array","nullable":true,"description":"Preview media IDs visible before purchasing PPV content.","items":{"type":"integer"}},"postedAt":{"type":"string","format":"date-time","description":"Schedule date for the post (ISO 8601). If in the future, the post is scheduled.","example":"2025-06-15T14:00:00.000Z"},"postedAtPrecise":{"type":"string","description":"Precise timestamp string (unix ms). Used internally for ordering."},"expiredAt":{"type":"string","format":"date-time","nullable":true,"description":"Expiration date — post auto-deletes after this time.","example":"2025-07-15T14:00:00.000Z"},"isPublishedWithPeriod":{"type":"boolean","description":"Whether the post is published for a specific time period (used with expiredAt).","default":false},"voting":{"type":"array","nullable":true,"description":"Poll options. Each item is a poll choice string. Adds a poll/vote to the post.","items":{"type":"string"},"example":["Option A","Option B","Option C"]},"linkedPosts":{"type":"array","nullable":true,"description":"Array of post IDs to link to this post (related content).","items":{"type":"integer"}},"releaseForms":{"type":"array","description":"Release form IDs for compliance when media features other people.","items":{"type":"integer"}},"streamId":{"type":"integer","nullable":true,"description":"Link this post to a live stream by stream ID."}}},"examples":{"textPost":{"summary":"Simple text post","value":{"text":"Happy Monday everyone! ❤️"}},"mediaPost":{"summary":"Post with media","value":{"text":"New photoset just dropped!","media":[12345,67890,11111]}},"ppvPost":{"summary":"Pay-per-view post","value":{"text":"Unlock to see the full set 🔒","media":[12345,67890],"preview":[12345],"price":25,"lockedText":false}},"pollPost":{"summary":"Post with a poll","value":{"text":"What should I post next?","voting":["Beach photoshoot","Gym content","Behind the scenes"]}},"scheduledPost":{"summary":"Scheduled post","value":{"text":"Coming soon...","media":[12345],"postedAt":"2025-06-20T18:00:00.000Z"}}}}}},"responses":{"200":{"description":"Post created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPost"}}}}}}},"/api2/v2/posts/{post_id}":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Content"],"summary":"Get specific post","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"post_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"skip_users","in":"query","schema":{"type":"string","default":"all"}}],"responses":{"200":{"description":"Post object","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFPost"}}}}}}},"/api2/v2/posts/{post_id}/comments":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Content"],"summary":"Get post comments","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"post_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","default":10}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Comments list","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"text":{"type":"string"},"author":{"$ref":"#/components/schemas/OFUserProfile"},"createdAt":{"type":"string","format":"date-time"},"likesCount":{"type":"integer"}}}},"hasMore":{"type":"boolean"}}}}}}}}},"/api2/v2/posts/{post_id}/favorites":{"post":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Content"],"summary":"Like / favorite a post","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"post_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Post liked"}}}},"/api2/v2/posts/{post_id}/vote":{"post":{"tags":["OF API — Content"],"summary":"Vote on a post poll","description":"Submit a vote on a post that has a poll attached. Get the `optionId` from the post's `voting` array.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"post_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["optionId"],"properties":{"optionId":{"type":"integer","description":"The poll option ID to vote for (from the post's voting array)."}}}}}},"responses":{"200":{"description":"Vote recorded"}}}},"/api2/v2/posts/pinned/sort":{"post":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"tags":["OF API — Content"],"summary":"Reorder pinned posts","description":"Change the display order of your pinned posts on your profile.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["order"],"properties":{"order":{"type":"array","items":{"type":"integer"},"description":"Ordered array of pinned post IDs. First ID appears first on profile.","example":[999,888,777]}}}}}},"responses":{"200":{"description":"Pin order updated"}}}},"/api2/v2/posts/stats-collect":{"post":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"tags":["OF API — Content"],"summary":"Report post view/interaction stats","description":"Submit post view and interaction analytics. Used by the client to report which posts were viewed, scrolled past, or interacted with.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["actions"],"properties":{"actions":{"type":"array","items":{"type":"object","properties":{"postId":{"type":"integer"},"type":{"type":"string","description":"Action type (e.g. 'view', 'scroll', 'click')"}}},"description":"Array of post interaction events"}}}}}},"responses":{"200":{"description":"Stats collected"}}}},"/api2/v2/posts/bookmarks":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Content"],"summary":"Get bookmarked posts","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":10}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Bookmarked posts","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"$ref":"#/components/schemas/OFPost"}},"hasMore":{"type":"boolean"}}}}}}}}},"/api2/v2/posts/bookmarks/categories":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Content"],"summary":"Get bookmark categories","responses":{"200":{"description":"Categories list","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OFBookmarkCategory"}}}}}}}},"/api2/v2/labels":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Content"],"summary":"Get user labels","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":10}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Labels list","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"$ref":"#/components/schemas/OFLabel"}},"hasMore":{"type":"boolean"}}}}}}}}},"/api2/v2/vault/lists":{"get":{"description":"List the vault folders for an account.\n\n**`view=main` is required** — omitting it makes OnlyFans respond `400 Bad view param` (passed through verbatim). The response is wrapped in the standard passthrough envelope (`{ success, status_code, data }`); the OnlyFans body is under `data`, where `data.list` is the folder array and `data.all` carries global counts across the whole vault.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Content"],"summary":"Get vault lists (folders)","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"view","in":"query","required":true,"description":"Required by OnlyFans. Use `main` — it returns the folder list plus the `all` bucket of global counts. Omitting it returns `400 Bad view param`.","schema":{"type":"string","enum":["main"],"default":"main"}},{"name":"limit","in":"query","schema":{"type":"integer","default":10}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Vault folders, wrapped in the passthrough envelope.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"all":{"type":"object","description":"Pseudo-folder holding global counts across the entire vault.","properties":{"photosCount":{"type":"integer"},"videosCount":{"type":"integer"},"gifsCount":{"type":"integer"},"audiosCount":{"type":"integer"}}},"list":{"type":"array","items":{"$ref":"#/components/schemas/OFVaultList"}},"hasMore":{"type":"boolean"},"order":{"type":"string"},"sort":{"type":"string"},"canCreateVaultLists":{"type":"boolean"}}}}}}}}}}},"/api2/v2/vault/media":{"get":{"description":"List vault media items. This is how you obtain the integer media IDs referenced by post creation, story creation, and messages.\n\n**Uploading new media:** use `POST /accounts/{of_user_id}/media`. A freshly uploaded file will **not** show up in this list right away — OnlyFans creates the vault entry when a post/message/story consumes the upload's `processId`, not at upload time. That is OnlyFans' own behaviour, not a limitation of this API: there is no upload-to-vault endpoint anywhere in the OnlyFans web client. (A `POST /api2/v2/media` upload route was documented in error until 2026-08-06 — it never existed and OnlyFans answers it with 404.)\n\n**Filtering (supported, but previously undocumented):** `list={list_id}` restricts results to one folder, `field=recent` chooses the ordering field, and `sort=asc|desc` the direction — combine with `limit`/`offset` for paging. The response is wrapped in the standard passthrough envelope; the OnlyFans body (`{ list, hasMore }`) is under `data`.\n\n**Matching a local file to an existing item:** the media object carries no filename or hash — use `GET /api2/v2/vault/media/hash?h={md5}&size={bytes}` instead.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Content"],"summary":"Get vault media","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list","in":"query","description":"Restrict results to a single vault folder (a list ID from GET /vault/lists).","schema":{"type":"integer"}},{"name":"field","in":"query","description":"Ordering field. `recent` orders by upload time.","schema":{"type":"string","enum":["recent"],"default":"recent"}},{"name":"sort","in":"query","description":"Sort direction.","schema":{"type":"string","enum":["asc","desc"],"default":"desc"}},{"name":"limit","in":"query","schema":{"type":"integer","default":24}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Media items, wrapped in the passthrough envelope.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"list":{"type":"array","items":{"$ref":"#/components/schemas/OFMedia"}},"hasMore":{"type":"boolean"}}}}}}}}}}},"/api2/v2/vault/media/{media_id}/attach":{"put":{"tags":["OF API — Content"],"summary":"Attach vault media to post/message","description":"Attach a media item from the vault to an existing post or message. The media must already be uploaded.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"media_id","in":"path","required":true,"schema":{"type":"integer"},"description":"Vault media ID"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"postId":{"type":"integer","description":"Post ID to attach media to"},"messageId":{"type":"integer","description":"Message ID to attach media to"}}}}}},"responses":{"200":{"description":"Media attached"}}}},"/api2/v2/vault/lists/{list_id}/media":{"post":{"tags":["OF API — Content"],"summary":"Add media to vault list","description":"Add one or more media items to a vault list (folder) for organization.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"integer"},"description":"Vault list ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["mediaIds"],"properties":{"mediaIds":{"type":"array","items":{"type":"integer"},"description":"Array of media IDs to add to the list","example":[12345,67890]}}}}}},"responses":{"200":{"description":"Media added to list"}}}},"/api2/v2/vault/media/hash":{"get":{"description":"**Deduplication / file→vault-item lookup.** Given the MD5 of an original file, returns the matching vault media if that exact byte content was previously uploaded, or `404 Media Not Found` if not.\n\nThis is the ONLY content-based way to correlate a local file with an existing vault item — OnlyFans stores no filename or hash on the media object. The hash is the MD5 of the **original uploaded bytes**: hashing a downloaded/CDN copy will NOT match (the served file differs from the original), and a file that was re-encoded/re-exported after upload will not match either. In that case, capture the media `id` at upload time and keep your own `source → id` map.\n\nResponse is wrapped in the passthrough envelope. **OnlyFans only.**","tags":["OF API — Content"],"summary":"Find vault media by MD5 (dedupe)","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"h","in":"query","required":true,"description":"MD5 hex digest of the ORIGINAL uploaded file bytes. Note the parameter is `h`, not `hash` — this was documented as `hash` in error until 2026-08-06. For a single-part upload the S3 ETag returned by `POST /accounts/{of_user_id}/media` is exactly this MD5, so you can feed it straight back in.","schema":{"type":"string","example":"9f86d081884c7d659a2feaa0c55ad015"}},{"name":"size","in":"query","required":true,"description":"Size of the original file in bytes. OnlyFans' own client always sends this alongside `h`.","schema":{"type":"integer","example":290758}}],"responses":{"200":{"description":"Matching media, wrapped in the passthrough envelope. On no match: `success:false`, `status_code:404`, and `data.error.message = \"Media Not Found\"`.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"$ref":"#/components/schemas/OFMedia"}}}}}}}}},"/api2/v2/vault/media/types":{"get":{"description":"Report which media types exist in the vault. `data` is a flat object of booleans (NOT counts) — one per type.\n\nResponse is wrapped in the passthrough envelope. **OnlyFans only.**","tags":["OF API — Content"],"summary":"Get vault media types present","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"Booleans for which media types are present, wrapped in the passthrough envelope.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"hasPhoto":{"type":"boolean"},"hasVideo":{"type":"boolean"},"hasGif":{"type":"boolean"},"hasAudio":{"type":"boolean"}}}}}}}}}}},"/api2/v2/vault/media/processing":{"get":{"description":"List media still being processed/transcoded by OnlyFans (items whose `isReady` is false).\n\n`data` carries its OWN `success` flag alongside `list` — distinct from the outer envelope's `success`.\n\nResponse is wrapped in the passthrough envelope. **OnlyFans only.**","tags":["OF API — Content"],"summary":"Get vault media being processed","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"responses":{"200":{"description":"Media currently processing, wrapped in the passthrough envelope.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"is_processing":{"type":"boolean"},"success":{"type":"boolean","description":"OnlyFans' own flag, nested inside data."},"list":{"type":"array","items":{"$ref":"#/components/schemas/OFMedia"}}}}}}}}}}}},"/api2/v2/vault/media/{media_id}":{"get":{"description":"Get a single vault media item by ID.\n\nResponse is wrapped in the passthrough envelope; the OnlyFans media object is under `data`. **OnlyFans only.**","tags":["OF API — Content"],"summary":"Get vault media item","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"media_id","in":"path","required":true,"schema":{"type":"integer"},"description":"Vault media ID."}],"responses":{"200":{"description":"The media item, wrapped in the passthrough envelope.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"$ref":"#/components/schemas/OFMedia"}}}}}}}}},"/api2/v2/vault/media/{media_id}/posts":{"get":{"description":"List posts that use a specific vault media item.\n\nResponse is wrapped in the passthrough envelope. **OnlyFans only.**","tags":["OF API — Content"],"summary":"Get posts using a vault media item","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"media_id","in":"path","required":true,"schema":{"type":"integer"},"description":"Vault media ID."}],"responses":{"200":{"description":"Posts referencing the media, wrapped in the passthrough envelope.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"list":{"type":"array","items":{"$ref":"#/components/schemas/OFPost"}},"hasMore":{"type":"boolean"}}}}}}}}}}},"/api2/v2/schedules":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Content"],"summary":"Get scheduled posts","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":10}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Scheduled posts","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"$ref":"#/components/schemas/OFPost"}},"hasMore":{"type":"boolean"}}}}}}}}},"/api2/v2/schedules/counters":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Content"],"summary":"Get scheduled-post counts per day","responses":{"200":{"description":"Scheduled-post counts, wrapped in the passthrough envelope. `data.list` is an OBJECT keyed by ISO date (e.g. \"2026-07-28\") whose value is `{ post: <count> }` — it is NOT an array.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer"},"data":{"type":"object","properties":{"list":{"type":"object","description":"Keyed by ISO date string.","additionalProperties":{"type":"object","properties":{"post":{"type":"integer"}}}},"syncInProcess":{"type":"boolean"}}}}}}}}}}},"/api2/v2/users/me/stories":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Stories"],"summary":"Get your active stories","responses":{"200":{"description":"Story list","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OFStory"}}}}}}},"post":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"tags":["OF API — Stories"],"summary":"Create a new story","description":"Post a new story. Stories expire after 24 hours.\n\n**Attaching media:** `POST /accounts/{of_user_id}/media` (raw bytes or a `source_url` we fetch for you) returns a `media` object — pass it through here. Media already in the vault is referenced by its integer ID from `GET /api2/v2/vault/media`.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"media":{"type":"array","description":"Array of media IDs from vault upload. At least one required.","items":{"type":"integer"},"example":[12345]},"question":{"type":"string","nullable":true,"description":"Optional question text to display on the story (interactive Q&A).","example":"What should I post next?"}}}}}},"responses":{"200":{"description":"Story created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFStory"}}}}}}},"/api2/v2/users/{user_id}/stories":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Stories"],"summary":"Get user's stories","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Story list","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OFStory"}}}}}}}},"/api2/v2/users/{user_id}/stories/highlights":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Stories"],"summary":"Get story highlights","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Highlights list","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"title":{"type":"string"},"cover":{"type":"string","description":"Cover image URL"},"stories":{"type":"array","items":{"$ref":"#/components/schemas/OFStory"}}}}}}}}}}},"/api2/v2/stories/highlights":{"post":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"tags":["OF API — Stories"],"summary":"Create a story highlight","description":"Save stories to a permanent highlight collection on your profile. Stories in highlights don't expire.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Highlight title/name","example":"Best Moments ✨"},"storyIds":{"type":"array","items":{"type":"integer"},"description":"Story IDs to include in the highlight","example":[111,222,333]},"cover":{"type":"integer","nullable":true,"description":"Story ID to use as the highlight cover image"}}}}}},"responses":{"200":{"description":"Highlight created"}}}},"/api2/v2/stories/archive":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Stories"],"summary":"Get archived stories","responses":{"200":{"description":"Archived stories","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OFStory"}}}}}}}},"/api2/v2/streams":{"post":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"tags":["OF API — Streams"],"summary":"Create / start a live stream","description":"Start a new live stream. Returns stream configuration including the room ID and streaming credentials.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Stream title displayed to viewers","example":"Friday night live! 🎉"},"description":{"type":"string","description":"Stream description"},"isFree":{"type":"boolean","description":"If true, stream is free for all. If false, only subscribers can watch.","default":false}}}}}},"responses":{"200":{"description":"Stream created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFStream"}}}}}}},"/api2/v2/streams/active":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Streams"],"summary":"Get active live streams","responses":{"200":{"description":"Active streams","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OFStream"}}}}}}}},"/api2/v2/streams/{stream_id}/stats":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Streams"],"summary":"Get stream statistics","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Stream stats","content":{"application/json":{"schema":{"type":"object","properties":{"viewersCount":{"type":"integer"},"likesCount":{"type":"integer"},"tipsAmount":{"type":"number"},"commentsCount":{"type":"integer"}}}}}}}}},"/api2/v2/streams/feed":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Streams"],"summary":"Stream discovery feed","responses":{"200":{"description":"Stream feed","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"$ref":"#/components/schemas/OFStream"}},"hasMore":{"type":"boolean"}}}}}}}}},"/api2/v2/streams/{stream_id}/vote":{"post":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Streams"],"summary":"Vote on a stream poll","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["optionId"],"properties":{"optionId":{"type":"integer","description":"The poll option ID to vote for"}}}}}},"responses":{"200":{"description":"Vote recorded"}}}},"/api2/v2/streams/{stream_id}/kick":{"post":{"tags":["OF API — Streams"],"summary":"Kick a user from stream","description":"Remove a viewer from the live stream (creator only).\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"stream_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["userId"],"properties":{"userId":{"type":"integer","description":"User ID to kick from the stream"}}}}}},"responses":{"200":{"description":"User kicked"}}}},"/api2/v2/campaigns":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Campaigns"],"summary":"List campaigns","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":10}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}},{"name":"pagination","in":"query","schema":{"type":"integer","default":1}},{"name":"stats","in":"query","schema":{"type":"string","default":"true"}}],"responses":{"200":{"description":"Campaign list","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"$ref":"#/components/schemas/OFCampaign"}},"hasMore":{"type":"boolean"}}}}}}}},"post":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Campaigns"],"summary":"Create campaign","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","example":"My Campaign"}}}}}},"responses":{"200":{"description":"Campaign created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFCampaign"}}}}}}},"/api2/v2/campaigns/{campaign_id}/claimers":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Campaigns"],"summary":"Get campaign conversions","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","default":10}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Claimer list","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"$ref":"#/components/schemas/OFUserProfile"}},"hasMore":{"type":"boolean"}}}}}}}}},"/api2/v2/payouts/balances":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Payouts"],"summary":"Get payout balances","responses":{"200":{"description":"Balance data","content":{"application/json":{"schema":{"type":"object","properties":{"current":{"type":"number","description":"Current available balance"},"pending":{"type":"number","description":"Pending balance"},"total":{"type":"number","description":"Total lifetime earnings"}}}}}}}}},"/api2/v2/payouts/chargebacks":{"get":{"tags":["OF API — Payouts"],"summary":"List chargebacks / disputes","description":"Paginated list of chargeback transactions. Uses marker-based pagination.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":10}},{"name":"startDate","in":"query","schema":{"type":"string","format":"date-time"},"description":"Filter start date"},{"name":"endDate","in":"query","schema":{"type":"string","format":"date-time"},"description":"Filter end date"}],"responses":{"200":{"description":"Chargeback list","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"type":"object"}},"marker":{"type":"number","nullable":true,"description":"Pagination marker (unix timestamp)"},"hasMore":{"type":"boolean"}}}}}}}}},"/api2/v2/payouts/chargebacks/ratio":{"get":{"tags":["OF API — Payouts"],"summary":"Get chargeback ratio","description":"Returns the chargeback-to-transaction ratio for the given date range.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"startDate","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"endDate","in":"query","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Chargeback ratio","content":{"application/json":{"schema":{"type":"object","properties":{"chargebacksRatio":{"type":"number","description":"Ratio of chargebacks (0–1)"}}}}}}}}},"/api2/v2/payouts/account":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"tags":["OF API — Payouts"],"summary":"Get payout account info","description":"Get saved banking/payment method info for payouts.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","responses":{"200":{"description":"Banking info","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","description":"Payment method type"},"isVerified":{"type":"boolean"}}}}}}}}},"/api2/v2/payments/all/transactions":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Payouts"],"summary":"Get all payment transactions","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":20}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"All transactions","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"type":"object"}},"hasMore":{"type":"boolean"}}}}}}}}},"/api2/v2/payments/referrals/balance":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Payouts"],"summary":"Get referral earnings balance","responses":{"200":{"description":"Referral balance","content":{"application/json":{"schema":{"type":"object","properties":{"balance":{"type":"number"}}}}}}}}},"/api2/v2/users/notifications":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Notifications"],"summary":"Get notifications","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":20}},{"name":"skip_users","in":"query","schema":{"type":"string","default":"all"}},{"name":"format","in":"query","schema":{"type":"string","default":"infinite"}}],"responses":{"200":{"description":"Notification list","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"type":"object"}},"hasMore":{"type":"boolean"}}}}}}}}},"/api2/v2/users/notifications/count":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Notifications"],"summary":"Get unread notification count","responses":{"200":{"description":"Unread count","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer"}}}}}}}}},"/api2/v2/users/notifications/read":{"post":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Notifications"],"summary":"Mark all notifications as read","responses":{"200":{"description":"Marked as read"}}}},"/api2/v2/lists":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Lists"],"summary":"Get user lists","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":10}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Lists","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"$ref":"#/components/schemas/OFList"}},"hasMore":{"type":"boolean"}}}}}}}},"post":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Lists"],"summary":"Create a new list","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","example":"My List"}}}}}},"responses":{"200":{"description":"List created"}}}},"/api2/v2/lists/{list_id}/users":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Lists"],"summary":"Get users in a list","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","default":10}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"User list","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"$ref":"#/components/schemas/OFUserProfile"}},"hasMore":{"type":"boolean"}}}}}}}}},"/api2/v2/lists/{list_id}/users/{user_id}":{"post":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Lists"],"summary":"Add user to list","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"User added"}}},"delete":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Lists"],"summary":"Remove user from list","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"list_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"User removed"}}}},"/api2/v2/promotions/offers":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Promotions"],"summary":"Get active promotional offers","responses":{"200":{"description":"Offers list","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OFPromotion"}}}}}}}},"/api2/v2/trials":{"get":{"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Promotions"],"summary":"Get free trial links","parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"},{"name":"limit","in":"query","schema":{"type":"integer","default":10}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Trial links","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":"array","items":{"$ref":"#/components/schemas/OFTrial"}},"hasMore":{"type":"boolean"}}}}}}}},"post":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Promotions"],"summary":"Create a free trial link","responses":{"200":{"description":"Trial created"}}}},"/api2/v2/helpers":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Helpers"],"summary":"List account helpers","responses":{"200":{"description":"Helper list","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OFHelper"}}}}}}}},"/api2/v2/helpers/permissions":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"description":"**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","tags":["OF API — Helpers"],"summary":"Get helper permissions","responses":{"200":{"description":"Permissions config","content":{"application/json":{"schema":{"type":"object","description":"Permission flags for each helper capability area"}}}}}}},"/api2/v2/init":{"get":{"parameters":[{"$ref":"#/components/parameters/ofUserIdHeader"},{"$ref":"#/components/parameters/proxyHeader"}],"tags":["OF API — Misc"],"summary":"App initialization data","description":"Returns comprehensive initialization data including authenticated user profile, feature flags, chat counts, and app configuration.\n\n**OnlyFans only.** Requests made with a `user-id` belonging to a Fansly account are rejected — use the platform-neutral CRM data routes (`/notifications`, `/balances`, `/chats`, `/purchases`, `/subscribers/cached`, ...) which are Fansly-aware.","responses":{"200":{"description":"Init payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OFInitPayload"}}}}}}},"/import/preview":{"post":{"tags":["Bulk Import"],"summary":"Preview an import paste","description":"Parse and validate a pasted account list **without side effects** — nothing is written, no login is attempted, and no slot is consumed. Use it to show which rows are valid, which lane each one will take (`cookie` or `password`), and exactly why a bad row is bad, before committing to an import.\n\nSecrets are never echoed back: passwords and TOTP secrets are reduced to `has_password` / `has_totp_secret`, cookies to a `cookie_fields` name list, and a proxy URL to `host:port` with its credentials stripped.\n\nAccepted formats are auto-detected — CSV, TSV, colon- or pipe-separated, with or without a header row. Limits are 1,000 rows and 2 MiB per paste; exceeding either is a rejection, never a silent truncation.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["text"],"properties":{"text":{"type":"string","description":"The pasted account list, one account per line.","example":"email,password,proxy\ncreator1@example.com,pw1,http://user:pass@host:1080\ncreator2@example.com,pw2,"},"default_platform":{"type":"string","enum":["onlyfans","fansly"],"default":"onlyfans","description":"Platform for rows that do not name one themselves. Also accepted as `platform`."}}}}}},"responses":{"200":{"description":"Parse result. `rows[]` is redacted; `lanes` and `platforms` count the valid rows by lane and platform."},"400":{"description":"`text` missing, `default_platform` not one of `onlyfans`/`fansly`, or the paste could not be parsed (`code: \"IMPORT_PARSE\"`)."},"413":{"description":"Paste exceeds `max_rows` (1,000) or `max_bytes` (2 MiB). Returns `code: \"IMPORT_LIMIT\"` with a `limits` object. Nothing is imported — split the paste and retry."}}}},"/import/jobs":{"post":{"tags":["Bulk Import"],"summary":"Start a bulk import","description":"Create an import job and start working through the rows in the background. Returns `202` immediately with the job and its per-status counts.\n\nProgress streams over [`GET /events/stream`](#tag/events-and-streaming) as `import.progress` (coalesced to at most one per second) and finishes with `import.complete`. The job row is the source of truth — if you miss an event, `GET /import/jobs/{job_id}` still has the current state.\n\nRows that hit a 2FA prompt do not fail: they park as `needs_2fa` holding the challenge, and wait for a code via `POST /import/jobs/{job_id}/rows/{row_id}/otp`. Parked rows expire after 10 minutes (retry them for a fresh login) and their stored credential is destroyed after 24 hours regardless.\n\nInvalid rows are reported but never attempted. If *no* row is valid the request fails with `400` and the per-row reasons rather than creating a job that is 100% failures.","parameters":[{"name":"X-User-Email","in":"header","required":false,"schema":{"type":"string"},"description":"Optional label recorded as the job's `requested_by`, for panels where several operators share a key."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["text"],"properties":{"text":{"type":"string","description":"The pasted account list, one account per line."},"default_platform":{"type":"string","enum":["onlyfans","fansly"],"default":"onlyfans","description":"Platform for rows that do not name one themselves. Also accepted as `platform`."},"source":{"type":"string","default":"paste","maxLength":20,"description":"Free-text label for where the list came from, stored on the job."}}}}}},"responses":{"202":{"description":"Job created and running. `warning` is present only when some rows failed validation and will not be attempted."},"400":{"description":"No valid rows. The body carries the same `rows[]` and counts as `POST /import/preview` so you can show what to fix."},"413":{"description":"Paste exceeds `max_rows` or `max_bytes` (`code: \"IMPORT_LIMIT\"`)."}}},"get":{"tags":["Bulk Import"],"summary":"List import jobs","description":"Import history for the panel, newest first, each job carrying its per-status row counts.","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","default":50,"minimum":1,"maximum":200},"description":"Jobs per page (1–200)."},{"$ref":"#/components/parameters/offsetParam"}],"responses":{"200":{"description":"Job history with `total` for pagination."}}}},"/import/jobs/{job_id}":{"get":{"tags":["Bulk Import"],"summary":"Get an import job","description":"One job with its rows and counts. This is the fallback whenever an SSE event is missed — the stored row is authoritative, the events are only a live nudge.\n\n`counts` is zero-filled across every state, so a missing key never has to be read as \"unknown\". `row_states` echoes the full state vocabulary: `pending`, `running`, `success`, `needs_2fa`, `needs_2fa_expired`, `failed`, `canceled`, `invalid`, `skipped`, `slot_exhausted`.\n\nRows never carry credentials — each has `has_password` / `has_totp_secret` flags instead, and a row parked on 2FA also reports `two_fa_remaining_seconds`.","parameters":[{"$ref":"#/components/parameters/importJobId"},{"name":"limit","in":"query","schema":{"type":"integer","default":1000,"minimum":1,"maximum":1000},"description":"Rows per page (1–1000)."},{"$ref":"#/components/parameters/offsetParam"},{"name":"status","in":"query","schema":{"type":"string","enum":["pending","running","success","needs_2fa","needs_2fa_expired","failed","canceled","invalid","skipped","slot_exhausted"]},"description":"Return only rows in this state. An unrecognised value is a `400`, not an empty page."}],"responses":{"200":{"description":"Job, counts, and one page of rows."},"400":{"description":"Unknown `status` filter."},"404":{"description":"No such job in this panel."}}}},"/import/jobs/{job_id}/cancel":{"post":{"tags":["Bulk Import"],"summary":"Cancel an import job","description":"Stop an import. Cancellation is cooperative: `pending` rows are canceled at once and nothing further is claimed, but a row already mid-login runs to completion — we cannot un-send a login attempt — and records its real outcome. Expect a few more successes after cancelling.\n\nOnly a `queued` or `running` job can be canceled.","parameters":[{"$ref":"#/components/parameters/importJobId"}],"responses":{"200":{"description":"Canceled. `canceled_rows` is how many pending rows were dropped."},"404":{"description":"No such job in this panel."},"409":{"description":"Job is already finished — the body reports its actual status."}}}},"/import/jobs/{job_id}/rows/{row_id}/otp":{"post":{"tags":["Bulk Import"],"summary":"Supply a 2FA code for an import row","description":"Complete one row parked on `needs_2fa` by submitting the code the creator received.\n\nSynchronous by design: it is a single platform call and an operator is watching, so the response carries the real outcome rather than \"queued\". `success: true` means the account is connected.\n\nThe field is `code` (`otp_code` is also accepted here), 4–12 characters, letters, digits and hyphens. This route is on the standard sensitive tier (100/minute), not the login tier — clearing a queue of parked rows back to back is the expected workflow, and the code is verified by the platform rather than by us.","parameters":[{"$ref":"#/components/parameters/importJobId"},{"$ref":"#/components/parameters/importRowId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["code"],"properties":{"code":{"type":"string","pattern":"^[A-Za-z0-9-]{4,12}$","example":"123456","description":"The 2FA code. Also accepted as `otp_code`."}}}}}},"responses":{"200":{"description":"Connected.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"row":{"$ref":"#/components/schemas/ImportJobRow"}},"required":["success","row"]}}}},"400":{"description":"Malformed code, or the platform rejected it. The row stays parked when the window is still open, so a mistyped code can be resubmitted."},"404":{"description":"No such row in this job."},"409":{"description":"Row is not waiting for a code — either it never was, or its 10-minute window closed (`status: \"needs_2fa_expired\"`). Retry the row to start a fresh login."},"503":{"description":"The platform's OTP verification is temporarily unavailable. The row stays parked; try again."}}}},"/import/jobs/{job_id}/rows/{row_id}/retry":{"post":{"tags":["Bulk Import"],"summary":"Retry an import row","description":"Re-queue one row and restart the job's worker. A `needs_2fa_expired` row re-runs the login **from scratch** — the parked challenge is dead, so replaying it would only earn another rejection.\n\nA row cannot be retried once its stored credential has been destroyed (which happens when a password-lane row reaches a terminal state or ages out after 24 hours); re-import it instead.","parameters":[{"$ref":"#/components/parameters/importJobId"},{"$ref":"#/components/parameters/importRowId"}],"responses":{"202":{"description":"Row re-queued; the job is running again.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"row":{"$ref":"#/components/schemas/ImportJobRow"}},"required":["success","row"]}}}},"404":{"description":"No such row in this job."},"409":{"description":"Not retryable: already queued or running, already connected, never parsed (`invalid`), or its credential is gone."}}}},"/import/pending-2fa":{"get":{"tags":["Bulk Import"],"summary":"List rows waiting on a 2FA code","description":"Every parked 2FA row on the panel, across **all** jobs — `needs_2fa` first, soonest to expire first.\n\nThis is deliberately panel-wide rather than per-job: an operator closes the importer and the dashboard still has to be able to say \"9 accounts need a 2FA code\". `count` is how many are still answerable, `expired_count` how many need a retry instead, and `expiry_seconds` is the window length each one gets.","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","default":200,"minimum":1,"maximum":1000},"description":"Maximum rows to return (1–1000)."}],"responses":{"200":{"description":"Parked rows across every job in the panel."}}}},"/integrations/telegram":{"get":{"tags":["Integrations"],"summary":"Get the Telegram integration","description":"The panel's Telegram channel, or `integration: null` if none is configured.\n\n`shared_bot_available` says whether this deployment has our shared bot configured — check it before offering \"use the Xcelerate bot\", so you never present a button that cannot work. `event_types` lists every event type you may subscribe to.\n\nThe bot token is never returned by this or any other route; `has_custom_token` only reports whether one is stored.","responses":{"200":{"description":"Integration state, plus the deployment's shared-bot availability and the allowed event types."}}},"patch":{"tags":["Integrations"],"summary":"Update the Telegram integration","description":"Change which events are delivered, or pause delivery without unpairing. Send only the fields you want to change.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"event_types":{"type":"array","items":{"type":"string"},"description":"Event types to deliver. `[\"*\"]` means every type.","example":["new_subscriber","new_tip"]},"is_active":{"type":"boolean","description":"Set `false` to stop delivery while keeping the pairing."}}}}}},"responses":{"200":{"description":"Updated integration."},"400":{"description":"An event type is not recognised."},"404":{"description":"No Telegram integration configured."}}},"delete":{"tags":["Integrations"],"summary":"Remove the Telegram integration","description":"Unpair the channel and delete the stored configuration, including any custom bot token.","responses":{"200":{"description":"Removed."},"404":{"description":"No Telegram integration configured."}}}},"/integrations/telegram/pair":{"post":{"tags":["Integrations"],"summary":"Start Telegram pairing","description":"Begin (or restart) pairing and get a `t.me` deep link. Open the link, and the chat that the bot lands in becomes the panel's channel.\n\nTwo modes:\n\n- `bot_mode: \"shared\"` — use our bot. Nothing else to supply. Returns `409` if this deployment has no shared bot configured, rather than handing back a link that can never resolve.\n- `bot_mode: \"custom\"` — use your own bot, passing its `bot_token`. We verify the token with Telegram before storing it, encrypt it at rest, and clear any existing webhook on that bot (otherwise our polling would conflict with it and the deep link would silently never resolve).\n\nThe response carries the deep link and its expiry, never a token. Calling this again replaces any pending code.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"bot_mode":{"type":"string","enum":["shared","custom"],"default":"shared"},"bot_token":{"type":"string","description":"Required when `bot_mode` is `custom`. Stored encrypted and never returned.","example":"123456789:AA…"},"event_types":{"type":"array","items":{"type":"string"},"description":"Which events to deliver. Defaults to every type."}}}}}},"responses":{"200":{"description":"Pairing started. Open `deep_link` before `expires_at` (`ttl_seconds` from now)."},"400":{"description":"`bot_mode` invalid, an event type is not recognised, the token is malformed, Telegram rejected it, or it is the shared token (use `bot_mode: \"shared\"`)."},"409":{"description":"`bot_mode: \"shared\"` requested but this deployment has no shared bot. Connect your own bot instead."}}}},"/integrations/telegram/test":{"post":{"tags":["Integrations"],"summary":"Send a Telegram test message","description":"Post a test message to the paired chat, so you can confirm the whole path end to end. Returns `400` with the failure reason — and the current integration state, including `last_error` — if Telegram refuses it.","responses":{"200":{"description":"Test message delivered."},"400":{"description":"Not paired, inactive, or Telegram rejected the send. The body carries the reason."}}}},"/balances/summary":{"get":{"tags":["Earnings"],"summary":"Get panel-wide payout totals","description":"Available and pending payout totals across every connected account, in **one** query and **zero** platform calls.\n\nThese are last-known values, not live ones. `payoutAvailable` is only knowable by asking the platform, so a live panel-wide total would cost one round trip per account — the honest trade is to serve the last sample and say how old it is. `GET /accounts/{of_user_id}/balances` stamps a fresh sample each time it runs, so normal dashboard use keeps the figures warm at no extra cost.\n\nLabel the number with `oldest_sample_at` / `newest_sample_at`, and treat `accounts_never_sampled` as \"not counted yet\" rather than zero. `currency` is the panel's most common currency.","responses":{"200":{"description":"Panel totals with sample-freshness metadata. Amounts are rounded to 2 decimal places."}}}},"/accounts/{of_user_id}/earnings/verify":{"get":{"tags":["Earnings"],"summary":"Verify cached earnings against the platform","description":"Cross-check one account's cached earnings against OnlyFans' own chart for the same window, and report both numbers plus the difference.\n\n`GET /earnings/summary` sums the local transaction cache rather than asking the platform, because the live path cannot converge at panel scale. The cost of that choice is a trust question, and this is the answer to it: same period, both sources, side by side.\n\n**Per-account on purpose.** It makes real upstream calls and sits on the sensitive rate-limit tier so it can never become something a dashboard fans out over hundreds of accounts.\n\nIf the platform is unreachable, `live_available` is `false` and `live_total`/`difference` are `null` — that is \"could not check\", not \"matches\".","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"name":"period","in":"query","schema":{"type":"string","enum":["today","week","month"],"default":"week"},"description":"Window to compare. Ignored when `startDate` and `endDate` are both supplied."},{"name":"startDate","in":"query","schema":{"type":"string","format":"date"},"description":"Custom range start. Requires `endDate`."},{"name":"endDate","in":"query","schema":{"type":"string","format":"date"},"description":"Custom range end. Requires `startDate`."}],"responses":{"200":{"description":"Both totals and their difference. `matches` is the verdict for this window."},"400":{"description":"`period` not one of `today`/`week`/`month`, or an unparseable `startDate`/`endDate`."},"404":{"description":"Account not found in this panel."}}}},"/accounts/{of_user_id}/tags":{"post":{"tags":["Accounts"],"summary":"Tag a connected account","description":"Add a tag to a connected account, for grouping creators inside the panel. Tags are panel-local — they are never sent to the platform. Max 40 characters; re-adding an existing tag is a no-op.","parameters":[{"$ref":"#/components/parameters/ofUserId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tag"],"properties":{"tag":{"type":"string","maxLength":40,"example":"vip"}}}}}},"responses":{"200":{"description":"The stored tag. Send this exact string back when deleting it."},"400":{"description":"Missing tag, or longer than 40 characters."},"403":{"description":"Account does not belong to this panel."}}}},"/accounts/{of_user_id}/tags/{tag}":{"delete":{"tags":["Accounts"],"summary":"Remove an account tag","description":"Remove a tag from a connected account. Pass the tag exactly as the API returned it — the stored form is escaped once on the way in, so re-escaping it here would look for a different string and silently delete nothing. `success: false` means there was no such tag.","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"name":"tag","in":"path","required":true,"schema":{"type":"string","maxLength":40},"description":"The tag, exactly as returned by the API."}],"responses":{"200":{"description":"`success` reports whether a tag was actually removed."},"400":{"description":"Missing tag, or longer than 40 characters."},"403":{"description":"Account does not belong to this panel."}}}},"/accounts/{of_user_id}/fansly-credentials":{"get":{"tags":["Accounts"],"summary":"Read a Fansly account's stored session","description":"Return the **current** Fansly session for one account you own, so an external worker can pick up refreshed tokens instead of holding a stale copy. We stay the credential authority: we are the thing that re-logs in and rewrites these tokens, so a caller that re-reads here each run is always current.\n\nRead-only — it mutates nothing, and is gated by the same API key and ownership checks as every other account route. **Fansly only**; an OnlyFans account returns `400`.","parameters":[{"$ref":"#/components/parameters/ofUserId"}],"responses":{"200":{"description":"Current session material: `auth_token`, `session_id`, `client_id`, and the account's `proxy`."},"400":{"description":"Not a Fansly account."},"403":{"description":"Account does not belong to this panel."},"404":{"description":"Account not found, or no Fansly session is stored yet — connect it first."}}}},"/accounts/{of_user_id}/refresh/{kind}/clear":{"post":{"tags":["Cache & Sync"],"summary":"Clear a stuck refresh job","description":"Force a wedged refresh job to a terminal state so a new one can start.\n\nThe refresh routes already supersede a stale job automatically, but that is a timer — you have to wait it out, and it cannot help at all with a job that keeps re-stamping its progress while making none. This is the escape hatch for that case.\n\nMakes no platform calls, so it costs no quota. Always returns `200`; `cleared` says whether there was in fact something to clear, and `state` is the job's final state (or `null`).","parameters":[{"$ref":"#/components/parameters/ofUserId"},{"$ref":"#/components/parameters/refreshKind"}],"responses":{"200":{"description":"`cleared: true` when a job was terminated, `false` when none was running."},"400":{"description":"`kind` is not one of `subs`, `tx`, `campaigns`."},"403":{"description":"Account does not belong to this panel."}}}},"/metrics/requests":{"get":{"tags":["Panel & Usage"],"summary":"Get request metrics for your panel","description":"Request outcomes for your own panel over a time window: volume, status classes, error rate, latency, and the slowest and most error-prone routes. This is what the dashboard Overview charts.\n\nThe current partial bucket is included, so the numbers are current rather than up to a minute stale. `top_tenants` is always empty here — it exists only so one component can render this and the platform-wide admin view interchangeably.","parameters":[{"name":"hours","in":"query","schema":{"type":"integer","default":24,"minimum":1,"maximum":2160},"description":"Window to report, in hours (1–2160, i.e. up to 90 days)."},{"name":"granularity","in":"query","schema":{"type":"string","enum":["5m","15m","1h","6h","1d"]},"description":"Bucket size for `series`. Defaults to a sensible size for `hours`. Also accepted as `bucket`."},{"name":"limit","in":"query","schema":{"type":"integer","default":10,"minimum":1,"maximum":50},"description":"How many rows in each top-N breakdown (1–50)."}],"responses":{"200":{"description":"`metrics` carries `range`, `totals`, `series`, `status_codes`, `top_routes`, `slowest_routes`, `top_error_routes`, `top_tenants` (empty) and `collector`."},"400":{"description":"`hours` or `limit` out of range, or an unknown `granularity`."}}}}}}