{"openapi":"3.1.0","info":{"title":"OnlyX API","summary":"Run your OnlyX workspace (creators, inbox, fans, stats and the AI chatter) from code and AI tools.","description":"The OnlyX API runs your OnlyX workspace from your own code and AI tools: creators and their\nconnection, the fan inbox, fans, vault media, statistics, the AI chatter's setup and tracking links.\n\n**Authentication.** Send an API key as `Authorization: Bearer onx_sk_…` (create one in OnlyX under\nSettings → API & MCP), or an OAuth access token issued to a connected app. Keys belong to the\nworkspace, carry scopes, and can be limited to some creators.\n\n**Conventions.** JSON with camelCase fields; timestamps are ISO-8601 UTC with a `Z`; money is integer\nUS cents (fields end in `Cents`). Lists return `{\"data\": [...], \"hasMore\": bool, \"nextCursor\": string|null}`\nand page with `limit` and `cursor`.\n\n**Scopes.** Every operation names the scope it needs in `x-required-scope` and in its description; a\ncredential without it gets `403 INSUFFICIENT_SCOPE`.\n\n**Errors and limits.** Errors are `{\"error\": {\"code\", \"message\", \"requestId\"}}`; every response\ncarries `X-Request-Id`. The default limit is 120 requests per 60 seconds per\ncredential, reported in `X-RateLimit-*` headers; over it you get `429 RATE_LIMITED` with `Retry-After`.\nOperations with limits of their own list them in `x-rate-limits`.\n\n**Idempotency and safety.** Every write accepts an `Idempotency-Key` header (required when sending a\nmessage, adding a creator and creating a tracking link): retry with the same key and you get the\noriginal answer instead of a second action. Operations marked `x-reaches-fans: true` can reach a real\nfan or change the live OnlyFans account.\n\nGuides, the MCP server and more: [docs.onlyx.ai](https://docs.onlyx.ai).\n","contact":{"name":"OnlyX developers","url":"https://docs.onlyx.ai/","email":"developers@onlyx.ai"},"version":"1.0.0"},"servers":[{"url":"https://api.onlyx.ai","description":"Production"}],"paths":{"/v1/conversations":{"get":{"tags":["Conversations"],"summary":"List conversations","description":"Lists conversations across every creator the credential can see, or one creator's with `creatorId`. Filter by `status` to find chats your team holds (`team`), chats waiting on a hand-off (`handoff`) or chats with the AI off (`ai_off`); `unread=true` finds fans waiting for an answer, and the two combine. `sort=spend` puts the biggest spenders first. Each item carries the fan, the last message and the unread count, so a list call is usually enough for an inbox view. A `creatorId` the credential cannot see is `404 CREATOR_NOT_FOUND`, never an empty list.\n\n**Scope:** requires `inbox:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"listConversations","parameters":[{"name":"creatorId","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"description":"Only this creator's conversations.","title":"Creatorid"},"description":"Only this creator's conversations."},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"enum":["ai","team","handoff","ai_off"],"type":"string"},{"type":"null"}],"description":"Only conversations in this status.","title":"Status"},"description":"Only conversations in this status."},{"name":"unread","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"`true`: only conversations with unread fan messages. `false`: only ones with none.","title":"Unread"},"description":"`true`: only conversations with unread fan messages. `false`: only ones with none."},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"description":"Search by fan name (OnlyFans name or your custom name).","title":"Q"},"description":"Search by fan name (OnlyFans name or your custom name)."},{"name":"sort","in":"query","required":false,"schema":{"enum":["recent","spend","unread","name"],"type":"string","description":"`recent` (latest message first, default), `spend` (biggest spenders first), `unread` (most unread first) or `name`.","default":"recent","title":"Sort"},"description":"`recent` (latest message first, default), `spend` (biggest spenders first), `unread` (most unread first) or `name`."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"description":"Page size, 1-50. Default 25.","default":25,"title":"Limit"},"description":"Page size, 1-50. Default 25."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"description":"Opaque cursor from the previous page's `nextCursor`. Omit for the first page.","title":"Cursor"},"description":"Opaque cursor from the previous page's `nextCursor`. Omit for the first page."}],"responses":{"200":{"description":"A page of conversations.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationPage"},"example":{"data":[{"id":"cnv_3d9e7b1a5c2f4e8d6a0b","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","fan":{"id":"fan_6b2e8d4a1c9f3e7b5d0a","displayName":"Mike","username":null,"avatarUrl":null},"status":"team","unreadCount":2,"lastMessage":{"text":"are you online tonight?","direction":"in","at":"2026-09-25T21:40:12.000Z"},"lastMessageAt":"2026-09-25T21:40:12.000Z","takenOverUntil":"2026-09-26T09:40:12.000Z","totalSpentCents":48500}],"hasMore":true,"nextCursor":"eyJrIjoiY3JlYXRvcnMiLCJwIjp7ImkiOiJjcmVfOGYyYzFhOWIwZDdlNGMzZjJhMWIifX0.Yp3k0Qm7T2vX9aLc4R8sWg"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"limit: Input should be less than or equal to 50","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `inbox:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `inbox:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: the `creatorId` filter names a creator this credential cannot see (never an empty result).","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"inbox:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false}},"/v1/conversations/counts":{"get":{"tags":["Conversations"],"summary":"Count conversations","description":"Returns how many conversations are in each status, and how many have unread fan messages, for every visible creator or for one with `creatorId`. Use it for badges and dashboards instead of paging through the list. It scans the whole inbox, so it has its own limit of 30 calls per 60 seconds per credential.\n\n**Scope:** requires `inbox:read`.\n\n**Rate limits:** 30 requests per 60 seconds per credential — on top of the general limit of 120 requests per 60 seconds per credential.","operationId":"getConversationCounts","parameters":[{"name":"creatorId","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"description":"Only this creator's conversations.","title":"Creatorid"},"description":"Only this creator's conversations."}],"responses":{"200":{"description":"How many conversations are in each status, and how many have unread fan messages.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationCounts"},"example":{"total":1240,"ai":1102,"team":96,"handoff":7,"aiOff":35,"unread":58}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"creatorId: String should have at most 40 characters","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `inbox:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `inbox:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: the `creatorId` filter names a creator this credential cannot see (never an empty result).","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"inbox:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":30,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false}},"/v1/conversations/{conversationId}":{"get":{"tags":["Conversations"],"summary":"Get a conversation","description":"Returns one conversation with the full fan (subscription, spend, notes, lists and `salesOptOut`), the open hand-off if there is one, and `aiCanReply`: whether the AI would answer this chat right now. Read it before writing to a fan so you know who you are talking to and whether a paid message is allowed. A conversation the credential cannot see is `404 CONVERSATION_NOT_FOUND`.\n\n**Scope:** requires `inbox:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"getConversation","parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","maxLength":40,"description":"The conversation id (`cnv_…`), from `GET /v1/conversations`.","title":"Conversationid"},"description":"The conversation id (`cnv_…`), from `GET /v1/conversations`."}],"responses":{"200":{"description":"The conversation, with the full fan, the open hand-off and whether the AI would reply.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationDetail"},"example":{"id":"cnv_3d9e7b1a5c2f4e8d6a0b","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","fan":{"id":"fan_6b2e8d4a1c9f3e7b5d0a","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","conversationId":"cnv_3d9e7b1a5c2f4e8d6a0b","displayName":"Mike","customName":"Mike (birthday 14 May)","username":null,"avatarUrl":null,"subscribed":true,"subscribedAt":"2026-03-02T18:21:07.000Z","subscriptionExpiresAt":"2026-10-02T18:21:07.000Z","subscriptionPriceCents":999,"renewOn":true,"totalSpentCents":48500,"tipsCents":12000,"paidMessagesCents":36500,"lastActiveAt":"2026-09-25T21:40:12.000Z","lastPurchaseAt":"2026-09-24T22:03:55.000Z","muted":false,"notes":"Likes lingerie sets. Tips on Fridays.","salesOptOut":false,"lists":[{"id":"lst_9c1d2e3f4a5b6c7d8e9f","name":"VIP"}],"createdAt":"2026-03-02T18:21:07.000Z"},"status":"team","unreadCount":2,"lastMessage":{"text":"are you online tonight?","direction":"in","at":"2026-09-25T21:40:12.000Z"},"lastMessageAt":"2026-09-25T21:40:12.000Z","takenOverUntil":"2026-09-26T09:40:12.000Z","totalSpentCents":48500,"handoff":null,"aiCanReply":false}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"conversationId: String should have at most 40 characters","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `inbox:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `inbox:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CONVERSATION_NOT_FOUND`: no conversation with this `conversationId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CONVERSATION_NOT_FOUND":{"summary":"CONVERSATION_NOT_FOUND","value":{"error":{"code":"CONVERSATION_NOT_FOUND","message":"Conversation not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"inbox:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false}},"/v1/conversations/{conversationId}/ai":{"put":{"tags":["Conversations"],"summary":"Switch the AI on or off for a conversation","description":"Switches the AI chatter on or off for this one conversation. `enabled: false` sets the status to `ai_off`: the AI never answers this fan until it is switched back on. `enabled: true` gives the chat to the AI, which **can reach the fan at once**: the AI may immediately answer a waiting message, so confirm with a person first. The creator's own AI switch still applies on top of this. Switching it on is refused with `503 SENDING_DISABLED` while sending through the API is switched off.\n\n**Scope:** requires `inbox:write`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.\n\n**Reaches fans:** this operation can reach a real fan or change the live OnlyFans account. Confirm it with a person before calling it on their behalf.","operationId":"setConversationAi","parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","maxLength":40,"description":"The conversation id (`cnv_…`).","title":"Conversationid"},"description":"The conversation id (`cnv_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetConversationAiRequest"}}}},"responses":{"200":{"description":"The updated conversation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Conversation"},"example":{"id":"cnv_3d9e7b1a5c2f4e8d6a0b","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","fan":{"id":"fan_6b2e8d4a1c9f3e7b5d0a","displayName":"Mike","username":null,"avatarUrl":null},"status":"team","unreadCount":2,"lastMessage":{"text":"are you online tonight?","direction":"in","at":"2026-09-25T21:40:12.000Z"},"lastMessageAt":"2026-09-25T21:40:12.000Z","takenOverUntil":"2026-09-26T09:40:12.000Z","totalSpentCents":48500}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"enabled: Field required","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `inbox:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `inbox:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CONVERSATION_NOT_FOUND`: no conversation with this `conversationId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CONVERSATION_NOT_FOUND":{"summary":"CONVERSATION_NOT_FOUND","value":{"error":{"code":"CONVERSATION_NOT_FOUND","message":"Conversation not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Temporarily unavailable. Retry later. `SENDING_DISABLED`: `enabled: true` while actions that can make the AI answer a fan are switched off for the API. Nothing was changed.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"SENDING_DISABLED":{"summary":"SENDING_DISABLED","value":{"error":{"code":"SENDING_DISABLED","message":"Actions that can make the AI answer a fan are switched off through the API for now. Nothing was changed.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}}},"x-required-scope":"inbox:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":true}},"/v1/conversations/{conversationId}/handoff/resolve":{"post":{"tags":["Hand-offs"],"summary":"Resolve a hand-off","description":"Closes the open hand-off on this conversation, with an optional note saying what your team did. **This can reach the fan**: if the chat is still in `handoff` (nobody replied or took it over), it returns to the AI at once and the AI may answer the fan's waiting message within seconds. If your team already replied or took the chat over, it stays with your team and resolving only closes the record. Returns `404 NOT_FOUND` when the conversation has no open hand-off (so resolving twice is harmless), and `503 SENDING_DISABLED` while sending through the API is switched off.\n\n**Scope:** requires `inbox:write`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.\n\n**Reaches fans:** this operation can reach a real fan or change the live OnlyFans account. Confirm it with a person before calling it on their behalf.","operationId":"resolveHandoff","parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","maxLength":40,"description":"The conversation id (`cnv_…`).","title":"Conversationid"},"description":"The conversation id (`cnv_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ResolveHandoffRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"The resolved hand-off.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Handoff"},"example":{"id":"esc_5e1f2a3b4c5d6e7f8a9b","conversationId":"cnv_3d9e7b1a5c2f4e8d6a0b","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","fanName":"Mike","reason":"payment_dispute","label":"Asks for a refund","note":"He says the video he bought does not play and wants his money back.","status":"open","createdAt":"2026-09-25T21:41:03.000Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"note: String should have at most 300 characters","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `inbox:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `inbox:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CONVERSATION_NOT_FOUND`: no conversation with this `conversationId` is visible to this credential. `NOT_FOUND`: the conversation has no open hand-off (so resolving twice is harmless).","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CONVERSATION_NOT_FOUND":{"summary":"CONVERSATION_NOT_FOUND","value":{"error":{"code":"CONVERSATION_NOT_FOUND","message":"Conversation not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"NOT_FOUND":{"summary":"NOT_FOUND","value":{"error":{"code":"NOT_FOUND","message":"Not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Temporarily unavailable. Retry later. `SENDING_DISABLED`: actions that can make the AI answer a fan are switched off for the API. Nothing was changed.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"SENDING_DISABLED":{"summary":"SENDING_DISABLED","value":{"error":{"code":"SENDING_DISABLED","message":"Actions that can make the AI answer a fan are switched off through the API for now. Nothing was changed.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}}},"x-required-scope":"inbox:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":true}},"/v1/conversations/{conversationId}/messages":{"get":{"tags":["Messages"],"summary":"List messages","description":"Returns the conversation's messages, oldest to newest, the newest page first. To read further back, pass the id of the oldest message you have as `before`; `hasMore` tells you whether older messages exist. Outgoing messages carry `delivery`, and paid messages show whether the fan unlocked them. A `before` id that is not a message of this conversation is `404 MESSAGE_NOT_FOUND`.\n\n**Scope:** requires `inbox:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"listMessages","parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","maxLength":40,"description":"The conversation id (`cnv_…`), from `GET /v1/conversations`.","title":"Conversationid"},"description":"The conversation id (`cnv_…`), from `GET /v1/conversations`."},{"name":"before","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"description":"A message id: return only messages older than it.","title":"Before"},"description":"A message id: return only messages older than it."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Page size, 1-100. Default 25.","default":25,"title":"Limit"},"description":"Page size, 1-100. Default 25."}],"responses":{"200":{"description":"A page of messages, oldest to newest.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessagePage"},"example":{"data":[{"id":"msg_1a2b3c4d5e6f7a8b9c0d","conversationId":"cnv_3d9e7b1a5c2f4e8d6a0b","direction":"out","sender":"team","text":"Made this one just for you 😘","media":[{"id":"4012345678","type":"photo","preview":true},{"id":"4012345679","type":"photo","preview":false}],"paid":{"priceCents":1500,"purchased":false,"purchasedAt":null},"tipCents":null,"createdAt":"2026-09-26T10:02:11.000Z","delivery":{"status":"queued","reason":null}}],"hasMore":true}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"limit: Input should be less than or equal to 100","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `inbox:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `inbox:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CONVERSATION_NOT_FOUND`: no conversation with this `conversationId` is visible to this credential. `MESSAGE_NOT_FOUND`: no message with this id is in this conversation.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CONVERSATION_NOT_FOUND":{"summary":"CONVERSATION_NOT_FOUND","value":{"error":{"code":"CONVERSATION_NOT_FOUND","message":"Conversation not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"MESSAGE_NOT_FOUND":{"summary":"MESSAGE_NOT_FOUND","value":{"error":{"code":"MESSAGE_NOT_FOUND","message":"Message not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"inbox:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false},"post":{"tags":["Messages"],"summary":"Send a message","description":"Sends a message to the fan on OnlyFans: text, free vault media (`previewMediaIds`), or a paid message (`mediaIds` locked behind `priceCents`, optionally with free teasers). **This reaches a real fan and cannot be taken back through the API**: when an automation or an AI assistant composes it, show a person the exact text, media and price first. The answer is `202` with the message; follow it with `GET …/messages/{messageId}` until `delivery.status` is `sent`, `failed`, `unconfirmed` or `not_sent`, and never resend an `unconfirmed` message. Side effects: the conversation moves to `team` (the AI pauses on it) for 12 hours, and the AI's pending follow-ups in it are cancelled.\n\nErrors: `409 SALES_OPTED_OUT` for a price when the fan asked not to be sold to; `400 SEND_REFUSED` when OnlyFans or OnlyX refuses this particular send (the message says why; the same send gets the same answer); `409 CREATOR_NOT_CONNECTED` when her account is not connected; `503 SEND_UNAVAILABLE` when sending is briefly unavailable (retry with the same key); `503 SENDING_DISABLED` when sending through the API is switched off.\n\nLimits: 30 sends per 60 seconds per credential and 300 per hour per creator (all credentials together). An `Idempotency-Key` header is required: a retry with the same key returns the original message with `Idempotent-Replayed: true`, and the fan never gets it twice.\n\n**Scope:** requires `messages:send`.\n\n**Rate limits:** 30 requests per 60 seconds per credential; 300 messages per hour per creator, across all credentials — on top of the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** an `Idempotency-Key` header is required. Retry a failed or timed-out request with the same key: a replay returns the original response with `Idempotent-Replayed: true`, and nothing is done twice.\n\n**Reaches fans:** this operation can reach a real fan or change the live OnlyFans account. Confirm it with a person before calling it on their behalf.","operationId":"sendMessage","parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","maxLength":40,"description":"The conversation id (`cnv_…`), from `GET /v1/conversations`.","title":"Conversationid"},"description":"The conversation id (`cnv_…`), from `GET /v1/conversations`."},{"$ref":"#/components/parameters/IdempotencyKeyRequired"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageRequest"}}}},"responses":{"202":{"description":"Accepted: the message, with its delivery status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"},"example":{"id":"msg_1a2b3c4d5e6f7a8b9c0d","conversationId":"cnv_3d9e7b1a5c2f4e8d6a0b","direction":"out","sender":"team","text":"Made this one just for you 😘","media":[{"id":"4012345678","type":"photo","preview":true},{"id":"4012345679","type":"photo","preview":false}],"paid":{"priceCents":1500,"purchased":false,"purchasedAt":null},"tipCents":null,"createdAt":"2026-09-26T10:02:11.000Z","delivery":{"status":"queued","reason":null}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem. `IDEMPOTENCY_KEY_REQUIRED`: the `Idempotency-Key` header is missing; this operation needs one. `SEND_REFUSED`: OnlyFans or OnlyX refuses this particular send; the message says why, and the same send gets the same answer. Do not retry it unchanged.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"Value error, a paid message needs mediaIds to lock behind the price","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_KEY_REQUIRED":{"summary":"IDEMPOTENCY_KEY_REQUIRED","value":{"error":{"code":"IDEMPOTENCY_KEY_REQUIRED","message":"This request needs an `Idempotency-Key` header: a unique value of 8-64 letters, digits, '-' or '_'.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"SEND_REFUSED":{"summary":"SEND_REFUSED","value":{"error":{"code":"SEND_REFUSED","message":"This fan's subscription lapsed — OnlyFans closed the chat.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `messages:send` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `messages:send` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CONVERSATION_NOT_FOUND`: no conversation with this `conversationId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CONVERSATION_NOT_FOUND":{"summary":"CONVERSATION_NOT_FOUND","value":{"error":{"code":"CONVERSATION_NOT_FOUND","message":"Conversation not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `SALES_OPTED_OUT`: a paid message (a price) to a fan who asked not to be sold to. `CREATOR_NOT_CONNECTED`: her OnlyFans account is not connected. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"SALES_OPTED_OUT":{"summary":"SALES_OPTED_OUT","value":{"error":{"code":"SALES_OPTED_OUT","message":"This fan stopped sales offers. Explicit opt-in is required before a paid offer; ordinary support remains available.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"CREATOR_NOT_CONNECTED":{"summary":"CREATOR_NOT_CONNECTED","value":{"error":{"code":"CREATOR_NOT_CONNECTED","message":"This creator's OnlyFans account is not connected, so nothing can be sent. Reconnect her first.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"description":"Over a rate limit (`RATE_LIMITED`): the general limit, or this operation's own (300 messages per hour per creator, across all credentials). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED (general limit)","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_1":{"summary":"RATE_LIMITED (300 messages per hour per creator, across all credentials)","value":{"error":{"code":"RATE_LIMITED","message":"This creator has reached the limit of 300 API messages per hour. Try again later.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Temporarily unavailable. Retry later. `SEND_UNAVAILABLE`: sending is briefly unavailable for this creator; nothing was sent. Retry later with the same `Idempotency-Key`. `SENDING_DISABLED`: sending through the API is switched off; nothing was sent.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"SEND_UNAVAILABLE":{"summary":"SEND_UNAVAILABLE","value":{"error":{"code":"SEND_UNAVAILABLE","message":"Sending is briefly unavailable for this creator. Nothing was sent. Retry later with the same Idempotency-Key.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"SENDING_DISABLED":{"summary":"SENDING_DISABLED","value":{"error":{"code":"SENDING_DISABLED","message":"Sending to fans through the API is switched off for now. Nothing was sent. Try again later.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}}},"x-required-scope":"messages:send","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":30,"windowSeconds":60,"per":"credential"},{"limit":300,"windowSeconds":3600,"per":"creator"}],"x-idempotency-key":"required","x-reaches-fans":true}},"/v1/conversations/{conversationId}/messages/{messageId}":{"get":{"tags":["Messages"],"summary":"Get a message","description":"Returns one message. Use it to follow a message you sent: poll every few seconds until `delivery.status` leaves `queued`. On `unconfirmed`, never resend automatically: the fan usually received it. A message that is not in this conversation is `404 MESSAGE_NOT_FOUND`.\n\n**Scope:** requires `inbox:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"getMessage","parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","maxLength":40,"description":"The conversation id (`cnv_…`).","title":"Conversationid"},"description":"The conversation id (`cnv_…`)."},{"name":"messageId","in":"path","required":true,"schema":{"type":"string","maxLength":40,"description":"The message id (`msg_…`).","title":"Messageid"},"description":"The message id (`msg_…`)."}],"responses":{"200":{"description":"The message, with its delivery status if it is outgoing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"},"example":{"id":"msg_1a2b3c4d5e6f7a8b9c0d","conversationId":"cnv_3d9e7b1a5c2f4e8d6a0b","direction":"out","sender":"team","text":"Made this one just for you 😘","media":[{"id":"4012345678","type":"photo","preview":true},{"id":"4012345679","type":"photo","preview":false}],"paid":{"priceCents":1500,"purchased":false,"purchasedAt":null},"tipCents":null,"createdAt":"2026-09-26T10:02:11.000Z","delivery":{"status":"queued","reason":null}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"conversationId: String should have at most 40 characters","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `inbox:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `inbox:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CONVERSATION_NOT_FOUND`: no conversation with this `conversationId` is visible to this credential. `MESSAGE_NOT_FOUND`: no message with this id is in this conversation.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CONVERSATION_NOT_FOUND":{"summary":"CONVERSATION_NOT_FOUND","value":{"error":{"code":"CONVERSATION_NOT_FOUND","message":"Conversation not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"MESSAGE_NOT_FOUND":{"summary":"MESSAGE_NOT_FOUND","value":{"error":{"code":"MESSAGE_NOT_FOUND","message":"Message not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"inbox:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false}},"/v1/conversations/{conversationId}/read":{"post":{"tags":["Conversations"],"summary":"Mark a conversation read","description":"Marks every fan message in the conversation as read in OnlyX, so `unreadCount` becomes 0. It changes only OnlyX's inbox state: nothing is sent to the fan, no read receipt is shown, and nothing changes on OnlyFans.\n\n**Scope:** requires `inbox:write`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.","operationId":"markConversationRead","parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","maxLength":40,"description":"The conversation id (`cnv_…`).","title":"Conversationid"},"description":"The conversation id (`cnv_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The updated conversation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Conversation"},"example":{"id":"cnv_3d9e7b1a5c2f4e8d6a0b","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","fan":{"id":"fan_6b2e8d4a1c9f3e7b5d0a","displayName":"Mike","username":null,"avatarUrl":null},"status":"team","unreadCount":2,"lastMessage":{"text":"are you online tonight?","direction":"in","at":"2026-09-25T21:40:12.000Z"},"lastMessageAt":"2026-09-25T21:40:12.000Z","takenOverUntil":"2026-09-26T09:40:12.000Z","totalSpentCents":48500}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"conversationId: String should have at most 40 characters","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `inbox:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `inbox:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CONVERSATION_NOT_FOUND`: no conversation with this `conversationId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CONVERSATION_NOT_FOUND":{"summary":"CONVERSATION_NOT_FOUND","value":{"error":{"code":"CONVERSATION_NOT_FOUND","message":"Conversation not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"inbox:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":false}},"/v1/conversations/{conversationId}/release":{"post":{"tags":["Conversations"],"summary":"Release a conversation to the AI","description":"Gives a conversation your team holds back to the AI now: its status becomes `ai`. **This can reach the fan right away**: if the fan's last message is unanswered, the AI may reply to it within seconds, so confirm with a person before releasing on someone's behalf. If the creator's AI is switched off, the chat stays with your team. Refused with `503 SENDING_DISABLED` while sending through the API is switched off.\n\n**Scope:** requires `inbox:write`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.\n\n**Reaches fans:** this operation can reach a real fan or change the live OnlyFans account. Confirm it with a person before calling it on their behalf.","operationId":"releaseConversation","parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","maxLength":40,"description":"The conversation id (`cnv_…`).","title":"Conversationid"},"description":"The conversation id (`cnv_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The updated conversation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Conversation"},"example":{"id":"cnv_3d9e7b1a5c2f4e8d6a0b","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","fan":{"id":"fan_6b2e8d4a1c9f3e7b5d0a","displayName":"Mike","username":null,"avatarUrl":null},"status":"team","unreadCount":2,"lastMessage":{"text":"are you online tonight?","direction":"in","at":"2026-09-25T21:40:12.000Z"},"lastMessageAt":"2026-09-25T21:40:12.000Z","takenOverUntil":"2026-09-26T09:40:12.000Z","totalSpentCents":48500}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"conversationId: String should have at most 40 characters","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `inbox:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `inbox:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CONVERSATION_NOT_FOUND`: no conversation with this `conversationId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CONVERSATION_NOT_FOUND":{"summary":"CONVERSATION_NOT_FOUND","value":{"error":{"code":"CONVERSATION_NOT_FOUND","message":"Conversation not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Temporarily unavailable. Retry later. `SENDING_DISABLED`: actions that can make the AI answer a fan are switched off for the API. Nothing was changed.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"SENDING_DISABLED":{"summary":"SENDING_DISABLED","value":{"error":{"code":"SENDING_DISABLED","message":"Actions that can make the AI answer a fan are switched off through the API for now. Nothing was changed.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}}},"x-required-scope":"inbox:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":true}},"/v1/conversations/{conversationId}/takeover":{"post":{"tags":["Conversations"],"summary":"Take over a conversation","description":"Gives the conversation to your team: its status becomes `team`, and the AI stops replying and withdraws any reply it had waiting in it until `takenOverUntil`, after which the chat returns to the AI on its own. Use it before your team works a chat, or to keep the AI out of a delicate conversation. `holdMinutes` sets the hold, from 15 minutes to 24 hours (default 12 hours); taking over a chat your team already holds restarts the hold. Nothing is sent to the fan.\n\n**Scope:** requires `inbox:write`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.","operationId":"takeOverConversation","parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","maxLength":40,"description":"The conversation id (`cnv_…`).","title":"Conversationid"},"description":"The conversation id (`cnv_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/TakeoverRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"The updated conversation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Conversation"},"example":{"id":"cnv_3d9e7b1a5c2f4e8d6a0b","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","fan":{"id":"fan_6b2e8d4a1c9f3e7b5d0a","displayName":"Mike","username":null,"avatarUrl":null},"status":"team","unreadCount":2,"lastMessage":{"text":"are you online tonight?","direction":"in","at":"2026-09-25T21:40:12.000Z"},"lastMessageAt":"2026-09-25T21:40:12.000Z","takenOverUntil":"2026-09-26T09:40:12.000Z","totalSpentCents":48500}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"holdMinutes: Input should be less than or equal to 1440.0","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `inbox:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `inbox:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CONVERSATION_NOT_FOUND`: no conversation with this `conversationId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CONVERSATION_NOT_FOUND":{"summary":"CONVERSATION_NOT_FOUND","value":{"error":{"code":"CONVERSATION_NOT_FOUND","message":"Conversation not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"inbox:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":false}},"/v1/conversations/{conversationId}/unread":{"post":{"tags":["Conversations"],"summary":"Mark a conversation unread","description":"Marks the conversation as unread in OnlyX so it shows up again for your team, for example to flag it for follow-up. It changes only OnlyX's inbox state; nothing is sent to the fan.\n\n**Scope:** requires `inbox:write`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.","operationId":"markConversationUnread","parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","maxLength":40,"description":"The conversation id (`cnv_…`).","title":"Conversationid"},"description":"The conversation id (`cnv_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The updated conversation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Conversation"},"example":{"id":"cnv_3d9e7b1a5c2f4e8d6a0b","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","fan":{"id":"fan_6b2e8d4a1c9f3e7b5d0a","displayName":"Mike","username":null,"avatarUrl":null},"status":"team","unreadCount":2,"lastMessage":{"text":"are you online tonight?","direction":"in","at":"2026-09-25T21:40:12.000Z"},"lastMessageAt":"2026-09-25T21:40:12.000Z","takenOverUntil":"2026-09-26T09:40:12.000Z","totalSpentCents":48500}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"conversationId: String should have at most 40 characters","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `inbox:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `inbox:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CONVERSATION_NOT_FOUND`: no conversation with this `conversationId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CONVERSATION_NOT_FOUND":{"summary":"CONVERSATION_NOT_FOUND","value":{"error":{"code":"CONVERSATION_NOT_FOUND","message":"Conversation not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"inbox:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":false}},"/v1/creators":{"get":{"tags":["Creators"],"summary":"List creators","description":"Lists every creator this credential can see, oldest first, with the connection status, the AI switch and headline stats (fans, conversations, unread conversations, open hand-offs, earnings). A key limited to some creators lists only those. Page through with `limit` (up to 100) and the `nextCursor` of the previous page. Use the ids in every creator-scoped call; never guess them.\n\n**Scope:** requires `creators:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"listCreators","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Page size, 1-100. Default 25.","default":25,"title":"Limit"},"description":"Page size, 1-100. Default 25."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"description":"Opaque cursor from the previous page's `nextCursor`. Omit for the first page.","title":"Cursor"},"description":"Opaque cursor from the previous page's `nextCursor`. Omit for the first page."}],"responses":{"200":{"description":"A page of creators, oldest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatorPage"},"example":{"data":[{"id":"cre_8f2c1a9b0d7e4c3f2a1b","displayName":"Mia Rose","handle":"miarose","publicName":"Mia","avatarUrl":"https://media.example.com/avatars/miarose.jpg","platform":"onlyfans","aiEnabled":true,"connection":{"status":"connected","connected":true},"onlyfans":{"userId":"412345678","verified":true,"subscribePriceCents":999,"postsCount":812,"photosCount":1540,"videosCount":230},"stats":{"fans":2841,"conversations":1912,"unreadConversations":14,"openHandoffs":2,"revenueCents":1284350,"pendingCents":96420,"revenueKnown":true},"contentConsentRequired":false,"createdAt":"2026-03-02T09:15:44.000Z"}],"hasMore":true,"nextCursor":"eyJrIjoiY3JlYXRvcnMiLCJwIjp7ImkiOiJjcmVfOGYyYzFhOWIwZDdlNGMzZjJhMWIifX0.Yp3k0Qm7T2vX9aLc4R8sWg"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"limit: Input should be less than or equal to 100","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `creators:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `creators:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"creators:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false},"post":{"tags":["Creators"],"summary":"Add a creator","description":"Adds an OnlyFans creator to your workspace. She starts `disconnected`: next, create a connect link (`POST /v1/creators/{creatorId}/connect-link`) and send it to her so she can sign in to OnlyFans herself. The API never takes her OnlyFans password or codes. Only OnlyFans creators can be added in v1 (Telegram is coming soon). An `Idempotency-Key` header is required: retry a failed or timed-out request with the same key and you get the original answer instead of a second creator. A workspace can add at most 10 creators per 24 hours through the API (`429 RATE_LIMITED` beyond that). If OnlyX cannot take a new creator right now the answer is `409 CAPACITY_UNAVAILABLE` (or `503 CAPACITY_UNAVAILABLE` with `Retry-After` while the sign-in service is briefly unreachable): retry in a few minutes with the same key. A handle that already exists in the workspace is `409 CONFLICT`. A credential limited to some creators cannot add creators (`403 INSUFFICIENT_SCOPE`).\n\n**Scope:** requires `creators:write`.\n\n**Rate limits:** 10 creators per 24 hours per workspace — on top of the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** an `Idempotency-Key` header is required. Retry a failed or timed-out request with the same key: a replay returns the original response with `Idempotent-Replayed: true`, and nothing is done twice.","operationId":"createCreator","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCreatorRequest"}}}},"responses":{"201":{"description":"The new creator, `disconnected`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Creator"},"example":{"id":"cre_8f2c1a9b0d7e4c3f2a1b","displayName":"Mia Rose","handle":"miarose","publicName":null,"avatarUrl":null,"platform":"onlyfans","aiEnabled":true,"connection":{"status":"disconnected","connected":false},"onlyfans":null,"stats":{"fans":0,"conversations":0,"unreadConversations":0,"openHandoffs":0,"revenueCents":0,"pendingCents":0,"revenueKnown":false},"contentConsentRequired":null,"createdAt":"2026-09-26T14:02:31.000Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem. `IDEMPOTENCY_KEY_REQUIRED`: the `Idempotency-Key` header is missing; this operation needs one.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"displayName: Field required","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_KEY_REQUIRED":{"summary":"IDEMPOTENCY_KEY_REQUIRED","value":{"error":{"code":"IDEMPOTENCY_KEY_REQUIRED","message":"This request needs an `Idempotency-Key` header: a unique value of 8-64 letters, digits, '-' or '_'.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `creators:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended. `INSUFFICIENT_SCOPE`: the credential is limited to some creators; only a credential with access to every creator can add one.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `creators:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"INSUFFICIENT_SCOPE_RESTRICTED_KEY":{"summary":"INSUFFICIENT_SCOPE (key limited to some creators)","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This credential is limited to some creators, so it cannot add new ones. Use a credential with access to every creator.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `CAPACITY_UNAVAILABLE`: OnlyX cannot take a new creator right now. Retry in a few minutes with the same `Idempotency-Key`. `CONFLICT`: a creator with this handle already exists in the workspace. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CAPACITY_UNAVAILABLE":{"summary":"CAPACITY_UNAVAILABLE","value":{"error":{"code":"CAPACITY_UNAVAILABLE","message":"We can't add a creator right now. Try again in a few minutes.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"CONFLICT":{"summary":"CONFLICT","value":{"error":{"code":"CONFLICT","message":"A creator with this handle already exists in this workspace.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"description":"Over a rate limit (`RATE_LIMITED`): the general limit, or this operation's own (10 creators per 24 hours per workspace). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED (general limit)","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_1":{"summary":"RATE_LIMITED (10 creators per 24 hours per workspace)","value":{"error":{"code":"RATE_LIMITED","message":"This workspace has added 10 creators through the API in the last 24 hours. Try again later.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Temporarily unavailable. Retry later. `CAPACITY_UNAVAILABLE`: the sign-in service is briefly unreachable. Wait `Retry-After` seconds and retry with the same `Idempotency-Key`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CAPACITY_UNAVAILABLE":{"summary":"CAPACITY_UNAVAILABLE","value":{"error":{"code":"CAPACITY_UNAVAILABLE","message":"We can't add a creator right now. Try again in a few minutes.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}}},"x-required-scope":"creators:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":10,"windowSeconds":86400,"per":"workspace"}],"x-idempotency-key":"required","x-reaches-fans":false,"parameters":[{"$ref":"#/components/parameters/IdempotencyKeyRequired"}]}},"/v1/creators/{creatorId}":{"get":{"tags":["Creators"],"summary":"Get a creator","description":"Returns one creator plus `readiness`: whether she is set up well enough for the AI chatter to chat and sell (OnlyFans connected, persona, hard limits, fans synced, content mapped), with a hint for each check that fails. Use it after onboarding to see what is still missing. Reading a creator never changes anything. An id from another workspace, or one this credential is not allowed to see, is `404 CREATOR_NOT_FOUND`, exactly like an id that does not exist.\n\n**Scope:** requires `creators:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"getCreator","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."}],"responses":{"200":{"description":"The creator, with the readiness checks for the AI chatter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatorDetail"},"example":{"id":"cre_8f2c1a9b0d7e4c3f2a1b","displayName":"Mia Rose","handle":"miarose","publicName":"Mia","avatarUrl":"https://media.example.com/avatars/miarose.jpg","platform":"onlyfans","aiEnabled":true,"connection":{"status":"connected","connected":true},"onlyfans":{"userId":"412345678","verified":true,"subscribePriceCents":999,"postsCount":812,"photosCount":1540,"videosCount":230},"stats":{"fans":2841,"conversations":1912,"unreadConversations":14,"openHandoffs":2,"revenueCents":1284350,"pendingCents":96420,"revenueKnown":true},"contentConsentRequired":false,"createdAt":"2026-03-02T09:15:44.000Z","readiness":{"ready":false,"checks":[{"key":"connection","label":"OnlyFans connected","ok":true,"hint":null},{"key":"persona","label":"Persona","ok":true,"hint":null},{"key":"limits","label":"Hard limits","ok":false,"hint":"Add the things she never does."},{"key":"fans","label":"Fans synced","ok":true,"hint":null},{"key":"content","label":"Content mapped","ok":false,"hint":"Add a priced level with media to her AI content."}]}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"The request could not be read.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `creators:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `creators:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"creators:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false},"patch":{"tags":["Creators"],"summary":"Update a creator","description":"Renames a creator (`displayName`) or switches her AI chatter on or off (`aiEnabled`). Send only the fields you want to change; any other field, including the connection status, is refused with `400 VALIDATION_ERROR` because it can never be written. Turning `aiEnabled` on reaches real fans: the AI starts answering every fan who is waiting in her AI chats, usually within seconds, so confirm with a person first. Turning it off stops all AI replies and follow-ups for her.\n\n**Scope:** requires `creators:write`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.\n\n**Reaches fans:** this operation can reach a real fan or change the live OnlyFans account. Confirm it with a person before calling it on their behalf.","operationId":"updateCreator","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCreatorRequest"}}}},"responses":{"200":{"description":"The updated creator.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Creator"},"example":{"id":"cre_8f2c1a9b0d7e4c3f2a1b","displayName":"Mia Rose","handle":"miarose","publicName":"Mia","avatarUrl":"https://media.example.com/avatars/miarose.jpg","platform":"onlyfans","aiEnabled":true,"connection":{"status":"connected","connected":true},"onlyfans":{"userId":"412345678","verified":true,"subscribePriceCents":999,"postsCount":812,"photosCount":1540,"videosCount":230},"stats":{"fans":2841,"conversations":1912,"unreadConversations":14,"openHandoffs":2,"revenueCents":1284350,"pendingCents":96420,"revenueKnown":true},"contentConsentRequired":false,"createdAt":"2026-03-02T09:15:44.000Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem. `VALIDATION_ERROR`: the body is empty, or names a field that cannot be written.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"connectionStatus: Extra inputs are not permitted","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"VALIDATION_ERROR_EMPTY_BODY":{"summary":"VALIDATION_ERROR (empty body)","value":{"error":{"code":"VALIDATION_ERROR","message":"body: send `displayName`, `aiEnabled` or both.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `creators:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `creators:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"creators:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":true}},"/v1/creators/{creatorId}/ai-content":{"get":{"tags":["AI content"],"summary":"Get the AI content board","description":"Returns everything the AI chatter can sell for this creator: folders, ladders (collections), their priced levels (sets) with preview and paid media, and how much of the vault is used. Each level's `stats` are AI-attributed: they count what the AI chatter offered and sold, not your team's sales. Read it before changing anything, and after every change to verify prices: a level with `priceCents: 0` is free and gives its media away.\n\n**Scope:** requires `ai:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"getAiContent","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."}],"responses":{"200":{"description":"The creator's folders, ladders, levels with their media, and vault usage.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentBoard"},"example":{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","folders":[{"id":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Sexting","position":1,"collectionCount":1}],"collections":[{"id":"col_2b3c4d5e6f7a8b9c0d1e","folderId":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Shower set","mode":"sequential","description":"A slow tease that ends in the shower.","active":true,"position":1,"levelCount":2}],"levels":[{"id":"set_3c4d5e6f7a8b9c0d1e2f","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Opener","position":1,"priceCents":0,"free":true,"tags":["Solo","Teasing"],"highlight":"the one where I get ready","description":"Two selfies in a towel.","availableInPaidMessages":true,"active":true,"stats":{"sent":40,"purchased":0,"revenueCents":0},"preview":[{"id":"4498297883","type":"photo","durationSeconds":null,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297883/thumbnail"}],"paid":[]},{"id":"set_4d5e6f7a8b9c0d1e2f3a","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Shower video","position":2,"priceCents":1500,"free":false,"tags":["Shower"],"highlight":null,"description":"Four-minute shower video.","availableInPaidMessages":true,"active":true,"stats":{"sent":31,"purchased":9,"revenueCents":13500},"preview":[],"paid":[{"id":"4498297884","type":"video","durationSeconds":241,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297884/thumbnail"}]}],"vault":{"total":512,"assigned":3,"unassigned":509}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"The request could not be read.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `ai:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `ai:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"ai:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false}},"/v1/creators/{creatorId}/ai-content/collections":{"post":{"tags":["AI content"],"summary":"Create a ladder","description":"Creates a ladder (collection): a themed series of levels the AI sells in order (`sequential`, the default) or whenever they fit (`standard`). Create it with `active: false`, add its levels and media, verify, then switch it on, so the AI never offers a half-built ladder. `folderId` must be a folder of the same creator (`404` otherwise). Returns the whole updated content board. Answers `201` with the whole board. Nothing is sent to fans: the AI uses active ladders in its own conversations. AI content writes are limited to 60 a minute per credential.\n\n**Scope:** requires `ai:write`.\n\n**Rate limits:** 60 changes per 60 seconds per credential — on top of the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.","operationId":"createContentLadder","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCollectionRequest"}}}},"responses":{"201":{"description":"Created: the whole content board after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentBoard"},"example":{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","folders":[{"id":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Sexting","position":1,"collectionCount":1}],"collections":[{"id":"col_2b3c4d5e6f7a8b9c0d1e","folderId":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Shower set","mode":"sequential","description":"A slow tease that ends in the shower.","active":true,"position":1,"levelCount":2}],"levels":[{"id":"set_3c4d5e6f7a8b9c0d1e2f","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Opener","position":1,"priceCents":0,"free":true,"tags":["Solo","Teasing"],"highlight":"the one where I get ready","description":"Two selfies in a towel.","availableInPaidMessages":true,"active":true,"stats":{"sent":40,"purchased":0,"revenueCents":0},"preview":[{"id":"4498297883","type":"photo","durationSeconds":null,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297883/thumbnail"}],"paid":[]},{"id":"set_4d5e6f7a8b9c0d1e2f3a","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Shower video","position":2,"priceCents":1500,"free":false,"tags":["Shower"],"highlight":null,"description":"Four-minute shower video.","availableInPaidMessages":true,"active":true,"stats":{"sent":31,"purchased":9,"revenueCents":13500},"preview":[],"paid":[{"id":"4498297884","type":"video","durationSeconds":241,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297884/thumbnail"}]}],"vault":{"total":512,"assigned":3,"unassigned":509}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"name: Field required","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `ai:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `ai:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential. `NOT_FOUND`: no folder with this id belongs to this creator.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"NOT_FOUND_FOLDER":{"summary":"NOT_FOUND (folder)","value":{"error":{"code":"NOT_FOUND","message":"Not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"description":"Over a rate limit (`RATE_LIMITED`): the general limit, or this operation's own (60 changes per 60 seconds per credential). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED (general limit)","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_1":{"summary":"RATE_LIMITED (60 changes per 60 seconds per credential)","value":{"error":{"code":"RATE_LIMITED","message":"Too many AI content changes: at most 60 a minute per credential. Try again shortly.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"ai:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":60,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":false}},"/v1/creators/{creatorId}/ai-content/collections/{collectionId}":{"patch":{"tags":["AI content"],"summary":"Update a ladder","description":"Changes a ladder's name, folder, mode, description or `active` switch; send only what changes. `active: true` puts a finished ladder in front of the AI and `active: false` pauses all its levels at once. `folderId: null` takes it out of its folder. Returns the whole updated content board. Nothing is sent to fans: the AI uses active ladders in its own conversations. AI content writes are limited to 60 a minute per credential.\n\n**Scope:** requires `ai:write`.\n\n**Rate limits:** 60 changes per 60 seconds per credential — on top of the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.","operationId":"updateContentLadder","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"name":"collectionId","in":"path","required":true,"schema":{"type":"string","description":"The ladder (collection) id (`col_…`).","title":"Collectionid"},"description":"The ladder (collection) id (`col_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCollectionRequest"}}}},"responses":{"200":{"description":"The whole content board after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentBoard"},"example":{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","folders":[{"id":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Sexting","position":1,"collectionCount":1}],"collections":[{"id":"col_2b3c4d5e6f7a8b9c0d1e","folderId":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Shower set","mode":"sequential","description":"A slow tease that ends in the shower.","active":true,"position":1,"levelCount":2}],"levels":[{"id":"set_3c4d5e6f7a8b9c0d1e2f","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Opener","position":1,"priceCents":0,"free":true,"tags":["Solo","Teasing"],"highlight":"the one where I get ready","description":"Two selfies in a towel.","availableInPaidMessages":true,"active":true,"stats":{"sent":40,"purchased":0,"revenueCents":0},"preview":[{"id":"4498297883","type":"photo","durationSeconds":null,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297883/thumbnail"}],"paid":[]},{"id":"set_4d5e6f7a8b9c0d1e2f3a","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Shower video","position":2,"priceCents":1500,"free":false,"tags":["Shower"],"highlight":null,"description":"Four-minute shower video.","availableInPaidMessages":true,"active":true,"stats":{"sent":31,"purchased":9,"revenueCents":13500},"preview":[],"paid":[{"id":"4498297884","type":"video","durationSeconds":241,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297884/thumbnail"}]}],"vault":{"total":512,"assigned":3,"unassigned":509}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"name: String should have at most 120 characters","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `ai:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `ai:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential. `NOT_FOUND`: no ladder with this id belongs to this creator. `NOT_FOUND`: no folder with this id belongs to this creator.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"NOT_FOUND_LADDER":{"summary":"NOT_FOUND (ladder)","value":{"error":{"code":"NOT_FOUND","message":"Not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"NOT_FOUND_FOLDER":{"summary":"NOT_FOUND (folder)","value":{"error":{"code":"NOT_FOUND","message":"Not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"description":"Over a rate limit (`RATE_LIMITED`): the general limit, or this operation's own (60 changes per 60 seconds per credential). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED (general limit)","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_1":{"summary":"RATE_LIMITED (60 changes per 60 seconds per credential)","value":{"error":{"code":"RATE_LIMITED","message":"Too many AI content changes: at most 60 a minute per credential. Try again shortly.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"ai:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":60,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":false},"delete":{"tags":["AI content"],"summary":"Delete a ladder","description":"Deletes a ladder together with all its levels and their media placements. The media stays in her vault. The AI stops offering it at once; to stop selling it temporarily, set `active: false` instead. Returns the whole updated content board. Nothing is sent to fans: the AI uses active ladders in its own conversations. AI content writes are limited to 60 a minute per credential.\n\n**Scope:** requires `ai:write`.\n\n**Rate limits:** 60 changes per 60 seconds per credential — on top of the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.","operationId":"deleteContentLadder","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"name":"collectionId","in":"path","required":true,"schema":{"type":"string","description":"The ladder (collection) id (`col_…`).","title":"Collectionid"},"description":"The ladder (collection) id (`col_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The whole content board after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentBoard"},"example":{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","folders":[{"id":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Sexting","position":1,"collectionCount":1}],"collections":[{"id":"col_2b3c4d5e6f7a8b9c0d1e","folderId":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Shower set","mode":"sequential","description":"A slow tease that ends in the shower.","active":true,"position":1,"levelCount":2}],"levels":[{"id":"set_3c4d5e6f7a8b9c0d1e2f","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Opener","position":1,"priceCents":0,"free":true,"tags":["Solo","Teasing"],"highlight":"the one where I get ready","description":"Two selfies in a towel.","availableInPaidMessages":true,"active":true,"stats":{"sent":40,"purchased":0,"revenueCents":0},"preview":[{"id":"4498297883","type":"photo","durationSeconds":null,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297883/thumbnail"}],"paid":[]},{"id":"set_4d5e6f7a8b9c0d1e2f3a","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Shower video","position":2,"priceCents":1500,"free":false,"tags":["Shower"],"highlight":null,"description":"Four-minute shower video.","availableInPaidMessages":true,"active":true,"stats":{"sent":31,"purchased":9,"revenueCents":13500},"preview":[],"paid":[{"id":"4498297884","type":"video","durationSeconds":241,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297884/thumbnail"}]}],"vault":{"total":512,"assigned":3,"unassigned":509}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"Idempotency-Key: must be 8-64 characters of letters, digits, '-' or '_'.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `ai:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `ai:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential. `NOT_FOUND`: no ladder with this id belongs to this creator.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"NOT_FOUND_LADDER":{"summary":"NOT_FOUND (ladder)","value":{"error":{"code":"NOT_FOUND","message":"Not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"description":"Over a rate limit (`RATE_LIMITED`): the general limit, or this operation's own (60 changes per 60 seconds per credential). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED (general limit)","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_1":{"summary":"RATE_LIMITED (60 changes per 60 seconds per credential)","value":{"error":{"code":"RATE_LIMITED","message":"Too many AI content changes: at most 60 a minute per credential. Try again shortly.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"ai:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":60,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":false}},"/v1/creators/{creatorId}/ai-content/collections/{collectionId}/order":{"put":{"tags":["AI content"],"summary":"Reorder a ladder's levels","description":"Sets the order of a ladder's levels. In a sequential ladder this is the climb every fan follows: the AI always offers a fan the first level they have not bought. Levels you list come first, in your order; any you leave out keep their order after them, and an id that is not a level of this ladder is a `400`. Returns the whole updated content board. Nothing is sent to fans: the AI uses active ladders in its own conversations. AI content writes are limited to 60 a minute per credential.\n\n**Scope:** requires `ai:write`.\n\n**Rate limits:** 60 changes per 60 seconds per credential — on top of the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.","operationId":"reorderContentLevels","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"name":"collectionId","in":"path","required":true,"schema":{"type":"string","description":"The ladder (collection) id (`col_…`).","title":"Collectionid"},"description":"The ladder (collection) id (`col_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReorderLevelsRequest"}}}},"responses":{"200":{"description":"The whole content board after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentBoard"},"example":{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","folders":[{"id":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Sexting","position":1,"collectionCount":1}],"collections":[{"id":"col_2b3c4d5e6f7a8b9c0d1e","folderId":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Shower set","mode":"sequential","description":"A slow tease that ends in the shower.","active":true,"position":1,"levelCount":2}],"levels":[{"id":"set_3c4d5e6f7a8b9c0d1e2f","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Opener","position":1,"priceCents":0,"free":true,"tags":["Solo","Teasing"],"highlight":"the one where I get ready","description":"Two selfies in a towel.","availableInPaidMessages":true,"active":true,"stats":{"sent":40,"purchased":0,"revenueCents":0},"preview":[{"id":"4498297883","type":"photo","durationSeconds":null,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297883/thumbnail"}],"paid":[]},{"id":"set_4d5e6f7a8b9c0d1e2f3a","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Shower video","position":2,"priceCents":1500,"free":false,"tags":["Shower"],"highlight":null,"description":"Four-minute shower video.","availableInPaidMessages":true,"active":true,"stats":{"sent":31,"purchased":9,"revenueCents":13500},"preview":[],"paid":[{"id":"4498297884","type":"video","durationSeconds":241,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297884/thumbnail"}]}],"vault":{"total":512,"assigned":3,"unassigned":509}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem. `VALIDATION_ERROR`: an id in `levelIds` is not a level of this ladder.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"levelIds: Field required","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"VALIDATION_ERROR_UNKNOWN_LEVEL":{"summary":"VALIDATION_ERROR (not a level of this ladder)","value":{"error":{"code":"VALIDATION_ERROR","message":"levelIds: item 2 is not a level of this ladder","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `ai:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `ai:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential. `NOT_FOUND`: no ladder with this id belongs to this creator.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"NOT_FOUND_LADDER":{"summary":"NOT_FOUND (ladder)","value":{"error":{"code":"NOT_FOUND","message":"Not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"description":"Over a rate limit (`RATE_LIMITED`): the general limit, or this operation's own (60 changes per 60 seconds per credential). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED (general limit)","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_1":{"summary":"RATE_LIMITED (60 changes per 60 seconds per credential)","value":{"error":{"code":"RATE_LIMITED","message":"Too many AI content changes: at most 60 a minute per credential. Try again shortly.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"ai:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":60,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":false}},"/v1/creators/{creatorId}/ai-content/folders":{"post":{"tags":["AI content"],"summary":"Create a folder","description":"Creates a folder to organize ladders on the board. Folders are only for people; the AI ignores them. Returns the whole updated content board; find the new folder in `folders`. Answers `201` with the whole board. Nothing is sent to fans: the AI uses active ladders in its own conversations. AI content writes are limited to 60 a minute per credential.\n\n**Scope:** requires `ai:write`.\n\n**Rate limits:** 60 changes per 60 seconds per credential — on top of the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.","operationId":"createContentFolder","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFolderRequest"}}}},"responses":{"201":{"description":"Created: the whole content board after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentBoard"},"example":{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","folders":[{"id":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Sexting","position":1,"collectionCount":1}],"collections":[{"id":"col_2b3c4d5e6f7a8b9c0d1e","folderId":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Shower set","mode":"sequential","description":"A slow tease that ends in the shower.","active":true,"position":1,"levelCount":2}],"levels":[{"id":"set_3c4d5e6f7a8b9c0d1e2f","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Opener","position":1,"priceCents":0,"free":true,"tags":["Solo","Teasing"],"highlight":"the one where I get ready","description":"Two selfies in a towel.","availableInPaidMessages":true,"active":true,"stats":{"sent":40,"purchased":0,"revenueCents":0},"preview":[{"id":"4498297883","type":"photo","durationSeconds":null,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297883/thumbnail"}],"paid":[]},{"id":"set_4d5e6f7a8b9c0d1e2f3a","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Shower video","position":2,"priceCents":1500,"free":false,"tags":["Shower"],"highlight":null,"description":"Four-minute shower video.","availableInPaidMessages":true,"active":true,"stats":{"sent":31,"purchased":9,"revenueCents":13500},"preview":[],"paid":[{"id":"4498297884","type":"video","durationSeconds":241,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297884/thumbnail"}]}],"vault":{"total":512,"assigned":3,"unassigned":509}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"name: Field required","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `ai:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `ai:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"description":"Over a rate limit (`RATE_LIMITED`): the general limit, or this operation's own (60 changes per 60 seconds per credential). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED (general limit)","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_1":{"summary":"RATE_LIMITED (60 changes per 60 seconds per credential)","value":{"error":{"code":"RATE_LIMITED","message":"Too many AI content changes: at most 60 a minute per credential. Try again shortly.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"ai:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":60,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":false}},"/v1/creators/{creatorId}/ai-content/folders/{folderId}":{"patch":{"tags":["AI content"],"summary":"Rename a folder","description":"Renames a folder. Returns the whole updated content board. Nothing is sent to fans: the AI uses active ladders in its own conversations. AI content writes are limited to 60 a minute per credential.\n\n**Scope:** requires `ai:write`.\n\n**Rate limits:** 60 changes per 60 seconds per credential — on top of the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.","operationId":"updateContentFolder","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"name":"folderId","in":"path","required":true,"schema":{"type":"string","description":"The folder id (`fld_…`).","title":"Folderid"},"description":"The folder id (`fld_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFolderRequest"}}}},"responses":{"200":{"description":"The whole content board after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentBoard"},"example":{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","folders":[{"id":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Sexting","position":1,"collectionCount":1}],"collections":[{"id":"col_2b3c4d5e6f7a8b9c0d1e","folderId":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Shower set","mode":"sequential","description":"A slow tease that ends in the shower.","active":true,"position":1,"levelCount":2}],"levels":[{"id":"set_3c4d5e6f7a8b9c0d1e2f","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Opener","position":1,"priceCents":0,"free":true,"tags":["Solo","Teasing"],"highlight":"the one where I get ready","description":"Two selfies in a towel.","availableInPaidMessages":true,"active":true,"stats":{"sent":40,"purchased":0,"revenueCents":0},"preview":[{"id":"4498297883","type":"photo","durationSeconds":null,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297883/thumbnail"}],"paid":[]},{"id":"set_4d5e6f7a8b9c0d1e2f3a","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Shower video","position":2,"priceCents":1500,"free":false,"tags":["Shower"],"highlight":null,"description":"Four-minute shower video.","availableInPaidMessages":true,"active":true,"stats":{"sent":31,"purchased":9,"revenueCents":13500},"preview":[],"paid":[{"id":"4498297884","type":"video","durationSeconds":241,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297884/thumbnail"}]}],"vault":{"total":512,"assigned":3,"unassigned":509}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"name: Field required","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `ai:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `ai:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential. `NOT_FOUND`: no folder with this id belongs to this creator.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"NOT_FOUND_FOLDER":{"summary":"NOT_FOUND (folder)","value":{"error":{"code":"NOT_FOUND","message":"Not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"description":"Over a rate limit (`RATE_LIMITED`): the general limit, or this operation's own (60 changes per 60 seconds per credential). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED (general limit)","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_1":{"summary":"RATE_LIMITED (60 changes per 60 seconds per credential)","value":{"error":{"code":"RATE_LIMITED","message":"Too many AI content changes: at most 60 a minute per credential. Try again shortly.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"ai:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":60,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":false},"delete":{"tags":["AI content"],"summary":"Delete a folder","description":"Deletes a folder. Its ladders are kept and simply become unfiled; nothing the AI sells changes. Returns the whole updated content board. Nothing is sent to fans: the AI uses active ladders in its own conversations. AI content writes are limited to 60 a minute per credential.\n\n**Scope:** requires `ai:write`.\n\n**Rate limits:** 60 changes per 60 seconds per credential — on top of the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.","operationId":"deleteContentFolder","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"name":"folderId","in":"path","required":true,"schema":{"type":"string","description":"The folder id (`fld_…`).","title":"Folderid"},"description":"The folder id (`fld_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The whole content board after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentBoard"},"example":{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","folders":[{"id":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Sexting","position":1,"collectionCount":1}],"collections":[{"id":"col_2b3c4d5e6f7a8b9c0d1e","folderId":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Shower set","mode":"sequential","description":"A slow tease that ends in the shower.","active":true,"position":1,"levelCount":2}],"levels":[{"id":"set_3c4d5e6f7a8b9c0d1e2f","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Opener","position":1,"priceCents":0,"free":true,"tags":["Solo","Teasing"],"highlight":"the one where I get ready","description":"Two selfies in a towel.","availableInPaidMessages":true,"active":true,"stats":{"sent":40,"purchased":0,"revenueCents":0},"preview":[{"id":"4498297883","type":"photo","durationSeconds":null,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297883/thumbnail"}],"paid":[]},{"id":"set_4d5e6f7a8b9c0d1e2f3a","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Shower video","position":2,"priceCents":1500,"free":false,"tags":["Shower"],"highlight":null,"description":"Four-minute shower video.","availableInPaidMessages":true,"active":true,"stats":{"sent":31,"purchased":9,"revenueCents":13500},"preview":[],"paid":[{"id":"4498297884","type":"video","durationSeconds":241,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297884/thumbnail"}]}],"vault":{"total":512,"assigned":3,"unassigned":509}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"Idempotency-Key: must be 8-64 characters of letters, digits, '-' or '_'.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `ai:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `ai:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential. `NOT_FOUND`: no folder with this id belongs to this creator.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"NOT_FOUND_FOLDER":{"summary":"NOT_FOUND (folder)","value":{"error":{"code":"NOT_FOUND","message":"Not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"description":"Over a rate limit (`RATE_LIMITED`): the general limit, or this operation's own (60 changes per 60 seconds per credential). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED (general limit)","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_1":{"summary":"RATE_LIMITED (60 changes per 60 seconds per credential)","value":{"error":{"code":"RATE_LIMITED","message":"Too many AI content changes: at most 60 a minute per credential. Try again shortly.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"ai:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":60,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":false}},"/v1/creators/{creatorId}/ai-content/levels":{"post":{"tags":["AI content"],"summary":"Create a level","description":"Adds a level (set) at the end of a ladder. `priceCents` is required: it is the floor price the AI may sell above but never below, and `0` makes a free level whose media is given away to every fan who reaches it (right for an opener or a thank-you, expensive by mistake). Every field you send is applied on create. Add media with `POST …/levels/{levelId}/media`. Returns the whole updated content board. Answers `201` with the whole board. Nothing is sent to fans: the AI uses active ladders in its own conversations. AI content writes are limited to 60 a minute per credential.\n\n**Scope:** requires `ai:write`.\n\n**Rate limits:** 60 changes per 60 seconds per credential — on top of the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.","operationId":"createContentLevel","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLevelRequest"}}}},"responses":{"201":{"description":"Created: the whole content board after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentBoard"},"example":{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","folders":[{"id":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Sexting","position":1,"collectionCount":1}],"collections":[{"id":"col_2b3c4d5e6f7a8b9c0d1e","folderId":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Shower set","mode":"sequential","description":"A slow tease that ends in the shower.","active":true,"position":1,"levelCount":2}],"levels":[{"id":"set_3c4d5e6f7a8b9c0d1e2f","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Opener","position":1,"priceCents":0,"free":true,"tags":["Solo","Teasing"],"highlight":"the one where I get ready","description":"Two selfies in a towel.","availableInPaidMessages":true,"active":true,"stats":{"sent":40,"purchased":0,"revenueCents":0},"preview":[{"id":"4498297883","type":"photo","durationSeconds":null,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297883/thumbnail"}],"paid":[]},{"id":"set_4d5e6f7a8b9c0d1e2f3a","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Shower video","position":2,"priceCents":1500,"free":false,"tags":["Shower"],"highlight":null,"description":"Four-minute shower video.","availableInPaidMessages":true,"active":true,"stats":{"sent":31,"purchased":9,"revenueCents":13500},"preview":[],"paid":[{"id":"4498297884","type":"video","durationSeconds":241,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297884/thumbnail"}]}],"vault":{"total":512,"assigned":3,"unassigned":509}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"collectionId: Field required","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `ai:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `ai:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential. `NOT_FOUND`: no ladder with this id belongs to this creator.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"NOT_FOUND_LADDER":{"summary":"NOT_FOUND (ladder)","value":{"error":{"code":"NOT_FOUND","message":"Not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"description":"Over a rate limit (`RATE_LIMITED`): the general limit, or this operation's own (60 changes per 60 seconds per credential). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED (general limit)","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_1":{"summary":"RATE_LIMITED (60 changes per 60 seconds per credential)","value":{"error":{"code":"RATE_LIMITED","message":"Too many AI content changes: at most 60 a minute per credential. Try again shortly.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"ai:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":60,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":false}},"/v1/creators/{creatorId}/ai-content/levels/{levelId}":{"patch":{"tags":["AI content"],"summary":"Update a level","description":"Changes a level; send only what changes. Changing `priceCents` between 0 and a price converts its media: free makes everything preview, priced makes everything paid (add the teaser back as preview afterwards). `active: false` pauses the level and `availableInPaidMessages: false` keeps it on the board but out of chats. Returns the whole updated content board. Nothing is sent to fans: the AI uses active ladders in its own conversations. AI content writes are limited to 60 a minute per credential.\n\n**Scope:** requires `ai:write`.\n\n**Rate limits:** 60 changes per 60 seconds per credential — on top of the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.","operationId":"updateContentLevel","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"name":"levelId","in":"path","required":true,"schema":{"type":"string","description":"The level (set) id (`set_…`).","title":"Levelid"},"description":"The level (set) id (`set_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLevelRequest"}}}},"responses":{"200":{"description":"The whole content board after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentBoard"},"example":{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","folders":[{"id":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Sexting","position":1,"collectionCount":1}],"collections":[{"id":"col_2b3c4d5e6f7a8b9c0d1e","folderId":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Shower set","mode":"sequential","description":"A slow tease that ends in the shower.","active":true,"position":1,"levelCount":2}],"levels":[{"id":"set_3c4d5e6f7a8b9c0d1e2f","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Opener","position":1,"priceCents":0,"free":true,"tags":["Solo","Teasing"],"highlight":"the one where I get ready","description":"Two selfies in a towel.","availableInPaidMessages":true,"active":true,"stats":{"sent":40,"purchased":0,"revenueCents":0},"preview":[{"id":"4498297883","type":"photo","durationSeconds":null,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297883/thumbnail"}],"paid":[]},{"id":"set_4d5e6f7a8b9c0d1e2f3a","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Shower video","position":2,"priceCents":1500,"free":false,"tags":["Shower"],"highlight":null,"description":"Four-minute shower video.","availableInPaidMessages":true,"active":true,"stats":{"sent":31,"purchased":9,"revenueCents":13500},"preview":[],"paid":[{"id":"4498297884","type":"video","durationSeconds":241,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297884/thumbnail"}]}],"vault":{"total":512,"assigned":3,"unassigned":509}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"name: String should have at most 120 characters","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `ai:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `ai:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential. `NOT_FOUND`: no level with this id belongs to this creator.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"NOT_FOUND_LEVEL":{"summary":"NOT_FOUND (level)","value":{"error":{"code":"NOT_FOUND","message":"Not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"description":"Over a rate limit (`RATE_LIMITED`): the general limit, or this operation's own (60 changes per 60 seconds per credential). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED (general limit)","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_1":{"summary":"RATE_LIMITED (60 changes per 60 seconds per credential)","value":{"error":{"code":"RATE_LIMITED","message":"Too many AI content changes: at most 60 a minute per credential. Try again shortly.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"ai:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":60,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":false},"delete":{"tags":["AI content"],"summary":"Delete a level","description":"Deletes a level and renumbers the levels after it. Its media stays in her vault, unplaced. Returns the whole updated content board. Nothing is sent to fans: the AI uses active ladders in its own conversations. AI content writes are limited to 60 a minute per credential.\n\n**Scope:** requires `ai:write`.\n\n**Rate limits:** 60 changes per 60 seconds per credential — on top of the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.","operationId":"deleteContentLevel","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"name":"levelId","in":"path","required":true,"schema":{"type":"string","description":"The level (set) id (`set_…`).","title":"Levelid"},"description":"The level (set) id (`set_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The whole content board after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentBoard"},"example":{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","folders":[{"id":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Sexting","position":1,"collectionCount":1}],"collections":[{"id":"col_2b3c4d5e6f7a8b9c0d1e","folderId":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Shower set","mode":"sequential","description":"A slow tease that ends in the shower.","active":true,"position":1,"levelCount":2}],"levels":[{"id":"set_3c4d5e6f7a8b9c0d1e2f","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Opener","position":1,"priceCents":0,"free":true,"tags":["Solo","Teasing"],"highlight":"the one where I get ready","description":"Two selfies in a towel.","availableInPaidMessages":true,"active":true,"stats":{"sent":40,"purchased":0,"revenueCents":0},"preview":[{"id":"4498297883","type":"photo","durationSeconds":null,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297883/thumbnail"}],"paid":[]},{"id":"set_4d5e6f7a8b9c0d1e2f3a","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Shower video","position":2,"priceCents":1500,"free":false,"tags":["Shower"],"highlight":null,"description":"Four-minute shower video.","availableInPaidMessages":true,"active":true,"stats":{"sent":31,"purchased":9,"revenueCents":13500},"preview":[],"paid":[{"id":"4498297884","type":"video","durationSeconds":241,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297884/thumbnail"}]}],"vault":{"total":512,"assigned":3,"unassigned":509}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"Idempotency-Key: must be 8-64 characters of letters, digits, '-' or '_'.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `ai:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `ai:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential. `NOT_FOUND`: no level with this id belongs to this creator.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"NOT_FOUND_LEVEL":{"summary":"NOT_FOUND (level)","value":{"error":{"code":"NOT_FOUND","message":"Not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"description":"Over a rate limit (`RATE_LIMITED`): the general limit, or this operation's own (60 changes per 60 seconds per credential). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED (general limit)","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_1":{"summary":"RATE_LIMITED (60 changes per 60 seconds per credential)","value":{"error":{"code":"RATE_LIMITED","message":"Too many AI content changes: at most 60 a minute per credential. Try again shortly.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"ai:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":60,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":false}},"/v1/creators/{creatorId}/ai-content/levels/{levelId}/media":{"post":{"tags":["AI content"],"summary":"Add media to a level","description":"Places vault media on a level as `paid` (locked behind the price) or `preview` (the free teaser that sells it). A media item can be on only one level, so adding it here moves it from any other level. On a free level everything is stored as preview. A priced level needs at least one paid item before the AI can sell it. Returns the whole updated content board. Nothing is sent to fans: the AI uses active ladders in its own conversations. AI content writes are limited to 60 a minute per credential.\n\n**Scope:** requires `ai:write`.\n\n**Rate limits:** 60 changes per 60 seconds per credential — on top of the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.","operationId":"addMediaToLevel","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"name":"levelId","in":"path","required":true,"schema":{"type":"string","description":"The level (set) id (`set_…`).","title":"Levelid"},"description":"The level (set) id (`set_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddLevelMediaRequest"}}}},"responses":{"200":{"description":"The whole content board after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentBoard"},"example":{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","folders":[{"id":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Sexting","position":1,"collectionCount":1}],"collections":[{"id":"col_2b3c4d5e6f7a8b9c0d1e","folderId":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Shower set","mode":"sequential","description":"A slow tease that ends in the shower.","active":true,"position":1,"levelCount":2}],"levels":[{"id":"set_3c4d5e6f7a8b9c0d1e2f","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Opener","position":1,"priceCents":0,"free":true,"tags":["Solo","Teasing"],"highlight":"the one where I get ready","description":"Two selfies in a towel.","availableInPaidMessages":true,"active":true,"stats":{"sent":40,"purchased":0,"revenueCents":0},"preview":[{"id":"4498297883","type":"photo","durationSeconds":null,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297883/thumbnail"}],"paid":[]},{"id":"set_4d5e6f7a8b9c0d1e2f3a","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Shower video","position":2,"priceCents":1500,"free":false,"tags":["Shower"],"highlight":null,"description":"Four-minute shower video.","availableInPaidMessages":true,"active":true,"stats":{"sent":31,"purchased":9,"revenueCents":13500},"preview":[],"paid":[{"id":"4498297884","type":"video","durationSeconds":241,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297884/thumbnail"}]}],"vault":{"total":512,"assigned":3,"unassigned":509}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"mediaIds: Field required","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `ai:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `ai:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential. `NOT_FOUND`: no level with this id belongs to this creator. `NOT_FOUND`: an `ox:` media id is not an item of this creator's library.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"NOT_FOUND_LEVEL":{"summary":"NOT_FOUND (level)","value":{"error":{"code":"NOT_FOUND","message":"Not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"NOT_FOUND_MEDIA":{"summary":"NOT_FOUND (library item)","value":{"error":{"code":"NOT_FOUND","message":"Not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"description":"Over a rate limit (`RATE_LIMITED`): the general limit, or this operation's own (60 changes per 60 seconds per credential). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED (general limit)","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_1":{"summary":"RATE_LIMITED (60 changes per 60 seconds per credential)","value":{"error":{"code":"RATE_LIMITED","message":"Too many AI content changes: at most 60 a minute per credential. Try again shortly.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"ai:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":60,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":false}},"/v1/creators/{creatorId}/ai-content/levels/{levelId}/media/order":{"put":{"tags":["AI content"],"summary":"Reorder a level's media","description":"Sets the order of one side of a level: `preview` is the order fans see the teasers, `paid` the unlock order. List the media ids of that side in the order you want; any you leave out follow them in their current order, and an id that is not on that side of the level is a `400`. Returns the whole updated content board. Nothing is sent to fans: the AI uses active ladders in its own conversations. AI content writes are limited to 60 a minute per credential.\n\n**Scope:** requires `ai:write`.\n\n**Rate limits:** 60 changes per 60 seconds per credential — on top of the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.","operationId":"reorderLevelMedia","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"name":"levelId","in":"path","required":true,"schema":{"type":"string","description":"The level (set) id (`set_…`).","title":"Levelid"},"description":"The level (set) id (`set_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReorderLevelMediaRequest"}}}},"responses":{"200":{"description":"The whole content board after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentBoard"},"example":{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","folders":[{"id":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Sexting","position":1,"collectionCount":1}],"collections":[{"id":"col_2b3c4d5e6f7a8b9c0d1e","folderId":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Shower set","mode":"sequential","description":"A slow tease that ends in the shower.","active":true,"position":1,"levelCount":2}],"levels":[{"id":"set_3c4d5e6f7a8b9c0d1e2f","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Opener","position":1,"priceCents":0,"free":true,"tags":["Solo","Teasing"],"highlight":"the one where I get ready","description":"Two selfies in a towel.","availableInPaidMessages":true,"active":true,"stats":{"sent":40,"purchased":0,"revenueCents":0},"preview":[{"id":"4498297883","type":"photo","durationSeconds":null,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297883/thumbnail"}],"paid":[]},{"id":"set_4d5e6f7a8b9c0d1e2f3a","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Shower video","position":2,"priceCents":1500,"free":false,"tags":["Shower"],"highlight":null,"description":"Four-minute shower video.","availableInPaidMessages":true,"active":true,"stats":{"sent":31,"purchased":9,"revenueCents":13500},"preview":[],"paid":[{"id":"4498297884","type":"video","durationSeconds":241,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297884/thumbnail"}]}],"vault":{"total":512,"assigned":3,"unassigned":509}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem. `VALIDATION_ERROR`: a media id is not on that side of the level.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"role: Field required","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"VALIDATION_ERROR_UNKNOWN_MEDIA":{"summary":"VALIDATION_ERROR (not on this side of the level)","value":{"error":{"code":"VALIDATION_ERROR","message":"mediaIds: `4498297886` is not preview media on this level","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `ai:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `ai:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential. `NOT_FOUND`: no level with this id belongs to this creator.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"NOT_FOUND_LEVEL":{"summary":"NOT_FOUND (level)","value":{"error":{"code":"NOT_FOUND","message":"Not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"description":"Over a rate limit (`RATE_LIMITED`): the general limit, or this operation's own (60 changes per 60 seconds per credential). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED (general limit)","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_1":{"summary":"RATE_LIMITED (60 changes per 60 seconds per credential)","value":{"error":{"code":"RATE_LIMITED","message":"Too many AI content changes: at most 60 a minute per credential. Try again shortly.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"ai:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":60,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":false}},"/v1/creators/{creatorId}/ai-content/levels/{levelId}/media/{mediaId}":{"delete":{"tags":["AI content"],"summary":"Remove media from a level","description":"Takes one media item off a level; the item stays in her vault. A level that is not hers, or media that is not on that level, is a `404`. Returns the whole updated content board. Nothing is sent to fans: the AI uses active ladders in its own conversations. AI content writes are limited to 60 a minute per credential.\n\n**Scope:** requires `ai:write`.\n\n**Rate limits:** 60 changes per 60 seconds per credential — on top of the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.","operationId":"removeMediaFromLevel","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"name":"levelId","in":"path","required":true,"schema":{"type":"string","description":"The level (set) id (`set_…`).","title":"Levelid"},"description":"The level (set) id (`set_…`)."},{"name":"mediaId","in":"path","required":true,"schema":{"type":"string","description":"The vault media id placed on the level.","title":"Mediaid"},"description":"The vault media id placed on the level."},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The whole content board after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentBoard"},"example":{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","folders":[{"id":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Sexting","position":1,"collectionCount":1}],"collections":[{"id":"col_2b3c4d5e6f7a8b9c0d1e","folderId":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Shower set","mode":"sequential","description":"A slow tease that ends in the shower.","active":true,"position":1,"levelCount":2}],"levels":[{"id":"set_3c4d5e6f7a8b9c0d1e2f","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Opener","position":1,"priceCents":0,"free":true,"tags":["Solo","Teasing"],"highlight":"the one where I get ready","description":"Two selfies in a towel.","availableInPaidMessages":true,"active":true,"stats":{"sent":40,"purchased":0,"revenueCents":0},"preview":[{"id":"4498297883","type":"photo","durationSeconds":null,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297883/thumbnail"}],"paid":[]},{"id":"set_4d5e6f7a8b9c0d1e2f3a","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Shower video","position":2,"priceCents":1500,"free":false,"tags":["Shower"],"highlight":null,"description":"Four-minute shower video.","availableInPaidMessages":true,"active":true,"stats":{"sent":31,"purchased":9,"revenueCents":13500},"preview":[],"paid":[{"id":"4498297884","type":"video","durationSeconds":241,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297884/thumbnail"}]}],"vault":{"total":512,"assigned":3,"unassigned":509}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"Idempotency-Key: must be 8-64 characters of letters, digits, '-' or '_'.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `ai:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `ai:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential. `NOT_FOUND`: no level with this id belongs to this creator. `NOT_FOUND`: this media item is not on that level.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"NOT_FOUND_LEVEL":{"summary":"NOT_FOUND (level)","value":{"error":{"code":"NOT_FOUND","message":"Not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"NOT_FOUND_MEDIA":{"summary":"NOT_FOUND (media not on the level)","value":{"error":{"code":"NOT_FOUND","message":"Not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"description":"Over a rate limit (`RATE_LIMITED`): the general limit, or this operation's own (60 changes per 60 seconds per credential). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED (general limit)","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_1":{"summary":"RATE_LIMITED (60 changes per 60 seconds per credential)","value":{"error":{"code":"RATE_LIMITED","message":"Too many AI content changes: at most 60 a minute per credential. Try again shortly.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"ai:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":60,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":false}},"/v1/creators/{creatorId}/ai-settings":{"get":{"tags":["AI settings"],"summary":"Get AI settings","description":"Returns how the AI chatter behaves for this creator: the master AI switch, follow-ups to fans who went quiet (the setting, whether they actually go out, today's counts), review mode (whether every AI reply waits for a person, and how many are waiting now), and the AI's scheduled messages to fan lists, which are read-only in v1. Reading changes nothing.\n\n**Scope:** requires `ai:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"getAiSettings","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."}],"responses":{"200":{"description":"The master AI switch, follow-ups, review mode and scheduled mass messages.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiSettings"},"example":{"aiEnabled":true,"followUps":{"enabled":true,"effective":true,"maxPerFanPerDay":2,"unansweredAfterHours":24,"plannedToday":14,"sentToday":9},"review":{"enabled":false,"reviewMinutes":5,"whenUnreviewed":"send","waitingNow":0},"massMessages":{"enabled":true,"effective":true,"perDay":1,"nextSendAt":"2026-09-26T18:00:00.000Z"}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"The request could not be read.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `ai:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `ai:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"ai:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false},"patch":{"tags":["AI settings"],"summary":"Update AI settings","description":"Changes the AI switch, follow-ups or review mode; send only what changes. These settings reach real fans: `aiEnabled: true` lets the AI answer every waiting fan, follow-ups are real messages, and **turning review mode off releases every waiting draft to fans immediately** — except drafts that have waited longer than 10 minutes, which are discarded rather than sent late. Because of that, turning `review.enabled` from on to off also needs the `messages:send` scope (`403 INSUFFICIENT_SCOPE` otherwise) and is limited to 6 per hour per creator. Confirm with a person before changing any of them on someone's behalf.\n\n**Scope:** requires `ai:write`; turning `review.enabled` from on to off also requires `messages:send`.\n\n**Rate limits:** 6 review-off switches per hour per creator, across all credentials — on top of the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.\n\n**Reaches fans:** this operation can reach a real fan or change the live OnlyFans account. Confirm it with a person before calling it on their behalf.","operationId":"updateAiSettings","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAiSettingsRequest"}}}},"responses":{"200":{"description":"The AI settings after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiSettings"},"example":{"aiEnabled":true,"followUps":{"enabled":true,"effective":true,"maxPerFanPerDay":2,"unansweredAfterHours":24,"plannedToday":14,"sentToday":9},"review":{"enabled":false,"reviewMinutes":5,"whenUnreviewed":"send","waitingNow":0},"massMessages":{"enabled":true,"effective":true,"perDay":1,"nextSendAt":"2026-09-26T18:00:00.000Z"}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"aiEnabled: Input should be a valid boolean","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `ai:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `INSUFFICIENT_SCOPE`: turning `review.enabled` from on to off without the `messages:send` scope. `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `ai:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"INSUFFICIENT_SCOPE_MESSAGES_SEND":{"summary":"INSUFFICIENT_SCOPE (messages:send)","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `messages:send` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"description":"Over a rate limit (`RATE_LIMITED`): the general limit, or this operation's own (6 review-off switches per hour per creator, across all credentials). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED (general limit)","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_1":{"summary":"RATE_LIMITED (6 review-off switches per hour per creator, across all credentials)","value":{"error":{"code":"RATE_LIMITED","message":"Review mode can be turned off at most 6 times an hour for a creator, because each time releases every waiting draft to fans. Try again later.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"ai:write","x-conditional-scopes":[{"scope":"messages:send","when":"turning `review.enabled` from on to off"}],"x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":6,"windowSeconds":3600,"per":"creator"}],"x-idempotency-key":"optional","x-reaches-fans":true}},"/v1/creators/{creatorId}/connect-link":{"get":{"tags":["Connect"],"summary":"Get the connect link","description":"Returns the creator's current connect link: whether one is active, its URL and expiry, and how often it was opened. `status` is `none` when no link was ever created, and `url` is only present while the link is `active`. A rising `opens` count you do not expect is a sign the link was shared further than intended: revoke it and create a new one. The response is never cached (`Cache-Control: no-store`).\n\n**Scope:** requires `creators:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"getConnectLink","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."}],"responses":{"200":{"description":"The creator's current connect link, or `status: \"none\"` when she never had one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectLink"},"example":{"status":"active","url":"https://app.onlyx.ai/connect/Qm9vX3RoaXNfaXNfYW5fZXhhbXBsZV90b2tlbl9vbmx5","expiresAt":"2026-09-27T10:03:40.000Z","createdAt":"2026-09-26T10:03:40.000Z","opens":0,"lastOpenedAt":null}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Cache-Control":{"description":"`no-store`: the link is a credential and must not be cached.","schema":{"type":"string"}}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"The request could not be read.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `creators:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `creators:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"creators:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false},"post":{"tags":["Connect"],"summary":"Create a connect link","description":"Creates a one-time link the creator opens on her own phone or computer to connect her OnlyFans account. She needs no OnlyX account: she signs in to OnlyFans herself with the OnlyX Login app, entering her password, any 2FA or e-mail code, and completing OnlyFans' face (selfie) verification with her own camera. The API never sees her password or codes. A link lasts 24 hours and is burned when she connects. There is one live link per creator: if one is already active it is returned instead of a new one, so repeating this call is safe. Treat the URL like a password and send it only to her; the response is never cached (`Cache-Control: no-store`). Limited to 20 per hour per workspace. A creator that has no OnlyFans account behind her (a test creator), or whose connection is final (`not_a_creator`, `duplicate`), is `409 CREATOR_NOT_CONNECTABLE`. After sending the link, poll `GET /v1/creators/{creatorId}/connection`.\n\n**Scope:** requires `creators:write`.\n\n**Rate limits:** 20 links per hour per workspace — on top of the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** repeating this call is safe on its own; an `Idempotency-Key` is accepted and checked, but no response is stored for replay.","operationId":"createConnectLink","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"name":"Idempotency-Key","in":"header","required":false,"description":"Optional and not needed: this call is idempotent on its own, so repeating it is always safe and no response is stored for replay. A key you send is still checked (8 to 64 letters, digits, `-` or `_`; a malformed one is `400 VALIDATION_ERROR`).","schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{8,64}$","minLength":8,"maxLength":64},"example":"3b1f7c2e-9d4a-4e8b-a6c1-5f0d2e7b9a14"}],"responses":{"201":{"description":"The active connect link (a live one is returned instead of a new one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectLink"},"example":{"status":"active","url":"https://app.onlyx.ai/connect/Qm9vX3RoaXNfaXNfYW5fZXhhbXBsZV90b2tlbl9vbmx5","expiresAt":"2026-09-27T10:03:40.000Z","createdAt":"2026-09-26T10:03:40.000Z","opens":0,"lastOpenedAt":null}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Cache-Control":{"description":"`no-store`: the link is a credential and must not be cached.","schema":{"type":"string"}}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"Idempotency-Key: must be 8-64 characters of letters, digits, '-' or '_'.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `creators:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `creators:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `CREATOR_NOT_CONNECTABLE`: the creator has no OnlyFans account behind her (a test creator), or her connection is final (`not_a_creator`, `duplicate`). No link is created and no quota is used.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_CONNECTABLE":{"summary":"CREATOR_NOT_CONNECTABLE","value":{"error":{"code":"CREATOR_NOT_CONNECTABLE","message":"This creator cannot be connected through a link.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"description":"Over a rate limit (`RATE_LIMITED`): the general limit, or this operation's own (20 links per hour per workspace). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED (general limit)","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_1":{"summary":"RATE_LIMITED (20 links per hour per workspace)","value":{"error":{"code":"RATE_LIMITED","message":"This workspace has created 20 connect links in the last hour. Try again later.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"creators:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":20,"windowSeconds":3600,"per":"workspace"}],"x-idempotency-key":"optional","x-reaches-fans":false},"delete":{"tags":["Connect"],"summary":"Revoke the connect link","description":"Revokes every unused connect link of this creator, so nobody can start a sign-in with them. Use it when a link went to the wrong person or `opens` looks suspicious, then create a new one. Revoking is idempotent (revoking nothing succeeds) and does not disconnect a creator who is already connected.\n\n**Scope:** requires `creators:write`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.","operationId":"revokeConnectLink","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"204":{"description":"Revoked (or there was nothing to revoke). No body.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"Idempotency-Key: must be 8-64 characters of letters, digits, '-' or '_'.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `creators:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `creators:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"creators:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":false}},"/v1/creators/{creatorId}/connection":{"get":{"tags":["Connect"],"summary":"Get the connection status","description":"Returns the creator's OnlyFans connection in detail: the status, OnlyX's own explanation, the `nextStep` to take, and the progress of the sync that runs after she connects. Each call checks the live connection, so poll it every 10 seconds or more while she signs in through her connect link, until `status` is `connected`; stop on `not_a_creator` or `duplicate`, which are final. When `status` is `verification`, OnlyFans wants a human check that can only be completed in app.onlyx.ai. This call is limited to one per 5 seconds per creator for each credential (`429 RATE_LIMITED` with `Retry-After`).\n\n**Scope:** requires `creators:read`.\n\n**Rate limits:** 1 call per 5 seconds per creator for each credential — on top of the general limit of 120 requests per 60 seconds per credential.","operationId":"getConnection","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."}],"responses":{"200":{"description":"The connection status, OnlyX's explanation, the next step and the sync progress.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Connection"},"example":{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","status":"connected","connected":true,"verificationRequired":false,"message":"Connected. Her inbox is syncing.","nextStep":"none","duplicateOfCreatorId":null,"sync":{"phase":"initial","progress":35,"steps":[{"key":"conversations","label":"Recent conversations","status":"done"},{"key":"waiting","label":"Fans waiting for a reply","status":"running"},{"key":"vault","label":"Vault media","status":"pending"}]},"checkedAt":"2026-09-26T10:21:07.000Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"The request could not be read.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `creators:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `creators:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"description":"Over a rate limit (`RATE_LIMITED`): the general limit, or this operation's own (1 call per 5 seconds per creator for each credential). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED (general limit)","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_1":{"summary":"RATE_LIMITED (1 call per 5 seconds per creator for each credential)","value":{"error":{"code":"RATE_LIMITED","message":"Check the connection at most once every 5 seconds per creator. Try again shortly.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"creators:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":1,"windowSeconds":5,"per":"credential_creator"}],"x-reaches-fans":false}},"/v1/creators/{creatorId}/media":{"get":{"tags":["Media"],"summary":"List vault media","description":"Lists the creator's OnlyFans vault: photos, videos, GIFs and audio with size, length, the vault lists they are in and how they performed. The ids are what you use in messages (`mediaIds`, `previewMediaIds`), in the welcome message and on AI content levels. The list is OnlyX's stored copy of her vault, refreshed by OnlyX on its own schedule — reading it never touches her OnlyFans session; a creator who is not connected has an empty vault. Uploading is not available through the API yet. A creator the credential cannot see is `404 CREATOR_NOT_FOUND`.\n\n**Scope:** requires `media:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"listVaultMedia","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","maxLength":40,"description":"The creator id (`cre_…`), from `GET /v1/creators`.","title":"Creatorid"},"description":"The creator id (`cre_…`), from `GET /v1/creators`."},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"enum":["photo","video","gif","audio"],"type":"string"},{"type":"null"}],"description":"Only this media type.","title":"Type"},"description":"Only this media type."},{"name":"listId","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":40,"pattern":"^[A-Za-z0-9_-]{1,40}$"},{"type":"null"}],"description":"Only media in this vault list (an id from `listIds`).","title":"Listid"},"description":"Only media in this vault list (an id from `listIds`)."},{"name":"sort","in":"query","required":false,"schema":{"enum":["newest","oldest","earnings"],"type":"string","description":"`newest` (default), `oldest` or `earnings`.","default":"newest","title":"Sort"},"description":"`newest` (default), `oldest` or `earnings`."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Page size, 1-100. Default 25.","default":25,"title":"Limit"},"description":"Page size, 1-100. Default 25."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"description":"Opaque cursor from the previous page's `nextCursor`. Omit for the first page.","title":"Cursor"},"description":"Opaque cursor from the previous page's `nextCursor`. Omit for the first page."}],"responses":{"200":{"description":"A page of vault media.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VaultMediaPage"},"example":{"data":[{"id":"4012345678","type":"photo","createdAt":"2026-08-30T14:12:09.000Z","width":1080,"height":1350,"durationSeconds":null,"listIds":["1234567"],"stats":{"likes":212,"tipsCents":4500,"buyers":9},"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4012345678/thumbnail"}],"hasMore":true,"nextCursor":"eyJrIjoiY3JlYXRvcnMiLCJwIjp7ImkiOiJjcmVfOGYyYzFhOWIwZDdlNGMzZjJhMWIifX0.Yp3k0Qm7T2vX9aLc4R8sWg"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"limit: Input should be less than or equal to 100","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `media:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `media:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Temporarily unavailable. Retry later. `MEDIA_UNAVAILABLE`: the vault can't be read right now. Try again in a few minutes.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"MEDIA_UNAVAILABLE":{"summary":"MEDIA_UNAVAILABLE","value":{"error":{"code":"MEDIA_UNAVAILABLE","message":"The vault can't be read right now. Try again in a few minutes.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}}},"x-required-scope":"media:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false}},"/v1/creators/{creatorId}/media/{mediaId}/thumbnail":{"get":{"tags":["Media"],"summary":"Get a thumbnail","description":"Returns a small preview image of one vault item as image bytes, not JSON. Only small previews are served: full-size originals and videos are not available through the API. The call needs your credential, so a browser cannot load `thumbnailUrl` directly in an `<img>` tag: fetch it on your server. Responses may be cached privately for five minutes; check the `Content-Type` header for the image format. Limited to 60 calls per 60 seconds per credential. An item with no stored preview is `404 MEDIA_NOT_AVAILABLE`.\n\n**Scope:** requires `media:read`.\n\n**Rate limits:** 60 requests per 60 seconds per credential — on top of the general limit of 120 requests per 60 seconds per credential.","operationId":"getVaultMediaThumbnail","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","maxLength":40,"description":"The creator id (`cre_…`), from `GET /v1/creators`.","title":"Creatorid"},"description":"The creator id (`cre_…`), from `GET /v1/creators`."},{"name":"mediaId","in":"path","required":true,"schema":{"type":"string","pattern":"^\\d{1,25}$","description":"The vault media id (digits), from `GET /v1/creators/{creatorId}/media`.","title":"Mediaid"},"description":"The vault media id (digits), from `GET /v1/creators/{creatorId}/media`."}],"responses":{"200":{"description":"The thumbnail image.","content":{"image/jpeg":{"schema":{"type":"string","format":"binary"}},"image/png":{"schema":{"type":"string","format":"binary"}},"image/webp":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Cache-Control":{"description":"`private, max-age=300`: the image may be cached privately for five minutes.","schema":{"type":"string"}}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"creatorId: String should have at most 40 characters","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `media:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `media:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential. `MEDIA_NOT_AVAILABLE`: this item has no stored preview: it was removed from the vault, its preview was never stored, or the creator has no OnlyFans vault.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"MEDIA_NOT_AVAILABLE":{"summary":"MEDIA_NOT_AVAILABLE","value":{"error":{"code":"MEDIA_NOT_AVAILABLE","message":"This media has no preview available right now. It may have been removed from the vault, or its preview has not been stored yet.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Temporarily unavailable. Retry later. `MEDIA_UNAVAILABLE`: the vault can't be read right now. Try again in a few minutes.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"MEDIA_UNAVAILABLE":{"summary":"MEDIA_UNAVAILABLE","value":{"error":{"code":"MEDIA_UNAVAILABLE","message":"The vault can't be read right now. Try again in a few minutes.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}}},"x-required-scope":"media:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":60,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false}},"/v1/creators/{creatorId}/persona":{"get":{"tags":["AI persona"],"summary":"Get the AI persona","description":"Returns the creator's AI persona: the brief your team wrote so the AI chatter can chat as her (name, age, city, backstory, voice, boundaries, hard limits, sample messages, custom-content prices, and which situations hand off to your team). It also reports `completeness`, `readyForAi` and `requiredMissing`, and lists every hand-off kind you can choose from in `handoffCatalogue`. A creator whose persona was never written gets the defaults with `updatedAt: null`; reading never creates anything.\n\n**Scope:** requires `ai:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"getPersona","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."}],"responses":{"200":{"description":"The persona, its completeness, and the hand-off catalogue.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Persona"},"example":{"personaName":"Mia","nickname":"Mimi","age":24,"city":"Austin","origin":"San Diego","occupation":"Yoga instructor","archetype":"girl_next_door","lore":"Grew up by the beach, moved to Austin for college and stayed for the music.","accountContext":"Paid page. The wall has photos; videos are sold in messages.","physicalDescription":"Blonde, 5'6\", athletic, small tattoo on her left wrist.","interests":["hiking","live music","tacos"],"contentMenu":["lingerie photos","shower videos"],"contentNotes":"No face in explicit videos.","boundaries":["Does not talk about her family."],"hardLimits":["No meetups.","No custom videos with other people."],"exemplars":["heyy you 😘 how was your day?"],"primaryLanguage":"en","additionalLanguages":["es"],"timezone":"America/Chicago","capitalization":"lowercase","punctuation":"relaxed","messageSplitting":"sometimes","typingSpeed":"natural","emojiPolicy":"some","allowTypos":true,"customContentEnabled":true,"priceCustomPhotoCents":2500,"priceCustomVideoPerMinCents":1000,"priceCustomVideoMinCents":5000,"customPhotoDeliveryDays":3,"customVideoDeliveryDays":7,"aiDisclosureEnabled":false,"aiDisclosureText":"","handoffKinds":null,"handoffEffective":["welfare.self_harm","welfare.immediate_danger","prohibited.underage","prohibited.animals_illegal","real_world.stalking","real_world.threat","payment.fraud_threat","payment.refund_demand","payment.double_charge","payment.not_received","payment.custom_overdue","whale.broken_promise","whale.feels_scammed"],"handoffCatalogue":[{"key":"welfare.self_harm","reason":"welfare","label":"Suicidal or self-harm language","default":true,"locked":true},{"key":"payment.refund_demand","reason":"payment_dispute","label":"Asks for a refund","default":true,"locked":false},{"key":"real_world.meet_request","reason":"real_world","label":"Asks to meet","default":false,"locked":false}],"completeness":86,"readyForAi":true,"requiredMissing":[],"updatedAt":"2026-09-26T14:02:31.000Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"The request could not be read.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `ai:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `ai:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"ai:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false},"patch":{"tags":["AI persona"],"summary":"Update the AI persona","description":"Changes the AI persona. It is a true PATCH: fields you leave out stay as they are, `null` clears a field, and a field name that is not part of the persona is refused with `400 VALIDATION_ERROR`. Lists (`boundaries`, `hardLimits`, `exemplars`, …) are replaced whole, `timezone` must be a valid IANA zone and `primaryLanguage` a BCP-47 tag. The AI reads the persona fresh for every reply, so a change applies to the next message it writes; nothing is sent to fans by this call. The AI needs `personaName`, `age`, `city`, `occupation` and `lore` before `readyForAi` becomes `true`.\n\n**Scope:** requires `ai:write`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.","operationId":"updatePersona","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePersonaRequest"}}}},"responses":{"200":{"description":"The whole persona after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Persona"},"example":{"personaName":"Mia","nickname":"Mimi","age":24,"city":"Austin","origin":"San Diego","occupation":"Yoga instructor","archetype":"girl_next_door","lore":"Grew up by the beach, moved to Austin for college and stayed for the music.","accountContext":"Paid page. The wall has photos; videos are sold in messages.","physicalDescription":"Blonde, 5'6\", athletic, small tattoo on her left wrist.","interests":["hiking","live music","tacos"],"contentMenu":["lingerie photos","shower videos"],"contentNotes":"No face in explicit videos.","boundaries":["Does not talk about her family."],"hardLimits":["No meetups.","No custom videos with other people."],"exemplars":["heyy you 😘 how was your day?"],"primaryLanguage":"en","additionalLanguages":["es"],"timezone":"America/Chicago","capitalization":"lowercase","punctuation":"relaxed","messageSplitting":"sometimes","typingSpeed":"natural","emojiPolicy":"some","allowTypos":true,"customContentEnabled":true,"priceCustomPhotoCents":2500,"priceCustomVideoPerMinCents":1000,"priceCustomVideoMinCents":5000,"customPhotoDeliveryDays":3,"customVideoDeliveryDays":7,"aiDisclosureEnabled":false,"aiDisclosureText":"","handoffKinds":null,"handoffEffective":["welfare.self_harm","welfare.immediate_danger","prohibited.underage","prohibited.animals_illegal","real_world.stalking","real_world.threat","payment.fraud_threat","payment.refund_demand","payment.double_charge","payment.not_received","payment.custom_overdue","whale.broken_promise","whale.feels_scammed"],"handoffCatalogue":[{"key":"welfare.self_harm","reason":"welfare","label":"Suicidal or self-harm language","default":true,"locked":true},{"key":"payment.refund_demand","reason":"payment_dispute","label":"Asks for a refund","default":true,"locked":false},{"key":"real_world.meet_request","reason":"real_world","label":"Asks to meet","default":false,"locked":false}],"completeness":86,"readyForAi":true,"requiredMissing":[],"updatedAt":"2026-09-26T14:02:31.000Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"handoffKinds: Value error, there is no hand-off kind named `welfare.unknown`; use a key from `handoffCatalogue`","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `ai:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `ai:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"ai:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":false}},"/v1/creators/{creatorId}/tracking-links":{"get":{"tags":["Tracking links"],"summary":"List tracking links","description":"Lists the creator's OnlyFans tracking and trial links, most clicks first, with clicks and subscribers from OnlyFans, your cost fields, and computed earnings, profit, ROI and per-click and per-fan figures (all money in cents; a figure that cannot be worked out is `null`, never `0`). OnlyX reads the links from OnlyFans about once an hour while she is connected, so there is no need to poll this more than every few minutes. Archived links are left out unless `includeArchived=true`. Page with `limit` (up to 100) and `cursor`.\n\n**Scope:** requires `links:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"listTrackingLinks","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"name":"includeArchived","in":"query","required":false,"schema":{"type":"boolean","description":"Include links you archived in OnlyX.","default":false,"title":"Includearchived"},"description":"Include links you archived in OnlyX."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Page size, 1-100. Default 25.","default":25,"title":"Limit"},"description":"Page size, 1-100. Default 25."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"description":"Opaque cursor from the previous page's `nextCursor`. Omit for the first page.","title":"Cursor"},"description":"Opaque cursor from the previous page's `nextCursor`. Omit for the first page."}],"responses":{"200":{"description":"A page of tracking links, most clicks first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackingLinkPage"},"example":{"data":[{"id":"trk_5a7c9e1b3d5f7a9c1e3b","kind":"tracking","name":"Reddit September","code":"17","url":"https://onlyfans.com/miarose/c17","clicks":4210,"subscribers":388,"claims":0,"claimsLimit":null,"trialDays":null,"expiresAt":null,"ofCreatedAt":"2026-09-01T10:00:00.000Z","costCents":25000,"source":"reddit","note":"Paid posts in three subreddits","archived":false,"earningsCents":184300,"earningsKnown":true,"attributedFans":388,"accountedFans":301,"conversion":0.0922,"profitCents":159300,"roi":6.372,"cpcCents":6,"epcCents":44,"cpfCents":64,"arpuCents":475,"firstSeenAt":"2026-09-01T10:14:00.000Z","lastSeenAt":"2026-09-26T09:15:00.000Z"}],"hasMore":true,"nextCursor":"eyJrIjoiY3JlYXRvcnMiLCJwIjp7ImkiOiJjcmVfOGYyYzFhOWIwZDdlNGMzZjJhMWIifX0.Yp3k0Qm7T2vX9aLc4R8sWg"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"limit: Input should be less than or equal to 100","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `links:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `links:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"links:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false},"post":{"tags":["Tracking links"],"summary":"Create a tracking link","description":"Creates a REAL tracking or free-trial link on the creator's live OnlyFans account; it cannot be deleted through the API, so confirm the name (and, for a trial, the length, claim limit and expiry) with a person first. An `Idempotency-Key` header is required, and a retry with the same key never creates a second link. Creation runs in the background: the answer is `202` with an opaque `creationId`; poll `GET …/tracking-links/creations/{creationId}` every 5 seconds or more until `completed` or `failed`. The creator must be connected (`409 CREATOR_NOT_CONNECTED` otherwise). Quotas: 5 per day per creator and 10 per hour per workspace (`429 RATE_LIMITED`). OnlyFans refuses free-trial links on a page whose subscription is free.\n\n**Scope:** requires `links:write`.\n\n**Rate limits:** 5 links per 24 hours per creator, across all credentials; 10 links per hour per workspace — on top of the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** an `Idempotency-Key` header is required. Retry a failed or timed-out request with the same key: a replay returns the original response with `Idempotent-Replayed: true`, and nothing is done twice.\n\n**Reaches fans:** this operation can reach a real fan or change the live OnlyFans account. Confirm it with a person before calling it on their behalf.","operationId":"createTrackingLink","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"$ref":"#/components/parameters/IdempotencyKeyRequired"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTrackingLinkRequest"}}}},"responses":{"202":{"description":"Accepted: the link is being created. Poll the `creationId`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackingLinkCreationStarted"},"example":{"creationId":"Xq3Lk9vB2mT7cR1eY6wN0pA4sD8fGhJuZiOl5nWxVb2Qe7TyUr","state":"creating"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem. `IDEMPOTENCY_KEY_REQUIRED`: the `Idempotency-Key` header is missing; this operation needs one. `VALIDATION_ERROR`: a `tracking` link was sent trial settings.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"kind: Field required","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_KEY_REQUIRED":{"summary":"IDEMPOTENCY_KEY_REQUIRED","value":{"error":{"code":"IDEMPOTENCY_KEY_REQUIRED","message":"This request needs an `Idempotency-Key` header: a unique value of 8-64 letters, digits, '-' or '_'.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"VALIDATION_ERROR_TRIAL_SETTINGS":{"summary":"VALIDATION_ERROR (trial settings on a tracking link)","value":{"error":{"code":"VALIDATION_ERROR","message":"trialDays: only a trial link can have trial settings.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `links:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `links:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `CREATOR_NOT_CONNECTED`: her OnlyFans account is not connected. No quota is used. `LINK_REFUSED`: OnlyFans refused to create the link. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_CONNECTED":{"summary":"CREATOR_NOT_CONNECTED","value":{"error":{"code":"CREATOR_NOT_CONNECTED","message":"The creator's OnlyFans account is not connected.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"LINK_REFUSED":{"summary":"LINK_REFUSED","value":{"error":{"code":"LINK_REFUSED","message":"OnlyFans refused to create this link. Check the name and settings, then try again.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"description":"Over a rate limit (`RATE_LIMITED`): the general limit, or this operation's own (5 links per 24 hours per creator, across all credentials; 10 links per hour per workspace). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED (general limit)","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_1":{"summary":"RATE_LIMITED (5 links per 24 hours per creator, across all credentials)","value":{"error":{"code":"RATE_LIMITED","message":"This creator has had 5 links created through the API in the last 24 hours. Try again tomorrow.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_2":{"summary":"RATE_LIMITED (10 links per hour per workspace)","value":{"error":{"code":"RATE_LIMITED","message":"This workspace has created 10 links through the API in the last hour. Try again later.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Temporarily unavailable. Retry later. `SERVICE_UNAVAILABLE`: OnlyX could not reach the service that works her OnlyFans account. Wait `Retry-After` seconds and retry.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"SERVICE_UNAVAILABLE":{"summary":"SERVICE_UNAVAILABLE","value":{"error":{"code":"SERVICE_UNAVAILABLE","message":"This is temporarily unavailable. Try again in a few minutes.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}}},"x-required-scope":"links:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":5,"windowSeconds":86400,"per":"creator"},{"limit":10,"windowSeconds":3600,"per":"workspace"}],"x-idempotency-key":"required","x-reaches-fans":true}},"/v1/creators/{creatorId}/tracking-links/creations/{creationId}":{"get":{"tags":["Tracking links"],"summary":"Get a tracking link creation","description":"Returns the progress of a tracking-link creation started with `POST …/tracking-links`. Poll every 5 seconds or more. `completed` carries the new link's `url` as soon as OnlyFans shows it; `linkId` can stay `null` until OnlyX next reads her links (up to about an hour). `failed` carries an `error` with a stable code and a readable message; the link was not created. A creation can be polled for about two hours; after that, or for an id that was not issued for this creator, the answer is `404 NOT_FOUND` — look for the link in the list instead.\n\n**Scope:** requires `links:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"getTrackingLinkCreation","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"name":"creationId","in":"path","required":true,"schema":{"type":"string","maxLength":256,"description":"The `creationId` returned by `POST …/tracking-links`.","title":"Creationid"},"description":"The `creationId` returned by `POST …/tracking-links`."}],"responses":{"200":{"description":"The creation's state: `creating`, `completed` (with the link) or `failed` (with the reason).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackingLinkCreation"},"example":{"creationId":"Xq3Lk9vB2mT7cR1eY6wN0pA4sD8fGhJuZiOl5nWxVb2Qe7TyUr","state":"creating","linkId":null,"url":null,"error":null}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"creationId: String should have at most 256 characters","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `links:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `links:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential. `NOT_FOUND`: the `creationId` was not issued for this creator, or it is older than about two hours: look for the link in the list instead.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"NOT_FOUND":{"summary":"NOT_FOUND","value":{"error":{"code":"NOT_FOUND","message":"Not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Temporarily unavailable. Retry later. `SERVICE_UNAVAILABLE`: OnlyX could not reach the service that works her OnlyFans account. Wait `Retry-After` seconds and retry.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"SERVICE_UNAVAILABLE":{"summary":"SERVICE_UNAVAILABLE","value":{"error":{"code":"SERVICE_UNAVAILABLE","message":"This is temporarily unavailable. Try again in a few minutes.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}}},"x-required-scope":"links:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false}},"/v1/creators/{creatorId}/tracking-links/{linkId}":{"get":{"tags":["Tracking links"],"summary":"Get a tracking link","description":"Returns one tracking link with its daily history: cumulative clicks and subscribers at the end of each day OnlyX read it, and what each day added. `days` sets the window, 1 to 365 (default 90). A link that belongs to another creator is `404 NOT_FOUND`, even in your own workspace.\n\n**Scope:** requires `links:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"getTrackingLink","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"name":"linkId","in":"path","required":true,"schema":{"type":"string","description":"The tracking link id (`trk_…`).","title":"Linkid"},"description":"The tracking link id (`trk_…`)."},{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"description":"How many days of history, 1 to 365. Default 90.","default":90,"title":"Days"},"description":"How many days of history, 1 to 365. Default 90."}],"responses":{"200":{"description":"The tracking link with its daily history.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackingLinkDetail"},"example":{"id":"trk_5a7c9e1b3d5f7a9c1e3b","kind":"tracking","name":"Reddit September","code":"17","url":"https://onlyfans.com/miarose/c17","clicks":4210,"subscribers":388,"claims":0,"claimsLimit":null,"trialDays":null,"expiresAt":null,"ofCreatedAt":"2026-09-01T10:00:00.000Z","costCents":25000,"source":"reddit","note":"Paid posts in three subreddits","archived":false,"earningsCents":184300,"earningsKnown":true,"attributedFans":388,"accountedFans":301,"conversion":0.0922,"profitCents":159300,"roi":6.372,"cpcCents":6,"epcCents":44,"cpfCents":64,"arpuCents":475,"firstSeenAt":"2026-09-01T10:14:00.000Z","lastSeenAt":"2026-09-26T09:15:00.000Z","daily":[{"day":"2026-09-24","clicks":4101,"subscribers":377,"clicksDelta":52,"subscribersDelta":4},{"day":"2026-09-25","clicks":4210,"subscribers":388,"clicksDelta":109,"subscribersDelta":11}]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"days: Input should be less than or equal to 365","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `links:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `links:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential. `NOT_FOUND`: no tracking link with this `linkId` belongs to this creator.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"NOT_FOUND":{"summary":"NOT_FOUND","value":{"error":{"code":"NOT_FOUND","message":"Not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"links:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false},"patch":{"tags":["Tracking links"],"summary":"Update a tracking link","description":"Records what a promotion cost and where it ran, adds a note, or archives the link in OnlyX. Send only what changes; `null` clears `costCents`, `source` or `note`. Cost drives the computed profit, ROI, cost per click and cost per fan. These fields live only in OnlyX: nothing changes on OnlyFans, and fields OnlyFans owns (name, code, counts) cannot be sent.\n\n**Scope:** requires `links:write`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.","operationId":"updateTrackingLink","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"name":"linkId","in":"path","required":true,"schema":{"type":"string","description":"The tracking link id (`trk_…`).","title":"Linkid"},"description":"The tracking link id (`trk_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTrackingLinkRequest"}}}},"responses":{"200":{"description":"The updated tracking link.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackingLink"},"example":{"id":"trk_5a7c9e1b3d5f7a9c1e3b","kind":"tracking","name":"Reddit September","code":"17","url":"https://onlyfans.com/miarose/c17","clicks":4210,"subscribers":388,"claims":0,"claimsLimit":null,"trialDays":null,"expiresAt":null,"ofCreatedAt":"2026-09-01T10:00:00.000Z","costCents":25000,"source":"reddit","note":"Paid posts in three subreddits","archived":false,"earningsCents":184300,"earningsKnown":true,"attributedFans":388,"accountedFans":301,"conversion":0.0922,"profitCents":159300,"roi":6.372,"cpcCents":6,"epcCents":44,"cpfCents":64,"arpuCents":475,"firstSeenAt":"2026-09-01T10:14:00.000Z","lastSeenAt":"2026-09-26T09:15:00.000Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem. `VALIDATION_ERROR`: the body is empty, or names a field OnlyFans owns.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"costCents: Input should be less than or equal to 100000000.0","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"VALIDATION_ERROR_EMPTY_BODY":{"summary":"VALIDATION_ERROR (empty body)","value":{"error":{"code":"VALIDATION_ERROR","message":"body: send at least one of `costCents`, `source`, `note`, `archived`.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `links:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `links:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential. `NOT_FOUND`: no tracking link with this `linkId` belongs to this creator.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"NOT_FOUND":{"summary":"NOT_FOUND","value":{"error":{"code":"NOT_FOUND","message":"Not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"links:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":false}},"/v1/creators/{creatorId}/transactions":{"get":{"tags":["Stats"],"summary":"List a creator's transactions","description":"Returns one creator's earnings ledger, newest first: every subscription, paid message, tip and other sale with what the fan paid (`amountCents`), what she receives after the platform's fee (`netCents`), who paid and whether the money is settled or still clearing. The ledger is the copy OnlyX keeps while the creator is connected; this call never reaches OnlyFans. A creator with no connected account returns an empty list. Requires the separate `money:read` scope; a creator the credential cannot see (or another workspace's) is `404 CREATOR_NOT_FOUND`. Page with `limit` (1-100) and `cursor`; new sales arriving between pages can shift entries by a position.\n\n**Scope:** requires `money:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"listTransactions","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Page size, 1-100. Default 25.","default":25,"title":"Limit"},"description":"Page size, 1-100. Default 25."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"description":"Opaque cursor from the previous page's `nextCursor`. Omit for the first page.","title":"Cursor"},"description":"Opaque cursor from the previous page's `nextCursor`. Omit for the first page."}],"responses":{"200":{"description":"A page of the creator's earnings ledger, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionPage"},"example":{"data":[{"type":"ppv","amountCents":1500,"netCents":1200,"fanName":"Jake","at":"2026-09-25T22:41:09.000Z","status":"pending"}],"hasMore":true,"nextCursor":"eyJrIjoiY3JlYXRvcnMiLCJwIjp7ImkiOiJjcmVfOGYyYzFhOWIwZDdlNGMzZjJhMWIifX0.Yp3k0Qm7T2vX9aLc4R8sWg"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"limit: Input should be less than or equal to 100","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `money:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `money:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"money:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false}},"/v1/creators/{creatorId}/welcome-message":{"get":{"tags":["AI settings"],"summary":"Get the welcome message","description":"Returns OnlyFans' automatic welcome message for this creator, the message every new subscriber receives: whether it is on, its text, price and media, whether a change you made is still being applied (`pending`), and how your latest change went (`lastUpdate`). When she is not connected, `connected` is `false` and the other fields are empty. Reading never touches her OnlyFans account.\n\n**Scope:** requires `ai:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"getWelcomeMessage","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."}],"responses":{"200":{"description":"The welcome message as OnlyFans has it, and how your latest change went.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WelcomeMessage"},"example":{"connected":true,"enabled":true,"text":"hey you 😘 thanks for subscribing!","priceCents":499,"media":[{"id":"4498297883","type":"photo","preview":true},{"id":"4498297884","type":"video","preview":false}],"pending":false,"lastUpdate":{"status":"done","at":"2026-09-26T12:58:00.000Z","error":null}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"The request could not be read.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `ai:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `ai:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"ai:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false},"put":{"tags":["AI settings"],"summary":"Replace the welcome message","description":"Replaces the welcome message on her live OnlyFans account. This rewrites what every NEW subscriber receives and pays from now on, so confirm the exact text, media and price with a person first. The change is applied on OnlyFans in the background: the answer is `202` and `GET …/welcome-message` shows `pending` until it is done, then `lastUpdate`. Send `dryRun: true` to check a change on OnlyFans without saving it. One change runs at a time per creator (`409 CONFLICT` while another is applied), at most 5 per hour (dry runs included), and a creator who is not connected is `409 CREATOR_NOT_CONNECTED`.\n\n**Scope:** requires `ai:write`.\n\n**Rate limits:** 5 changes per hour per creator, across all credentials — on top of the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.\n\n**Reaches fans:** this operation can reach a real fan or change the live OnlyFans account. Confirm it with a person before calling it on their behalf.","operationId":"updateWelcomeMessage","parameters":[{"name":"creatorId","in":"path","required":true,"schema":{"type":"string","description":"The creator id (`cre_…`).","title":"Creatorid"},"description":"The creator id (`cre_…`)."},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWelcomeMessageRequest"}}}},"responses":{"202":{"description":"Accepted: the change is queued and will be applied on OnlyFans shortly.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WelcomeMessageQueued"},"example":{"queued":true,"updateId":"wmu_5f0c2a9e41b7d3c8a6e1f024"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem. `VALIDATION_ERROR`: OnlyFans does not accept the price, media or text for her account.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"Value error, a price needs `paidMediaIds` to lock behind it","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"VALIDATION_ERROR_PRICE":{"summary":"VALIDATION_ERROR (price OnlyFans refuses)","value":{"error":{"code":"VALIDATION_ERROR","message":"priceCents: OnlyFans does not accept this price for her account. Use a price inside her paid-message range.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `ai:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `ai:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: no creator with this `creatorId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `CONFLICT`: another welcome message change is still being applied; wait until `pending` is false. `CREATOR_NOT_CONNECTED`: her OnlyFans account is not connected. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CONFLICT":{"summary":"CONFLICT","value":{"error":{"code":"CONFLICT","message":"Another welcome message change is still being applied. Wait until `pending` is false, then try again.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"CREATOR_NOT_CONNECTED":{"summary":"CREATOR_NOT_CONNECTED","value":{"error":{"code":"CREATOR_NOT_CONNECTED","message":"Her OnlyFans account is not connected right now, so the welcome message cannot be changed. Reconnect her first.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"description":"Over a rate limit (`RATE_LIMITED`): the general limit, or this operation's own (5 changes per hour per creator, across all credentials). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED (general limit)","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"RATE_LIMITED_1":{"summary":"RATE_LIMITED (5 changes per hour per creator, across all credentials)","value":{"error":{"code":"RATE_LIMITED","message":"The welcome message can be changed at most 5 times an hour for a creator. Try again later.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Temporarily unavailable. Retry later. `SEND_UNAVAILABLE`: the change could not be queued right now; nothing was changed.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"SEND_UNAVAILABLE":{"summary":"SEND_UNAVAILABLE","value":{"error":{"code":"SEND_UNAVAILABLE","message":"The change could not be queued right now. Nothing was changed; try again in a few minutes.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}}},"x-required-scope":"ai:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":5,"windowSeconds":3600,"per":"creator"}],"x-idempotency-key":"optional","x-reaches-fans":true}},"/v1/fan-lists":{"get":{"tags":["Fans"],"summary":"List fan lists","description":"Lists the creators' OnlyFans fan lists — system lists such as all subscribers or renew-off, and lists the creator made herself — with how many fans are on each. List ids appear on each fan's `lists`. Lists are mirrored from OnlyFans; managing them is not available through the API yet. A `creatorId` the credential cannot see is `404 CREATOR_NOT_FOUND`.\n\n**Scope:** requires `fans:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"listFanLists","parameters":[{"name":"creatorId","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"description":"Only this creator's lists.","title":"Creatorid"},"description":"Only this creator's lists."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Page size, 1-100. Default 25.","default":25,"title":"Limit"},"description":"Page size, 1-100. Default 25."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"description":"Opaque cursor from the previous page's `nextCursor`. Omit for the first page.","title":"Cursor"},"description":"Opaque cursor from the previous page's `nextCursor`. Omit for the first page."}],"responses":{"200":{"description":"A page of fan lists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FanListPage"},"example":{"data":[{"id":"lst_9c1d2e3f4a5b6c7d8e9f","name":"VIP","kind":"custom","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","count":42}],"hasMore":true,"nextCursor":"eyJrIjoiY3JlYXRvcnMiLCJwIjp7ImkiOiJjcmVfOGYyYzFhOWIwZDdlNGMzZjJhMWIifX0.Yp3k0Qm7T2vX9aLc4R8sWg"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"limit: Input should be less than or equal to 100","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `fans:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `fans:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: the `creatorId` filter names a creator this credential cannot see (never an empty result).","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"fans:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false}},"/v1/fans":{"get":{"tags":["Fans"],"summary":"List fans","description":"Lists fans across every creator the credential can see, or one creator's with `creatorId`. Segments give you ready-made groups: `whales` ($200 or more spent), `spenders`, `never_spent`, `new` (subscribed in the last 7 days), `recent_buyer` (bought in the last 30 days), `lapsing` (auto-renew off, subscription ending within 7 days) and `winback` (subscription ended in the last 30 days). Combine a segment with `status`, `sort` and `order` — for example lapsing fans by spend — to build call lists for your team. The default order is biggest spenders first. A `creatorId` the credential cannot see is `404 CREATOR_NOT_FOUND`, never an empty list.\n\n**Scope:** requires `fans:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"listFans","parameters":[{"name":"creatorId","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"description":"Only this creator's fans.","title":"Creatorid"},"description":"Only this creator's fans."},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"description":"Search by name (OnlyFans name or your custom name).","title":"Q"},"description":"Search by name (OnlyFans name or your custom name)."},{"name":"segment","in":"query","required":false,"schema":{"anyOf":[{"enum":["whales","spenders","never_spent","new","recent_buyer","lapsing","winback"],"type":"string"},{"type":"null"}],"description":"A ready-made group of fans (see above).","title":"Segment"},"description":"A ready-made group of fans (see above)."},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"enum":["active","expired"],"type":"string"},{"type":"null"}],"description":"`active` (subscribed now) or `expired`.","title":"Status"},"description":"`active` (subscribed now) or `expired`."},{"name":"sort","in":"query","required":false,"schema":{"enum":["spend","subscribed","last_active","last_purchase","name"],"type":"string","description":"Sort key. Default `spend`.","default":"spend","title":"Sort"},"description":"Sort key. Default `spend`."},{"name":"order","in":"query","required":false,"schema":{"anyOf":[{"enum":["asc","desc"],"type":"string"},{"type":"null"}],"description":"Sort direction. Default `desc`, except `asc` for `name`.","title":"Order"},"description":"Sort direction. Default `desc`, except `asc` for `name`."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Page size, 1-100. Default 25.","default":25,"title":"Limit"},"description":"Page size, 1-100. Default 25."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"description":"Opaque cursor from the previous page's `nextCursor`. Omit for the first page.","title":"Cursor"},"description":"Opaque cursor from the previous page's `nextCursor`. Omit for the first page."}],"responses":{"200":{"description":"A page of fans.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FanPage"},"example":{"data":[{"id":"fan_6b2e8d4a1c9f3e7b5d0a","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","conversationId":"cnv_3d9e7b1a5c2f4e8d6a0b","displayName":"Mike","customName":"Mike (birthday 14 May)","username":null,"avatarUrl":null,"subscribed":true,"subscribedAt":"2026-03-02T18:21:07.000Z","subscriptionExpiresAt":"2026-10-02T18:21:07.000Z","subscriptionPriceCents":999,"renewOn":true,"totalSpentCents":48500,"tipsCents":12000,"paidMessagesCents":36500,"lastActiveAt":"2026-09-25T21:40:12.000Z","lastPurchaseAt":"2026-09-24T22:03:55.000Z","muted":false,"notes":"Likes lingerie sets. Tips on Fridays.","salesOptOut":false,"lists":[{"id":"lst_9c1d2e3f4a5b6c7d8e9f","name":"VIP"}],"createdAt":"2026-03-02T18:21:07.000Z"}],"hasMore":true,"nextCursor":"eyJrIjoiY3JlYXRvcnMiLCJwIjp7ImkiOiJjcmVfOGYyYzFhOWIwZDdlNGMzZjJhMWIifX0.Yp3k0Qm7T2vX9aLc4R8sWg"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"limit: Input should be less than or equal to 100","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `fans:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `fans:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: the `creatorId` filter names a creator this credential cannot see (never an empty result).","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"fans:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false}},"/v1/fans/{fanId}":{"get":{"tags":["Fans"],"summary":"Get a fan","description":"Returns one fan: subscription, lifetime spend split into tips and paid messages, last activity, the creator's fan lists they are on, your team's custom name and notes, and `salesOptOut`. A fan belongs to one creator; the same person subscribed to two of your creators is two fans. A fan the credential cannot see is `404 FAN_NOT_FOUND`, exactly like an id that does not exist.\n\n**Scope:** requires `fans:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"getFan","parameters":[{"name":"fanId","in":"path","required":true,"schema":{"type":"string","maxLength":40,"description":"The fan id (`fan_…`), from `GET /v1/fans`.","title":"Fanid"},"description":"The fan id (`fan_…`), from `GET /v1/fans`."}],"responses":{"200":{"description":"The fan.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fan"},"example":{"id":"fan_6b2e8d4a1c9f3e7b5d0a","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","conversationId":"cnv_3d9e7b1a5c2f4e8d6a0b","displayName":"Mike","customName":"Mike (birthday 14 May)","username":null,"avatarUrl":null,"subscribed":true,"subscribedAt":"2026-03-02T18:21:07.000Z","subscriptionExpiresAt":"2026-10-02T18:21:07.000Z","subscriptionPriceCents":999,"renewOn":true,"totalSpentCents":48500,"tipsCents":12000,"paidMessagesCents":36500,"lastActiveAt":"2026-09-25T21:40:12.000Z","lastPurchaseAt":"2026-09-24T22:03:55.000Z","muted":false,"notes":"Likes lingerie sets. Tips on Fridays.","salesOptOut":false,"lists":[{"id":"lst_9c1d2e3f4a5b6c7d8e9f","name":"VIP"}],"createdAt":"2026-03-02T18:21:07.000Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"fanId: String should have at most 40 characters","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `fans:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `fans:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `FAN_NOT_FOUND`: no fan with this `fanId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"FAN_NOT_FOUND":{"summary":"FAN_NOT_FOUND","value":{"error":{"code":"FAN_NOT_FOUND","message":"Fan not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"fans:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false},"patch":{"tags":["Fans"],"summary":"Update a fan","description":"Sets your team's own fields on a fan: a `customName`, free-text `notes`, and the `muted` flag. Send only the fields you want to change; an empty string or `null` clears a text field. These fields live only in OnlyX: nothing is sent to the fan, and muting never mutes or blocks anyone on OnlyFans. Any other field (the AI's own settings for a fan included) is refused with `400 VALIDATION_ERROR`.\n\n**Scope:** requires `fans:write`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.\n\n**Idempotency:** send an `Idempotency-Key` header to make retries safe: a replay within 24 hours returns the original response with `Idempotent-Replayed: true`.","operationId":"updateFan","parameters":[{"name":"fanId","in":"path","required":true,"schema":{"type":"string","maxLength":40,"description":"The fan id (`fan_…`), from `GET /v1/fans`.","title":"Fanid"},"description":"The fan id (`fan_…`), from `GET /v1/fans`."},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFanRequest"}}}},"responses":{"200":{"description":"The updated fan.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fan"},"example":{"id":"fan_6b2e8d4a1c9f3e7b5d0a","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","conversationId":"cnv_3d9e7b1a5c2f4e8d6a0b","displayName":"Mike","customName":"Mike (birthday 14 May)","username":null,"avatarUrl":null,"subscribed":true,"subscribedAt":"2026-03-02T18:21:07.000Z","subscriptionExpiresAt":"2026-10-02T18:21:07.000Z","subscriptionPriceCents":999,"renewOn":true,"totalSpentCents":48500,"tipsCents":12000,"paidMessagesCents":36500,"lastActiveAt":"2026-09-25T21:40:12.000Z","lastPurchaseAt":"2026-09-24T22:03:55.000Z","muted":false,"notes":"Likes lingerie sets. Tips on Fridays.","salesOptOut":false,"lists":[{"id":"lst_9c1d2e3f4a5b6c7d8e9f","name":"VIP"}],"createdAt":"2026-03-02T18:21:07.000Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"displayName: Extra inputs are not permitted","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `fans:write` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `fans:write` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `FAN_NOT_FOUND`: no fan with this `fanId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"FAN_NOT_FOUND":{"summary":"FAN_NOT_FOUND","value":{"error":{"code":"FAN_NOT_FOUND","message":"Fan not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"409":{"description":"The request conflicts with the current state. `IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used for a different request (or by another credential). Use a new key for a new request. `IDEMPOTENCY_IN_PROGRESS`: a request with this `Idempotency-Key` is still running. Retry in a few seconds with the same key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"IDEMPOTENCY_KEY_REUSED","value":{"error":{"code":"IDEMPOTENCY_KEY_REUSED","message":"This Idempotency-Key was already used for a different request. Use a new key for a new request.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"IDEMPOTENCY_IN_PROGRESS":{"summary":"IDEMPOTENCY_IN_PROGRESS","value":{"error":{"code":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry in a few seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"fans:write","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-idempotency-key":"optional","x-reaches-fans":false}},"/v1/fans/{fanId}/purchases":{"get":{"tags":["Fans"],"summary":"List a fan's purchases","description":"Returns what a fan has bought from the creator, newest first: paid-message unlocks (with the message id) and tips, each with the amount in cents. Sales the AI made, sales your team made and unlocks bought on OnlyFans directly are all included; refunded sales are not. Use it to see what a fan already owns before offering something. Subscription payments are in the creator's transaction ledger (`GET /v1/creators/{creatorId}/transactions`).\n\n**Scope:** requires `fans:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"listFanPurchases","parameters":[{"name":"fanId","in":"path","required":true,"schema":{"type":"string","maxLength":40,"description":"The fan id (`fan_…`), from `GET /v1/fans`.","title":"Fanid"},"description":"The fan id (`fan_…`), from `GET /v1/fans`."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Page size, 1-100. Default 25.","default":25,"title":"Limit"},"description":"Page size, 1-100. Default 25."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"description":"Opaque cursor from the previous page's `nextCursor`. Omit for the first page.","title":"Cursor"},"description":"Opaque cursor from the previous page's `nextCursor`. Omit for the first page."}],"responses":{"200":{"description":"A page of purchases, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchasePage"},"example":{"data":[{"type":"ppv","amountCents":2500,"at":"2026-09-24T22:03:55.000Z","messageId":"msg_1a2b3c4d5e6f7a8b9c0d"}],"hasMore":true,"nextCursor":"eyJrIjoiY3JlYXRvcnMiLCJwIjp7ImkiOiJjcmVfOGYyYzFhOWIwZDdlNGMzZjJhMWIifX0.Yp3k0Qm7T2vX9aLc4R8sWg"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"limit: Input should be less than or equal to 100","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `fans:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `fans:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `FAN_NOT_FOUND`: no fan with this `fanId` is visible to this credential.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"FAN_NOT_FOUND":{"summary":"FAN_NOT_FOUND","value":{"error":{"code":"FAN_NOT_FOUND","message":"Fan not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"fans:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false}},"/v1/handoffs":{"get":{"tags":["Hand-offs"],"summary":"List open hand-offs","description":"Lists the open hand-offs, oldest first: conversations where the AI chatter decided a person should take over (a fan in distress, a refund demand, a request to meet, something it must not handle), and the few OnlyX raises itself when paid content did not arrive. The AI is silent in these chats and the fan is waiting, so they are the most time-sensitive items in the inbox. Filter with `creatorId`; a creator the credential cannot see is `404 CREATOR_NOT_FOUND`.\n\n**Scope:** requires `inbox:read`.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"listHandoffs","parameters":[{"name":"creatorId","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"description":"Only this creator's hand-offs.","title":"Creatorid"},"description":"Only this creator's hand-offs."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Page size, 1-100. Default 25.","default":25,"title":"Limit"},"description":"Page size, 1-100. Default 25."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"description":"Opaque cursor from the previous page's `nextCursor`. Omit for the first page.","title":"Cursor"},"description":"Opaque cursor from the previous page's `nextCursor`. Omit for the first page."}],"responses":{"200":{"description":"A page of open hand-offs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandoffPage"},"example":{"data":[{"id":"esc_5e1f2a3b4c5d6e7f8a9b","conversationId":"cnv_3d9e7b1a5c2f4e8d6a0b","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","fanName":"Mike","reason":"payment_dispute","label":"Asks for a refund","note":"He says the video he bought does not play and wants his money back.","status":"open","createdAt":"2026-09-25T21:41:03.000Z"}],"hasMore":true,"nextCursor":"eyJrIjoiY3JlYXRvcnMiLCJwIjp7ImkiOiJjcmVfOGYyYzFhOWIwZDdlNGMzZjJhMWIifX0.Yp3k0Qm7T2vX9aLc4R8sWg"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem. `VALIDATION_ERROR`: the queue can be paged 1000 deep; narrow it with `creatorId`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"limit: Input should be less than or equal to 100","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"VALIDATION_ERROR_TOO_DEEP":{"summary":"VALIDATION_ERROR (paged too deep)","value":{"error":{"code":"VALIDATION_ERROR","message":"cursor: the hand-off queue can be paged 1000 deep; narrow it with creatorId.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `inbox:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `inbox:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: the `creatorId` filter names a creator this credential cannot see (never an empty result).","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"inbox:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false}},"/v1/me":{"get":{"tags":["Workspace"],"summary":"Get the current workspace and credential","description":"Returns the workspace this API key or access token belongs to, what it is allowed to do (its scopes and, if it is limited to some creators, which ones) and the default rate limit that applies to it. Every valid credential may call this endpoint — `workspace:read` is granted implicitly — so it is the right first call to check that a key works.\n\n**Scope:** `workspace:read`, which every valid credential holds.\n\n**Rate limit:** the general limit of 120 requests per 60 seconds per credential.","operationId":"getMe","responses":{"200":{"description":"The workspace, the credential's scopes and creator list, and its default rate limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"},"example":{"workspace":{"id":"agc_4f1c9e0b2d7a8c3e5f60","name":"Velvet Agency","timezone":"Europe/London"},"credential":{"kind":"api_key","id":"apk_9a8b7c6d5e4f3a2b1c0d","name":"Reporting script","scopes":["workspace:read","creators:read","stats:read"],"creatorIds":["cre_8f2c1a9b0d7e4c3f2a1b"]},"rateLimit":{"limit":120,"windowSeconds":60}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"workspace:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false}},"/v1/stats/audience":{"get":{"tags":["Stats"],"summary":"Get the audience","description":"Summarises the fans: how many are active, expired or not yet reported by OnlyFans, auto-renew among active fans, how they spread over lifetime-spend bands, how many wrote in the last 7, 30 and 90 days, and week-over-week retention of fans who write. Covers every creator the credential can see, or one with `creatorId` (a creator it cannot see is `404 CREATOR_NOT_FOUND`). Cached for several minutes. Limited to 20 requests per minute per credential across all `/stats` endpoints.\n\n**Scope:** requires `stats:read`.\n\n**Rate limits:** 20 requests to `/stats` endpoints per 60 seconds per credential — on top of the general limit of 120 requests per 60 seconds per credential.","operationId":"getAudience","parameters":[{"name":"creatorId","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}],"description":"Only this creator (`cre_…`). Without it, every creator the credential can see. A creator the credential cannot see is `404 CREATOR_NOT_FOUND`, never an empty report.","title":"Creatorid"},"description":"Only this creator (`cre_…`). Without it, every creator the credential can see. A creator the credential cannot see is `404 CREATOR_NOT_FOUND`, never an empty report."}],"responses":{"200":{"description":"The fans by status, renewal, spend band, activity and retention.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudienceReport"},"example":{"asOf":"2026-09-26T14:00:02.000Z","timezone":"Europe/London","fans":{"total":2841,"active":2210,"expired":520,"unknown":111,"spenders":1439},"renewal":{"on":1892,"off":318},"spendBands":[{"band":"none","fans":1402},{"band":"under_50","fans":1022},{"band":"50_to_200","fans":314},{"band":"200_plus","fans":103}],"activity":{"last7Days":640,"last30Days":1311,"last90Days":1950},"retention":{"previousStart":"2026-09-12","previousEnd":"2026-09-18","currentStart":"2026-09-19","currentEnd":"2026-09-25","previousFans":580,"retainedFans":402,"rate":0.693}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"creatorId: String should have at most 64 characters","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `stats:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `stats:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: the `creatorId` filter names a creator this credential cannot see (never an empty result).","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"stats:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":20,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false}},"/v1/stats/overview":{"get":{"tags":["Stats"],"summary":"Get the overview report","description":"Returns the dashboard's activity report for a window: active and answered conversations, messages in and out, how many the AI and your team wrote, reply times, new fans, the inbox right now, and daily, hourly and per-creator breakdowns. It is expensive, so it is never computed while you wait: if a copy is cached (at most about five minutes old — see `generatedAt`) you get `200` with the report; if not, you get `202` with `{\"status\": \"warming\", \"retryAfterSeconds\": N}` and a `Retry-After` header while OnlyX prepares it in the background — ask again after that many seconds. One report is prepared at a time per workspace, so asking more often does not make it faster. Limited to 20 requests per minute per credential across all `/stats` endpoints.\n\n**Scope:** requires `stats:read`.\n\n**Rate limits:** 20 requests to `/stats` endpoints per 60 seconds per credential — on top of the general limit of 120 requests per 60 seconds per credential.","operationId":"getOverview","parameters":[{"name":"creatorId","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}],"description":"Only this creator (`cre_…`). Without it, every creator the credential can see. A creator the credential cannot see is `404 CREATOR_NOT_FOUND`, never an empty report.","title":"Creatorid"},"description":"Only this creator (`cre_…`). Without it, every creator the credential can see. A creator the credential cannot see is `404 CREATOR_NOT_FOUND`, never an empty report."},{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":366,"minimum":1,"description":"The last N calendar days including today (workspace time zone), 1 to 366. Ignored when `start` or `end` is sent.","default":30,"title":"Days"},"description":"The last N calendar days including today (workspace time zone), 1 to 366. Ignored when `start` or `end` is sent."},{"name":"start","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"First day of the window (`YYYY-MM-DD`, workspace time zone), included.","title":"Start"},"description":"First day of the window (`YYYY-MM-DD`, workspace time zone), included."},{"name":"end","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Last day of the window (`YYYY-MM-DD`), included. The window never extends past today and keeps at most its latest 366 days.","title":"End"},"description":"Last day of the window (`YYYY-MM-DD`), included. The window never extends past today and keeps at most its latest 366 days."}],"responses":{"200":{"description":"The cached activity report for the window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OverviewReport"},"example":{"start":"2026-09-20","end":"2026-09-26","days":7,"timezone":"Europe/London","generatedAt":"2026-09-26T13:52:40.000Z","conversations":{"active":1204,"answered":1122},"messages":{"in":8420,"out":9315,"ai":8790,"team":525},"aiShare":0.944,"replyTime":{"medianSeconds":94,"p90Seconds":402,"slowReplies":611,"repliedTo":6230},"fans":{"new":263,"unknownStart":40,"total":28410},"inbox":{"unread":37,"handoffs":9},"daily":[{"date":"2026-09-26","messagesIn":1320,"messagesOut":1488,"newFans":41}],"hourly":[{"hour":21,"messagesIn":702,"messagesOut":760}],"creators":[{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","name":"Mia Rose","messagesIn":8420,"messagesOut":9315,"ai":8790,"team":525,"answered":1122,"unread":37,"medianReplySeconds":94}]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"202":{"description":"Not cached yet (`REPORT_WARMING`). OnlyX is preparing it: ask again after `Retry-After` seconds.","headers":{"Retry-After":{"description":"Seconds to wait before asking again.","schema":{"type":"integer"}},"Cache-Control":{"description":"`no-store`: ask again rather than caching this answer.","schema":{"type":"string"}},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportWarming"},"example":{"status":"warming","retryAfterSeconds":15}}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"creatorId: String should have at most 64 characters","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `stats:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `stats:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: the `creatorId` filter names a creator this credential cannot see (never an empty result).","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"stats:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":20,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false}},"/v1/stats/revenue":{"get":{"tags":["Stats"],"summary":"Get revenue","description":"Returns earnings for a window: totals (net after OnlyFans' fee, gross, fee, settled versus still clearing, refunds), a split by what was paid for, one entry per day, per creator, and paid-message sales by who sent them — the AI (`ai`) or your team (`team`). Choose the window with `days`, or with `start` and `end` (both included, workspace time zone). Figures come from each creator's OnlyFans earnings ledger as OnlyX has read it; `complete: false` means some ledger could not be read in full and the totals are a lower bound. Cached for a few minutes. Limited to 20 requests per minute per credential across all `/stats` endpoints.\n\n**Scope:** requires `stats:read`.\n\n**Rate limits:** 20 requests to `/stats` endpoints per 60 seconds per credential — on top of the general limit of 120 requests per 60 seconds per credential.","operationId":"getRevenue","parameters":[{"name":"creatorId","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}],"description":"Only this creator (`cre_…`). Without it, every creator the credential can see. A creator the credential cannot see is `404 CREATOR_NOT_FOUND`, never an empty report.","title":"Creatorid"},"description":"Only this creator (`cre_…`). Without it, every creator the credential can see. A creator the credential cannot see is `404 CREATOR_NOT_FOUND`, never an empty report."},{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":366,"minimum":1,"description":"The last N calendar days including today (workspace time zone), 1 to 366. Ignored when `start` or `end` is sent.","default":30,"title":"Days"},"description":"The last N calendar days including today (workspace time zone), 1 to 366. Ignored when `start` or `end` is sent."},{"name":"start","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"First day of the window (`YYYY-MM-DD`, workspace time zone), included.","title":"Start"},"description":"First day of the window (`YYYY-MM-DD`, workspace time zone), included."},{"name":"end","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Last day of the window (`YYYY-MM-DD`), included. The window never extends past today and keeps at most its latest 366 days.","title":"End"},"description":"Last day of the window (`YYYY-MM-DD`), included. The window never extends past today and keeps at most its latest 366 days."}],"responses":{"200":{"description":"Earnings for the window: totals, by type, by day, by seller and by creator.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevenueReport"},"example":{"start":"2026-09-01","end":"2026-09-26","timezone":"Europe/London","asOf":"2026-09-26T14:02:11.000Z","totals":{"netCents":4213000,"grossCents":5266250,"feeCents":1053250,"transactions":1840,"spenders":612,"settledNetCents":3701300,"pendingNetCents":511700,"unknownSettlementNetCents":0,"refundedNetCents":2400,"refundedTransactions":2},"byType":[{"type":"ppv","netCents":2214800,"grossCents":2768500},{"type":"subscription","netCents":1510400,"grossCents":1888000},{"type":"tip","netCents":487800,"grossCents":609750}],"byDay":[{"date":"2026-09-25","netCents":171900,"grossCents":214875}],"bySeller":{"ai":{"netCents":1904500,"grossCents":2380625,"transactions":612},"team":{"netCents":310300,"grossCents":387875,"transactions":138},"unattributedNetCents":0},"byCreator":[{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","name":"Mia Rose","netCents":4213000,"grossCents":5266250,"transactions":1840,"spenders":612}],"balance":{"availableCents":820000,"pendingCents":511700},"coverageFrom":"2025-01-04","complete":true}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"creatorId: String should have at most 64 characters","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `stats:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `stats:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: the `creatorId` filter names a creator this credential cannot see (never an empty result).","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"stats:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":20,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false}},"/v1/stats/today":{"get":{"tags":["Stats"],"summary":"Get today's numbers","description":"Returns today so far, from local midnight in the workspace time zone: earnings (settled and still clearing), new fans, buyers, paid messages unlocked, message volume and how much of it the AI wrote — with yesterday up to the same time of day and all of yesterday for comparison. Covers every creator the credential can see, or one with `creatorId` (a creator it cannot see is `404 CREATOR_NOT_FOUND`). The figures are cached for a minute or two, so polling faster does not make them fresher. Limited to 20 requests per minute per credential across all `/stats` endpoints.\n\n**Scope:** requires `stats:read`.\n\n**Rate limits:** 20 requests to `/stats` endpoints per 60 seconds per credential — on top of the general limit of 120 requests per 60 seconds per credential.","operationId":"getTodayStats","parameters":[{"name":"creatorId","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}],"description":"Only this creator (`cre_…`). Without it, every creator the credential can see. A creator the credential cannot see is `404 CREATOR_NOT_FOUND`, never an empty report.","title":"Creatorid"},"description":"Only this creator (`cre_…`). Without it, every creator the credential can see. A creator the credential cannot see is `404 CREATOR_NOT_FOUND`, never an empty report."}],"responses":{"200":{"description":"Today so far, with yesterday for comparison.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TodayStats"},"example":{"date":"2026-09-26","timezone":"Europe/London","asOf":"2026-09-26T14:05:00.000Z","earnings":{"netCents":184250,"settledCents":0,"pendingCents":184250,"complete":true},"newFans":41,"buyers":57,"paidMessagesUnlocked":38,"messages":{"in":1320,"out":1488,"ai":1402,"team":86},"aiShare":0.942,"yesterdaySameTime":{"netCents":171900,"newFans":36,"buyers":51,"paidMessagesUnlocked":33},"yesterday":{"netCents":301400,"newFans":64,"buyers":90,"paidMessagesUnlocked":61}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"The request is not valid and was not carried out. Fix it before retrying. `VALIDATION_ERROR`: the request is not valid; `message` names the field and the problem.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"VALIDATION_ERROR":{"summary":"VALIDATION_ERROR","value":{"error":{"code":"VALIDATION_ERROR","message":"creatorId: String should have at most 64 characters","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The credential may not do this. `INSUFFICIENT_SCOPE`: the credential lacks the `stats:read` scope (the `WWW-Authenticate` header names it; an OAuth token's message says \"token\" instead of \"key\"). `WORKSPACE_SUSPENDED`: the workspace is suspended.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"summary":"INSUFFICIENT_SCOPE","value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This key does not have the `stats:read` scope.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"WORKSPACE_SUSPENDED":{"summary":"WORKSPACE_SUSPENDED","value":{"error":{"code":"WORKSPACE_SUSPENDED","message":"This workspace is suspended.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"404":{"description":"Not found. An id that does not exist, belongs to another workspace, or is outside this credential's creators all get the same answer. `CREATOR_NOT_FOUND`: the `creatorId` filter names a creator this credential cannot see (never an empty result).","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CREATOR_NOT_FOUND":{"summary":"CREATOR_NOT_FOUND","value":{"error":{"code":"CREATOR_NOT_FOUND","message":"Creator not found.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-required-scope":"stats:read","x-rate-limits":[{"limit":120,"windowSeconds":60,"per":"credential"},{"limit":20,"windowSeconds":60,"per":"credential"}],"x-reaches-fans":false}}},"components":{"schemas":{"AddLevelMediaRequest":{"properties":{"mediaIds":{"items":{"type":"string","maxLength":40,"minLength":1,"description":"An OnlyFans vault media id (a string of digits), or `ox:<id>` for an item of an OnlyX library."},"type":"array","maxItems":200,"minItems":1,"description":"1 to 200 vault media ids."},"role":{"type":"string","enum":["paid","preview"],"description":"`paid`: locked behind the price. `preview`: the free teaser. On a free level everything is stored as `preview`."}},"additionalProperties":false,"type":"object","required":["mediaIds","role"],"title":"AddLevelMediaRequest","examples":[{"mediaIds":["4498297884","4498297885"],"role":"paid"}]},"AiSettings":{"properties":{"aiEnabled":{"type":"boolean","description":"The creator's master AI switch (the same value as `aiEnabled` on the Creator)."},"followUps":{"$ref":"#/components/schemas/FollowUps"},"review":{"$ref":"#/components/schemas/Review"},"massMessages":{"$ref":"#/components/schemas/MassMessages"}},"type":"object","required":["aiEnabled","followUps","review","massMessages"],"title":"AiSettings","description":"How the AI chatter behaves for one creator. New fields may be added to this report; ignore\nfields you do not know.","examples":[{"aiEnabled":true,"followUps":{"enabled":true,"effective":true,"maxPerFanPerDay":2,"unansweredAfterHours":24,"plannedToday":14,"sentToday":9},"review":{"enabled":false,"reviewMinutes":5,"whenUnreviewed":"send","waitingNow":0},"massMessages":{"enabled":true,"effective":true,"perDay":1,"nextSendAt":"2026-09-26T18:00:00.000Z"}}]},"AudienceActivity":{"properties":{"last7Days":{"type":"integer","description":"Fans who wrote at least once in the last 7 days (today included)."},"last30Days":{"type":"integer","description":"Fans who wrote in the last 30 days."},"last90Days":{"type":"integer","description":"Fans who wrote in the last 90 days."}},"type":"object","required":["last7Days","last30Days","last90Days"],"title":"AudienceActivity"},"AudienceFans":{"properties":{"total":{"type":"integer","description":"All fans."},"active":{"type":"integer","description":"Fans with an active subscription (as last reported by OnlyFans)."},"expired":{"type":"integer","description":"Fans whose subscription ended."},"unknown":{"type":"integer","description":"Fans whose subscription state OnlyFans has not reported yet."},"spenders":{"type":"integer","description":"Fans who have paid anything."}},"type":"object","required":["total","active","expired","unknown","spenders"],"title":"AudienceFans"},"AudienceRenewal":{"properties":{"on":{"type":"integer","description":"Active fans with auto-renew on."},"off":{"type":"integer","description":"Active fans with auto-renew off: they leave when the period ends unless they renew."}},"type":"object","required":["on","off"],"title":"AudienceRenewal"},"AudienceReport":{"properties":{"asOf":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the activity figures were computed (UTC)."},"timezone":{"type":"string"},"fans":{"$ref":"#/components/schemas/AudienceFans"},"renewal":{"$ref":"#/components/schemas/AudienceRenewal","description":"Auto-renew among active fans."},"spendBands":{"items":{"$ref":"#/components/schemas/SpendBand"},"type":"array","description":"Fans by lifetime spend, every band listed."},"activity":{"$ref":"#/components/schemas/AudienceActivity","description":"Fans who wrote to a creator recently."},"retention":{"$ref":"#/components/schemas/AudienceRetention","description":"Week-over-week: of the fans who wrote in one week, how many wrote again the next."}},"type":"object","required":["asOf","timezone","fans","renewal","spendBands","activity","retention"],"title":"AudienceReport","description":"Who the fans are: subscription status, renewal, spend and activity. New fields may be added.","examples":[{"asOf":"2026-09-26T14:00:02.000Z","timezone":"Europe/London","fans":{"total":2841,"active":2210,"expired":520,"unknown":111,"spenders":1439},"renewal":{"on":1892,"off":318},"spendBands":[{"band":"none","fans":1402},{"band":"under_50","fans":1022},{"band":"50_to_200","fans":314},{"band":"200_plus","fans":103}],"activity":{"last7Days":640,"last30Days":1311,"last90Days":1950},"retention":{"previousStart":"2026-09-12","previousEnd":"2026-09-18","currentStart":"2026-09-19","currentEnd":"2026-09-25","previousFans":580,"retainedFans":402,"rate":0.693}}]},"AudienceRetention":{"properties":{"previousStart":{"type":"string","description":"First day of the earlier week (`YYYY-MM-DD`)."},"previousEnd":{"type":"string","description":"Last day of the earlier week."},"currentStart":{"type":"string","description":"First day of the later week."},"currentEnd":{"type":"string","description":"Last day of the later week."},"previousFans":{"type":"integer","description":"Fans who wrote in the earlier week."},"retainedFans":{"type":"integer","description":"Of those, how many wrote again in the later week."},"rate":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"`retainedFans / previousFans`, 0 to 1. `null` when nobody wrote in the earlier week."}},"type":"object","required":["previousStart","previousEnd","currentStart","currentEnd","previousFans","retainedFans","rate"],"title":"AudienceRetention"},"Balance":{"properties":{"availableCents":{"type":"integer","description":"What OnlyFans shows as available to withdraw now, across the creators, in cents."},"pendingCents":{"type":"integer","description":"What OnlyFans still holds, in cents."}},"type":"object","required":["availableCents","pendingCents"],"title":"Balance"},"Collection":{"properties":{"id":{"type":"string","description":"The ladder id (`col_…`)."},"folderId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The folder it is filed in, or `null`."},"name":{"type":"string"},"mode":{"type":"string","enum":["sequential","standard"],"description":"`sequential`: the AI offers each fan the first level they have not bought, and never skips ahead. `standard`: the AI may offer any level whenever it fits the conversation."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The theme, in words the AI can use."},"active":{"type":"boolean","description":"`false` pauses the whole ladder: the AI offers none of its levels."},"position":{"type":"integer","description":"Order on the board, from 1."},"levelCount":{"type":"integer","description":"Levels in this ladder."}},"type":"object","required":["id","folderId","name","mode","description","active","position","levelCount"],"title":"Collection"},"ConnectLink":{"properties":{"status":{"type":"string","enum":["active","used","expired","revoked","none"],"description":"`active`: can be opened now. `used`: she connected with it (links are burned on success). `expired`: older than 24 hours. `revoked`: you cancelled it. `none`: no link was ever created."},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The link to send her. Anyone holding it can start the sign-in for this creator, so send it only to her. `null` unless `status` is `active`."},"expiresAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the link stops working (24 hours after creation)."},"createdAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the link was created."},"opens":{"type":"integer","description":"How many times the link was opened. More opens than you expect can mean the link leaked: revoke it and create a new one."},"lastOpenedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When it was last opened."}},"type":"object","required":["status","url","expiresAt","createdAt","opens","lastOpenedAt"],"title":"ConnectLink","examples":[{"status":"active","url":"https://app.onlyx.ai/connect/Qm9vX3RoaXNfaXNfYW5fZXhhbXBsZV90b2tlbl9vbmx5","expiresAt":"2026-09-27T10:03:40.000Z","createdAt":"2026-09-26T10:03:40.000Z","opens":0,"lastOpenedAt":null},{"status":"none","url":null,"expiresAt":null,"createdAt":null,"opens":0,"lastOpenedAt":null}]},"Connection":{"properties":{"creatorId":{"type":"string","description":"The creator this status is for."},"status":{"type":"string","enum":["disconnected","connecting","verification","connected","not_a_creator","duplicate"],"description":"`disconnected`: not signed in (send a connect link). `connecting`: the creator is signing in, or OnlyX is re-establishing the session. `verification`: OnlyFans asked for a human check; resolve it in app.onlyx.ai. `connected`: live. `not_a_creator`: a fan account signed in (final). `duplicate`: this OnlyFans account is already another creator in your workspace (final)."},"connected":{"type":"boolean","description":"`true` only when `status` is `connected`."},"verificationRequired":{"type":"boolean","description":"`true` when OnlyFans wants a human check on the session (`status` is `verification`). Resolve it in app.onlyx.ai; the API reports it but cannot complete it."},"message":{"type":"string","description":"OnlyX's own explanation of the current status, one or two sentences for a person."},"nextStep":{"type":"string","enum":["send_connect_link","wait","complete_verification_in_app","none","delete_and_readd","use_duplicate"],"description":"What to do now. `send_connect_link`: create a connect link and send it to her. `wait`: poll again in 10 seconds or more. `complete_verification_in_app`: open the creator in app.onlyx.ai. `none`: nothing to do. `delete_and_readd`: she signed in with a fan account; delete this creator in the dashboard, add her again and ask her to sign in with her creator account. `use_duplicate`: use the creator in `duplicateOfCreatorId` and delete this copy in the dashboard."},"duplicateOfCreatorId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When `status` is `duplicate`: the creator in your workspace that already holds this OnlyFans account, if this credential can see it. Otherwise `null`."},"sync":{"$ref":"#/components/schemas/Sync","description":"Progress of the sync that runs after she connects."},"checkedAt":{"type":"string","description":"When OnlyX checked this status (ISO-8601 UTC)."}},"type":"object","required":["creatorId","status","connected","verificationRequired","message","nextStep","duplicateOfCreatorId","sync","checkedAt"],"title":"Connection","examples":[{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","status":"connected","connected":true,"verificationRequired":false,"message":"Connected. Her inbox is syncing.","nextStep":"none","duplicateOfCreatorId":null,"sync":{"phase":"initial","progress":35,"steps":[{"key":"conversations","label":"Recent conversations","status":"done"},{"key":"waiting","label":"Fans waiting for a reply","status":"running"},{"key":"vault","label":"Vault media","status":"pending"}]},"checkedAt":"2026-09-26T10:21:07.000Z"}]},"ContentBoard":{"properties":{"creatorId":{"type":"string","description":"The creator this board belongs to."},"folders":{"items":{"$ref":"#/components/schemas/Folder"},"type":"array","description":"Folders, by position."},"collections":{"items":{"$ref":"#/components/schemas/Collection"},"type":"array","description":"Ladders, by position."},"levels":{"items":{"$ref":"#/components/schemas/Level"},"type":"array","description":"Every level of every ladder, by ladder and then by rung."},"vault":{"$ref":"#/components/schemas/VaultUsage","description":"How much of the vault the ladders use."}},"type":"object","required":["creatorId","folders","collections","levels","vault"],"title":"ContentBoard","description":"A creator's whole AI content setup. Every AI content write returns the updated board.","examples":[{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","folders":[{"id":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Sexting","position":1,"collectionCount":1}],"collections":[{"id":"col_2b3c4d5e6f7a8b9c0d1e","folderId":"fld_1a2b3c4d5e6f7a8b9c0d","name":"Shower set","mode":"sequential","description":"A slow tease that ends in the shower.","active":true,"position":1,"levelCount":2}],"levels":[{"id":"set_3c4d5e6f7a8b9c0d1e2f","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Opener","position":1,"priceCents":0,"free":true,"tags":["Solo","Teasing"],"highlight":"the one where I get ready","description":"Two selfies in a towel.","availableInPaidMessages":true,"active":true,"stats":{"sent":40,"purchased":0,"revenueCents":0},"preview":[{"id":"4498297883","type":"photo","durationSeconds":null,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297883/thumbnail"}],"paid":[]},{"id":"set_4d5e6f7a8b9c0d1e2f3a","collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Shower video","position":2,"priceCents":1500,"free":false,"tags":["Shower"],"highlight":null,"description":"Four-minute shower video.","availableInPaidMessages":true,"active":true,"stats":{"sent":31,"purchased":9,"revenueCents":13500},"preview":[],"paid":[{"id":"4498297884","type":"video","durationSeconds":241,"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4498297884/thumbnail"}]}],"vault":{"total":512,"assigned":3,"unassigned":509}}]},"ContentMedia":{"properties":{"id":{"type":"string","description":"An OnlyFans vault media id (a string of digits), or `ox:<id>` for an item of an OnlyX library."},"type":{"anyOf":[{"type":"string","enum":["photo","video","gif","audio"]},{"type":"null"}],"description":"The media type, when known."},"durationSeconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Length of a video or audio item, when known."},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"API URL of a small preview image (needs your credential with `media:read`); `null` when none is available."}},"type":"object","required":["id","type","durationSeconds","thumbnailUrl"],"title":"ContentMedia"},"Conversation":{"properties":{"id":{"type":"string","description":"The conversation id (`cnv_…`)."},"creatorId":{"type":"string","description":"The creator whose inbox this conversation is in."},"fan":{"$ref":"#/components/schemas/ConversationFan","description":"Who the creator is talking to."},"status":{"type":"string","enum":["ai","team","handoff","ai_off"],"description":"Who answers this chat. `ai`: the AI chatter. `team`: your team (the AI is paused on it until `takenOverUntil`). `handoff`: the AI handed it to your team and is silent until the hand-off is resolved. `ai_off`: the AI is switched off for this chat."},"unreadCount":{"type":"integer","description":"Fan messages not yet marked read in OnlyX."},"lastMessage":{"anyOf":[{"$ref":"#/components/schemas/LastMessage"},{"type":"null"}],"description":"The latest message, or `null` for an empty conversation."},"lastMessageAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the latest message was sent."},"takenOverUntil":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"While `status` is `team`: when the chat returns to the AI on its own. Otherwise `null`."},"totalSpentCents":{"type":"integer","description":"Everything this fan has spent on the creator, in cents."}},"type":"object","required":["id","creatorId","fan","status","unreadCount","lastMessage","lastMessageAt","takenOverUntil","totalSpentCents"],"title":"Conversation","examples":[{"id":"cnv_3d9e7b1a5c2f4e8d6a0b","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","fan":{"id":"fan_6b2e8d4a1c9f3e7b5d0a","displayName":"Mike","username":null,"avatarUrl":null},"status":"team","unreadCount":2,"lastMessage":{"text":"are you online tonight?","direction":"in","at":"2026-09-25T21:40:12.000Z"},"lastMessageAt":"2026-09-25T21:40:12.000Z","takenOverUntil":"2026-09-26T09:40:12.000Z","totalSpentCents":48500}]},"ConversationCounts":{"properties":{"total":{"type":"integer","description":"All conversations."},"ai":{"type":"integer","description":"Conversations the AI answers."},"team":{"type":"integer","description":"Conversations your team has taken over."},"handoff":{"type":"integer","description":"Conversations handed off and waiting for your team."},"aiOff":{"type":"integer","description":"Conversations with the AI switched off."},"unread":{"type":"integer","description":"Conversations with at least one unread fan message."}},"type":"object","required":["total","ai","team","handoff","aiOff","unread"],"title":"ConversationCounts","examples":[{"total":1240,"ai":1102,"team":96,"handoff":7,"aiOff":35,"unread":58}]},"ConversationDetail":{"properties":{"id":{"type":"string","description":"The conversation id (`cnv_…`)."},"creatorId":{"type":"string","description":"The creator whose inbox this conversation is in."},"fan":{"$ref":"#/components/schemas/Fan","description":"The fan, in full."},"status":{"type":"string","enum":["ai","team","handoff","ai_off"],"description":"Who answers this chat (see `Conversation.status`)."},"unreadCount":{"type":"integer","description":"Fan messages not yet marked read in OnlyX."},"lastMessage":{"anyOf":[{"$ref":"#/components/schemas/LastMessage"},{"type":"null"}],"description":"The latest message, or `null` for an empty conversation."},"lastMessageAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the latest message was sent."},"takenOverUntil":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"While `status` is `team`: when the chat returns to the AI on its own. Otherwise `null`."},"totalSpentCents":{"type":"integer","description":"Everything this fan has spent on the creator, in cents."},"handoff":{"anyOf":[{"$ref":"#/components/schemas/Handoff"},{"type":"null"}],"description":"The open hand-off on this conversation, or `null`."},"aiCanReply":{"type":"boolean","description":"Whether the AI would answer this chat right now: the creator's AI is on and set up, the chat is `ai`, and she is connected."}},"type":"object","required":["id","creatorId","fan","status","unreadCount","lastMessage","lastMessageAt","takenOverUntil","totalSpentCents","handoff","aiCanReply"],"title":"ConversationDetail","examples":[{"id":"cnv_3d9e7b1a5c2f4e8d6a0b","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","fan":{"id":"fan_6b2e8d4a1c9f3e7b5d0a","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","conversationId":"cnv_3d9e7b1a5c2f4e8d6a0b","displayName":"Mike","customName":"Mike (birthday 14 May)","username":null,"avatarUrl":null,"subscribed":true,"subscribedAt":"2026-03-02T18:21:07.000Z","subscriptionExpiresAt":"2026-10-02T18:21:07.000Z","subscriptionPriceCents":999,"renewOn":true,"totalSpentCents":48500,"tipsCents":12000,"paidMessagesCents":36500,"lastActiveAt":"2026-09-25T21:40:12.000Z","lastPurchaseAt":"2026-09-24T22:03:55.000Z","muted":false,"notes":"Likes lingerie sets. Tips on Fridays.","salesOptOut":false,"lists":[{"id":"lst_9c1d2e3f4a5b6c7d8e9f","name":"VIP"}],"createdAt":"2026-03-02T18:21:07.000Z"},"status":"team","unreadCount":2,"lastMessage":{"text":"are you online tonight?","direction":"in","at":"2026-09-25T21:40:12.000Z"},"lastMessageAt":"2026-09-25T21:40:12.000Z","takenOverUntil":"2026-09-26T09:40:12.000Z","totalSpentCents":48500,"handoff":null,"aiCanReply":false}]},"ConversationFan":{"properties":{"id":{"type":"string","description":"The fan id (`fan_…`)."},"displayName":{"type":"string","description":"The fan's name: the custom name your team gave them, else their OnlyFans name."},"username":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The fan's OnlyFans username, when known."},"avatarUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The fan's OnlyFans avatar, when known."}},"type":"object","required":["id","displayName","avatarUrl"],"title":"ConversationFan"},"ConversationPage":{"properties":{"data":{"items":{"$ref":"#/components/schemas/Conversation"},"type":"array"},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"type":"object","required":["data","hasMore"],"title":"ConversationPage","description":"A page of conversations: `{data, hasMore, nextCursor}`.","examples":[{"data":[{"id":"cnv_3d9e7b1a5c2f4e8d6a0b","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","fan":{"id":"fan_6b2e8d4a1c9f3e7b5d0a","displayName":"Mike","username":null,"avatarUrl":null},"status":"team","unreadCount":2,"lastMessage":{"text":"are you online tonight?","direction":"in","at":"2026-09-25T21:40:12.000Z"},"lastMessageAt":"2026-09-25T21:40:12.000Z","takenOverUntil":"2026-09-26T09:40:12.000Z","totalSpentCents":48500}],"hasMore":true,"nextCursor":"eyJrIjoiY3JlYXRvcnMiLCJwIjp7ImkiOiJjcmVfOGYyYzFhOWIwZDdlNGMzZjJhMWIifX0.Yp3k0Qm7T2vX9aLc4R8sWg"}]},"CreateCollectionRequest":{"properties":{"name":{"type":"string","maxLength":120,"minLength":1,"description":"The ladder name, 1 to 120 characters."},"folderId":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"description":"A folder of the same creator (`404` otherwise)."},"mode":{"type":"string","enum":["sequential","standard"],"description":"`sequential` (default) or `standard`.","default":"sequential"},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"description":"The theme, up to 2,000 characters."},"active":{"type":"boolean","description":"`false` keeps the AI from offering it. Create it switched off and switch it on when it is complete.","default":true}},"additionalProperties":false,"type":"object","required":["name"],"title":"CreateCollectionRequest","examples":[{"name":"Shower set","folderId":"fld_1a2b3c4d5e6f7a8b9c0d","mode":"sequential","description":"A slow tease that ends in the shower.","active":false}]},"CreateCreatorRequest":{"properties":{"displayName":{"type":"string","maxLength":120,"minLength":1,"description":"Your team's label for her, 1 to 120 characters."},"handle":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"description":"Her OnlyFans username, without `@` (a leading `@` is ignored), up to 80 characters. Optional and only a hint: after she connects, her real username replaces it."},"proxyCountry":{"anyOf":[{"type":"string","pattern":"^[A-Za-z]{2}$"},{"type":"null"}],"description":"Optional two-letter country code (ISO 3166-1 alpha-2) for the region OnlyX connects to OnlyFans from on her behalf. Choose the country she usually signs in from. A preference: if nothing is free there, a nearby region is used."}},"additionalProperties":false,"type":"object","required":["displayName"],"title":"CreateCreatorRequest","examples":[{"displayName":"Mia Rose","handle":"miarose","proxyCountry":"US"}]},"CreateFolderRequest":{"properties":{"name":{"type":"string","maxLength":120,"minLength":1,"description":"The folder name, 1 to 120 characters."}},"additionalProperties":false,"type":"object","required":["name"],"title":"CreateFolderRequest","examples":[{"name":"Sexting"}]},"CreateLevelRequest":{"properties":{"collectionId":{"type":"string","maxLength":40,"minLength":1,"description":"The ladder to add the level to. Levels are appended at the end."},"name":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"description":"What your team calls it, up to 120 characters."},"priceCents":{"type":"integer","maximum":1000000.0,"minimum":0.0,"description":"The floor price in cents. `0` makes a free level whose media is given away. Required, so a free level is never made by accident."},"tags":{"items":{"type":"string","maxLength":40,"minLength":1},"type":"array","maxItems":30,"description":"Up to 30 tags of up to 40 characters."},"highlight":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"description":"A hook line, up to 80 characters."},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"description":"What is in it, up to 2,000 characters."},"availableInPaidMessages":{"type":"boolean","description":"`true` (default) lets the AI sell it in chats.","default":true},"active":{"type":"boolean","description":"`false` pauses the level.","default":true}},"additionalProperties":false,"type":"object","required":["collectionId","priceCents"],"title":"CreateLevelRequest","description":"Every field you send is applied on create.","examples":[{"collectionId":"col_2b3c4d5e6f7a8b9c0d1e","name":"Shower video","priceCents":1500,"tags":["Shower","Solo"],"highlight":"the one in the rain shower","description":"Four-minute shower video.","availableInPaidMessages":true,"active":true}]},"CreateTrackingLinkRequest":{"properties":{"kind":{"type":"string","enum":["tracking","trial"],"description":"`tracking` or `trial`."},"name":{"type":"string","maxLength":100,"minLength":1,"description":"The name, 1 to 100 characters, as it will appear on OnlyFans."},"trialDays":{"type":"integer","maximum":365.0,"minimum":1.0,"description":"Trial links only: free-trial length, 1 to 365 days."},"claimsLimit":{"type":"integer","maximum":100000.0,"minimum":1.0,"description":"Trial links only: the most trials it may give, 1 to 100000."},"expiresAt":{"type":"string","format":"date-time","description":"Trial links only: when the link stops working (ISO-8601, in the future)."}},"additionalProperties":false,"type":"object","required":["kind","name"],"title":"CreateTrackingLinkRequest","examples":[{"kind":"tracking","name":"Reddit October"},{"kind":"trial","name":"Twitter trial","trialDays":7,"claimsLimit":100,"expiresAt":"2027-10-31T23:59:00Z"}]},"CreationError":{"properties":{"code":{"type":"string","description":"A stable code for why it failed: `LINK_NAME_TAKEN`, `TRIAL_NOT_ALLOWED`, `LINK_REFUSED` or `CREATION_FAILED`. More may be added."},"message":{"type":"string","description":"Why, as a sentence for a person."}},"type":"object","required":["code","message"],"title":"CreationError"},"Creator":{"properties":{"id":{"type":"string","description":"The creator id (`cre_…`)."},"displayName":{"type":"string","description":"Your team's label for the creator. Set by you, never changed by OnlyFans."},"handle":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Her OnlyFans username, without `@`. Replaced by the real username once she connects."},"publicName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The display name on her OnlyFans profile, once connected."},"avatarUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Her OnlyFans avatar, once connected."},"platform":{"type":"string","enum":["onlyfans","telegram"],"description":"Always `onlyfans` for creators added through the API in v1. `telegram` is reserved for Telegram creators, which are coming soon, so code that reads this should expect more than one value."},"aiEnabled":{"type":"boolean","description":"The creator's master AI switch. `false` means the AI chatter answers none of her fans."},"connection":{"$ref":"#/components/schemas/CreatorConnection","description":"Headline connection state. `GET /v1/creators/{creatorId}/connection` has the detail."},"onlyfans":{"anyOf":[{"$ref":"#/components/schemas/OnlyFansProfile"},{"type":"null"}],"description":"Profile facts read from OnlyFans. `null` until she has connected once."},"stats":{"$ref":"#/components/schemas/CreatorStats","description":"Headline numbers, cheap to read."},"contentConsentRequired":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"`true` when OnlyFans is asking her to accept its content-consent prompt; media sends fail until she does it in OnlyFans. `null` when not known yet."},"createdAt":{"type":"string","description":"When the creator was added to OnlyX (ISO-8601 UTC)."}},"type":"object","required":["id","displayName","handle","publicName","avatarUrl","platform","aiEnabled","connection","onlyfans","stats","contentConsentRequired","createdAt"],"title":"Creator","examples":[{"id":"cre_8f2c1a9b0d7e4c3f2a1b","displayName":"Mia Rose","handle":"miarose","publicName":"Mia","avatarUrl":"https://media.example.com/avatars/miarose.jpg","platform":"onlyfans","aiEnabled":true,"connection":{"status":"connected","connected":true},"onlyfans":{"userId":"412345678","verified":true,"subscribePriceCents":999,"postsCount":812,"photosCount":1540,"videosCount":230},"stats":{"fans":2841,"conversations":1912,"unreadConversations":14,"openHandoffs":2,"revenueCents":1284350,"pendingCents":96420,"revenueKnown":true},"contentConsentRequired":false,"createdAt":"2026-03-02T09:15:44.000Z"}]},"CreatorConnection":{"properties":{"status":{"type":"string","enum":["disconnected","connecting","verification","connected","not_a_creator","duplicate"],"description":"Connection status. `disconnected`: not signed in (send a connect link). `connecting`: the creator is signing in, or OnlyX is re-establishing the session. `verification`: OnlyFans asked for a human check; resolve it in app.onlyx.ai. `connected`: live, syncing and chatting. `not_a_creator`: the account that signed in is not an OnlyFans creator account. `duplicate`: this OnlyFans account is already connected to another creator in your workspace."},"connected":{"type":"boolean","description":"`true` only when `status` is `connected`."}},"type":"object","required":["status","connected"],"title":"CreatorConnection"},"CreatorDetail":{"properties":{"id":{"type":"string","description":"The creator id (`cre_…`)."},"displayName":{"type":"string","description":"Your team's label for the creator. Set by you, never changed by OnlyFans."},"handle":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Her OnlyFans username, without `@`. Replaced by the real username once she connects."},"publicName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The display name on her OnlyFans profile, once connected."},"avatarUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Her OnlyFans avatar, once connected."},"platform":{"type":"string","enum":["onlyfans","telegram"],"description":"Always `onlyfans` for creators added through the API in v1. `telegram` is reserved for Telegram creators, which are coming soon, so code that reads this should expect more than one value."},"aiEnabled":{"type":"boolean","description":"The creator's master AI switch. `false` means the AI chatter answers none of her fans."},"connection":{"$ref":"#/components/schemas/CreatorConnection","description":"Headline connection state. `GET /v1/creators/{creatorId}/connection` has the detail."},"onlyfans":{"anyOf":[{"$ref":"#/components/schemas/OnlyFansProfile"},{"type":"null"}],"description":"Profile facts read from OnlyFans. `null` until she has connected once."},"stats":{"$ref":"#/components/schemas/CreatorStats","description":"Headline numbers, cheap to read."},"contentConsentRequired":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"`true` when OnlyFans is asking her to accept its content-consent prompt; media sends fail until she does it in OnlyFans. `null` when not known yet."},"createdAt":{"type":"string","description":"When the creator was added to OnlyX (ISO-8601 UTC)."},"readiness":{"$ref":"#/components/schemas/Readiness"}},"type":"object","required":["id","displayName","handle","publicName","avatarUrl","platform","aiEnabled","connection","onlyfans","stats","contentConsentRequired","createdAt","readiness"],"title":"CreatorDetail","examples":[{"id":"cre_8f2c1a9b0d7e4c3f2a1b","displayName":"Mia Rose","handle":"miarose","publicName":"Mia","avatarUrl":"https://media.example.com/avatars/miarose.jpg","platform":"onlyfans","aiEnabled":true,"connection":{"status":"connected","connected":true},"onlyfans":{"userId":"412345678","verified":true,"subscribePriceCents":999,"postsCount":812,"photosCount":1540,"videosCount":230},"stats":{"fans":2841,"conversations":1912,"unreadConversations":14,"openHandoffs":2,"revenueCents":1284350,"pendingCents":96420,"revenueKnown":true},"contentConsentRequired":false,"createdAt":"2026-03-02T09:15:44.000Z","readiness":{"ready":false,"checks":[{"key":"connection","label":"OnlyFans connected","ok":true,"hint":null},{"key":"persona","label":"Persona","ok":true,"hint":null},{"key":"limits","label":"Hard limits","ok":false,"hint":"Add the things she never does."},{"key":"fans","label":"Fans synced","ok":true,"hint":null},{"key":"content","label":"Content mapped","ok":false,"hint":"Add a priced level with media to her AI content."}]}}]},"CreatorPage":{"properties":{"data":{"items":{"$ref":"#/components/schemas/Creator"},"type":"array"},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"type":"object","required":["data","hasMore"],"title":"CreatorPage","description":"A page of creators.","examples":[{"data":[{"id":"cre_8f2c1a9b0d7e4c3f2a1b","displayName":"Mia Rose","handle":"miarose","publicName":"Mia","avatarUrl":"https://media.example.com/avatars/miarose.jpg","platform":"onlyfans","aiEnabled":true,"connection":{"status":"connected","connected":true},"onlyfans":{"userId":"412345678","verified":true,"subscribePriceCents":999,"postsCount":812,"photosCount":1540,"videosCount":230},"stats":{"fans":2841,"conversations":1912,"unreadConversations":14,"openHandoffs":2,"revenueCents":1284350,"pendingCents":96420,"revenueKnown":true},"contentConsentRequired":false,"createdAt":"2026-03-02T09:15:44.000Z"}],"hasMore":true,"nextCursor":"eyJrIjoiY3JlYXRvcnMiLCJwIjp7ImkiOiJjcmVfOGYyYzFhOWIwZDdlNGMzZjJhMWIifX0.Yp3k0Qm7T2vX9aLc4R8sWg"}]},"CreatorStats":{"properties":{"fans":{"type":"integer","description":"Fans OnlyX knows for this creator (active and expired)."},"conversations":{"type":"integer","description":"Conversations in her inbox."},"unreadConversations":{"type":"integer","description":"Conversations with at least one unread fan message."},"openHandoffs":{"type":"integer","description":"Conversations handed off to your team and waiting for a person."},"revenueCents":{"type":"integer","description":"Lifetime net earnings read from OnlyFans, in cents."},"pendingCents":{"type":"integer","description":"Earnings OnlyFans still holds as pending, in cents."},"revenueKnown":{"type":"boolean","description":"`false` when her earnings could not be read from OnlyFans. While `false`, treat `revenueCents` as unknown, not zero."}},"type":"object","required":["fans","conversations","unreadConversations","openHandoffs","revenueCents","pendingCents","revenueKnown"],"title":"CreatorStats"},"Credential":{"properties":{"kind":{"type":"string","enum":["api_key","oauth"],"description":"`api_key`, or `oauth` for a connected app's token."},"id":{"type":"string","description":"The API key id (`apk_…`) or the connected app's grant id (`oag_…`)."},"name":{"type":"string","description":"The key's name, or the connected app's name."},"scopes":{"items":{"type":"string"},"type":"array","description":"Scopes this credential holds, including the implicit `workspace:read`."},"creatorIds":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"description":"The creators this credential is limited to, or `null` for every creator in the workspace."}},"type":"object","required":["kind","id","name","scopes","creatorIds"],"title":"Credential"},"DayComparison":{"properties":{"netCents":{"type":"integer","description":"Net earnings (after OnlyFans' fee), in cents."},"newFans":{"type":"integer","description":"Fans whose relationship with a creator started that day."},"buyers":{"type":"integer","description":"Distinct fans who paid anything."},"paidMessagesUnlocked":{"type":"integer","description":"Paid messages unlocked."}},"type":"object","required":["netCents","newFans","buyers","paidMessagesUnlocked"],"title":"DayComparison","description":"The same figures for yesterday — up to this time of day, or in full."},"Delivery":{"properties":{"status":{"type":"string","enum":["queued","sent","failed","unconfirmed","not_sent"],"description":"`queued`: accepted, not sent yet. `sent`: delivered to OnlyFans. `failed`: it did not go out (`reason` says why). `unconfirmed`: it was sent but OnlyFans gave no receipt; the fan usually got it, so never resend it automatically. `not_sent`: deliberately not sent (live sending is off for the workspace, or it was withdrawn before sending)."},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Why it failed or was not sent, as a sentence for a person. `null` otherwise."}},"type":"object","required":["status","reason"],"title":"Delivery"},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","requestId"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code."},"message":{"type":"string","description":"What went wrong, in a sentence for a person."},"requestId":{"type":"string","description":"The id of this request (also in the `X-Request-Id` header). Quote it to support."}}}}},"Fan":{"properties":{"id":{"type":"string","description":"The fan id (`fan_…`). A fan belongs to one creator."},"creatorId":{"type":"string","description":"The creator this fan subscribes (or subscribed) to."},"conversationId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The conversation with this fan, or `null` if there is none yet."},"displayName":{"type":"string","description":"The fan's OnlyFans display name."},"customName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The name your team gave this fan in OnlyX. Only your team sees it."},"username":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The fan's OnlyFans username, when known."},"avatarUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The fan's OnlyFans avatar, when known."},"subscribed":{"type":"boolean","description":"`true` while the subscription is active."},"subscribedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the current (or last) subscription started."},"subscriptionExpiresAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the current subscription ends or ended."},"subscriptionPriceCents":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"What the fan pays per period, in cents. `0` on a free page."},"renewOn":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Whether auto-renew is on. `null` when OnlyFans has not said."},"totalSpentCents":{"type":"integer","description":"Everything the fan has spent on this creator, in cents."},"tipsCents":{"type":"integer","description":"Tips, in cents."},"paidMessagesCents":{"type":"integer","description":"Paid-message purchases, in cents."},"lastActiveAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The fan's last activity OnlyX saw (a message or a purchase)."},"lastPurchaseAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The fan's last purchase."},"muted":{"type":"boolean","description":"Your team's mute flag in OnlyX. It never mutes or blocks the fan on OnlyFans."},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Your team's notes on this fan."},"salesOptOut":{"type":"boolean","description":"`true` when the fan asked not to be sold to. Paid messages to this fan are refused with `409 SALES_OPTED_OUT`, and the AI does not sell to them."},"lists":{"items":{"$ref":"#/components/schemas/FanListRef"},"type":"array","description":"The creator's fan lists this fan is on."},"createdAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When OnlyX first saw this fan."}},"type":"object","required":["id","creatorId","conversationId","displayName","customName","avatarUrl","subscribed","subscribedAt","subscriptionExpiresAt","subscriptionPriceCents","renewOn","totalSpentCents","tipsCents","paidMessagesCents","lastActiveAt","lastPurchaseAt","muted","notes","salesOptOut","lists","createdAt"],"title":"Fan","examples":[{"id":"fan_6b2e8d4a1c9f3e7b5d0a","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","conversationId":"cnv_3d9e7b1a5c2f4e8d6a0b","displayName":"Mike","customName":"Mike (birthday 14 May)","username":null,"avatarUrl":null,"subscribed":true,"subscribedAt":"2026-03-02T18:21:07.000Z","subscriptionExpiresAt":"2026-10-02T18:21:07.000Z","subscriptionPriceCents":999,"renewOn":true,"totalSpentCents":48500,"tipsCents":12000,"paidMessagesCents":36500,"lastActiveAt":"2026-09-25T21:40:12.000Z","lastPurchaseAt":"2026-09-24T22:03:55.000Z","muted":false,"notes":"Likes lingerie sets. Tips on Fridays.","salesOptOut":false,"lists":[{"id":"lst_9c1d2e3f4a5b6c7d8e9f","name":"VIP"}],"createdAt":"2026-03-02T18:21:07.000Z"}]},"FanList":{"properties":{"id":{"type":"string","description":"The fan list id (`lst_…`)."},"name":{"type":"string","description":"The list name as shown on OnlyFans."},"kind":{"type":"string","description":"What kind of list it is, for example `subscribers`, `rebill_off` (renew off) or `custom` (a list the creator made). More values may be added."},"creatorId":{"type":"string","description":"The creator the list belongs to."},"count":{"type":"integer","description":"How many fans are on the list (OnlyFans' own count when known)."}},"type":"object","required":["id","name","kind","creatorId","count"],"title":"FanList","examples":[{"id":"lst_9c1d2e3f4a5b6c7d8e9f","name":"VIP","kind":"custom","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","count":42}]},"FanListPage":{"properties":{"data":{"items":{"$ref":"#/components/schemas/FanList"},"type":"array"},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"type":"object","required":["data","hasMore"],"title":"FanListPage","description":"A page of fan lists: `{data, hasMore, nextCursor}`.","examples":[{"data":[{"id":"lst_9c1d2e3f4a5b6c7d8e9f","name":"VIP","kind":"custom","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","count":42}],"hasMore":true,"nextCursor":"eyJrIjoiY3JlYXRvcnMiLCJwIjp7ImkiOiJjcmVfOGYyYzFhOWIwZDdlNGMzZjJhMWIifX0.Yp3k0Qm7T2vX9aLc4R8sWg"}]},"FanListRef":{"properties":{"id":{"type":"string","description":"The fan list id (`lst_…`)."},"name":{"type":"string","description":"The list name."}},"type":"object","required":["id","name"],"title":"FanListRef"},"FanPage":{"properties":{"data":{"items":{"$ref":"#/components/schemas/Fan"},"type":"array"},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"type":"object","required":["data","hasMore"],"title":"FanPage","description":"A page of fans: `{data, hasMore, nextCursor}`.","examples":[{"data":[{"id":"fan_6b2e8d4a1c9f3e7b5d0a","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","conversationId":"cnv_3d9e7b1a5c2f4e8d6a0b","displayName":"Mike","customName":"Mike (birthday 14 May)","username":null,"avatarUrl":null,"subscribed":true,"subscribedAt":"2026-03-02T18:21:07.000Z","subscriptionExpiresAt":"2026-10-02T18:21:07.000Z","subscriptionPriceCents":999,"renewOn":true,"totalSpentCents":48500,"tipsCents":12000,"paidMessagesCents":36500,"lastActiveAt":"2026-09-25T21:40:12.000Z","lastPurchaseAt":"2026-09-24T22:03:55.000Z","muted":false,"notes":"Likes lingerie sets. Tips on Fridays.","salesOptOut":false,"lists":[{"id":"lst_9c1d2e3f4a5b6c7d8e9f","name":"VIP"}],"createdAt":"2026-03-02T18:21:07.000Z"}],"hasMore":true,"nextCursor":"eyJrIjoiY3JlYXRvcnMiLCJwIjp7ImkiOiJjcmVfOGYyYzFhOWIwZDdlNGMzZjJhMWIifX0.Yp3k0Qm7T2vX9aLc4R8sWg"}]},"Folder":{"properties":{"id":{"type":"string","description":"The folder id (`fld_…`)."},"name":{"type":"string"},"position":{"type":"integer","description":"Order on the board, from 1."},"collectionCount":{"type":"integer","description":"Ladders in this folder."}},"type":"object","required":["id","name","position","collectionCount"],"title":"Folder"},"FollowUps":{"properties":{"enabled":{"type":"boolean","description":"Your follow-up setting."},"effective":{"type":"boolean","description":"Whether follow-ups actually go out now: `enabled` and the creator's AI is on."},"maxPerFanPerDay":{"type":"integer","description":"At most this many follow-ups per fan per day, 1 to 4."},"unansweredAfterHours":{"type":"integer","description":"How long a fan stays silent before a follow-up is considered."},"plannedToday":{"type":"integer","description":"Follow-ups planned today (her time zone)."},"sentToday":{"type":"integer","description":"Follow-ups sent today (her time zone)."}},"type":"object","required":["enabled","effective","maxPerFanPerDay","unansweredAfterHours","plannedToday","sentToday"],"title":"FollowUps","description":"The AI's own follow-up messages to fans who went quiet. These are real messages to fans."},"FollowUpsPatch":{"properties":{"enabled":{"type":"boolean","description":"Switch follow-ups on or off."},"maxPerFanPerDay":{"type":"integer","maximum":4.0,"minimum":1.0,"description":"1 to 4."}},"additionalProperties":false,"type":"object","title":"FollowUpsPatch"},"Handoff":{"properties":{"id":{"type":"string","description":"The hand-off id (`esc_…`)."},"conversationId":{"type":"string","description":"The conversation that was handed off."},"creatorId":{"type":"string","description":"The creator of that conversation."},"fanName":{"type":"string","description":"The fan's name, for display."},"reason":{"type":"string","description":"Why the AI handed off, as a stable code: `welfare`, `prohibited_request`, `real_world`, `payment_dispute`, `custom_request`, `whale_risk`, `confusion` or `failed_delivery`. More values may be added."},"label":{"type":"string","description":"The specific situation, for people (for example \"Asks for a refund\")."},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A short summary of the situation when it was raised, or your team's note once resolved with one. `null` when there is none."},"status":{"type":"string","enum":["open","resolved"],"description":"`open` until your team resolves it."},"createdAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the conversation was handed off."}},"type":"object","required":["id","conversationId","creatorId","fanName","reason","label","note","status","createdAt"],"title":"Handoff","examples":[{"id":"esc_5e1f2a3b4c5d6e7f8a9b","conversationId":"cnv_3d9e7b1a5c2f4e8d6a0b","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","fanName":"Mike","reason":"payment_dispute","label":"Asks for a refund","note":"He says the video he bought does not play and wants his money back.","status":"open","createdAt":"2026-09-25T21:41:03.000Z"}]},"HandoffKind":{"properties":{"key":{"type":"string","description":"The kind's stable key, for `handoffKinds`."},"reason":{"type":"string","description":"The hand-off `reason` this kind reports (`welfare`, `payment_dispute`, …)."},"label":{"type":"string","description":"What the situation is, for people."},"default":{"type":"boolean","description":"Whether the kind is in the recommended set."},"locked":{"type":"boolean","description":"`true` for kinds that always hand off and cannot be switched off."}},"type":"object","required":["key","reason","label","default","locked"],"title":"HandoffKind"},"HandoffPage":{"properties":{"data":{"items":{"$ref":"#/components/schemas/Handoff"},"type":"array"},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"type":"object","required":["data","hasMore"],"title":"HandoffPage","description":"A page of open hand-offs: `{data, hasMore, nextCursor}`.","examples":[{"data":[{"id":"esc_5e1f2a3b4c5d6e7f8a9b","conversationId":"cnv_3d9e7b1a5c2f4e8d6a0b","creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","fanName":"Mike","reason":"payment_dispute","label":"Asks for a refund","note":"He says the video he bought does not play and wants his money back.","status":"open","createdAt":"2026-09-25T21:41:03.000Z"}],"hasMore":true,"nextCursor":"eyJrIjoiY3JlYXRvcnMiLCJwIjp7ImkiOiJjcmVfOGYyYzFhOWIwZDdlNGMzZjJhMWIifX0.Yp3k0Qm7T2vX9aLc4R8sWg"}]},"LastMessage":{"properties":{"text":{"type":"string","description":"The message text, shortened for a preview (may be empty for a media-only message)."},"direction":{"type":"string","enum":["in","out"],"description":"`in` from the fan, `out` to the fan."},"at":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When it was sent."}},"type":"object","required":["text","direction","at"],"title":"LastMessage"},"Level":{"properties":{"id":{"type":"string","description":"The level id (`set_…`)."},"collectionId":{"type":"string","description":"The ladder it belongs to."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"What your team calls it."},"position":{"type":"integer","description":"Its rung in the ladder, from 1. In a sequential ladder, the order fans climb."},"priceCents":{"type":"integer","description":"The floor price in cents: the AI may sell higher, never lower. `0` makes the level free."},"free":{"type":"boolean","description":"`true` when the level is free: everything on it is given away as preview media."},"tags":{"items":{"type":"string"},"type":"array","description":"Tags matched against what fans talk about."},"highlight":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A hook line the AI can lead with."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"What is in it, in words the AI can use."},"availableInPaidMessages":{"type":"boolean","description":"`false` keeps the level on the board but out of chats."},"active":{"type":"boolean","description":"`false` pauses this level."},"stats":{"$ref":"#/components/schemas/LevelStats","description":"AI-attributed: what the AI chatter offered and sold from this level."},"preview":{"items":{"$ref":"#/components/schemas/ContentMedia"},"type":"array","description":"Free teaser media, in the order fans see it."},"paid":{"items":{"$ref":"#/components/schemas/ContentMedia"},"type":"array","description":"Media locked behind the price, in unlock order."}},"type":"object","required":["id","collectionId","name","position","priceCents","free","tags","highlight","description","availableInPaidMessages","active","stats","preview","paid"],"title":"Level"},"LevelStats":{"properties":{"sent":{"type":"integer","description":"Times the AI offered it."},"purchased":{"type":"integer","description":"Times fans bought it from the AI."},"revenueCents":{"type":"integer","description":"What those AI sales earned, in cents."}},"type":"object","required":["sent","purchased","revenueCents"],"title":"LevelStats","description":"Sales by the AI chatter only: offers and sales your team made are not counted here."},"MassMessages":{"properties":{"enabled":{"type":"boolean","description":"Your mass-message setting."},"effective":{"type":"boolean","description":"Whether mass messages actually go out now."},"perDay":{"type":"integer","description":"Mass messages per day."},"nextSendAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the next one is planned, or `null`."}},"type":"object","required":["enabled","effective","perDay","nextSendAt"],"title":"MassMessages","description":"The AI's scheduled messages to fan lists. Read-only in v1: configure them in the dashboard."},"Me":{"properties":{"workspace":{"$ref":"#/components/schemas/Workspace"},"credential":{"$ref":"#/components/schemas/Credential"},"rateLimit":{"$ref":"#/components/schemas/RateLimit"}},"type":"object","required":["workspace","credential","rateLimit"],"title":"Me","examples":[{"workspace":{"id":"agc_4f1c9e0b2d7a8c3e5f60","name":"Velvet Agency","timezone":"Europe/London"},"credential":{"kind":"api_key","id":"apk_9a8b7c6d5e4f3a2b1c0d","name":"Reporting script","scopes":["workspace:read","creators:read","stats:read"],"creatorIds":["cre_8f2c1a9b0d7e4c3f2a1b"]},"rateLimit":{"limit":120,"windowSeconds":60}}]},"Message":{"properties":{"id":{"type":"string","description":"The message id (`msg_…`)."},"conversationId":{"type":"string","description":"The conversation it belongs to."},"direction":{"type":"string","enum":["in","out"],"description":"`in` from the fan, `out` to the fan."},"sender":{"type":"string","enum":["fan","ai","team"],"description":"Who wrote it: the fan, the AI chatter, or your team (including anything sent through the API)."},"text":{"type":"string","description":"The message text. May be empty when the message is media only."},"media":{"items":{"$ref":"#/components/schemas/MessageMedia"},"type":"array","description":"Media attached to the message."},"paid":{"anyOf":[{"$ref":"#/components/schemas/Paid"},{"type":"null"}],"description":"Set on a paid message, otherwise `null`."},"tipCents":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"A tip that came with this message, in cents, or `null`."},"createdAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the message was sent (or, for a queued send, accepted)."},"delivery":{"anyOf":[{"$ref":"#/components/schemas/Delivery"},{"type":"null"}],"description":"Delivery of an outgoing message. `null` for incoming messages."}},"type":"object","required":["id","conversationId","direction","sender","text","media","paid","tipCents","createdAt","delivery"],"title":"Message","examples":[{"id":"msg_1a2b3c4d5e6f7a8b9c0d","conversationId":"cnv_3d9e7b1a5c2f4e8d6a0b","direction":"out","sender":"team","text":"Made this one just for you 😘","media":[{"id":"4012345678","type":"photo","preview":true},{"id":"4012345679","type":"photo","preview":false}],"paid":{"priceCents":1500,"purchased":false,"purchasedAt":null},"tipCents":null,"createdAt":"2026-09-26T10:02:11.000Z","delivery":{"status":"queued","reason":null}}]},"MessageCounts":{"properties":{"in":{"type":"integer","description":"Messages from fans."},"out":{"type":"integer","description":"Messages delivered to fans (only messages OnlyFans confirmed)."},"ai":{"type":"integer","description":"Of `out`, written by the AI chatter."},"team":{"type":"integer","description":"Of `out`, written by your team (including messages sent through this API)."}},"type":"object","required":["in","out","ai","team"],"title":"MessageCounts"},"MessageMedia":{"properties":{"id":{"type":"string","description":"The vault media id (digits)."},"type":{"anyOf":[{"type":"string","enum":["photo","video","gif","audio"]},{"type":"null"}],"description":"The media type, or `null` when OnlyFans did not say."},"preview":{"type":"boolean","description":"`true` for free media the fan sees unlocked; `false` for media behind the price."}},"type":"object","required":["id","type","preview"],"title":"MessageMedia"},"MessagePage":{"properties":{"data":{"items":{"$ref":"#/components/schemas/Message"},"type":"array","description":"Messages, oldest to newest."},"hasMore":{"type":"boolean","description":"`true` when there are older messages. Fetch them with `before` set to the first (oldest) id on this page."}},"type":"object","required":["data","hasMore"],"title":"MessagePage","description":"`{data, hasMore}`: messages oldest to newest; page back with `before`.","examples":[{"data":[{"id":"msg_1a2b3c4d5e6f7a8b9c0d","conversationId":"cnv_3d9e7b1a5c2f4e8d6a0b","direction":"out","sender":"team","text":"Made this one just for you 😘","media":[{"id":"4012345678","type":"photo","preview":true},{"id":"4012345679","type":"photo","preview":false}],"paid":{"priceCents":1500,"purchased":false,"purchasedAt":null},"tipCents":null,"createdAt":"2026-09-26T10:02:11.000Z","delivery":{"status":"queued","reason":null}}],"hasMore":true}]},"OnlyFansProfile":{"properties":{"userId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Her numeric OnlyFans user id, as a string."},"verified":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Whether OnlyFans shows her as verified."},"subscribePriceCents":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Her current subscription price in cents. `0` for a free page."},"postsCount":{"type":"integer","description":"Posts on her profile."},"photosCount":{"type":"integer","description":"Photos on her profile."},"videosCount":{"type":"integer","description":"Videos on her profile."}},"type":"object","required":["userId","verified","subscribePriceCents","postsCount","photosCount","videosCount"],"title":"OnlyFansProfile"},"OverviewConversations":{"properties":{"active":{"type":"integer","description":"Conversations with at least one message in the window."},"answered":{"type":"integer","description":"Conversations that received at least one delivered message in the window."}},"type":"object","required":["active","answered"],"title":"OverviewConversations"},"OverviewCreator":{"properties":{"creatorId":{"type":"string","description":"The creator (`cre_…`)."},"name":{"type":"string","description":"The creator's display name in OnlyX."},"messagesIn":{"type":"integer","description":"Messages from her fans in the window."},"messagesOut":{"type":"integer","description":"Messages delivered to her fans in the window."},"ai":{"type":"integer","description":"Delivered messages the AI wrote."},"team":{"type":"integer","description":"Delivered messages your team wrote."},"answered":{"type":"integer","description":"Conversations that received a delivered message in the window."},"unread":{"type":"integer","description":"Conversations unread right now."},"medianReplySeconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Median reply time for her fans, in seconds. `null` when nothing was answered."}},"type":"object","required":["creatorId","name","messagesIn","messagesOut","ai","team","answered","unread","medianReplySeconds"],"title":"OverviewCreator"},"OverviewDay":{"properties":{"date":{"type":"string","description":"The day (`YYYY-MM-DD`, workspace time zone)."},"messagesIn":{"type":"integer","description":"Messages from fans that day."},"messagesOut":{"type":"integer","description":"Messages delivered to fans that day."},"newFans":{"type":"integer","description":"Fans whose relationship with a creator started that day."}},"type":"object","required":["date","messagesIn","messagesOut","newFans"],"title":"OverviewDay"},"OverviewFans":{"properties":{"new":{"type":"integer","description":"Fans whose relationship with a creator started in the window."},"unknownStart":{"type":"integer","description":"Fans whose start cannot be dated at all (not counted in `new`)."},"total":{"type":"integer","description":"All fans now (current state, not windowed)."}},"type":"object","required":["new","unknownStart","total"],"title":"OverviewFans"},"OverviewHour":{"properties":{"hour":{"type":"integer","description":"Hour of day, 0-23, workspace time zone."},"messagesIn":{"type":"integer","description":"Messages from fans in this hour of day, over the whole window."},"messagesOut":{"type":"integer","description":"Messages delivered to fans in this hour of day, over the whole window."}},"type":"object","required":["hour","messagesIn","messagesOut"],"title":"OverviewHour"},"OverviewInbox":{"properties":{"unread":{"type":"integer","description":"Conversations with unread fan messages right now (current state, not windowed)."},"handoffs":{"type":"integer","description":"Conversations waiting for your team after the AI handed them off, right now."}},"type":"object","required":["unread","handoffs"],"title":"OverviewInbox"},"OverviewReport":{"properties":{"start":{"type":"string","description":"First day of the window, included."},"end":{"type":"string","description":"Last day of the window, included."},"days":{"type":"integer","description":"Days in the window."},"timezone":{"type":"string"},"generatedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When this cached report was computed (UTC). It may be up to about five minutes old."},"conversations":{"$ref":"#/components/schemas/OverviewConversations"},"messages":{"$ref":"#/components/schemas/MessageCounts"},"aiShare":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Share of delivered messages the AI wrote, 0 to 1. `null` when nothing was sent."},"replyTime":{"$ref":"#/components/schemas/ReplyTime"},"fans":{"$ref":"#/components/schemas/OverviewFans"},"inbox":{"$ref":"#/components/schemas/OverviewInbox","description":"The inbox right now (not windowed)."},"daily":{"items":{"$ref":"#/components/schemas/OverviewDay"},"type":"array","description":"One entry per day of the window, oldest first."},"hourly":{"items":{"$ref":"#/components/schemas/OverviewHour"},"type":"array","description":"Messages by hour of day over the whole window."},"creators":{"items":{"$ref":"#/components/schemas/OverviewCreator"},"type":"array","description":"Per creator."}},"type":"object","required":["start","end","days","timezone","generatedAt","conversations","messages","aiShare","replyTime","fans","inbox","daily","hourly","creators"],"title":"OverviewReport","description":"The dashboard's activity report for a window, served from cache. New fields may be added.","examples":[{"start":"2026-09-20","end":"2026-09-26","days":7,"timezone":"Europe/London","generatedAt":"2026-09-26T13:52:40.000Z","conversations":{"active":1204,"answered":1122},"messages":{"in":8420,"out":9315,"ai":8790,"team":525},"aiShare":0.944,"replyTime":{"medianSeconds":94,"p90Seconds":402,"slowReplies":611,"repliedTo":6230},"fans":{"new":263,"unknownStart":40,"total":28410},"inbox":{"unread":37,"handoffs":9},"daily":[{"date":"2026-09-26","messagesIn":1320,"messagesOut":1488,"newFans":41}],"hourly":[{"hour":21,"messagesIn":702,"messagesOut":760}],"creators":[{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","name":"Mia Rose","messagesIn":8420,"messagesOut":9315,"ai":8790,"team":525,"answered":1122,"unread":37,"medianReplySeconds":94}]}]},"Paid":{"properties":{"priceCents":{"type":"integer","description":"The unlock price, in cents."},"purchased":{"type":"boolean","description":"Whether the fan has unlocked it."},"purchasedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the fan unlocked it."}},"type":"object","required":["priceCents","purchased","purchasedAt"],"title":"Paid"},"Persona":{"properties":{"personaName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The first name she uses with fans. Required for the AI."},"nickname":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A pet name or short name."},"age":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Her age, 18 to 99. Required for the AI."},"city":{"type":"string","description":"Where she says she lives. Required for the AI."},"origin":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Where she is from."},"occupation":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"What she does besides OnlyFans. Required for the AI."},"archetype":{"type":"string","description":"Her overall character, for example `girl_next_door`."},"lore":{"type":"string","description":"Her backstory in her own world: family, home, routine, personality. Required for the AI."},"accountContext":{"type":"string","description":"How her page works: free or paid, what is on the wall, what is sold in messages."},"physicalDescription":{"type":"string","description":"What she looks like, so the AI never contradicts her content."},"interests":{"items":{"type":"string"},"type":"array","description":"Things she likes talking about."},"contentMenu":{"items":{"type":"string"},"type":"array","description":"The kinds of content she makes."},"contentNotes":{"type":"string","description":"Anything else about her content: lengths, what is shown, what is never shown."},"boundaries":{"items":{"type":"string"},"type":"array","description":"Topics she avoids or steers away from, in plain sentences."},"hardLimits":{"items":{"type":"string"},"type":"array","description":"Things she never does or offers, whatever a fan says. The AI treats them as a hard stop."},"exemplars":{"items":{"type":"string"},"type":"array","description":"Sample messages in her voice. The strongest single influence on how the AI sounds."},"primaryLanguage":{"type":"string","description":"The language she chats in, as a BCP-47 tag (`en`, `es`, `pt-BR`)."},"additionalLanguages":{"items":{"type":"string"},"type":"array","description":"Other languages she can chat in (BCP-47 tags)."},"timezone":{"type":"string","description":"Her IANA time zone (`America/Chicago`). The AI's sense of her local time uses it."},"capitalization":{"type":"string","description":"How she capitalizes: `normal`, all `lowercase`, or a `casual_mix`."},"punctuation":{"type":"string","description":"How carefully she punctuates: `proper`, `relaxed` or `minimal`."},"messageSplitting":{"type":"string","description":"How often one reply is split into several short messages: `never`, `sometimes`, `often`."},"typingSpeed":{"type":"string","description":"How quickly replies go out, like a person typing: `fast`, `natural`, `slow`."},"emojiPolicy":{"type":"string","description":"How many emojis she uses: `none`, `rare`, `some`, `heavy`."},"allowTypos":{"type":"boolean","description":"Whether the AI may make an occasional natural typo."},"customContentEnabled":{"type":"boolean","description":"Whether the AI may quote custom content at the prices below."},"priceCustomPhotoCents":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Minimum price for a custom photo, in cents."},"priceCustomVideoPerMinCents":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Minimum price per minute of custom video, in cents."},"priceCustomVideoMinCents":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Minimum total price for any custom video, in cents."},"customPhotoDeliveryDays":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Days she needs to deliver a custom photo. `null` uses the default of 3."},"customVideoDeliveryDays":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Days she needs to deliver a custom video. `null` uses the default of 7."},"aiDisclosureEnabled":{"type":"boolean","description":"Whether fans are told they may be talking to an AI."},"aiDisclosureText":{"type":"string","description":"The disclosure fans see when `aiDisclosureEnabled` is on."},"handoffKinds":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"description":"The hand-off kinds (keys from `handoffCatalogue`) that make the AI hand a chat to your team. `null` means the recommended set; `[]` means only the kinds that can never be switched off."},"handoffEffective":{"items":{"type":"string"},"type":"array","description":"The hand-off kinds actually in force: your `handoffKinds` (or the recommended set) plus the locked kinds."},"handoffCatalogue":{"items":{"$ref":"#/components/schemas/HandoffKind"},"type":"array","description":"Every hand-off kind you can choose from (the example shows three of them)."},"completeness":{"type":"integer","description":"How complete the persona is, 0 to 100."},"readyForAi":{"type":"boolean","description":"`true` when every required field is filled: `personaName`, `age`, `city`, `occupation` and `lore`."},"requiredMissing":{"items":{"type":"string"},"type":"array","description":"Required fields that are still empty."},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the persona was last changed; `null` if it was never written."}},"type":"object","required":["personaName","nickname","age","city","origin","occupation","archetype","lore","accountContext","physicalDescription","interests","contentMenu","contentNotes","boundaries","hardLimits","exemplars","primaryLanguage","additionalLanguages","timezone","capitalization","punctuation","messageSplitting","typingSpeed","emojiPolicy","allowTypos","customContentEnabled","priceCustomPhotoCents","priceCustomVideoPerMinCents","priceCustomVideoMinCents","customPhotoDeliveryDays","customVideoDeliveryDays","aiDisclosureEnabled","aiDisclosureText","handoffKinds","handoffEffective","handoffCatalogue","completeness","readyForAi","requiredMissing","updatedAt"],"title":"Persona","description":"The AI persona: the brief the AI chatter follows to chat as this creator. Every field is\nwritten by your team.","examples":[{"personaName":"Mia","nickname":"Mimi","age":24,"city":"Austin","origin":"San Diego","occupation":"Yoga instructor","archetype":"girl_next_door","lore":"Grew up by the beach, moved to Austin for college and stayed for the music.","accountContext":"Paid page. The wall has photos; videos are sold in messages.","physicalDescription":"Blonde, 5'6\", athletic, small tattoo on her left wrist.","interests":["hiking","live music","tacos"],"contentMenu":["lingerie photos","shower videos"],"contentNotes":"No face in explicit videos.","boundaries":["Does not talk about her family."],"hardLimits":["No meetups.","No custom videos with other people."],"exemplars":["heyy you 😘 how was your day?"],"primaryLanguage":"en","additionalLanguages":["es"],"timezone":"America/Chicago","capitalization":"lowercase","punctuation":"relaxed","messageSplitting":"sometimes","typingSpeed":"natural","emojiPolicy":"some","allowTypos":true,"customContentEnabled":true,"priceCustomPhotoCents":2500,"priceCustomVideoPerMinCents":1000,"priceCustomVideoMinCents":5000,"customPhotoDeliveryDays":3,"customVideoDeliveryDays":7,"aiDisclosureEnabled":false,"aiDisclosureText":"","handoffKinds":null,"handoffEffective":["welfare.self_harm","welfare.immediate_danger","prohibited.underage","prohibited.animals_illegal","real_world.stalking","real_world.threat","payment.fraud_threat","payment.refund_demand","payment.double_charge","payment.not_received","payment.custom_overdue","whale.broken_promise","whale.feels_scammed"],"handoffCatalogue":[{"key":"welfare.self_harm","reason":"welfare","label":"Suicidal or self-harm language","default":true,"locked":true},{"key":"payment.refund_demand","reason":"payment_dispute","label":"Asks for a refund","default":true,"locked":false},{"key":"real_world.meet_request","reason":"real_world","label":"Asks to meet","default":false,"locked":false}],"completeness":86,"readyForAi":true,"requiredMissing":[],"updatedAt":"2026-09-26T14:02:31.000Z"}]},"Purchase":{"properties":{"type":{"type":"string","enum":["ppv","tip"],"description":"`ppv`: a paid-message unlock (`messageId` names the message). `tip`: a tip. Subscription payments are in the creator's transaction ledger, not here. More values may be added: ignore ones you do not know."},"amountCents":{"type":"integer","description":"What the fan paid, in cents."},"at":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the purchase happened."},"messageId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"For a `ppv`, the message that was unlocked, when known. Otherwise `null`."}},"type":"object","required":["type","amountCents","at","messageId"],"title":"Purchase","examples":[{"type":"ppv","amountCents":2500,"at":"2026-09-24T22:03:55.000Z","messageId":"msg_1a2b3c4d5e6f7a8b9c0d"}]},"PurchasePage":{"properties":{"data":{"items":{"$ref":"#/components/schemas/Purchase"},"type":"array"},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"type":"object","required":["data","hasMore"],"title":"PurchasePage","description":"A page of purchases: `{data, hasMore, nextCursor}`.","examples":[{"data":[{"type":"ppv","amountCents":2500,"at":"2026-09-24T22:03:55.000Z","messageId":"msg_1a2b3c4d5e6f7a8b9c0d"}],"hasMore":true,"nextCursor":"eyJrIjoiY3JlYXRvcnMiLCJwIjp7ImkiOiJjcmVfOGYyYzFhOWIwZDdlNGMzZjJhMWIifX0.Yp3k0Qm7T2vX9aLc4R8sWg"}]},"RateLimit":{"properties":{"limit":{"type":"integer","description":"Requests allowed per window with this credential (the default bucket)."},"windowSeconds":{"type":"integer"}},"type":"object","required":["limit","windowSeconds"],"title":"RateLimit"},"Readiness":{"properties":{"ready":{"type":"boolean","description":"`true` when every check passes and the AI is set up well enough to chat and sell."},"checks":{"items":{"$ref":"#/components/schemas/ReadinessCheck"},"type":"array","description":"Individual readiness checks."}},"type":"object","required":["ready","checks"],"title":"Readiness"},"ReadinessCheck":{"properties":{"key":{"type":"string","description":"Stable check key: `connection`, `persona`, `limits`, `fans` or `content`. More may be added."},"label":{"type":"string","description":"The check's name, for people."},"ok":{"type":"boolean","description":"Whether the check passes."},"hint":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"What to do when it fails, for people. `null` when it passes."}},"type":"object","required":["key","label","ok","hint"],"title":"ReadinessCheck"},"ReorderLevelMediaRequest":{"properties":{"role":{"type":"string","enum":["paid","preview"],"description":"Which side of the level to reorder."},"mediaIds":{"items":{"type":"string","maxLength":40,"minLength":1,"description":"An OnlyFans vault media id (a string of digits), or `ox:<id>` for an item of an OnlyX library."},"type":"array","maxItems":500,"minItems":1,"description":"Media ids of that side in the new order; any you leave out follow them."}},"additionalProperties":false,"type":"object","required":["role","mediaIds"],"title":"ReorderLevelMediaRequest","examples":[{"role":"preview","mediaIds":["4498297885","4498297883"]}]},"ReorderLevelsRequest":{"properties":{"levelIds":{"items":{"type":"string","maxLength":40,"minLength":1},"type":"array","maxItems":100,"minItems":1,"description":"The ladder's level ids in the new order. Listed levels come first; any you leave out keep their order after them."}},"additionalProperties":false,"type":"object","required":["levelIds"],"title":"ReorderLevelsRequest","examples":[{"levelIds":["set_4d5e6f7a8b9c0d1e2f3a","set_3c4d5e6f7a8b9c0d1e2f"]}]},"ReplyTime":{"properties":{"medianSeconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Median time from a fan's message to the answer, in seconds. `null` when nothing was answered."},"p90Seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"90th percentile of the same — the slow tail the median hides."},"slowReplies":{"type":"integer","description":"Answers that took longer than 5 minutes."},"repliedTo":{"type":"integer","description":"Fan messages answered in the window (a run of fan messages counts once)."}},"type":"object","required":["medianSeconds","p90Seconds","slowReplies","repliedTo"],"title":"ReplyTime"},"ReportWarming":{"properties":{"status":{"type":"string","const":"warming","description":"Always `warming`."},"retryAfterSeconds":{"type":"integer","minimum":1.0,"description":"Ask again after this many seconds (also in the `Retry-After` header)."}},"type":"object","required":["status","retryAfterSeconds"],"title":"ReportWarming","description":"Not an error (this answer is named `REPORT_WARMING`): the report is not cached yet and is being prepared.","examples":[{"status":"warming","retryAfterSeconds":15}]},"ResolveHandoffRequest":{"properties":{"note":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}],"description":"Optional: what your team did, up to 300 characters."}},"additionalProperties":false,"type":"object","title":"ResolveHandoffRequest","examples":[{"note":"Re-sent the video as a free message; fan confirmed it plays."}]},"RevenueBySeller":{"properties":{"ai":{"$ref":"#/components/schemas/SellerSales","description":"Paid messages the AI chatter sent that fans unlocked in the window."},"team":{"$ref":"#/components/schemas/SellerSales","description":"Paid messages your team sent that fans unlocked in the window."},"unattributedNetCents":{"type":"integer","description":"Paid-message earnings in the window that no chat sender accounts for — mass messages, and purchases of messages sent more than 30 days before."}},"type":"object","required":["ai","team","unattributedNetCents"],"title":"RevenueBySeller"},"RevenueByType":{"properties":{"type":{"type":"string","description":"What was paid for: `subscription`, `ppv` (paid messages), `tip`, `post`, `stream` or `other`. More values may be added."},"netCents":{"type":"integer","description":"Net earnings of this type in the window, after OnlyFans' fee, in cents."},"grossCents":{"type":"integer","description":"What fans paid for this type in the window, in cents."}},"type":"object","required":["type","netCents","grossCents"],"title":"RevenueByType"},"RevenueCreator":{"properties":{"creatorId":{"type":"string","description":"The creator (`cre_…`)."},"name":{"type":"string","description":"The creator's display name in OnlyX."},"netCents":{"type":"integer","description":"Her net earnings in the window, after OnlyFans' fee, in cents."},"grossCents":{"type":"integer","description":"What her fans paid in the window, in cents."},"transactions":{"type":"integer","description":"Sales counted for her in the window."},"spenders":{"type":"integer","description":"Distinct fans who paid her anything in the window."}},"type":"object","required":["creatorId","name","netCents","grossCents","transactions","spenders"],"title":"RevenueCreator"},"RevenueDay":{"properties":{"date":{"type":"string","description":"The day (`YYYY-MM-DD`, workspace time zone)."},"netCents":{"type":"integer","description":"Net earnings that day, after OnlyFans' fee, in cents."},"grossCents":{"type":"integer","description":"What fans paid that day, in cents."}},"type":"object","required":["date","netCents","grossCents"],"title":"RevenueDay"},"RevenueReport":{"properties":{"start":{"type":"string","description":"First day of the window (`YYYY-MM-DD`), included."},"end":{"type":"string","description":"Last day of the window, included."},"timezone":{"type":"string","description":"The workspace time zone days are counted in."},"asOf":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When these figures were read from the ledger (UTC)."},"totals":{"$ref":"#/components/schemas/RevenueTotals"},"byType":{"items":{"$ref":"#/components/schemas/RevenueByType"},"type":"array","description":"The window split by what was paid for, largest first."},"byDay":{"items":{"$ref":"#/components/schemas/RevenueDay"},"type":"array","description":"One entry per day in the window, oldest first, including days with no earnings."},"bySeller":{"$ref":"#/components/schemas/RevenueBySeller","description":"Paid-message sales in chats, split by who sent the message: the AI (`ai`) or your team (`team`)."},"byCreator":{"items":{"$ref":"#/components/schemas/RevenueCreator"},"type":"array","description":"Per creator, largest first."},"balance":{"$ref":"#/components/schemas/Balance"},"coverageFrom":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The oldest day the earnings ledger has been read back to for the worst-covered creator; earlier days may be missing."},"complete":{"type":"boolean","description":"`false` when some creator's ledger could not be read in full, so the figures are a lower bound."}},"type":"object","required":["start","end","timezone","asOf","totals","byType","byDay","bySeller","byCreator","balance","coverageFrom","complete"],"title":"RevenueReport","description":"Revenue for a window. New fields may be added; ignore fields you do not know.","examples":[{"start":"2026-09-01","end":"2026-09-26","timezone":"Europe/London","asOf":"2026-09-26T14:02:11.000Z","totals":{"netCents":4213000,"grossCents":5266250,"feeCents":1053250,"transactions":1840,"spenders":612,"settledNetCents":3701300,"pendingNetCents":511700,"unknownSettlementNetCents":0,"refundedNetCents":2400,"refundedTransactions":2},"byType":[{"type":"ppv","netCents":2214800,"grossCents":2768500},{"type":"subscription","netCents":1510400,"grossCents":1888000},{"type":"tip","netCents":487800,"grossCents":609750}],"byDay":[{"date":"2026-09-25","netCents":171900,"grossCents":214875}],"bySeller":{"ai":{"netCents":1904500,"grossCents":2380625,"transactions":612},"team":{"netCents":310300,"grossCents":387875,"transactions":138},"unattributedNetCents":0},"byCreator":[{"creatorId":"cre_8f2c1a9b0d7e4c3f2a1b","name":"Mia Rose","netCents":4213000,"grossCents":5266250,"transactions":1840,"spenders":612}],"balance":{"availableCents":820000,"pendingCents":511700},"coverageFrom":"2025-01-04","complete":true}]},"RevenueTotals":{"properties":{"netCents":{"type":"integer","description":"Net earnings after OnlyFans' fee, in cents. Refunds are excluded."},"grossCents":{"type":"integer","description":"What fans paid, in cents."},"feeCents":{"type":"integer","description":"OnlyFans' fee, in cents."},"transactions":{"type":"integer","description":"Number of sales counted."},"spenders":{"type":"integer","description":"Distinct fans who paid anything (per creator)."},"settledNetCents":{"type":"integer","description":"The part of `netCents` OnlyFans has settled."},"pendingNetCents":{"type":"integer","description":"The part of `netCents` still clearing."},"unknownSettlementNetCents":{"type":"integer","description":"The part of `netCents` whose settlement state OnlyFans did not report."},"refundedNetCents":{"type":"integer","description":"Net value of refunded sales in the window (not in `netCents`)."},"refundedTransactions":{"type":"integer","description":"Number of refunded sales in the window."}},"type":"object","required":["netCents","grossCents","feeCents","transactions","spenders","settledNetCents","pendingNetCents","unknownSettlementNetCents","refundedNetCents","refundedTransactions"],"title":"RevenueTotals"},"Review":{"properties":{"enabled":{"type":"boolean","description":"`true` makes every AI reply wait for a person to review it."},"reviewMinutes":{"type":"integer","description":"How long a reply waits for review, 1 to 60 minutes."},"whenUnreviewed":{"type":"string","enum":["send","hold"],"description":"What happens when nobody reviews in time: `send` it, or `hold` it."},"waitingNow":{"type":"integer","description":"AI replies waiting for review right now."}},"type":"object","required":["enabled","reviewMinutes","whenUnreviewed","waitingNow"],"title":"Review","description":"Review mode: whether every AI reply waits for a person to approve it."},"ReviewPatch":{"properties":{"enabled":{"type":"boolean","description":"Switch review mode. **Turning it off releases every waiting draft to fans immediately** (drafts that have waited longer than 10 minutes are discarded instead of sent late), so it also needs the `messages:send` scope and is limited to 6 per hour per creator."},"reviewMinutes":{"type":"integer","maximum":60.0,"minimum":1.0,"description":"1 to 60."},"whenUnreviewed":{"type":"string","enum":["send","hold"],"description":"`send` or `hold`."}},"additionalProperties":false,"type":"object","title":"ReviewPatch"},"SellerSales":{"properties":{"netCents":{"type":"integer","description":"Net value of the paid messages this seller sold, in cents (after OnlyFans' 20%)."},"grossCents":{"type":"integer","description":"What fans paid for them, in cents."},"transactions":{"type":"integer","description":"Paid messages unlocked."}},"type":"object","required":["netCents","grossCents","transactions"],"title":"SellerSales"},"SendMessageRequest":{"properties":{"text":{"type":"string","maxLength":4000,"description":"The message text, up to 4,000 characters. Optional when media is attached.","default":""},"mediaIds":{"items":{"type":"string","pattern":"^\\d{1,25}$"},"type":"array","maxItems":20,"uniqueItems":true,"description":"Up to 20 vault media ids locked behind `priceCents`. Requires a price of at least 300 cents."},"previewMediaIds":{"items":{"type":"string","pattern":"^\\d{1,25}$"},"type":"array","maxItems":20,"uniqueItems":true,"description":"Up to 20 vault media ids the fan receives free and unlocked. On a paid message these are the teaser."},"priceCents":{"type":"integer","maximum":500000.0,"minimum":0.0,"description":"The unlock price in cents: `0` (free) or 300 to 500000 ($3.00 to $5,000.00). A price requires `mediaIds`.","default":0}},"additionalProperties":false,"type":"object","title":"SendMessageRequest","examples":[{"text":"Thinking of you 😘"},{"text":"Made this one just for you","previewMediaIds":["4012345678"],"mediaIds":["4012345679"],"priceCents":1500}]},"SetConversationAiRequest":{"properties":{"enabled":{"type":"boolean","description":"`true` gives the chat to the AI (it may answer a waiting fan at once); `false` switches the AI off for this chat."}},"additionalProperties":false,"type":"object","required":["enabled"],"title":"SetConversationAiRequest","examples":[{"enabled":false}]},"SpendBand":{"properties":{"band":{"type":"string","enum":["none","under_50","50_to_200","200_plus"],"description":"Lifetime spend in US dollars: nothing, under $50, $50 to under $200, $200 or more."},"fans":{"type":"integer","description":"Fans in this band."}},"type":"object","required":["band","fans"],"title":"SpendBand"},"Sync":{"properties":{"phase":{"anyOf":[{"type":"string","enum":["initial","collections","paid_history","all_history","complete"]},{"type":"null"}],"description":"The sync after connecting runs in phases: `initial` (recent conversations, fans waiting for a reply, the newest vault files, recent earnings; the inbox, sending and the AI already work), `collections` (the rest of her fans, fan lists, vault and earnings), `paid_history` (older history with fans who have paid), `all_history` (older history with everyone else), then `complete` (new activity keeps syncing). `null` before the sync starts."},"progress":{"anyOf":[{"type":"integer","maximum":100.0,"minimum":0.0},{"type":"null"}],"description":"Overall sync progress, 0 to 100. `null` before the first sync starts."},"steps":{"items":{"$ref":"#/components/schemas/SyncStep"},"type":"array","description":"The steps of the current sync phase."}},"type":"object","required":["phase","progress","steps"],"title":"Sync"},"SyncStep":{"properties":{"key":{"type":"string","description":"Stable step key."},"label":{"type":"string","description":"The step's name, for people."},"status":{"type":"string","enum":["pending","running","done","failed","skipped"],"description":"The step's state: `pending`, `running`, `done`, `failed` or `skipped`. More values may be added."}},"type":"object","required":["key","label","status"],"title":"SyncStep"},"TakeoverRequest":{"properties":{"holdMinutes":{"anyOf":[{"type":"integer","maximum":1440.0,"minimum":15.0},{"type":"null"}],"description":"How long your team keeps the chat before it returns to the AI on its own: 15 to 1440 minutes. Default 720 (12 hours)."}},"additionalProperties":false,"type":"object","title":"TakeoverRequest","examples":[{"holdMinutes":120}]},"TodayEarnings":{"properties":{"netCents":{"type":"integer","description":"What the creator(s) earned since local midnight, after OnlyFans' fee, in cents."},"settledCents":{"type":"integer","description":"The part of `netCents` OnlyFans has already settled."},"pendingCents":{"type":"integer","description":"The part of `netCents` still clearing (OnlyFans holds new earnings for about a week)."},"complete":{"type":"boolean","description":"`false` when some creator's earnings could not be read in full just now, so the figures are a lower bound. Ask again in a few minutes."}},"type":"object","required":["netCents","settledCents","pendingCents","complete"],"title":"TodayEarnings"},"TodayStats":{"properties":{"date":{"type":"string","description":"Today's date in the workspace time zone (`YYYY-MM-DD`)."},"timezone":{"type":"string","description":"The workspace time zone the day is counted in (IANA)."},"asOf":{"type":"string","description":"When the earnings and fan figures were computed (UTC). They are refreshed every minute or two."},"earnings":{"$ref":"#/components/schemas/TodayEarnings"},"newFans":{"type":"integer","description":"Fans whose relationship with a creator started today (earliest subscription or message)."},"buyers":{"type":"integer","description":"Distinct fans who paid anything today."},"paidMessagesUnlocked":{"type":"integer","description":"Paid messages unlocked today."},"messages":{"$ref":"#/components/schemas/MessageCounts","description":"Message volume since local midnight."},"aiShare":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Share of today's delivered messages the AI wrote, 0 to 1. `null` when nothing was sent yet."},"yesterdaySameTime":{"$ref":"#/components/schemas/DayComparison","description":"Yesterday up to the same local time — the fair comparison for a day still in progress."},"yesterday":{"$ref":"#/components/schemas/DayComparison","description":"All of yesterday."}},"type":"object","required":["date","timezone","asOf","earnings","newFans","buyers","paidMessagesUnlocked","messages","aiShare","yesterdaySameTime","yesterday"],"title":"TodayStats","description":"Today so far, in the workspace time zone. New fields may be added; ignore fields you do not know.","examples":[{"date":"2026-09-26","timezone":"Europe/London","asOf":"2026-09-26T14:05:00.000Z","earnings":{"netCents":184250,"settledCents":0,"pendingCents":184250,"complete":true},"newFans":41,"buyers":57,"paidMessagesUnlocked":38,"messages":{"in":1320,"out":1488,"ai":1402,"team":86},"aiShare":0.942,"yesterdaySameTime":{"netCents":171900,"newFans":36,"buyers":51,"paidMessagesUnlocked":33},"yesterday":{"netCents":301400,"newFans":64,"buyers":90,"paidMessagesUnlocked":61}}]},"TrackingLink":{"properties":{"id":{"type":"string","description":"The tracking link id (`trk_…`)."},"kind":{"type":"string","enum":["tracking","trial"],"description":"`tracking`: a campaign link. `trial`: a free-trial link."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The link name on OnlyFans."},"code":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The link code on OnlyFans."},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The public link to share."},"clicks":{"type":"integer","description":"Clicks, as counted by OnlyFans."},"subscribers":{"type":"integer","description":"Subscribers it brought, as counted by OnlyFans."},"claims":{"type":"integer","description":"Trial links: trials claimed."},"claimsLimit":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Trial links: the most trials it gives, or `null` for no limit."},"trialDays":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Trial links: the length of the free trial in days."},"expiresAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the link stops working, or `null`."},"ofCreatedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When it was created on OnlyFans."},"costCents":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"What you paid for the promotion, in cents. `null` = never costed, `0` = free."},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Where you ran it (your label)."},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Your note."},"archived":{"type":"boolean","description":"Hidden from the default list."},"earningsCents":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Lifetime spend of the fans this link brought, in cents. A floor when `accountedFans` is below `attributedFans`. `null` when no attributed fan is known. Never add link earnings up into creator revenue: a fan on two links counts on both."},"earningsKnown":{"type":"boolean","description":"`false` until earnings could be worked out."},"attributedFans":{"type":"integer","description":"Fans OnlyFans attributes to this link."},"accountedFans":{"type":"integer","description":"Of those, fans OnlyX could match to spend."},"conversion":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"`subscribers / clicks`, or `null`."},"profitCents":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"`earnings − cost`, in cents, or `null`."},"roi":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"`profit / cost`, or `null` when there is no cost."},"cpcCents":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Cost per click, in cents, or `null`."},"epcCents":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Earnings per click, in cents, or `null`."},"cpfCents":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Cost per fan (subscriber), in cents, or `null`."},"arpuCents":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Earnings per fan, in cents, or `null`."},"firstSeenAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When OnlyX first read the link."},"lastSeenAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When OnlyX last read it (about hourly while the creator is connected)."}},"type":"object","required":["id","kind","name","code","url","clicks","subscribers","claims","claimsLimit","trialDays","expiresAt","ofCreatedAt","costCents","source","note","archived","earningsCents","earningsKnown","attributedFans","accountedFans","conversion","profitCents","roi","cpcCents","epcCents","cpfCents","arpuCents","firstSeenAt","lastSeenAt"],"title":"TrackingLink","examples":[{"id":"trk_5a7c9e1b3d5f7a9c1e3b","kind":"tracking","name":"Reddit September","code":"17","url":"https://onlyfans.com/miarose/c17","clicks":4210,"subscribers":388,"claims":0,"claimsLimit":null,"trialDays":null,"expiresAt":null,"ofCreatedAt":"2026-09-01T10:00:00.000Z","costCents":25000,"source":"reddit","note":"Paid posts in three subreddits","archived":false,"earningsCents":184300,"earningsKnown":true,"attributedFans":388,"accountedFans":301,"conversion":0.0922,"profitCents":159300,"roi":6.372,"cpcCents":6,"epcCents":44,"cpfCents":64,"arpuCents":475,"firstSeenAt":"2026-09-01T10:14:00.000Z","lastSeenAt":"2026-09-26T09:15:00.000Z"}]},"TrackingLinkCreation":{"properties":{"creationId":{"type":"string","description":"The creation id."},"state":{"type":"string","enum":["creating","completed","failed"],"description":"`creating` (poll again in 5 seconds or more), `completed` or `failed`."},"linkId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When completed and read back: the new link's id. May stay `null` for up to an hour after completion."},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When completed: the new link."},"error":{"anyOf":[{"$ref":"#/components/schemas/CreationError"},{"type":"null"}],"description":"Set when `state` is `failed`."}},"type":"object","required":["creationId","state","linkId","url","error"],"title":"TrackingLinkCreation","examples":[{"creationId":"Xq3Lk9vB2mT7cR1eY6wN0pA4sD8fGhJuZiOl5nWxVb2Qe7TyUr","state":"creating","linkId":null,"url":null,"error":null},{"creationId":"Xq3Lk9vB2mT7cR1eY6wN0pA4sD8fGhJuZiOl5nWxVb2Qe7TyUr","state":"completed","linkId":"trk_9c1e3a5b7d9f1b3d5a7c","url":"https://onlyfans.com/miarose/c18","error":null},{"creationId":"Xq3Lk9vB2mT7cR1eY6wN0pA4sD8fGhJuZiOl5nWxVb2Qe7TyUr","state":"failed","linkId":null,"url":null,"error":{"code":"LINK_NAME_TAKEN","message":"A link with this name already exists on her OnlyFans account. Choose another name."}}]},"TrackingLinkCreationStarted":{"properties":{"creationId":{"type":"string","description":"An opaque id to poll with `GET …/tracking-links/creations/{creationId}`."},"state":{"type":"string","const":"creating","description":"Always `creating`."}},"type":"object","required":["creationId","state"],"title":"TrackingLinkCreationStarted","examples":[{"creationId":"Xq3Lk9vB2mT7cR1eY6wN0pA4sD8fGhJuZiOl5nWxVb2Qe7TyUr","state":"creating"}]},"TrackingLinkDay":{"properties":{"day":{"type":"string","description":"The day (UTC), `YYYY-MM-DD`."},"clicks":{"type":"integer","description":"Cumulative clicks at the end of the day."},"subscribers":{"type":"integer","description":"Cumulative subscribers at the end of the day."},"clicksDelta":{"type":"integer","description":"Clicks gained that day."},"subscribersDelta":{"type":"integer","description":"Subscribers gained that day."}},"type":"object","required":["day","clicks","subscribers","clicksDelta","subscribersDelta"],"title":"TrackingLinkDay"},"TrackingLinkDetail":{"properties":{"id":{"type":"string","description":"The tracking link id (`trk_…`)."},"kind":{"type":"string","enum":["tracking","trial"],"description":"`tracking`: a campaign link. `trial`: a free-trial link."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The link name on OnlyFans."},"code":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The link code on OnlyFans."},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The public link to share."},"clicks":{"type":"integer","description":"Clicks, as counted by OnlyFans."},"subscribers":{"type":"integer","description":"Subscribers it brought, as counted by OnlyFans."},"claims":{"type":"integer","description":"Trial links: trials claimed."},"claimsLimit":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Trial links: the most trials it gives, or `null` for no limit."},"trialDays":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Trial links: the length of the free trial in days."},"expiresAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the link stops working, or `null`."},"ofCreatedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When it was created on OnlyFans."},"costCents":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"What you paid for the promotion, in cents. `null` = never costed, `0` = free."},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Where you ran it (your label)."},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Your note."},"archived":{"type":"boolean","description":"Hidden from the default list."},"earningsCents":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Lifetime spend of the fans this link brought, in cents. A floor when `accountedFans` is below `attributedFans`. `null` when no attributed fan is known. Never add link earnings up into creator revenue: a fan on two links counts on both."},"earningsKnown":{"type":"boolean","description":"`false` until earnings could be worked out."},"attributedFans":{"type":"integer","description":"Fans OnlyFans attributes to this link."},"accountedFans":{"type":"integer","description":"Of those, fans OnlyX could match to spend."},"conversion":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"`subscribers / clicks`, or `null`."},"profitCents":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"`earnings − cost`, in cents, or `null`."},"roi":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"`profit / cost`, or `null` when there is no cost."},"cpcCents":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Cost per click, in cents, or `null`."},"epcCents":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Earnings per click, in cents, or `null`."},"cpfCents":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Cost per fan (subscriber), in cents, or `null`."},"arpuCents":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Earnings per fan, in cents, or `null`."},"firstSeenAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When OnlyX first read the link."},"lastSeenAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When OnlyX last read it (about hourly while the creator is connected)."},"daily":{"items":{"$ref":"#/components/schemas/TrackingLinkDay"},"type":"array","description":"One entry per day read in the window, oldest first."}},"type":"object","required":["id","kind","name","code","url","clicks","subscribers","claims","claimsLimit","trialDays","expiresAt","ofCreatedAt","costCents","source","note","archived","earningsCents","earningsKnown","attributedFans","accountedFans","conversion","profitCents","roi","cpcCents","epcCents","cpfCents","arpuCents","firstSeenAt","lastSeenAt","daily"],"title":"TrackingLinkDetail","examples":[{"id":"trk_5a7c9e1b3d5f7a9c1e3b","kind":"tracking","name":"Reddit September","code":"17","url":"https://onlyfans.com/miarose/c17","clicks":4210,"subscribers":388,"claims":0,"claimsLimit":null,"trialDays":null,"expiresAt":null,"ofCreatedAt":"2026-09-01T10:00:00.000Z","costCents":25000,"source":"reddit","note":"Paid posts in three subreddits","archived":false,"earningsCents":184300,"earningsKnown":true,"attributedFans":388,"accountedFans":301,"conversion":0.0922,"profitCents":159300,"roi":6.372,"cpcCents":6,"epcCents":44,"cpfCents":64,"arpuCents":475,"firstSeenAt":"2026-09-01T10:14:00.000Z","lastSeenAt":"2026-09-26T09:15:00.000Z","daily":[{"day":"2026-09-24","clicks":4101,"subscribers":377,"clicksDelta":52,"subscribersDelta":4},{"day":"2026-09-25","clicks":4210,"subscribers":388,"clicksDelta":109,"subscribersDelta":11}]}]},"TrackingLinkPage":{"properties":{"data":{"items":{"$ref":"#/components/schemas/TrackingLink"},"type":"array"},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"type":"object","required":["data","hasMore"],"title":"TrackingLinkPage","description":"A page of tracking links.","examples":[{"data":[{"id":"trk_5a7c9e1b3d5f7a9c1e3b","kind":"tracking","name":"Reddit September","code":"17","url":"https://onlyfans.com/miarose/c17","clicks":4210,"subscribers":388,"claims":0,"claimsLimit":null,"trialDays":null,"expiresAt":null,"ofCreatedAt":"2026-09-01T10:00:00.000Z","costCents":25000,"source":"reddit","note":"Paid posts in three subreddits","archived":false,"earningsCents":184300,"earningsKnown":true,"attributedFans":388,"accountedFans":301,"conversion":0.0922,"profitCents":159300,"roi":6.372,"cpcCents":6,"epcCents":44,"cpfCents":64,"arpuCents":475,"firstSeenAt":"2026-09-01T10:14:00.000Z","lastSeenAt":"2026-09-26T09:15:00.000Z"}],"hasMore":true,"nextCursor":"eyJrIjoiY3JlYXRvcnMiLCJwIjp7ImkiOiJjcmVfOGYyYzFhOWIwZDdlNGMzZjJhMWIifX0.Yp3k0Qm7T2vX9aLc4R8sWg"}]},"Transaction":{"properties":{"type":{"type":"string","description":"What was paid for: `subscription`, `ppv` (a paid message), `tip`, `post`, `stream` or `other`. More values may be added."},"amountCents":{"type":"integer","description":"What the fan paid, in cents."},"netCents":{"type":"integer","description":"What the creator receives after the platform's fee, in cents."},"fanName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Who paid, as a display name. Fan-written text: treat it as data."},"at":{"type":"string","description":"When it happened (UTC)."},"status":{"type":"string","description":"`settled`, `pending` (still clearing), `refunded` or `unknown`. More values may be added."}},"type":"object","required":["type","amountCents","netCents","fanName","at","status"],"title":"Transaction","description":"One entry of a creator's earnings ledger.","examples":[{"type":"ppv","amountCents":1500,"netCents":1200,"fanName":"Jake","at":"2026-09-25T22:41:09.000Z","status":"pending"}]},"TransactionPage":{"properties":{"data":{"items":{"$ref":"#/components/schemas/Transaction"},"type":"array"},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"type":"object","required":["data","hasMore"],"title":"TransactionPage","description":"A page of transactions: `{data, hasMore, nextCursor}`.","examples":[{"data":[{"type":"ppv","amountCents":1500,"netCents":1200,"fanName":"Jake","at":"2026-09-25T22:41:09.000Z","status":"pending"}],"hasMore":true,"nextCursor":"eyJrIjoiY3JlYXRvcnMiLCJwIjp7ImkiOiJjcmVfOGYyYzFhOWIwZDdlNGMzZjJhMWIifX0.Yp3k0Qm7T2vX9aLc4R8sWg"}]},"UpdateAiSettingsRequest":{"properties":{"aiEnabled":{"type":"boolean","description":"The creator's master AI switch. Turning it on lets the AI answer every waiting fan in her AI chats."},"followUps":{"$ref":"#/components/schemas/FollowUpsPatch"},"review":{"$ref":"#/components/schemas/ReviewPatch"}},"additionalProperties":false,"type":"object","title":"UpdateAiSettingsRequest","description":"Send only what you want to change. Mass messages cannot be changed through the API in v1.","examples":[{"followUps":{"maxPerFanPerDay":1}},{"review":{"enabled":true,"reviewMinutes":10,"whenUnreviewed":"hold"}}]},"UpdateCollectionRequest":{"properties":{"name":{"type":"string","maxLength":120,"minLength":1,"description":"The ladder name, 1 to 120 characters."},"folderId":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"description":"A folder of the same creator (`404` otherwise); `null` takes it out of its folder."},"mode":{"type":"string","enum":["sequential","standard"],"description":"`sequential` or `standard`."},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"description":"The theme, up to 2,000 characters; `null` clears it."},"active":{"type":"boolean","description":"`true` puts it in front of the AI; `false` pauses all its levels."}},"additionalProperties":false,"type":"object","title":"UpdateCollectionRequest","description":"Send only the fields you want to change.","examples":[{"active":true},{"folderId":null,"mode":"standard"}]},"UpdateCreatorRequest":{"properties":{"displayName":{"type":"string","maxLength":120,"minLength":1,"description":"A new label for her, 1 to 120 characters."},"aiEnabled":{"type":"boolean","description":"The creator's master AI switch. Turning it on lets the AI answer every fan who is waiting in her AI chats, within seconds. Turning it off stops all AI replies and follow-ups for her."}},"additionalProperties":false,"type":"object","title":"UpdateCreatorRequest","examples":[{"displayName":"Mia Rose (main)"},{"aiEnabled":false}]},"UpdateFanRequest":{"properties":{"customName":{"anyOf":[{"type":"string","maxLength":160},{"type":"null"}],"description":"A name your team recognizes, shown instead of the OnlyFans name in OnlyX. `\"\"` or `null` clears it."},"notes":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"description":"Free-text notes for your team (up to 4,000 characters). `\"\"` or `null` clears them."},"muted":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Your team's mute flag. It never mutes or blocks the fan on OnlyFans."}},"additionalProperties":false,"type":"object","title":"UpdateFanRequest","description":"Only your team's own fields. Anything else — including any AI setting — is refused.","examples":[{"customName":"Mike (birthday 14 May)","notes":"Tips on Fridays.","muted":false}]},"UpdateFolderRequest":{"properties":{"name":{"type":"string","maxLength":120,"minLength":1,"description":"The new name, 1 to 120 characters."}},"additionalProperties":false,"type":"object","required":["name"],"title":"UpdateFolderRequest","examples":[{"name":"Bundles"}]},"UpdateLevelRequest":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"description":"Up to 120 characters; `null` clears it."},"priceCents":{"type":"integer","maximum":1000000.0,"minimum":0.0,"description":"The floor price in cents. Moving a level between free and priced converts its media: free makes everything preview, priced makes everything paid."},"tags":{"items":{"type":"string","maxLength":40,"minLength":1},"type":"array","maxItems":30,"description":"Up to 30 tags of up to 40 characters; replaces the list."},"highlight":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"description":"Up to 80 characters; `null` clears it."},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"description":"Up to 2,000 characters; `null` clears it."},"availableInPaidMessages":{"type":"boolean","description":"`false` keeps the level on the board but out of chats."},"active":{"type":"boolean","description":"`false` pauses the level."}},"additionalProperties":false,"type":"object","title":"UpdateLevelRequest","description":"Send only the fields you want to change.","examples":[{"priceCents":2000,"highlight":null}]},"UpdatePersonaRequest":{"properties":{"personaName":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"description":"Up to 120 characters. `null` clears it."},"nickname":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"description":"Up to 80 characters. `null` clears it."},"age":{"anyOf":[{"type":"integer","maximum":99.0,"minimum":18.0},{"type":"null"}],"description":"18 to 99. `null` clears it."},"city":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"description":"Up to 120 characters. `null` clears it."},"origin":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"description":"Up to 120 characters. `null` clears it."},"occupation":{"anyOf":[{"type":"string","maxLength":160},{"type":"null"}],"description":"Up to 160 characters. `null` clears it."},"archetype":{"anyOf":[{"type":"string","maxLength":60},{"type":"null"}],"description":"Up to 60 characters. `null` puts back the default `girl_next_door`."},"lore":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"description":"Up to 4,000 characters. `null` clears it."},"accountContext":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"description":"Up to 1,000 characters. `null` clears it."},"physicalDescription":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"description":"Up to 1,000 characters. `null` clears it."},"interests":{"anyOf":[{"items":{"type":"string","maxLength":500},"type":"array","maxItems":50},{"type":"null"}],"description":"Up to 50 items of up to 500 characters. Replaces the whole list; `null` empties it."},"contentMenu":{"anyOf":[{"items":{"type":"string","maxLength":500},"type":"array","maxItems":50},{"type":"null"}],"description":"Up to 50 items of up to 500 characters. Replaces the whole list; `null` empties it."},"contentNotes":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"description":"Up to 1,000 characters. `null` clears it."},"boundaries":{"anyOf":[{"items":{"type":"string","maxLength":500},"type":"array","maxItems":50},{"type":"null"}],"description":"Up to 50 items of up to 500 characters. Replaces the whole list; `null` empties it."},"hardLimits":{"anyOf":[{"items":{"type":"string","maxLength":500},"type":"array","maxItems":50},{"type":"null"}],"description":"Up to 50 items of up to 500 characters. Replaces the whole list; `null` empties it."},"exemplars":{"anyOf":[{"items":{"type":"string","maxLength":500},"type":"array","maxItems":50},{"type":"null"}],"description":"Up to 50 items of up to 500 characters. Replaces the whole list; `null` empties it."},"primaryLanguage":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"description":"A BCP-47 tag (`en`, `pt-BR`), up to 20 characters. `null` puts back `en`."},"additionalLanguages":{"anyOf":[{"items":{"type":"string","maxLength":20,"minLength":2},"type":"array","maxItems":50},{"type":"null"}],"description":"Up to 50 BCP-47 tags. Replaces the whole list; `null` empties it."},"timezone":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"A valid IANA time zone (`America/Chicago`). `null` puts back `UTC`."},"capitalization":{"type":"string","enum":["normal","lowercase","casual_mix"],"description":"Cannot be cleared."},"punctuation":{"type":"string","enum":["proper","relaxed","minimal"],"description":"Cannot be cleared."},"messageSplitting":{"type":"string","enum":["never","sometimes","often"],"description":"Cannot be cleared."},"typingSpeed":{"type":"string","enum":["fast","natural","slow"],"description":"Cannot be cleared."},"emojiPolicy":{"type":"string","enum":["none","rare","some","heavy"],"description":"Cannot be cleared."},"allowTypos":{"type":"boolean","description":"Cannot be cleared."},"customContentEnabled":{"type":"boolean","description":"Cannot be cleared."},"priceCustomPhotoCents":{"anyOf":[{"type":"integer","maximum":1000000.0,"minimum":0.0},{"type":"null"}],"description":"0 to 1000000 cents. `null` clears it."},"priceCustomVideoPerMinCents":{"anyOf":[{"type":"integer","maximum":1000000.0,"minimum":0.0},{"type":"null"}],"description":"0 to 1000000 cents. `null` clears it."},"priceCustomVideoMinCents":{"anyOf":[{"type":"integer","maximum":1000000.0,"minimum":0.0},{"type":"null"}],"description":"0 to 1000000 cents. `null` clears it."},"customPhotoDeliveryDays":{"anyOf":[{"type":"integer","maximum":60.0,"minimum":1.0},{"type":"null"}],"description":"1 to 60. `null` uses the default of 3."},"customVideoDeliveryDays":{"anyOf":[{"type":"integer","maximum":60.0,"minimum":1.0},{"type":"null"}],"description":"1 to 60. `null` uses the default of 7."},"aiDisclosureEnabled":{"type":"boolean","description":"Cannot be cleared."},"aiDisclosureText":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"description":"Up to 500 characters. `null` clears it."},"handoffKinds":{"anyOf":[{"items":{"type":"string","maxLength":64},"type":"array","maxItems":100},{"type":"null"}],"description":"Keys from `handoffCatalogue`. `null` means the recommended set; `[]` means only the kinds that can never be switched off. Locked kinds always apply, whatever you send; an unknown key is a 400."}},"additionalProperties":false,"type":"object","title":"UpdatePersonaRequest","description":"A true PATCH: fields you leave out are unchanged; `null` clears a field (text becomes empty,\nlists become empty, numbers become unset, `archetype`/`primaryLanguage`/`timezone` go back to\ntheir defaults, `handoffKinds` goes back to the recommended set); a field that is not listed here\nis a `400 VALIDATION_ERROR`. Choice fields and switches cannot be cleared, only set.","examples":[{"personaName":"Mia","age":24,"city":"Austin","occupation":"Yoga instructor","lore":"Grew up by the beach, moved to Austin for college and stayed for the music.","timezone":"America/Chicago","hardLimits":["No meetups."],"nickname":null}]},"UpdateTrackingLinkRequest":{"properties":{"costCents":{"anyOf":[{"type":"integer","maximum":100000000.0,"minimum":0.0},{"type":"null"}],"description":"What the promotion cost, 0 to 100000000 cents. `null` clears it."},"source":{"anyOf":[{"type":"string","maxLength":60},{"type":"null"}],"description":"Where you ran it, up to 60 characters. `null` clears it."},"note":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"description":"Your note, up to 500 characters. `null` clears it."},"archived":{"type":"boolean","description":"Archive or unarchive it in OnlyX. Nothing changes on OnlyFans."}},"additionalProperties":false,"type":"object","title":"UpdateTrackingLinkRequest","examples":[{"costCents":25000,"source":"reddit","note":"Paid posts in three subreddits"},{"archived":true}]},"UpdateWelcomeMessageRequest":{"properties":{"text":{"type":"string","maxLength":2000,"description":"The message text, up to 2,000 characters."},"previewMediaIds":{"items":{"type":"string","pattern":"^\\d{1,25}$"},"type":"array","maxItems":20,"description":"Up to 20 vault ids sent free."},"paidMediaIds":{"items":{"type":"string","pattern":"^\\d{1,25}$"},"type":"array","maxItems":20,"description":"Up to 20 vault ids locked behind `priceCents`."},"priceCents":{"type":"integer","maximum":1000000.0,"minimum":0.0,"description":"0 to 1000000 cents. A price above 0 needs `paidMediaIds`.","default":0},"enabled":{"type":"boolean","description":"Switch the welcome message on or off. Leave it out to keep the current switch."},"dryRun":{"type":"boolean","description":"`true` checks the change on OnlyFans without saving it. Nothing changes for fans.","default":false}},"additionalProperties":false,"type":"object","required":["text"],"title":"UpdateWelcomeMessageRequest","description":"Replaces the whole welcome message.","examples":[{"text":"hey you 😘 thanks for subscribing! here's a little something","previewMediaIds":["4498297883"],"paidMediaIds":["4498297884"],"priceCents":499,"enabled":true,"dryRun":true}]},"VaultMedia":{"properties":{"id":{"type":"string","description":"The OnlyFans vault media id (a string of digits). Use it in `mediaIds` / `previewMediaIds`."},"type":{"type":"string","enum":["photo","video","gif","audio"],"description":"The media type."},"createdAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When it was added to the vault."},"width":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Width in pixels, when known."},"height":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Height in pixels, when known."},"durationSeconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Length of a video or audio item in seconds; `null` for photos."},"listIds":{"items":{"type":"string"},"type":"array","description":"The OnlyFans vault lists (folders) it is in, by OnlyFans list id."},"stats":{"$ref":"#/components/schemas/VaultMediaStats","description":"Performance, as reported by OnlyFans."},"thumbnailUrl":{"type":"string","description":"The API URL of a small preview image. It needs your credential like every other call."}},"type":"object","required":["id","type","createdAt","width","height","durationSeconds","listIds","stats","thumbnailUrl"],"title":"VaultMedia","examples":[{"id":"4012345678","type":"photo","createdAt":"2026-08-30T14:12:09.000Z","width":1080,"height":1350,"durationSeconds":null,"listIds":["1234567"],"stats":{"likes":212,"tipsCents":4500,"buyers":9},"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4012345678/thumbnail"}]},"VaultMediaPage":{"properties":{"data":{"items":{"$ref":"#/components/schemas/VaultMedia"},"type":"array"},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"type":"object","required":["data","hasMore"],"title":"VaultMediaPage","description":"A page of vault media: `{data, hasMore, nextCursor}`.","examples":[{"data":[{"id":"4012345678","type":"photo","createdAt":"2026-08-30T14:12:09.000Z","width":1080,"height":1350,"durationSeconds":null,"listIds":["1234567"],"stats":{"likes":212,"tipsCents":4500,"buyers":9},"thumbnailUrl":"https://api.onlyx.ai/v1/creators/cre_8f2c1a9b0d7e4c3f2a1b/media/4012345678/thumbnail"}],"hasMore":true,"nextCursor":"eyJrIjoiY3JlYXRvcnMiLCJwIjp7ImkiOiJjcmVfOGYyYzFhOWIwZDdlNGMzZjJhMWIifX0.Yp3k0Qm7T2vX9aLc4R8sWg"}]},"VaultMediaStats":{"properties":{"likes":{"type":"integer","description":"Likes on posts that used it."},"tipsCents":{"type":"integer","description":"Tips on posts that used it, in cents."},"buyers":{"type":"integer","description":"Fans who bought it in a paid message or post."}},"type":"object","required":["likes","tipsCents","buyers"],"title":"VaultMediaStats"},"VaultUsage":{"properties":{"total":{"type":"integer","description":"Items in her vault."},"assigned":{"type":"integer","description":"Items placed on a level."},"unassigned":{"type":"integer","description":"Items not on any level yet."}},"type":"object","required":["total","assigned","unassigned"],"title":"VaultUsage"},"WelcomeMedia":{"properties":{"id":{"type":"string","description":"The vault media id."},"type":{"anyOf":[{"type":"string","enum":["photo","video","gif","audio"]},{"type":"null"}],"description":"The media type, when known."},"preview":{"type":"boolean","description":"`true` for free media; `false` for media locked behind `priceCents`."}},"type":"object","required":["id","type","preview"],"title":"WelcomeMedia"},"WelcomeMessage":{"properties":{"connected":{"type":"boolean","description":"`false` when the creator is not connected; the other fields are then empty."},"enabled":{"type":"boolean","description":"Whether OnlyFans sends the welcome message to new subscribers at all."},"text":{"type":"string","description":"The message text."},"priceCents":{"type":"integer","description":"The unlock price for its paid media, in cents. `0` when free."},"media":{"items":{"$ref":"#/components/schemas/WelcomeMedia"},"type":"array","description":"Attached media."},"pending":{"type":"boolean","description":"`true` while a change you made is being applied on OnlyFans."},"lastUpdate":{"anyOf":[{"$ref":"#/components/schemas/WelcomeUpdate"},{"type":"null"}],"description":"Your latest change, or `null` if it was never changed through OnlyX."}},"type":"object","required":["connected","enabled","text","priceCents","media","pending","lastUpdate"],"title":"WelcomeMessage","description":"OnlyFans' automatic welcome message, sent to every new subscriber.","examples":[{"connected":true,"enabled":true,"text":"hey you 😘 thanks for subscribing!","priceCents":499,"media":[{"id":"4498297883","type":"photo","preview":true},{"id":"4498297884","type":"video","preview":false}],"pending":false,"lastUpdate":{"status":"done","at":"2026-09-26T12:58:00.000Z","error":null}}]},"WelcomeMessageQueued":{"properties":{"queued":{"type":"boolean","description":"`true`: the change was accepted and will be applied on OnlyFans shortly."},"updateId":{"type":"string","description":"An opaque id for this change."}},"type":"object","required":["queued","updateId"],"title":"WelcomeMessageQueued","description":"The change is being applied. Read `GET …/welcome-message` for `pending` and `lastUpdate`.","examples":[{"queued":true,"updateId":"wmu_5f0c2a9e41b7d3c8a6e1f024"}]},"WelcomeUpdate":{"properties":{"status":{"type":"string","enum":["queued","done","failed"],"description":"`queued`, `done` or `failed`."},"at":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When that status was reached."},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Why it failed, as a sentence for a person. `null` otherwise."}},"type":"object","required":["status","at","error"],"title":"WelcomeUpdate","description":"Your latest change to the welcome message and how it went."},"Workspace":{"properties":{"id":{"type":"string","description":"The workspace id (`agc_…`)."},"name":{"type":"string"},"timezone":{"type":"string","description":"IANA time zone every report and day boundary uses."}},"type":"object","required":["id","name","timezone"],"title":"Workspace"}},"responses":{"Unauthorized":{"description":"No credential was sent (`UNAUTHORIZED`), or it is invalid, expired or revoked (`INVALID_API_KEY` for API keys, `INVALID_TOKEN` for OAuth access tokens: refresh the token or reconnect the app). Too many failed attempts from one address with credentials OnlyX never issued are answered `429 RATE_LIMITED` instead.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"WWW-Authenticate":{"$ref":"#/components/headers/WWW-Authenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INVALID_API_KEY":{"summary":"INVALID_API_KEY","value":{"error":{"code":"INVALID_API_KEY","message":"The API key is invalid, expired or revoked.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"UNAUTHORIZED":{"summary":"UNAUTHORIZED","value":{"error":{"code":"UNAUTHORIZED","message":"Authentication required. Send your API key as `Authorization: Bearer onx_sk_…`.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}},"INVALID_TOKEN":{"summary":"INVALID_TOKEN","value":{"error":{"code":"INVALID_TOKEN","message":"The access token is invalid, expired or revoked.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"RateLimited":{"description":"Over a rate limit (`RATE_LIMITED`). Wait `Retry-After` seconds, then retry — with the same `Idempotency-Key` for a write.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"summary":"RATE_LIMITED","value":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Try again in 12 seconds.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}},"InternalError":{"description":"Something went wrong on OnlyX's side (`INTERNAL_ERROR`). Retry with backoff (reuse the `Idempotency-Key` for a write); quote `requestId` to support if it persists.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INTERNAL_ERROR":{"summary":"INTERNAL_ERROR","value":{"error":{"code":"INTERNAL_ERROR","message":"Something went wrong on our side.","requestId":"req-3f9a1c2b7d4e5f60a1b2c3d4"}}}}}}}},"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"onx_sk_… API key or onx_at_… access token","description":"An API key (`onx_sk_…`) created in OnlyX under Settings → API & MCP, or an OAuth access token (`onx_at_…`) issued to a connected app. Send it as `Authorization: Bearer <credential>`. API keys may also be sent as `X-API-Key: <key>`."}},"headers":{"X-Request-Id":{"description":"The id of this request. Your own `X-Request-Id` (8 to 64 letters, digits or `-`) is echoed back; otherwise OnlyX creates one. Also in every error body as `requestId`: quote it to support.","schema":{"type":"string"},"example":"req-3f9a1c2b7d4e5f60a1b2c3d4"},"X-RateLimit-Limit":{"description":"Requests allowed in the current window of the tightest limit this request counted against.","schema":{"type":"integer"},"example":120},"X-RateLimit-Remaining":{"description":"Requests left in that window.","schema":{"type":"integer"},"example":117},"X-RateLimit-Reset":{"description":"When that window resets, as Unix epoch seconds.","schema":{"type":"integer"},"example":1790431380},"Retry-After":{"description":"Seconds to wait before trying again.","schema":{"type":"integer"},"example":12},"Idempotent-Replayed":{"description":"`true` when this response is the stored answer to an earlier request with the same `Idempotency-Key`: nothing was done again. Absent on a first answer.","schema":{"type":"string","enum":["true"]}},"WWW-Authenticate":{"description":"The authentication challenge (RFC 6750), for example `Bearer error=\"insufficient_scope\", scope=\"messages:send\"`.","schema":{"type":"string"}},"Cache-Control":{"description":"Caching rules for this response.","schema":{"type":"string"}}},"parameters":{"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"description":"Optional. A unique value (8 to 64 letters, digits, `-` or `_`; a UUID is ideal) that makes a retry safe: the same key with the same request within 24 hours returns the stored response, with `Idempotent-Replayed: true`, instead of doing the work twice. The same key with a different request is `409 IDEMPOTENCY_KEY_REUSED`; while the first request still runs it is `409 IDEMPOTENCY_IN_PROGRESS`. A malformed key is `400 VALIDATION_ERROR`.","schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{8,64}$","minLength":8,"maxLength":64},"example":"3b1f7c2e-9d4a-4e8b-a6c1-5f0d2e7b9a14"},"IdempotencyKeyRequired":{"name":"Idempotency-Key","in":"header","required":true,"description":"Required. A new unique value per new request (8 to 64 letters, digits, `-` or `_`; a UUID is ideal). If the request times out or fails with a 5xx, retry with the SAME key: you get the original response (with `Idempotent-Replayed: true`) and nothing is done twice. Keys are kept for 24 hours. Without it the answer is `400 IDEMPOTENCY_KEY_REQUIRED`.","schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{8,64}$","minLength":8,"maxLength":64},"example":"3b1f7c2e-9d4a-4e8b-a6c1-5f0d2e7b9a14"}}},"security":[{"bearerAuth":[]}],"tags":[{"name":"Workspace","description":"Who you are: the workspace and credential behind a key."},{"name":"Creators","description":"The creators your workspace manages: list, read, add, rename, and the AI switch."},{"name":"Connect","description":"Connecting a creator's OnlyFans account: connect links she opens on her own device, and the connection status to poll."},{"name":"Conversations","description":"The fan inbox: list and count conversations, read state, take over, release, and the per-chat AI switch."},{"name":"Messages","description":"Read message history and send text, free media and paid messages to fans."},{"name":"Hand-offs","description":"Conversations the AI chatter handed to your team, and resolving them."},{"name":"Fans","description":"Fans, their purchases, fan lists, and your team's notes, custom names and mute flag."},{"name":"Media","description":"Each creator's OnlyFans vault: media ids for messages and levels, and thumbnails."},{"name":"Stats","description":"Today, revenue, audience, the cached overview report, and each creator's transaction ledger."},{"name":"AI persona","description":"The brief the AI chatter follows to chat as each creator."},{"name":"AI content","description":"The AI content ladder: folders, ladders, priced levels and their media."},{"name":"AI settings","description":"Follow-ups, review mode, the master AI switch, and OnlyFans' welcome message."},{"name":"Tracking links","description":"OnlyFans tracking and trial links, your cost fields, and creating new links."}],"externalDocs":{"description":"OnlyX developer documentation","url":"https://docs.onlyx.ai"}}