> ## Documentation Index
> Fetch the complete documentation index at: https://help.onlyx.ai/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> You are reading OnlyX Help: the OnlyX Help Center and the OnlyX developer documentation. OnlyX is an AI chatting and CRM platform for OnlyFans agencies. Its AI chatter is called Hugo in the app.
> Pages at the site root (for example /inbox/..., /hugo/..., /billing/...) are Help Center articles for agency owners, admins, chatters and creators who use the app at app.onlyx.ai. Words in bold are the exact button, menu and label names the app shows; keep them exactly as written. When these pages do not answer a question, the person can email support at support@onlyx.ai.
> Pages under /developers are the developer documentation. REST API base URL: https://api.onlyx.ai/v1 (authenticate with `Authorization: Bearer <API key>`; keys start with onx_sk_ and are created in app.onlyx.ai under Settings > API & MCP). MCP server: https://mcp.onlyx.ai/mcp (OAuth, or a Bearer API key). In the API the AI chatter is `ai` on the wire. Money is integer US cents in fields ending in Cents; timestamps are UTC ISO-8601.
> Rules for assistants acting on a user's behalf: discover ids with list calls and never invent them; before any call that reaches a real fan or the live OnlyFans account (sending a message, releasing a chat to the AI, turning AI on for a chat, resolving a hand-off, turning review mode off, changing the welcome message, creating a tracking link) show the user the exact content and get explicit confirmation; send every POST with an Idempotency-Key and reuse it on retry; never resend a message whose delivery status is unconfirmed; never ask a creator for her OnlyFans password or codes - she signs in herself through a connect link and the OnlyX Login app.

# Send a message

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

Errors: `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.

Limits: 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.

**Scope:** requires `messages:send`.

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

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

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



## OpenAPI

````yaml /developers/api-reference/openapi.json post /v1/conversations/{conversationId}/messages
openapi: 3.1.0
info:
  contact:
    email: developers@onlyx.ai
    name: OnlyX developers
    url: https://docs.onlyx.ai/
  description: >
    The OnlyX API runs your OnlyX workspace from your own code and AI tools:
    creators and their

    connection, the fan inbox, fans, vault media, statistics, the AI chatter's
    setup and tracking links.


    **Authentication.** Send an API key as `Authorization: Bearer onx_sk_…`
    (create one in OnlyX under

    Settings → API & MCP), or an OAuth access token issued to a connected app.
    Keys belong to the

    workspace, carry scopes, and can be limited to some creators.


    **Conventions.** JSON with camelCase fields; timestamps are ISO-8601 UTC
    with a `Z`; money is integer

    US cents (fields end in `Cents`). Lists return `{"data": [...], "hasMore":
    bool, "nextCursor": string|null}`

    and page with `limit` and `cursor`.


    **Scopes.** Every operation names the scope it needs in `x-required-scope`
    and in its description; a

    credential without it gets `403 INSUFFICIENT_SCOPE`.


    **Errors and limits.** Errors are `{"error": {"code", "message",
    "requestId"}}`; every response

    carries `X-Request-Id`. The default limit is 120 requests per 60 seconds per

    credential, reported in `X-RateLimit-*` headers; over it you get `429
    RATE_LIMITED` with `Retry-After`.

    Operations with limits of their own list them in `x-rate-limits`.


    **Idempotency and safety.** Every write accepts an `Idempotency-Key` header
    (required when sending a

    message, adding a creator and creating a tracking link): retry with the same
    key and you get the

    original answer instead of a second action. Operations marked
    `x-reaches-fans: true` can reach a real

    fan or change the live OnlyFans account.


    Guides, the MCP server and more: [docs.onlyx.ai](https://docs.onlyx.ai).
  summary: >-
    Run your OnlyX workspace (creators, inbox, fans, stats and the AI chatter)
    from code and AI tools.
  title: OnlyX API
  version: 1.0.0
servers:
  - description: Production
    url: https://api.onlyx.ai
security:
  - bearerAuth: []
tags:
  - description: 'Who you are: the workspace and credential behind a key.'
    name: Workspace
  - description: >-
      The creators your workspace manages: list, read, add, rename, and the AI
      switch.
    name: Creators
  - description: >-
      Connecting a creator's OnlyFans account: connect links she opens on her
      own device, and the connection status to poll.
    name: Connect
  - description: >-
      The fan inbox: list and count conversations, read state, take over,
      release, and the per-chat AI switch.
    name: Conversations
  - description: Read message history and send text, free media and paid messages to fans.
    name: Messages
  - description: Conversations the AI chatter handed to your team, and resolving them.
    name: Hand-offs
  - description: >-
      Fans, their purchases, fan lists, and your team's notes, custom names and
      mute flag.
    name: Fans
  - description: >-
      Each creator's OnlyFans vault: media ids for messages and levels, and
      thumbnails.
    name: Media
  - description: >-
      Today, revenue, audience, the cached overview report, and each creator's
      transaction ledger.
    name: Stats
  - description: The brief the AI chatter follows to chat as each creator.
    name: AI persona
  - description: 'The AI content ladder: folders, ladders, priced levels and their media.'
    name: AI content
  - description: >-
      Follow-ups, review mode, the master AI switch, and OnlyFans' welcome
      message.
    name: AI settings
  - description: >-
      OnlyFans tracking and trial links, your cost fields, and creating new
      links.
    name: Tracking links
externalDocs:
  description: OnlyX developer documentation
  url: https://docs.onlyx.ai
paths:
  /v1/conversations/{conversationId}/messages:
    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.


        Errors: `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.


        Limits: 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.


        **Scope:** requires `messages:send`.


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


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


        **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:
        - description: The conversation id (`cnv_…`), from `GET /v1/conversations`.
          in: path
          name: conversationId
          required: true
          schema:
            description: The conversation id (`cnv_…`), from `GET /v1/conversations`.
            maxLength: 40
            title: Conversationid
            type: string
        - $ref: '#/components/parameters/IdempotencyKeyRequired'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SendMessageRequest'
        required: true
      responses:
        '202':
          content:
            application/json:
              example:
                conversationId: cnv_3d9e7b1a5c2f4e8d6a0b
                createdAt: '2026-09-26T10:02:11.000Z'
                delivery:
                  reason: null
                  status: queued
                direction: out
                id: msg_1a2b3c4d5e6f7a8b9c0d
                media:
                  - id: '4012345678'
                    preview: true
                    type: photo
                  - id: '4012345679'
                    preview: false
                    type: photo
                paid:
                  priceCents: 1500
                  purchased: false
                  purchasedAt: null
                sender: team
                text: Made this one just for you 😘
                tipCents: null
              schema:
                $ref: '#/components/schemas/Message'
          description: 'Accepted: the message, with its delivery status.'
          headers:
            Idempotent-Replayed:
              $ref: '#/components/headers/Idempotent-Replayed'
            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'
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
        '400':
          content:
            application/json:
              examples:
                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
                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
              schema:
                $ref: '#/components/schemas/Error'
          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'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          content:
            application/json:
              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
              schema:
                $ref: '#/components/schemas/Error'
          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:
            WWW-Authenticate:
              $ref: '#/components/headers/WWW-Authenticate'
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
        '404':
          content:
            application/json:
              examples:
                CONVERSATION_NOT_FOUND:
                  summary: CONVERSATION_NOT_FOUND
                  value:
                    error:
                      code: CONVERSATION_NOT_FOUND
                      message: Conversation not found.
                      requestId: req-3f9a1c2b7d4e5f60a1b2c3d4
              schema:
                $ref: '#/components/schemas/Error'
          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'
        '409':
          content:
            application/json:
              examples:
                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_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
                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
                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
              schema:
                $ref: '#/components/schemas/Error'
          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'
        '429':
          content:
            application/json:
              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
              schema:
                $ref: '#/components/schemas/Error'
          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:
            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'
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
        '500':
          $ref: '#/components/responses/InternalError'
        '503':
          content:
            application/json:
              examples:
                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
                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
              schema:
                $ref: '#/components/schemas/Error'
          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'
components:
  parameters:
    IdempotencyKeyRequired:
      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`.
      example: 3b1f7c2e-9d4a-4e8b-a6c1-5f0d2e7b9a14
      in: header
      name: Idempotency-Key
      required: true
      schema:
        maxLength: 64
        minLength: 8
        pattern: ^[A-Za-z0-9_-]{8,64}$
        type: string
  schemas:
    SendMessageRequest:
      additionalProperties: false
      examples:
        - text: Thinking of you 😘
        - mediaIds:
            - '4012345679'
          previewMediaIds:
            - '4012345678'
          priceCents: 1500
          text: Made this one just for you
      properties:
        mediaIds:
          description: >-
            Up to 20 vault media ids locked behind `priceCents`. Requires a
            price of at least 300 cents.
          items:
            pattern: ^\d{1,25}$
            type: string
          maxItems: 20
          type: array
          uniqueItems: true
        previewMediaIds:
          description: >-
            Up to 20 vault media ids the fan receives free and unlocked. On a
            paid message these are the teaser.
          items:
            pattern: ^\d{1,25}$
            type: string
          maxItems: 20
          type: array
          uniqueItems: true
        priceCents:
          default: 0
          description: >-
            The unlock price in cents: `0` (free) or 300 to 500000 ($3.00 to
            $5,000.00). A price requires `mediaIds`.
          maximum: 500000
          minimum: 0
          type: integer
        text:
          default: ''
          description: >-
            The message text, up to 4,000 characters. Optional when media is
            attached.
          maxLength: 4000
          type: string
      title: SendMessageRequest
      type: object
    Message:
      examples:
        - conversationId: cnv_3d9e7b1a5c2f4e8d6a0b
          createdAt: '2026-09-26T10:02:11.000Z'
          delivery:
            reason: null
            status: queued
          direction: out
          id: msg_1a2b3c4d5e6f7a8b9c0d
          media:
            - id: '4012345678'
              preview: true
              type: photo
            - id: '4012345679'
              preview: false
              type: photo
          paid:
            priceCents: 1500
            purchased: false
            purchasedAt: null
          sender: team
          text: Made this one just for you 😘
          tipCents: null
      properties:
        conversationId:
          description: The conversation it belongs to.
          type: string
        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.
        direction:
          description: '`in` from the fan, `out` to the fan.'
          enum:
            - in
            - out
          type: string
        id:
          description: The message id (`msg_…`).
          type: string
        media:
          description: Media attached to the message.
          items:
            $ref: '#/components/schemas/MessageMedia'
          type: array
        paid:
          anyOf:
            - $ref: '#/components/schemas/Paid'
            - type: 'null'
          description: Set on a paid message, otherwise `null`.
        sender:
          description: >-
            Who wrote it: the fan, the AI chatter, or your team (including
            anything sent through the API).
          enum:
            - fan
            - ai
            - team
          type: string
        text:
          description: The message text. May be empty when the message is media only.
          type: string
        tipCents:
          anyOf:
            - type: integer
            - type: 'null'
          description: A tip that came with this message, in cents, or `null`.
      required:
        - id
        - conversationId
        - direction
        - sender
        - text
        - media
        - paid
        - tipCents
        - createdAt
        - delivery
      title: Message
      type: object
    Error:
      properties:
        error:
          properties:
            code:
              description: Stable, machine-readable error code.
              type: string
            message:
              description: What went wrong, in a sentence for a person.
              type: string
            requestId:
              description: >-
                The id of this request (also in the `X-Request-Id` header).
                Quote it to support.
              type: string
          required:
            - code
            - message
            - requestId
          type: object
      required:
        - error
      type: object
    Delivery:
      properties:
        reason:
          anyOf:
            - type: string
            - type: 'null'
          description: >-
            Why it failed or was not sent, as a sentence for a person. `null`
            otherwise.
        status:
          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).
          enum:
            - queued
            - sent
            - failed
            - unconfirmed
            - not_sent
          type: string
      required:
        - status
        - reason
      title: Delivery
      type: object
    MessageMedia:
      properties:
        id:
          description: The vault media id (digits).
          type: string
        preview:
          description: >-
            `true` for free media the fan sees unlocked; `false` for media
            behind the price.
          type: boolean
        type:
          anyOf:
            - enum:
                - photo
                - video
                - gif
                - audio
              type: string
            - type: 'null'
          description: The media type, or `null` when OnlyFans did not say.
      required:
        - id
        - type
        - preview
      title: MessageMedia
      type: object
    Paid:
      properties:
        priceCents:
          description: The unlock price, in cents.
          type: integer
        purchased:
          description: Whether the fan has unlocked it.
          type: boolean
        purchasedAt:
          anyOf:
            - type: string
            - type: 'null'
          description: When the fan unlocked it.
      required:
        - priceCents
        - purchased
        - purchasedAt
      title: Paid
      type: object
  headers:
    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:
        enum:
          - 'true'
        type: string
    X-RateLimit-Limit:
      description: >-
        Requests allowed in the current window of the tightest limit this
        request counted against.
      example: 120
      schema:
        type: integer
    X-RateLimit-Remaining:
      description: Requests left in that window.
      example: 117
      schema:
        type: integer
    X-RateLimit-Reset:
      description: When that window resets, as Unix epoch seconds.
      example: 1790431380
      schema:
        type: integer
    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.
      example: req-3f9a1c2b7d4e5f60a1b2c3d4
      schema:
        type: string
    WWW-Authenticate:
      description: >-
        The authentication challenge (RFC 6750), for example `Bearer
        error="insufficient_scope", scope="messages:send"`.
      schema:
        type: string
    Retry-After:
      description: Seconds to wait before trying again.
      example: 12
      schema:
        type: integer
  responses:
    Unauthorized:
      content:
        application/json:
          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
            INVALID_TOKEN:
              summary: INVALID_TOKEN
              value:
                error:
                  code: INVALID_TOKEN
                  message: The access token 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
          schema:
            $ref: '#/components/schemas/Error'
      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:
        WWW-Authenticate:
          $ref: '#/components/headers/WWW-Authenticate'
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
    InternalError:
      content:
        application/json:
          examples:
            INTERNAL_ERROR:
              summary: INTERNAL_ERROR
              value:
                error:
                  code: INTERNAL_ERROR
                  message: Something went wrong on our side.
                  requestId: req-3f9a1c2b7d4e5f60a1b2c3d4
          schema:
            $ref: '#/components/schemas/Error'
      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'
  securitySchemes:
    bearerAuth:
      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>`.
      scheme: bearer
      type: http

````