# GENERATED FILE — do not edit.
# Composed by scripts/docs/compose-specs.mjs from the sources in scripts/docs/compose.json.
# Regenerate with `pnpm docs:generate`; `pnpm docs:check` fails on drift.

asyncapi: 3.0.0
id: urn:com:joyride-core:v8:json-rpc-ws
info:
  title: Joyride Trading WebSocket
  version: 0.4.0
  description: |
    JSON-RPC 2.0 over one WebSocket. Every request is
    `{"jsonrpc":"2.0","id":<n>,"method":"<name>","params":{...}}` and the
    reply echoes `id` with either `result` or `error`. Server pushes arrive as
    `{"jsonrpc":"2.0","method":"subscription","params":{"channel":"<name>","data":{...}}}`
    with no `id`. The `params` and `result` objects of each method, and the
    `data` object of each push, are what the pages below describe.

    `public/*` methods need no authentication. Every `rfq.*` method needs a
    session from `public/auth` or `public/session_resume`; `rfq.maker.*`
    additionally needs the `rfq_quoter` role.
defaultContentType: application/json
servers:
  trading:
    host: joyride.exchange
    protocol: wss
    description: Trading JSON-RPC endpoint
    pathname: /api/client
  oracle:
    host: joyride.exchange
    pathname: /api/oracle
    protocol: wss
    description: Oracle broadcast feed (spot prices and TWAP previews)
  prices:
    host: joyride.exchange
    pathname: /api/v1/prices
    protocol: wss
    description: Price-history candle feed (subscribe / unsubscribe)
channels:
  public.get_nonce:
    address: /
    title: public/get_nonce
    summary: Fetch a single-use sign-in nonce.
    description: JSON-RPC method `public/get_nonce` on the trading socket. Takes no `params`. The nonce is valid for 300 seconds and consumed by the next `public/auth`.
    servers:
      - $ref: "#/servers/trading"
    messages:
      request:
        $ref: "#/components/messages/GetNonceRequest"
      result:
        $ref: "#/components/messages/GetNonceResult"
      error:
        $ref: "#/components/messages/JsonRpcError"
  public.auth:
    address: /
    title: public/auth
    summary: Sign in with a wallet signature over the nonce.
    description: "JSON-RPC method `public/auth` on the trading socket. The signed message is exactly `Sign in to Joyride\\n\\nWallet: {wallet}\\nNonce: {nonce}`. A wallet with no account is refused with `1001`; signing in never creates an account."
    servers:
      - $ref: "#/servers/trading"
    messages:
      request:
        $ref: "#/components/messages/AuthRequest"
      result:
        $ref: "#/components/messages/AuthResult"
      error:
        $ref: "#/components/messages/JsonRpcError"
  public.session_resume:
    address: /
    title: public/session_resume
    summary: Authenticate a new socket with a stored session token.
    description: JSON-RPC method `public/session_resume` on the trading socket. No new token is issued. The reply's `roles` is a snapshot; every `rfq.*` call checks the live role.
    servers:
      - $ref: "#/servers/trading"
    messages:
      request:
        $ref: "#/components/messages/SessionResumeRequest"
      result:
        $ref: "#/components/messages/SessionResumeResult"
      error:
        $ref: "#/components/messages/JsonRpcError"
  public.get_market_config:
    address: /
    title: public/get_market_config
    summary: Read the order-book surface's decimal conventions.
    description: JSON-RPC method `public/get_market_config` on the trading socket. The decimals here are the 8dp core convention; RFQ values use 6dp strings.
    servers:
      - $ref: "#/servers/trading"
    messages:
      request:
        $ref: "#/components/messages/GetMarketConfigRequest"
      result:
        $ref: "#/components/messages/GetMarketConfigResult"
      error:
        $ref: "#/components/messages/JsonRpcError"
  public.get_instruments:
    address: /
    title: public/get_instruments
    summary: List the tradable instruments.
    description: JSON-RPC method `public/get_instruments` on the trading socket.
    servers:
      - $ref: "#/servers/trading"
    messages:
      request:
        $ref: "#/components/messages/GetInstrumentsRequest"
      result:
        $ref: "#/components/messages/GetInstrumentsResult"
      error:
        $ref: "#/components/messages/JsonRpcError"
  rfq.create:
    address: /
    title: rfq.create
    summary: Open a request for quote on a package of 1 to 13 legs.
    description: JSON-RPC method `rfq.create` on the trading socket. Idempotent on `(account, client_ref)`. The request stays open for quotes for 5 seconds; follow it on the `rfq` channel after `rfq.subscribe`.
    servers:
      - $ref: "#/servers/trading"
    messages:
      request:
        $ref: "#/components/messages/RfqCreateRequest"
      result:
        $ref: "#/components/messages/RfqCreateResult"
      error:
        $ref: "#/components/messages/RfqError"
  rfq.accept:
    address: /
    title: rfq.accept
    summary: Accept one side of one quote on your request with a wallet signature.
    description: JSON-RPC method `rfq.accept` on the trading socket. Several makers may quote one request; `quote_id` names the quote to accept, which should be the request's `best.ask` to buy or `best.bid` to sell, and must still be live or the call is `RFQ_NOT_FOUND`. `expires_at` must equal that quote's. The two maxima are signed consent; a value below the current requirement is `RFQ_SIGNED_LIMIT_BELOW_REQUIREMENT`.
    servers:
      - $ref: "#/servers/trading"
    messages:
      request:
        $ref: "#/components/messages/RfqAcceptRequest"
      result:
        $ref: "#/components/messages/RfqAcceptResult"
      error:
        $ref: "#/components/messages/RfqError"
  rfq.get:
    address: /
    title: rfq.get
    summary: Read one of the caller's own requests.
    description: JSON-RPC method `rfq.get` on the trading socket. Returns only requests the caller created; a maker reads its fills from `GET /api/query/rfq-fills?role=maker`.
    servers:
      - $ref: "#/servers/trading"
    messages:
      request:
        $ref: "#/components/messages/RfqGetRequest"
      result:
        $ref: "#/components/messages/RfqGetResult"
      error:
        $ref: "#/components/messages/RfqError"
  rfq.list:
    address: /
    title: rfq.list
    summary: Page the caller's own live requests.
    description: JSON-RPC method `rfq.list` on the trading socket.
    servers:
      - $ref: "#/servers/trading"
    messages:
      request:
        $ref: "#/components/messages/RfqListRequest"
      result:
        $ref: "#/components/messages/RfqListResult"
      error:
        $ref: "#/components/messages/RfqError"
  rfq.subscribe:
    address: /
    title: rfq.subscribe
    summary: Subscribe this socket to the caller's `rfq` pushes.
    description: JSON-RPC method `rfq.subscribe` on the trading socket. Subscriptions are per socket and not restored by `public/session_resume`.
    servers:
      - $ref: "#/servers/trading"
    messages:
      request:
        $ref: "#/components/messages/RfqSubscribeRequest"
      result:
        $ref: "#/components/messages/RfqSubscribeResult"
      error:
        $ref: "#/components/messages/RfqError"
  rfq.maker.subscribe:
    address: /
    title: rfq.maker.subscribe
    summary: Subscribe this socket to open requests on the `rfq_maker` channel.
    description: JSON-RPC method `rfq.maker.subscribe` on the trading socket. Requires the `rfq_quoter` role, otherwise `RFQ_WRONG_ROLE`. Per socket; subscribe on every connection, then page `rfq.maker.poll` to catch up. Not charged against the rate budget.
    servers:
      - $ref: "#/servers/trading"
    messages:
      request:
        $ref: "#/components/messages/RfqMakerSubscribeRequest"
      result:
        $ref: "#/components/messages/RfqMakerSubscribeResult"
      error:
        $ref: "#/components/messages/RfqError"
  rfq.maker.poll:
    address: /
    title: rfq.maker.poll
    summary: Page the requests still open for quoting.
    description: JSON-RPC method `rfq.maker.poll` on the trading socket. Items have the same shape as the `open` push; deduplicate on `rfq_id`. Pass `next_cursor` back as `cursor` until a page has none.
    servers:
      - $ref: "#/servers/trading"
    messages:
      request:
        $ref: "#/components/messages/RfqMakerPollRequest"
      result:
        $ref: "#/components/messages/RfqMakerPollResult"
      error:
        $ref: "#/components/messages/RfqError"
  rfq.maker.respond:
    address: /
    title: rfq.maker.respond
    summary: Answer an open request with one signed quote.
    description: JSON-RPC method `rfq.maker.respond` on the trading socket. Several makers may quote one request, one quote each, while its 5-second window is open; the taker is shown the best price and accepts one quote. A delivered quote is firm until `expires_at`, with no cancel or replace. `bid` and `ask` are whole-package totals; send only the side(s) the request allows.
    servers:
      - $ref: "#/servers/trading"
    messages:
      request:
        $ref: "#/components/messages/RfqMakerRespondRequest"
      result:
        $ref: "#/components/messages/RfqMakerRespondResult"
      error:
        $ref: "#/components/messages/RfqError"
  rfq.maker.decline:
    address: /
    title: rfq.maker.decline
    summary: Decline an open request with a reason.
    description: JSON-RPC method `rfq.maker.decline` on the trading socket. A decline removes this maker alone from the request, which stays `pending` for every other quoter until a quote wins or the window closes. A decline is an answer sent to the trader; use it for a request you will not price, not as a connectivity check, and leave a request unanswered when you simply have no price.
    servers:
      - $ref: "#/servers/trading"
    messages:
      request:
        $ref: "#/components/messages/RfqMakerDeclineRequest"
      result:
        $ref: "#/components/messages/RfqMakerDeclineResult"
      error:
        $ref: "#/components/messages/RfqError"
  rfq:
    address: rfq
    title: rfq channel (pushes)
    summary: Lifecycle pushes for the caller's own requests.
    description: Delivered as `subscription` frames with `params.channel` `rfq` on a socket that called `rfq.subscribe`. Each push carries the whole request object.
    servers:
      - $ref: "#/servers/trading"
    messages:
      quote:
        $ref: "#/components/messages/RfqQuotePush"
      state:
        $ref: "#/components/messages/RfqStatePush"
      finality:
        $ref: "#/components/messages/RfqFinalityPush"
  rfq_maker:
    address: rfq_maker
    title: rfq_maker channel (pushes)
    summary: Open requests for every quoter, and the quoting maker's own lifecycle, fill and finality pushes.
    description: Delivered as `subscription` frames with `params.channel` `rfq_maker` on a socket that called `rfq.maker.subscribe`. `open` goes to every subscribed quoter; `state` goes to the maker whose quote it concerns (`quoted`, then `accepted` or `lost`, or `expired`); `fill` and `finality` go only to the maker whose quote was accepted. A maker never hears about another maker's quote.
    servers:
      - $ref: "#/servers/trading"
    messages:
      open:
        $ref: "#/components/messages/RfqOpenPush"
      state:
        $ref: "#/components/messages/RfqMakerStatePush"
      fill:
        $ref: "#/components/messages/RfqMakerFillPush"
      finality:
        $ref: "#/components/messages/RfqMakerFinalityPush"
  oracle:
    address: /
    servers:
      - $ref: "#/servers/oracle"
    description: |
      Receive-only. Every frame carries an RFC 3339 `timestamp` (the
      envelope serialization time) and a `type` tag, with the payload
      fields flattened into the same object. Spot frames also carry Pyth's
      own `publish_time` so a consumer can measure freshness independently
      of transport latency.
    messages:
      price:
        $ref: "#/components/messages/PlatformOraclePrice"
      twap_preview:
        $ref: "#/components/messages/PlatformOracleTwapPreview"
      connected:
        $ref: "#/components/messages/PlatformOracleConnected"
      disconnected:
        $ref: "#/components/messages/PlatformOracleDisconnected"
      error:
        $ref: "#/components/messages/PlatformOracleError"
      heartbeat:
        $ref: "#/components/messages/PlatformOracleHeartbeat"
  prices:
    address: /
    servers:
      - $ref: "#/servers/prices"
    description: |
      Bidirectional. The server sends `connected` immediately after the
      upgrade. The client then sends `subscribe` with one or more
      `{ symbol, timeframe }` channels and receives `subscription_confirmed`
      followed by a `candle_update` whenever a subscribed candle changes
      (the service polls its store every 250 ms, so updates arrive at most
      four times a second per channel).

      Keepalive is mandatory: the server sends `ping` every 30 s and closes
      any socket that has not answered with `pong` before the next ping.
      A client may also send `ping` and will receive `pong`.
    messages:
      subscribe:
        $ref: "#/components/messages/PlatformPricesSubscribe"
      unsubscribe:
        $ref: "#/components/messages/PlatformPricesUnsubscribe"
      clientPing:
        $ref: "#/components/messages/PlatformPricesClientPing"
      clientPong:
        $ref: "#/components/messages/PlatformPricesClientPong"
      connected:
        $ref: "#/components/messages/PlatformPricesConnected"
      subscription_confirmed:
        $ref: "#/components/messages/PlatformPricesSubscriptionConfirmed"
      unsubscription_confirmed:
        $ref: "#/components/messages/PlatformPricesUnsubscriptionConfirmed"
      candle_update:
        $ref: "#/components/messages/PlatformPricesCandleUpdate"
      serverPing:
        $ref: "#/components/messages/PlatformPricesServerPing"
      serverPong:
        $ref: "#/components/messages/PlatformPricesServerPong"
      error:
        $ref: "#/components/messages/PlatformPricesError"
operations:
  public.get_nonce.request:
    action: receive
    channel:
      $ref: "#/channels/public.get_nonce"
    title: Request
    messages:
      - $ref: "#/channels/public.get_nonce/messages/request"
  public.get_nonce.reply:
    action: send
    channel:
      $ref: "#/channels/public.get_nonce"
    title: Reply
    messages:
      - $ref: "#/channels/public.get_nonce/messages/result"
      - $ref: "#/channels/public.get_nonce/messages/error"
  public.auth.request:
    action: receive
    channel:
      $ref: "#/channels/public.auth"
    title: Request
    messages:
      - $ref: "#/channels/public.auth/messages/request"
  public.auth.reply:
    action: send
    channel:
      $ref: "#/channels/public.auth"
    title: Reply
    messages:
      - $ref: "#/channels/public.auth/messages/result"
      - $ref: "#/channels/public.auth/messages/error"
  public.session_resume.request:
    action: receive
    channel:
      $ref: "#/channels/public.session_resume"
    title: Request
    messages:
      - $ref: "#/channels/public.session_resume/messages/request"
  public.session_resume.reply:
    action: send
    channel:
      $ref: "#/channels/public.session_resume"
    title: Reply
    messages:
      - $ref: "#/channels/public.session_resume/messages/result"
      - $ref: "#/channels/public.session_resume/messages/error"
  public.get_market_config.request:
    action: receive
    channel:
      $ref: "#/channels/public.get_market_config"
    title: Request
    messages:
      - $ref: "#/channels/public.get_market_config/messages/request"
  public.get_market_config.reply:
    action: send
    channel:
      $ref: "#/channels/public.get_market_config"
    title: Reply
    messages:
      - $ref: "#/channels/public.get_market_config/messages/result"
      - $ref: "#/channels/public.get_market_config/messages/error"
  public.get_instruments.request:
    action: receive
    channel:
      $ref: "#/channels/public.get_instruments"
    title: Request
    messages:
      - $ref: "#/channels/public.get_instruments/messages/request"
  public.get_instruments.reply:
    action: send
    channel:
      $ref: "#/channels/public.get_instruments"
    title: Reply
    messages:
      - $ref: "#/channels/public.get_instruments/messages/result"
      - $ref: "#/channels/public.get_instruments/messages/error"
  rfq.create.request:
    action: receive
    channel:
      $ref: "#/channels/rfq.create"
    title: Request
    messages:
      - $ref: "#/channels/rfq.create/messages/request"
  rfq.create.reply:
    action: send
    channel:
      $ref: "#/channels/rfq.create"
    title: Reply
    messages:
      - $ref: "#/channels/rfq.create/messages/result"
      - $ref: "#/channels/rfq.create/messages/error"
  rfq.accept.request:
    action: receive
    channel:
      $ref: "#/channels/rfq.accept"
    title: Request
    messages:
      - $ref: "#/channels/rfq.accept/messages/request"
  rfq.accept.reply:
    action: send
    channel:
      $ref: "#/channels/rfq.accept"
    title: Reply
    messages:
      - $ref: "#/channels/rfq.accept/messages/result"
      - $ref: "#/channels/rfq.accept/messages/error"
  rfq.get.request:
    action: receive
    channel:
      $ref: "#/channels/rfq.get"
    title: Request
    messages:
      - $ref: "#/channels/rfq.get/messages/request"
  rfq.get.reply:
    action: send
    channel:
      $ref: "#/channels/rfq.get"
    title: Reply
    messages:
      - $ref: "#/channels/rfq.get/messages/result"
      - $ref: "#/channels/rfq.get/messages/error"
  rfq.list.request:
    action: receive
    channel:
      $ref: "#/channels/rfq.list"
    title: Request
    messages:
      - $ref: "#/channels/rfq.list/messages/request"
  rfq.list.reply:
    action: send
    channel:
      $ref: "#/channels/rfq.list"
    title: Reply
    messages:
      - $ref: "#/channels/rfq.list/messages/result"
      - $ref: "#/channels/rfq.list/messages/error"
  rfq.subscribe.request:
    action: receive
    channel:
      $ref: "#/channels/rfq.subscribe"
    title: Request
    messages:
      - $ref: "#/channels/rfq.subscribe/messages/request"
  rfq.subscribe.reply:
    action: send
    channel:
      $ref: "#/channels/rfq.subscribe"
    title: Reply
    messages:
      - $ref: "#/channels/rfq.subscribe/messages/result"
      - $ref: "#/channels/rfq.subscribe/messages/error"
  rfq.maker.subscribe.request:
    action: receive
    channel:
      $ref: "#/channels/rfq.maker.subscribe"
    title: Request
    messages:
      - $ref: "#/channels/rfq.maker.subscribe/messages/request"
  rfq.maker.subscribe.reply:
    action: send
    channel:
      $ref: "#/channels/rfq.maker.subscribe"
    title: Reply
    messages:
      - $ref: "#/channels/rfq.maker.subscribe/messages/result"
      - $ref: "#/channels/rfq.maker.subscribe/messages/error"
  rfq.maker.poll.request:
    action: receive
    channel:
      $ref: "#/channels/rfq.maker.poll"
    title: Request
    messages:
      - $ref: "#/channels/rfq.maker.poll/messages/request"
  rfq.maker.poll.reply:
    action: send
    channel:
      $ref: "#/channels/rfq.maker.poll"
    title: Reply
    messages:
      - $ref: "#/channels/rfq.maker.poll/messages/result"
      - $ref: "#/channels/rfq.maker.poll/messages/error"
  rfq.maker.respond.request:
    action: receive
    channel:
      $ref: "#/channels/rfq.maker.respond"
    title: Request
    messages:
      - $ref: "#/channels/rfq.maker.respond/messages/request"
  rfq.maker.respond.reply:
    action: send
    channel:
      $ref: "#/channels/rfq.maker.respond"
    title: Reply
    messages:
      - $ref: "#/channels/rfq.maker.respond/messages/result"
      - $ref: "#/channels/rfq.maker.respond/messages/error"
  rfq.maker.decline.request:
    action: receive
    channel:
      $ref: "#/channels/rfq.maker.decline"
    title: Request
    messages:
      - $ref: "#/channels/rfq.maker.decline/messages/request"
  rfq.maker.decline.reply:
    action: send
    channel:
      $ref: "#/channels/rfq.maker.decline"
    title: Reply
    messages:
      - $ref: "#/channels/rfq.maker.decline/messages/result"
      - $ref: "#/channels/rfq.maker.decline/messages/error"
  rfq.pushes:
    action: send
    channel:
      $ref: "#/channels/rfq"
    messages:
      - $ref: "#/channels/rfq/messages/quote"
      - $ref: "#/channels/rfq/messages/state"
      - $ref: "#/channels/rfq/messages/finality"
  rfq_maker.pushes:
    action: send
    channel:
      $ref: "#/channels/rfq_maker"
    messages:
      - $ref: "#/channels/rfq_maker/messages/open"
      - $ref: "#/channels/rfq_maker/messages/state"
      - $ref: "#/channels/rfq_maker/messages/fill"
      - $ref: "#/channels/rfq_maker/messages/finality"
  oracleReceive:
    action: receive
    channel:
      $ref: "#/channels/oracle"
    summary: Receive oracle broadcast frames
    messages:
      - $ref: "#/channels/oracle/messages/price"
      - $ref: "#/channels/oracle/messages/twap_preview"
      - $ref: "#/channels/oracle/messages/connected"
      - $ref: "#/channels/oracle/messages/disconnected"
      - $ref: "#/channels/oracle/messages/error"
      - $ref: "#/channels/oracle/messages/heartbeat"
  pricesSend:
    action: send
    channel:
      $ref: "#/channels/prices"
    summary: Subscribe, unsubscribe, and answer keepalive pings
    messages:
      - $ref: "#/channels/prices/messages/subscribe"
      - $ref: "#/channels/prices/messages/unsubscribe"
      - $ref: "#/channels/prices/messages/clientPing"
      - $ref: "#/channels/prices/messages/clientPong"
  pricesReceive:
    action: receive
    channel:
      $ref: "#/channels/prices"
    summary: Receive candle updates, confirmations, keepalive pings, and errors
    messages:
      - $ref: "#/channels/prices/messages/connected"
      - $ref: "#/channels/prices/messages/subscription_confirmed"
      - $ref: "#/channels/prices/messages/unsubscription_confirmed"
      - $ref: "#/channels/prices/messages/candle_update"
      - $ref: "#/channels/prices/messages/serverPing"
      - $ref: "#/channels/prices/messages/serverPong"
      - $ref: "#/channels/prices/messages/error"
components:
  messages:
    GetNonceRequest:
      name: GetNonceRequest
      title: Request params
      summary: "`params` is ignored."
      payload:
        type: object
        additionalProperties: false
        description: No parameters; send `{}` or omit `params`.
    GetNonceResult:
      name: GetNonceResult
      title: Result
      payload:
        $ref: "#/components/schemas/GetNonceResult"
      examples:
        - name: example
          payload:
            nonce: 3fA9kLm2PqRs7TuVwXyZ1bCdEfGh4JkL
    AuthRequest:
      name: AuthRequest
      title: Request params
      payload:
        $ref: "#/components/schemas/AuthParams"
      examples:
        - name: example
          payload:
            wallet: 7Gx2XvMk9Z1sZbY3qJ8pKcWnD4tFhRr6sA2eLmN5uVwQ
            signature: <base58 signature>
            message: "Sign in to Joyride\\n\\nWallet: 7Gx2XvMk9Z1sZbY3qJ8pKcWnD4tFhRr6sA2eLmN5uVwQ\\nNonce: 3fA9kLm2PqRs7TuVwXyZ1bCdEfGh4JkL"
    AuthResult:
      name: AuthResult
      title: Result
      payload:
        $ref: "#/components/schemas/AuthResult"
      examples:
        - name: example
          payload:
            wallet: 7Gx2XvMk9Z1sZbY3qJ8pKcWnD4tFhRr6sA2eLmN5uVwQ
            account_id: 1042
            roles:
              - rfq_quoter
            session_token: <jwt>
    SessionResumeRequest:
      name: SessionResumeRequest
      title: Request params
      payload:
        $ref: "#/components/schemas/SessionResumeParams"
      examples:
        - name: example
          payload:
            session_token: <jwt>
    SessionResumeResult:
      name: SessionResumeResult
      title: Result
      payload:
        $ref: "#/components/schemas/SessionResumeResult"
      examples:
        - name: example
          payload:
            wallet: 7Gx2XvMk9Z1sZbY3qJ8pKcWnD4tFhRr6sA2eLmN5uVwQ
            account_id: 1042
            roles:
              - rfq_quoter
    GetMarketConfigRequest:
      name: GetMarketConfigRequest
      title: Request params
      payload:
        type: object
        additionalProperties: false
        description: No parameters; send `{}` or omit `params`.
    GetMarketConfigResult:
      name: GetMarketConfigResult
      title: Result
      payload:
        $ref: "#/components/schemas/MarketConfig"
      examples:
        - name: example
          payload:
            version: v8
            price_decimals: 8
            quantity_decimals: 8
    GetInstrumentsRequest:
      name: GetInstrumentsRequest
      title: Request params
      payload:
        type: object
        additionalProperties: false
        description: No parameters; send `{}` or omit `params`.
    GetInstrumentsResult:
      name: GetInstrumentsResult
      title: Result
      payload:
        $ref: "#/components/schemas/InstrumentsResult"
      examples:
        - name: example
          payload:
            instruments:
              - symbol: BTC_USDC-2MAY26-77000-C
                underlying: BTC
                strike: "77000.00000000"
                option_type: call
                expiry: 1777708800
                state: open
                tick_size: "0.01000000"
                min_size: "0.01000000"
                contract_size: "1.00000000"
    JsonRpcError:
      name: JsonRpcError
      title: Error
      summary: "`1001` for an authentication or session failure, `-32602` for malformed params on a `public/*` method."
      payload:
        $ref: "#/components/schemas/JsonRpcError"
      examples:
        - name: example
          payload:
            code: 1001
            message: unknown account
    RfqCreateRequest:
      name: RfqCreateRequest
      title: Request params
      payload:
        $ref: "#/components/schemas/RfqCreateParams"
      examples:
        - name: example
          payload:
            client_ref: 123e4567-e89b-12d3-a456-426614174000
            side: two_way
            legs:
              - instrument: BTC_USDC-2MAY26-77000-C
                quantity: "1.000000"
              - instrument: BTC_USDC-2MAY26-80000-C
                quantity: "-2.000000"
    RfqCreateResult:
      name: RfqCreateResult
      title: Result
      payload:
        $ref: "#/components/schemas/RfqCreateResult"
      examples:
        - name: example
          payload:
            rfq_id: 0192f1a3b4c5d6e7f8091a2b3c4d5e6f
            state: pending
            request_deadline: 2026-05-01T15:40:06.000Z
    RfqAcceptRequest:
      name: RfqAcceptRequest
      title: Request params
      payload:
        $ref: "#/components/schemas/RfqAcceptParams"
      examples:
        - name: example
          payload:
            quote_id: <64 hex characters>
            side: buy
            expires_at: 1777650031
            max_taker_fee: "0.300000"
            max_taker_locked_balance: "0.000000"
            taker_signature: <base64 of 64 bytes>
    RfqAcceptResult:
      name: RfqAcceptResult
      title: Result
      payload:
        $ref: "#/components/schemas/RfqAcceptResult"
      examples:
        - name: example
          payload:
            rfq_id: 0192f1a3b4c5d6e7f8091a2b3c4d5e6f
            quote_id: <64 hex characters>
            state: accepted
    RfqGetRequest:
      name: RfqGetRequest
      title: Request params
      payload:
        $ref: "#/components/schemas/RfqGetParams"
      examples:
        - name: example
          payload:
            rfq_id: 0192f1a3b4c5d6e7f8091a2b3c4d5e6f
    RfqGetResult:
      name: RfqGetResult
      title: Result
      summary: The request object; the same shape every `rfq` push carries.
      payload:
        $ref: "#/components/schemas/RfqObject"
    RfqListRequest:
      name: RfqListRequest
      title: Request params
      payload:
        $ref: "#/components/schemas/RfqListParams"
      examples:
        - name: example
          payload:
            states:
              - quoted
              - accepted
            limit: 50
    RfqListResult:
      name: RfqListResult
      title: Result
      payload:
        $ref: "#/components/schemas/RfqListResult"
    RfqSubscribeRequest:
      name: RfqSubscribeRequest
      title: Request params
      payload:
        type: object
        additionalProperties: false
        description: No parameters; send `{}` or omit `params`.
    RfqSubscribeResult:
      name: RfqSubscribeResult
      title: Result
      payload:
        $ref: "#/components/schemas/SubscribeResult"
      examples:
        - name: example
          payload:
            channel: rfq
            subscribed: true
    RfqMakerSubscribeRequest:
      name: RfqMakerSubscribeRequest
      title: Request params
      payload:
        type: object
        additionalProperties: false
        description: No parameters; send `{}` or omit `params`.
    RfqMakerSubscribeResult:
      name: RfqMakerSubscribeResult
      title: Result
      payload:
        $ref: "#/components/schemas/SubscribeResult"
      examples:
        - name: example
          payload:
            channel: rfq_maker
            subscribed: true
    RfqMakerPollRequest:
      name: RfqMakerPollRequest
      title: Request params
      payload:
        $ref: "#/components/schemas/RfqPageParams"
      examples:
        - name: example
          payload:
            limit: 100
    RfqMakerPollResult:
      name: RfqMakerPollResult
      title: Result
      payload:
        $ref: "#/components/schemas/RfqMakerPollResult"
      examples:
        - name: example
          payload:
            items:
              - rfq_id: 0192f1a3b4c5d6e7f8091a2b3c4d5e6f
                side: two_way
                legs:
                  - instrument: BTC_USDC-2MAY26-77000-C
                    quantity: "1.000000"
                    asset: 0
                    strike: "77000.000000"
                    expiry: 1777708800
                    kind: call
                  - instrument: BTC_USDC-2MAY26-80000-C
                    quantity: "-2.000000"
                    asset: 0
                    strike: "80000.000000"
                    expiry: 1777708800
                    kind: call
                taker_account: 2077
                taker_subaccount: <base58 subaccount PDA>
                taker_funded: true
                maker_funding:
                  anchored: true
                  free_balance: "12500.000000"
                  buy:
                    required: "262.500000"
                    estimate: false
                    funded: true
                    package_cash_delta: "470.000000"
                    cash_debit: "0.000000"
                    fee: "0.000000"
                    max_fee: "0.000000"
                    writer: true
                    lock_increase: "250.000000"
                    post_fill_locked: "1250.000000"
                    max_locked_balance: "1262.500000"
                  sell:
                    required: "732.500000"
                    estimate: false
                    funded: true
                    package_cash_delta: "-470.000000"
                    cash_debit: "470.000000"
                    fee: "0.000000"
                    max_fee: "0.000000"
                    writer: true
                    lock_increase: "250.000000"
                    post_fill_locked: "1250.000000"
                    max_locked_balance: "1262.500000"
                request_deadline: 2026-05-01T15:40:06.000Z
                deployment:
                  program_id: 4BecHK2cEh6DgfpCdxbQAm812audv4ZXxTQB12RkMWB8
                  deployment_id: <64 hex characters>
                  config_pda: ECJGDQXPnzjoaaPveifT4FSKmYgSbNf1GeiHbtdi626u
                  epoch: 3
                nearest_leg_close: 2026-05-02T08:00:00.000Z
                max_quote_expires_at: 1777708798
    RfqMakerRespondRequest:
      name: RfqMakerRespondRequest
      title: Request params
      payload:
        $ref: "#/components/schemas/RfqMakerRespondParams"
      examples:
        - name: example
          payload:
            rfq_id: 0192f1a3b4c5d6e7f8091a2b3c4d5e6f
            quote_nonce: "1777650001234567890"
            bid: "440.000000"
            ask: "500.000000"
            issued_at: 1777650001
            expires_at: 1777650031
            max_maker_fee: "0.100000"
            max_maker_locked_balance: "1300.000000"
            maker_signature: <base64 of 64 bytes>
    RfqMakerRespondResult:
      name: RfqMakerRespondResult
      title: Result
      payload:
        $ref: "#/components/schemas/RfqMakerRespondResult"
      examples:
        - name: example
          payload:
            rfq_id: 0192f1a3b4c5d6e7f8091a2b3c4d5e6f
            quote_id: <64 hex characters>
            state: quoted
    RfqMakerDeclineRequest:
      name: RfqMakerDeclineRequest
      title: Request params
      payload:
        $ref: "#/components/schemas/RfqMakerDeclineParams"
      examples:
        - name: example
          payload:
            rfq_id: 0192f1a3b4c5d6e7f8091a2b3c4d5e6f
            reason: size_limit
    RfqMakerDeclineResult:
      name: RfqMakerDeclineResult
      title: Result
      payload:
        $ref: "#/components/schemas/RfqMakerDeclineResult"
      examples:
        - name: example
          payload:
            rfq_id: 0192f1a3b4c5d6e7f8091a2b3c4d5e6f
            state: declined
            decline_reason: size_limit
    RfqError:
      name: RfqError
      title: Error
      summary: Every `rfq.*` failure is JSON-RPC error `1200`; this is its `error.data` object.
      payload:
        $ref: "#/components/schemas/RfqTypedError"
      examples:
        - name: example
          payload:
            error: RFQ_NOT_READY
            message: RFQ chain-service state is not warm
            operation_id: rfq-18
            retryable: true
            rfq_id: 0192f1a3b4c5d6e7f8091a2b3c4d5e6f
    RfqOpenPush:
      name: RfqOpenPush
      title: open
      summary: A request opened; every subscribed quoter receives it.
      payload:
        $ref: "#/components/schemas/RfqOpenPush"
      examples:
        - name: example
          payload:
            type: open
            rfq:
              rfq_id: 0192f1a3b4c5d6e7f8091a2b3c4d5e6f
              side: two_way
              legs:
                - instrument: BTC_USDC-2MAY26-77000-C
                  quantity: "1.000000"
                  asset: 0
                  strike: "77000.000000"
                  expiry: 1777708800
                  kind: call
                - instrument: BTC_USDC-2MAY26-80000-C
                  quantity: "-2.000000"
                  asset: 0
                  strike: "80000.000000"
                  expiry: 1777708800
                  kind: call
              taker_account: 2077
              taker_subaccount: <base58 subaccount PDA>
              taker_funded: true
              maker_funding:
                anchored: true
                free_balance: "12500.000000"
                buy:
                  required: "262.500000"
                  estimate: false
                  funded: true
                  package_cash_delta: "470.000000"
                  cash_debit: "0.000000"
                  fee: "0.000000"
                  max_fee: "0.000000"
                  writer: true
                  lock_increase: "250.000000"
                  post_fill_locked: "1250.000000"
                  max_locked_balance: "1262.500000"
                sell:
                  required: "732.500000"
                  estimate: false
                  funded: true
                  package_cash_delta: "-470.000000"
                  cash_debit: "470.000000"
                  fee: "0.000000"
                  max_fee: "0.000000"
                  writer: true
                  lock_increase: "250.000000"
                  post_fill_locked: "1250.000000"
                  max_locked_balance: "1262.500000"
              request_deadline: 2026-05-01T15:40:06.000Z
              deployment:
                program_id: 4BecHK2cEh6DgfpCdxbQAm812audv4ZXxTQB12RkMWB8
                deployment_id: <64 hex characters>
                config_pda: ECJGDQXPnzjoaaPveifT4FSKmYgSbNf1GeiHbtdi626u
                epoch: 3
              nearest_leg_close: 2026-05-02T08:00:00.000Z
              max_quote_expires_at: 1777708798
    RfqMakerStatePush:
      name: RfqMakerStatePush
      title: state
      summary: "A transition on your own quote: `quoted` when it lands, then `accepted` or `lost` when the taker accepts a quote, or `expired`; `submitting` and `failed` follow for the accepted maker. `declined` with `quote_id` null to you alone when you decline, and to every quoter when the window closes with no quote."
      payload:
        $ref: "#/components/schemas/RfqMakerStatePush"
      examples:
        - name: example
          payload:
            type: state
            rfq_id: 0192f1a3b4c5d6e7f8091a2b3c4d5e6f
            quote_id: <64 hex characters>
            state: accepted
            accepted_side: buy
    RfqMakerFillPush:
      name: RfqMakerFillPush
      title: fill
      summary: Your fill landed on chain at `confirmed`.
      payload:
        $ref: "#/components/schemas/RfqMakerFillPush"
      examples:
        - name: example
          payload:
            type: fill
            fill:
              rfq_id: 0192f1a3b4c5d6e7f8091a2b3c4d5e6f
              quote_id: <64 hex characters>
              legs:
                - instrument: BTC_USDC-2MAY26-77000-C
                  quantity: "1.000000"
                  asset: 0
                  strike: "77000.000000"
                  expiry: 1777708800
                  kind: call
                - instrument: BTC_USDC-2MAY26-80000-C
                  quantity: "-2.000000"
                  asset: 0
                  strike: "80000.000000"
                  expiry: 1777708800
                  kind: call
              maker_side: sell
              execution_price: "500.000000"
              price_basis: package_total
              premium: "500.000000"
              maker_fee: "0.000000"
              writers:
                maker: true
                taker: true
                maker_short_legs: 1
                taker_short_legs: 1
              tx_signature: <base58 transaction signature>
              finality: confirmed
              filled_at: 2026-05-01T15:40:12.000Z
    RfqMakerFinalityPush:
      name: RfqMakerFinalityPush
      title: finality
      summary: The same fill object with `finality` moved to `finalized`, or to `reverted` if the chain dropped the transaction; a reverted fill may re-land.
      payload:
        $ref: "#/components/schemas/RfqMakerFinalityPush"
      examples:
        - name: example
          payload:
            type: finality
            fill:
              rfq_id: 0192f1a3b4c5d6e7f8091a2b3c4d5e6f
              quote_id: <64 hex characters>
              legs:
                - instrument: BTC_USDC-2MAY26-77000-C
                  quantity: "1.000000"
                  asset: 0
                  strike: "77000.000000"
                  expiry: 1777708800
                  kind: call
                - instrument: BTC_USDC-2MAY26-80000-C
                  quantity: "-2.000000"
                  asset: 0
                  strike: "80000.000000"
                  expiry: 1777708800
                  kind: call
              maker_side: sell
              execution_price: "500.000000"
              price_basis: package_total
              premium: "500.000000"
              maker_fee: "0.000000"
              writers:
                maker: true
                taker: true
                maker_short_legs: 1
                taker_short_legs: 1
              tx_signature: <base58 transaction signature>
              finality: finalized
              filled_at: 2026-05-01T15:40:12.000Z
    RfqQuotePush:
      name: RfqQuotePush
      title: quote
      summary: A quote arrived on your request; `rfq.quote`, `rfq.best` and `rfq.funding` hold the best live price. Sent for every quote, so a later push may show a better price.
      payload:
        $ref: "#/components/schemas/RfqQuotePush"
    RfqStatePush:
      name: RfqStatePush
      title: state
      summary: Your request changed state.
      payload:
        $ref: "#/components/schemas/RfqStatePush"
    RfqFinalityPush:
      name: RfqFinalityPush
      title: finality
      summary: "`rfq.fill_finality` moved to `finalized` or `reverted`."
      payload:
        $ref: "#/components/schemas/RfqFinalityPush"
    PlatformOraclePrice:
      name: price
      title: Spot price
      summary: A Pyth spot price for one asset
      payload:
        $ref: "#/components/schemas/PlatformOraclePriceFrame"
      examples:
        - name: btcSpot
          payload:
            type: price
            timestamp: 2026-09-09T14:03:12.417Z
            symbol: BTC
            price: 113842.31
            confidence: 41.27
            publish_time: 1788012192
            feed_id: e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43
    PlatformOracleTwapPreview:
      name: twap_preview
      title: TWAP preview
      summary: The running time-weighted average for the current settlement window
      payload:
        $ref: "#/components/schemas/PlatformOracleTwapPreviewFrame"
      examples:
        - name: btcTwap
          payload:
            type: twap_preview
            timestamp: 2026-09-09T14:03:12.902Z
            symbol: BTC
            twap: 113795.6642
            sample_count: 188
            coverage: 0.5222
    PlatformOracleConnected:
      name: connected
      title: Upstream connected
      summary: The oracle's own Pyth connection is up
      payload:
        $ref: "#/components/schemas/PlatformOracleStatusFrame"
    PlatformOracleDisconnected:
      name: disconnected
      title: Upstream disconnected
      summary: The oracle lost its Pyth connection; prices pause until `connected` returns
      payload:
        $ref: "#/components/schemas/PlatformOracleStatusFrame"
    PlatformOracleError:
      name: error
      title: Upstream error
      summary: A non-fatal error on the oracle's upstream feed
      payload:
        $ref: "#/components/schemas/PlatformOracleErrorFrame"
    PlatformOracleHeartbeat:
      name: heartbeat
      title: Heartbeat
      summary: Keepalive on a fixed interval; carries no data and needs no reply
      payload:
        $ref: "#/components/schemas/PlatformOracleHeartbeatFrame"
    PlatformPricesSubscribe:
      name: subscribe
      title: Subscribe
      summary: Start receiving candle updates for one or more symbol/timeframe channels
      payload:
        $ref: "#/components/schemas/PlatformPricesSubscribeFrame"
      examples:
        - name: btcOneMinute
          payload:
            type: subscribe
            channels:
              - symbol: BTC
                timeframe: 1m
    PlatformPricesUnsubscribe:
      name: unsubscribe
      title: Unsubscribe
      summary: Stop receiving candle updates for the listed channels
      payload:
        $ref: "#/components/schemas/PlatformPricesUnsubscribeFrame"
    PlatformPricesClientPing:
      name: ping
      title: Client ping
      summary: Optional client-initiated keepalive; the server answers with `pong`
      payload:
        type: object
        properties:
          type:
            type: string
            const: ping
        required:
          - type
        additionalProperties: true
    PlatformPricesClientPong:
      name: pong
      title: Client pong
      summary: Mandatory reply to the server's `ping`; a socket that misses one is closed
      payload:
        type: object
        properties:
          type:
            type: string
            const: pong
          timestamp:
            type: string
            format: date-time
            description: Optional; the reference client sends the current time.
        required:
          - type
        additionalProperties: true
    PlatformPricesConnected:
      name: connected
      title: Connected
      summary: Sent once, immediately after the upgrade
      payload:
        type: object
        properties:
          type:
            type: string
            const: connected
          connectionId:
            type: string
            description: Server-assigned connection id, for support correlation only.
            examples:
              - conn_42
          message:
            type: string
        required:
          - type
          - connectionId
          - message
    PlatformPricesSubscriptionConfirmed:
      name: subscription_confirmed
      title: Subscription confirmed
      summary: Echoes the channels from the `subscribe` that was applied
      payload:
        $ref: "#/components/schemas/PlatformPricesConfirmationFrame"
    PlatformPricesUnsubscriptionConfirmed:
      name: unsubscription_confirmed
      title: Unsubscription confirmed
      summary: Echoes the channels from the `unsubscribe` that was applied
      payload:
        $ref: "#/components/schemas/PlatformPricesConfirmationFrame"
    PlatformPricesCandleUpdate:
      name: candle_update
      title: Candle update
      summary: The current state of one subscribed candle after a change
      payload:
        $ref: "#/components/schemas/PlatformPricesCandleUpdateFrame"
      examples:
        - name: btcOneMinute
          payload:
            type: candle_update
            symbol: BTC
            timeframe: 1m
            candle:
              symbol: BTC
              timeframe: 1m
              openTime: 2026-09-09T14:03:00.000Z
              closeTime: 2026-09-09T14:04:00.000Z
              open: "113801.12"
              high: "113877.50"
              low: "113790.00"
              close: "113842.31"
    PlatformPricesServerPing:
      name: ping
      title: Server ping
      summary: Keepalive every 30 s; answer with `pong` before the next one
      payload:
        $ref: "#/components/schemas/PlatformPricesTimestampedFrame"
    PlatformPricesServerPong:
      name: pong
      title: Server pong
      summary: Reply to a client `ping`
      payload:
        $ref: "#/components/schemas/PlatformPricesTimestampedFrame"
    PlatformPricesError:
      name: error
      title: Error
      summary: A rejected client frame; the socket stays open
      payload:
        $ref: "#/components/schemas/PlatformPricesErrorFrame"
      examples:
        - name: badTimeframe
          payload:
            type: error
            error: VALIDATION_ERROR
            message: Invalid message format
  schemas:
    JsonRpcError:
      type: object
      description: The `error` object of a `public/*` failure.
      required:
        - code
        - message
      properties:
        code:
          type: integer
          description: "`1001` not authenticated (bad signature, expired or reused nonce, expired or invalid token, unknown or reassigned account, 60-second idle reap); `-32602` invalid params; `-32700`, `-32600`, `-32601`, `-32603` per JSON-RPC 2.0."
        message:
          type: string
    GetNonceResult:
      type: object
      required:
        - nonce
      properties:
        nonce:
          type: string
          description: Base58 nonce
          single use: null
          valid for 300 seconds. Not bound to a wallet.: null
    AuthParams:
      type: object
      additionalProperties: false
      required:
        - wallet
        - signature
        - message
      properties:
        wallet:
          type: string
          description: Base58 Ed25519 public key.
        signature:
          type: string
          description: Ed25519 signature over the UTF-8 bytes of `message`; base58
          hex or base64 of 64 bytes.: null
        message:
          type: string
          description: "Exactly `Sign in to Joyride\\n\\nWallet: {wallet}\\nNonce: {nonce}`; any byte difference is `1001`."
    AuthResult:
      type: object
      required:
        - wallet
        - account_id
        - roles
        - session_token
      properties:
        wallet:
          type: string
        account_id:
          type: integer
          minimum: 1
        roles:
          type: array
          items:
            type: string
            enum:
              - rfq_quoter
              - admin
          description: "`rfq_quoter` for an account the exchange has designated as a market maker; empty for every other account."
        session_token:
          type: string
          description: JWT (HS256); valid until its `exp`
          24 hours by default; no revocation. Also the Bearer token for the query API.: null
    SessionResumeParams:
      type: object
      additionalProperties: false
      required:
        - session_token
      properties:
        session_token:
          type: string
    SessionResumeResult:
      type: object
      required:
        - wallet
        - account_id
        - roles
      properties:
        wallet:
          type: string
        account_id:
          type: integer
          minimum: 1
        roles:
          type: array
          items:
            type: string
            enum:
              - rfq_quoter
              - admin
    MarketConfig:
      type: object
      required:
        - version
        - price_decimals
        - quantity_decimals
      properties:
        version:
          type: string
        price_decimals:
          type: integer
          description: Order-book price precision (8). RFQ money uses 6dp strings.
        quantity_decimals:
          type: integer
          description: Order-book quantity precision (8). RFQ quantities use 6dp strings.
    InstrumentsResult:
      type: object
      required:
        - instruments
      properties:
        instruments:
          type: array
          items:
            $ref: "#/components/schemas/PublicInstrument"
    PublicInstrument:
      type: object
      required:
        - symbol
        - underlying
        - strike
        - option_type
        - expiry
        - state
        - tick_size
        - min_size
        - contract_size
      properties:
        symbol:
          type: string
          description: "`{TICKER}_USDC-{DMMMYY}-{STRIKE}-{C|P}`; the only instrument identifier the API uses."
        underlying:
          type: string
          enum:
            - BTC
            - ETH
            - SOL
        strike:
          $ref: "#/components/schemas/WireDecimal"
        option_type:
          type: string
          enum:
            - call
            - put
        expiry:
          type: integer
          description: Unix seconds.
        state:
          type: string
          description: Lifecycle state; only tradable instruments are listed.
        tick_size:
          $ref: "#/components/schemas/WireDecimal"
        min_size:
          $ref: "#/components/schemas/WireDecimal"
        contract_size:
          $ref: "#/components/schemas/WireDecimal"
    SubscribeResult:
      type: object
      required:
        - channel
        - subscribed
      properties:
        channel:
          type: string
          enum:
            - rfq
            - rfq_maker
        subscribed:
          const: true
    RfqId:
      type: string
      pattern: ^[0-9a-f]{32}$
      description: 16-byte RFQ id encoded as lowercase hexadecimal.
    QuoteId:
      type: string
      pattern: ^[0-9a-f]{64}$
      description: SHA-256 of the canonical 1,066-byte StrategyQuotePayload.
    RfqDecimal:
      type: string
      pattern: ^(0|[1-9][0-9]*)(\.[0-9]{1,6})?$
      description: Canonical unsigned decimal with at most six fractional digits; JSON numbers and exponents are rejected.
    RfqSignedDecimal:
      type: string
      pattern: ^-?(0|[1-9][0-9]*)(\.[0-9]{1,6})?$
      description: |
        Canonical signed decimal with at most six fractional digits. Negative
        zero, JSON numbers, exponents, and values outside the signed 64-bit
        6dp range (`i64::MIN` excluded) are rejected.
    RfqRequestSide:
      type: string
      enum:
        - buy
        - sell
        - two_way
      description: The taker's direction over the package (spec 16 §2.1); `two_way` is the direction-blind request.
    RfqState:
      type: string
      enum:
        - pending
        - quoted
        - accepted
        - submitting
        - filled
        - declined
        - expired
        - failed
    RfqMakerQuoteState:
      type: string
      enum:
        - pending
        - quoted
        - accepted
        - lost
        - submitting
        - filled
        - declined
        - expired
        - failed
      description: "`RfqState` plus `lost`: the taker accepted another maker's quote. A lost quote stays firm until its `expires_at` but will never fill; reserved risk can be released on the push."
    RfqDeclineReason:
      type: string
      enum:
        - unlisted_instrument
        - near_expiry
        - size_limit
        - stale_data
        - low_confidence
        - queue_overflow
        - timeout
        - leg_not_tradable
      description: The six maker reasons, plus `timeout` (the window closed with no quote) and `leg_not_tradable` (a leg stopped trading while the request was open), which only the venue sets.
    RfqLeg:
      type: object
      additionalProperties: false
      description: |
        One strategy leg. `quantity` is signed and taker-buy oriented: on a
        `buy` accept the taker receives `quantity` and the maker its negation;
        on a `sell` accept the taker receives `-quantity` and the maker
        `quantity`. A 1x2 spread is `+1.000000` and `-2.000000`. Legs keep the
        taker's submitted order everywhere; nothing sorts them.
      properties:
        instrument:
          type: string
          description: Listed instrument symbol.
          example: SOL_USDC-6MAR26-150-C
        quantity:
          allOf:
            - $ref: "#/components/schemas/RfqSignedDecimal"
          description: Non-zero signed 6dp contracts in taker-buy orientation.
      required:
        - instrument
        - quantity
    RfqLegs:
      type: array
      minItems: 1
      maxItems: 13
      items:
        $ref: "#/components/schemas/RfqLeg"
      description: |
        Ordered strategy legs. Every instrument must be listed and open, share
        one underlying and expiry, and be unique both by symbol and by resolved
        terms `(asset, strike, expiry, kind)`.
    RfqResolvedLeg:
      type: object
      additionalProperties: false
      description: |
        One strategy leg as the venue returns it: the submitted `instrument`
        and `quantity` plus the instrument's resolved terms, which are the
        values the signed quote encodes for the leg.
      properties:
        instrument:
          type: string
          description: Listed instrument symbol.
          example: SOL_USDC-6MAR26-150-C
        quantity:
          allOf:
            - $ref: "#/components/schemas/RfqSignedDecimal"
          description: Non-zero signed 6dp contracts in taker-buy orientation.
        asset:
          type: integer
          enum:
            - 0
            - 1
            - 2
          description: Underlying id; 0 is BTC, 1 is ETH, 2 is SOL.
        strike:
          allOf:
            - $ref: "#/components/schemas/RfqDecimal"
          description: Strike in USDC, 6dp.
        expiry:
          type: integer
          description: Instrument expiry as Unix seconds.
        kind:
          type: string
          enum:
            - call
            - put
      required:
        - instrument
        - quantity
        - asset
        - strike
        - expiry
        - kind
    RfqResolvedLegs:
      type: array
      minItems: 1
      maxItems: 13
      items:
        $ref: "#/components/schemas/RfqResolvedLeg"
      description: Ordered strategy legs, in the taker's submitted order, with resolved terms.
    RfqPageParams:
      type: object
      additionalProperties: false
      properties:
        cursor:
          $ref: "#/components/schemas/RfqId"
        limit:
          type: integer
          minimum: 1
          maximum: 100
          default: 100
    RfqCreateParams:
      type: object
      additionalProperties: false
      required:
        - client_ref
        - legs
        - side
      properties:
        client_ref:
          type: string
          pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$
          description: Idempotency key. The same `(account, client_ref)` with different legs or a different `side` is `RFQ_STATE_CONFLICT`.
        legs:
          $ref: "#/components/schemas/RfqLegs"
        side:
          allOf:
            - $ref: "#/components/schemas/RfqRequestSide"
          description: "`buy` or `sell` shows makers the direction and asks for that one price; `two_way` hides it and admits a one- or two-sided quote. Only the named side (either, on `two_way`) can be accepted."
    RfqCreateResult:
      type: object
      required:
        - rfq_id
        - state
        - request_deadline
      properties:
        rfq_id:
          $ref: "#/components/schemas/RfqId"
        state:
          $ref: "#/components/schemas/RfqState"
        request_deadline:
          type: string
          format: date-time
          description: RFC3339 UTC; the request drains to `declined` if no quote arrives by then.
    RfqAcceptParams:
      type: object
      additionalProperties: false
      required:
        - quote_id
        - side
        - expires_at
        - max_taker_fee
        - max_taker_locked_balance
        - taker_signature
      properties:
        quote_id:
          allOf:
            - $ref: "#/components/schemas/QuoteId"
          description: "The quote to accept: `best.ask.quote_id` to buy, `best.bid.quote_id` to sell."
        side:
          type: string
          enum:
            - buy
            - sell
          description: Must be a side the request named and the quote priced.
        expires_at:
          type: integer
          format: int64
          description: Unix seconds; must equal the quote's `expires_at`.
        max_taker_fee:
          allOf:
            - $ref: "#/components/schemas/RfqDecimal"
          description: Signed fee maximum. Sign the funding view's `max_fee` for the chosen side; must be at least the fresh `fee`.
        max_taker_locked_balance:
          allOf:
            - $ref: "#/components/schemas/RfqDecimal"
          description: Signed absolute post-fill lock maximum. Sign the funding view's `max_locked_balance`; must be at least the fresh `post_fill_locked`.
        taker_signature:
          type: string
          description: Base64 of the 64-byte Ed25519 signature over the canonical acceptance
          which covers both maxima.: null
    RfqAcceptResult:
      type: object
      required:
        - rfq_id
        - quote_id
        - state
      properties:
        rfq_id:
          $ref: "#/components/schemas/RfqId"
        quote_id:
          $ref: "#/components/schemas/QuoteId"
        state:
          $ref: "#/components/schemas/RfqState"
    RfqGetParams:
      type: object
      additionalProperties: false
      required:
        - rfq_id
      properties:
        rfq_id:
          $ref: "#/components/schemas/RfqId"
    RfqListParams:
      type: object
      additionalProperties: false
      properties:
        states:
          type: array
          uniqueItems: true
          items:
            $ref: "#/components/schemas/RfqState"
        cursor:
          $ref: "#/components/schemas/RfqId"
        limit:
          type: integer
          minimum: 1
          maximum: 100
          default: 100
    RfqListResult:
      type: object
      required:
        - items
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/RfqObject"
        next_cursor:
          $ref: "#/components/schemas/RfqId"
    RfqMakerPollResult:
      type: object
      required:
        - items
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/OpenRfqObject"
        next_cursor:
          allOf:
            - $ref: "#/components/schemas/RfqId"
          description: Present when another page follows; pass it back as `cursor`.
    RfqMakerRespondParams:
      type: object
      additionalProperties: false
      description: One signed quote for the whole package. `bid` and `ask` are whole-package totals, individually optional, at least one required, and only the side(s) the request allows; a side the request did not ask for is `RFQ_SIDE_NOT_REQUESTED`. `bid <= ask`; either may be negative. The quote carries no quantity; the taker fixed the legs.
      required:
        - rfq_id
        - quote_nonce
        - issued_at
        - expires_at
        - max_maker_fee
        - max_maker_locked_balance
        - maker_signature
      properties:
        rfq_id:
          $ref: "#/components/schemas/RfqId"
        quote_nonce:
          type: string
          pattern: ^[1-9][0-9]*$
          description: Unsigned 64-bit integer as a decimal string, higher than every nonce this account has sent, across restarts; a nonce at or below the high-water mark is `RFQ_STATE_CONFLICT`.
        bid:
          allOf:
            - $ref: "#/components/schemas/RfqSignedDecimal"
          description: What the taker receives selling the package; the maker pays it.
        ask:
          allOf:
            - $ref: "#/components/schemas/RfqSignedDecimal"
          description: What the taker pays buying the package; the maker receives it.
        issued_at:
          type: integer
          format: int64
          description: Unix seconds.
        expires_at:
          type: integer
          format: int64
          description: Unix seconds. `issued_at < expires_at`, `expires_at - issued_at <= 3600`, and `expires_at <= max_quote_expires_at` from the open request; a later value is `RFQ_VALUE_OUT_OF_RANGE`, never clamped. The quote must reach the venue with more than 2 seconds of life left.
        max_maker_fee:
          allOf:
            - $ref: "#/components/schemas/RfqDecimal"
          description: Largest maker fee this quote authorizes, covering every side it prices; at least the larger of the quoted sides' `fee`.
        max_maker_locked_balance:
          allOf:
            - $ref: "#/components/schemas/RfqDecimal"
          description: Largest total locked balance the maker subaccount may hold after the fill, covering every side it prices; at least the larger of the quoted sides' `post_fill_locked`.
        maker_signature:
          type: string
          description: Base64 of the 64-byte Ed25519 signature by the maker's wallet over the canonical quote.
    RfqMakerRespondResult:
      type: object
      required:
        - rfq_id
        - quote_id
        - state
      properties:
        rfq_id:
          $ref: "#/components/schemas/RfqId"
        quote_id:
          $ref: "#/components/schemas/QuoteId"
        state:
          const: quoted
    RfqMakerDeclineParams:
      type: object
      additionalProperties: false
      required:
        - rfq_id
        - reason
      properties:
        rfq_id:
          $ref: "#/components/schemas/RfqId"
        reason:
          type: string
          enum:
            - unlisted_instrument
            - near_expiry
            - size_limit
            - stale_data
            - low_confidence
            - queue_overflow
    RfqMakerDeclineResult:
      type: object
      required:
        - rfq_id
        - state
        - decline_reason
      properties:
        rfq_id:
          $ref: "#/components/schemas/RfqId"
        state:
          const: declined
          description: This maker's state on the request; the request itself stays `pending` for the other quoters.
        decline_reason:
          $ref: "#/components/schemas/RfqDeclineReason"
    RfqDeployment:
      type: object
      description: The margin-vault deployment the request is bound to. `program_id` and `deployment_id` are copied into every signed quote and acceptance.
      properties:
        program_id:
          type: string
          description: Solana base58 program id.
        deployment_id:
          type: string
          pattern: ^[0-9a-f]{64}$
        config_pda:
          type: string
          description: Solana base58 config PDA.
      required:
        - program_id
        - deployment_id
        - config_pda
    RfqOpenDeployment:
      type: object
      description: "The deployment identity as the open request carries it: `RfqDeployment` plus the `epoch` the request was created under. Only `program_id` and `deployment_id` enter the signed quote."
      allOf:
        - $ref: "#/components/schemas/RfqDeployment"
        - type: object
          properties:
            epoch:
              type: integer
              minimum: 0
              description: Deployment epoch the RFQ was created under.
          required:
            - epoch
    RfqFundingRequirement:
      type: object
      additionalProperties: false
      description: |
        Taker-perspective requirement for one accept side, computed from one
        frozen strategy risk view:
        `required = cash_debit + max_fee + lock_increase`.
      properties:
        required:
          allOf:
            - $ref: "#/components/schemas/RfqDecimal"
          description: Free balance required to accept this side of the quote.
        estimate:
          type: boolean
          description: True only for a writer-side requirement computed over a never-anchored empty portfolio.
        funded:
          type: boolean
          description: True only for a non-estimated requirement with a covered anchor and `free_balance >= required`.
        package_cash_delta:
          allOf:
            - $ref: "#/components/schemas/RfqSignedDecimal"
          description: Signed cash movement before fees from the viewed party's seat; positive means that party receives premium.
        cash_debit:
          allOf:
            - $ref: "#/components/schemas/RfqDecimal"
          description: "`max(-package_cash_delta, 0)`."
        fee:
          allOf:
            - $ref: "#/components/schemas/RfqDecimal"
          description: "The viewed party's trading fee over the current prices, using its taker or maker tier as appropriate: per leg `min(index_notional × fee_bps / 10000, leg_value × trade_fee_cap_bps / 10000)`, floored and summed."
        max_fee:
          allOf:
            - $ref: "#/components/schemas/RfqDecimal"
          description: "`fee` with the venue's signed-fee tolerance applied and rounded up once; the figure to sign as the fee maximum. `required` funds it, and the signer refuses a fill whose exact fee exceeds the signed maximum."
        writer:
          type: boolean
          description: True when this side leaves the viewed party short at least one leg.
        lock_increase:
          allOf:
            - $ref: "#/components/schemas/RfqDecimal"
          description: "`max(post_fill_locked - current_locked, 0)`; zero when the viewed party is not a writer."
        post_fill_locked:
          allOf:
            - $ref: "#/components/schemas/RfqDecimal"
          description: Unbuffered absolute lock for the viewed party after the fill.
        max_locked_balance:
          allOf:
            - $ref: "#/components/schemas/RfqDecimal"
          description: Current lock plus `lock_increase` with the venue's signed-lock tolerance applied and rounded up once; the figure to sign as the lock maximum. Equals `post_fill_locked` when the party is not a writer.
      required:
        - required
        - estimate
        - funded
    RfqTakerFunding:
      type: object
      additionalProperties: false
      description: Live advisory funding view; the attestation signer remains authoritative.
      properties:
        anchored:
          type: boolean
          description: Whether a durable subaccount anchor is covered by the live watcher heartbeat.
        free_balance:
          oneOf:
            - $ref: "#/components/schemas/RfqDecimal"
            - type: "null"
        buy:
          oneOf:
            - $ref: "#/components/schemas/RfqFundingRequirement"
            - type: "null"
        sell:
          oneOf:
            - $ref: "#/components/schemas/RfqFundingRequirement"
            - type: "null"
      required:
        - anchored
        - free_balance
        - buy
        - sell
    OpenRfqObject:
      type: object
      description: |
        A pending request as seen by every quoter, from `rfq.maker.poll`
        items and the `open` push. Everything the maker signs is here:
        `rfq_id`, the ordered `legs`, `taker_subaccount`, and `deployment`.
      properties:
        rfq_id:
          $ref: "#/components/schemas/RfqId"
        legs:
          $ref: "#/components/schemas/RfqResolvedLegs"
        side:
          allOf:
            - $ref: "#/components/schemas/RfqRequestSide"
          description: Which side(s) the maker may quote. On `buy` send only `ask`; on `sell` only `bid`; on `two_way` either or both.
        taker_account:
          type: integer
          minimum: 0
          description: Taker account id.
        taker_subaccount:
          type: string
          description: Solana base58 subaccount PDA (`["subaccount", owner]` under `deployment.program_id`).
        taker_funded:
          type: boolean
          description: True when the taker has a covered anchor and positive free balance; balances, side requirements, and writer status are not disclosed.
        maker_funding:
          allOf:
            - $ref: "#/components/schemas/RfqTakerFunding"
          description: Your own economics for each taker side you may quote, priced at mark through your portfolio and fee tier; a side the request does not allow is `null`. Delivered only to you. `rfq.maker.respond` recomputes the same figures at your signed `bid` and `ask`.
        request_deadline:
          type: string
          format: date-time
          description: RFC3339 UTC; the RFQ drains to `declined/timeout` if no quote arrives by then.
        deployment:
          $ref: "#/components/schemas/RfqOpenDeployment"
        nearest_leg_close:
          type: string
          format: date-time
          description: RFC3339 UTC with milliseconds, the same format as `request_deadline`. The earliest expiry among the legs; no quote may expire after it.
        max_quote_expires_at:
          type: integer
          format: int64
          description: Unix seconds; `nearest_leg_close` less the landing haircut. Sign an `expires_at` at or before it; a later value is rejected, never clamped.
      required:
        - rfq_id
        - legs
        - side
        - taker_account
        - taker_subaccount
        - taker_funded
        - maker_funding
        - request_deadline
        - deployment
        - nearest_leg_close
        - max_quote_expires_at
    QuoteObject:
      type: object
      description: One maker's immutable signed quote as shown to the taker. The taker is never told which maker, or how many quoted.
      properties:
        quote_id:
          $ref: "#/components/schemas/QuoteId"
        rfq_id:
          $ref: "#/components/schemas/RfqId"
        legs:
          $ref: "#/components/schemas/RfqResolvedLegs"
        bid:
          oneOf:
            - $ref: "#/components/schemas/RfqSignedDecimal"
            - type: "null"
          description: What the taker receives selling the package; `null` when the maker did not quote that side. At least one of `bid` / `ask` is a price.
        ask:
          oneOf:
            - $ref: "#/components/schemas/RfqSignedDecimal"
            - type: "null"
          description: What the taker pays buying the package; `null` when the maker did not quote that side.
        maker_subaccount:
          type: string
          description: Solana base58 PDA.
        taker_subaccount:
          type: string
          description: Solana base58 PDA.
        issued_at:
          type: integer
          format: int64
        expires_at:
          type: integer
          format: int64
        max_maker_fee:
          $ref: "#/components/schemas/RfqDecimal"
        max_maker_locked_balance:
          $ref: "#/components/schemas/RfqDecimal"
        quote_payload:
          type: string
          description: Base64 of the canonical 1,066-byte v3 StrategyQuotePayload.
        maker_signature:
          type: string
          description: Base64 of the 64-byte Ed25519 signature.
      required:
        - quote_id
        - rfq_id
        - legs
        - bid
        - ask
        - maker_subaccount
        - taker_subaccount
        - issued_at
        - expires_at
        - max_maker_fee
        - max_maker_locked_balance
        - quote_payload
        - maker_signature
    RfqObject:
      type: object
      description: The caller's view of its own request, from `rfq.get`, `rfq.list`, and every `rfq` push.
      properties:
        rfq_id:
          $ref: "#/components/schemas/RfqId"
        client_ref:
          type: string
          format: uuid
        legs:
          $ref: "#/components/schemas/RfqResolvedLegs"
        side:
          allOf:
            - $ref: "#/components/schemas/RfqRequestSide"
          description: The side named at creation; `two_way` when none was. Only this side (either on `two_way`) may be accepted.
        state:
          $ref: "#/components/schemas/RfqState"
        request_deadline:
          type: string
          format: date-time
        nearest_leg_close:
          type: string
          format: date-time
          description: RFC3339 UTC with milliseconds; the earliest expiry among the legs.
        deployment:
          allOf:
            - $ref: "#/components/schemas/RfqDeployment"
          description: The deployment the request is bound to. `quote.taker_subaccount` is the PDA for seeds `["subaccount", owner]` under `program_id`; a client can re-derive it, and it also appears in `quote.quote_payload`, which hashes to `quote_id`.
        quote:
          allOf:
            - $ref: "#/components/schemas/QuoteObject"
          description: "The one quote the taker is shown: the accepted quote once accepted; before that, on a `buy` request the best live ask and on a `sell` request the best live bid, which can change between pushes as better quotes land. Absent on a `two_way` request while it is quoted, because its best bid and best ask may be different makers' quotes: read `best`."
        best:
          type: object
          description: "Present while `state` is `quoted`: the best live quote per side, possibly from different makers, and the only quotes a `two_way` request shows. Accept `ask` to buy and `bid` to sell."
          properties:
            bid:
              oneOf:
                - $ref: "#/components/schemas/QuoteObject"
                - type: "null"
            ask:
              oneOf:
                - $ref: "#/components/schemas/QuoteObject"
                - type: "null"
        funding:
          allOf:
            - $ref: "#/components/schemas/RfqTakerFunding"
          description: Present while the request is quoted and not yet accepted; each side computed from that side's best quote, recomputed from the live risk view.
        accepted_side:
          type: string
          enum:
            - buy
            - sell
        tx_signature:
          type: string
          description: Solana base58 transaction signature.
        fill_finality:
          type: string
          enum:
            - confirmed
            - finalized
            - reverted
        decline_reason:
          $ref: "#/components/schemas/RfqDeclineReason"
        decline_reasons:
          type: array
          items:
            $ref: "#/components/schemas/RfqDeclineReason"
          description: Each maker that declined this request, by reason, in arrival order; makers are not named. Present while the request is still `pending` and after it drains.
        failure:
          type: object
          properties:
            code:
              type: string
              enum:
                - RFQ_ATTESTATION_FAILED
                - RFQ_TX_FAILED
                - RFQ_PRICE_TOLERANCE
                - RFQ_BALANCE_INSUFFICIENT
                - RFQ_FEE_LIMIT_EXCEEDED
                - RFQ_LOCK_LIMIT_EXCEEDED
            detail:
              type: string
            tx_signature:
              type: string
            onchain_code:
              type: integer
          required:
            - code
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - rfq_id
        - client_ref
        - legs
        - side
        - state
        - request_deadline
        - created_at
        - updated_at
    MakerFillObject:
      type: object
      description: |
        The quoting maker's fill, carried by the `fill` push and re-emitted
        unchanged except for `finality` by the `finality` push. Durable
        history is `GET /api/query/rfq-fills?role=maker`.
      properties:
        rfq_id:
          $ref: "#/components/schemas/RfqId"
        quote_id:
          $ref: "#/components/schemas/QuoteId"
        legs:
          $ref: "#/components/schemas/RfqResolvedLegs"
        maker_side:
          type: string
          enum:
            - buy
            - sell
          description: The maker's side of the package; the opposite of the taker's accepted side.
        execution_price:
          allOf:
            - $ref: "#/components/schemas/RfqSignedDecimal"
          description: The signed package total the fill executed at — the quote's `ask` when the taker bought, its `bid` when the taker sold. Never a per-leg or per-contract price.
        price_basis:
          type: string
          enum:
            - package_total
          description: Always `package_total`; labels `execution_price` for the whole submitted leg vector.
        premium:
          allOf:
            - $ref: "#/components/schemas/RfqSignedDecimal"
          description: The accepted package total, identical to `execution_price`; the quote's `ask` when the taker bought, its `bid` when the taker sold, never negated. The maker receives it when `maker_side` is `sell` and pays it when `maker_side` is `buy`; a negative value reverses the direction.
        maker_fee:
          allOf:
            - $ref: "#/components/schemas/RfqDecimal"
          description: The maker's per-leg package trading fee settled on chain with the fill.
        writers:
          type: object
          additionalProperties: false
          properties:
            maker:
              type: boolean
            taker:
              type: boolean
            maker_short_legs:
              type: integer
              minimum: 0
            taker_short_legs:
              type: integer
              minimum: 0
          required:
            - maker
            - taker
            - maker_short_legs
            - taker_short_legs
        tx_signature:
          type: string
          description: Solana base58 transaction signature.
        finality:
          type: string
          enum:
            - confirmed
            - finalized
            - reverted
        filled_at:
          type: string
          format: date-time
          description: RFC3339 UTC chain block time of the fill; stable across finality re-emissions.
      required:
        - rfq_id
        - quote_id
        - legs
        - maker_side
        - execution_price
        - price_basis
        - premium
        - maker_fee
        - writers
        - tx_signature
        - finality
        - filled_at
    RfqOpenPush:
      type: object
      additionalProperties: false
      required:
        - type
        - rfq
      properties:
        type:
          const: open
        rfq:
          $ref: "#/components/schemas/OpenRfqObject"
    RfqMakerStatePush:
      type: object
      additionalProperties: false
      required:
        - type
        - rfq_id
        - quote_id
        - state
        - accepted_side
      properties:
        type:
          const: state
        rfq_id:
          $ref: "#/components/schemas/RfqId"
        quote_id:
          oneOf:
            - $ref: "#/components/schemas/QuoteId"
            - type: "null"
          description: Null on the broadcast `declined` push when nobody quoted, and on the `declined` push to a maker that declined.
        state:
          $ref: "#/components/schemas/RfqMakerQuoteState"
        accepted_side:
          oneOf:
            - type: string
              enum:
                - buy
                - sell
            - type: "null"
          description: The taker's accepted side from `accepted` onward; null before. On a taker buy the maker writes every positive leg; on a taker sell, every negative one.
    RfqMakerFillPush:
      type: object
      additionalProperties: false
      required:
        - type
        - fill
      properties:
        type:
          const: fill
        fill:
          $ref: "#/components/schemas/MakerFillObject"
    RfqMakerFinalityPush:
      type: object
      additionalProperties: false
      required:
        - type
        - fill
      properties:
        type:
          const: finality
        fill:
          $ref: "#/components/schemas/MakerFillObject"
    RfqQuotePush:
      type: object
      additionalProperties: false
      required:
        - type
        - rfq
      properties:
        type:
          const: quote
        rfq:
          $ref: "#/components/schemas/RfqObject"
    RfqStatePush:
      type: object
      additionalProperties: false
      required:
        - type
        - rfq
      properties:
        type:
          const: state
        rfq:
          $ref: "#/components/schemas/RfqObject"
    RfqFinalityPush:
      type: object
      additionalProperties: false
      required:
        - type
        - rfq
      properties:
        type:
          const: finality
        rfq:
          $ref: "#/components/schemas/RfqObject"
    RfqTypedError:
      type: object
      description: The `error.data` object of every `rfq.*` failure (JSON-RPC code `1200`).
      properties:
        error:
          type: string
          description: The RFQ error code. `retryable` says whether the same call can succeed later; for `RFQ_NOT_READY` the `message` names the cause. `RFQ_TAKER_UNFUNDED`, `RFQ_MAKER_UNFUNDED` and `RFQ_SIGNED_LIMIT_BELOW_REQUIREMENT` carry the funding view in `details`; `RFQ_INVALID_INSTRUMENT` and `RFQ_QUOTE_EXPIRED` from a leg check carry `{leg_index}`. `RFQ_SIDE_NOT_REQUESTED` is a side the request did not ask for, on respond or accept; `RFQ_SIDE_NOT_QUOTED` is an accept of a side the quote left absent.
          enum:
            - RFQ_NOT_ENABLED
            - RFQ_WRONG_ROLE
            - RFQ_NOT_READY
            - RFQ_INVALID_INSTRUMENT
            - RFQ_SIZE_INVALID
            - RFQ_INVALID_PARAMS
            - RFQ_VALUE_OUT_OF_RANGE
            - RFQ_PRECISION_UNSUPPORTED
            - RFQ_INVALID_SIGNATURE
            - RFQ_CANONICAL_MISMATCH
            - RFQ_NOT_FOUND
            - RFQ_STATE_CONFLICT
            - RFQ_OPEN_LIMIT
            - RFQ_QUOTE_EXPIRED
            - RFQ_SIGNED_LIMIT_BELOW_REQUIREMENT
            - RFQ_TAKER_UNFUNDED
            - RFQ_MAKER_UNFUNDED
            - RFQ_RATE_LIMITED
            - RFQ_SIDE_NOT_QUOTED
            - RFQ_SIDE_NOT_REQUESTED
        message:
          type: string
        operation_id:
          type: string
          description: Server-side correlation id; quote it when asking for support.
        retryable:
          type: boolean
        rfq_id:
          $ref: "#/components/schemas/RfqId"
        quote_id:
          $ref: "#/components/schemas/QuoteId"
        details:
          description: Code-specific context; the funding view for the three funding codes, `{leg_index}` for a leg that stopped trading. Absent otherwise.
          oneOf:
            - $ref: "#/components/schemas/RfqTakerFunding"
            - type: object
              properties:
                leg_index:
                  type: integer
                  minimum: 0
              required:
                - leg_index
      required:
        - error
        - message
        - operation_id
        - retryable
    WireDecimal:
      type: string
      description: |
        Signed fixed-point decimal with eight fractional digits, as a string.
        Strings rather than JSON numbers because values can exceed the exact
        integer range of an IEEE-754 double.
      pattern: ^-?[0-9]+\.[0-9]{8}$
    PlatformAssetSymbol:
      type: string
      description: Bare asset ticker.
      enum:
        - BTC
        - ETH
        - SOL
    PlatformOraclePriceFrame:
      type: object
      properties:
        type:
          type: string
          const: price
        timestamp:
          type: string
          format: date-time
          description: Envelope serialization time (RFC 3339).
        symbol:
          $ref: "#/components/schemas/PlatformAssetSymbol"
        price:
          type: number
          description: Spot price in USD.
        confidence:
          type: number
          description: Pyth confidence interval, plus or minus this amount in USD.
        publish_time:
          type: integer
          description: Pyth publish time, Unix seconds. Compare with `timestamp` to measure feed freshness.
        feed_id:
          type: string
          description: Pyth price-feed id, hex.
      required:
        - type
        - timestamp
        - symbol
        - price
        - confidence
        - publish_time
    PlatformOracleTwapPreviewFrame:
      type: object
      properties:
        type:
          type: string
          const: twap_preview
        timestamp:
          type: string
          format: date-time
          description: Envelope serialization time (RFC 3339).
        symbol:
          $ref: "#/components/schemas/PlatformAssetSymbol"
        twap:
          type: number
          description: Time-weighted average price over the samples collected so far in the current settlement window, USD.
        sample_count:
          type: integer
          minimum: 0
          description: Samples collected so far in the window.
        coverage:
          type: number
          minimum: 0
          maximum: 1
          description: Fraction of the settlement window covered by samples so far.
      required:
        - type
        - timestamp
        - symbol
        - twap
        - sample_count
        - coverage
    PlatformOracleStatusFrame:
      type: object
      properties:
        type:
          type: string
          enum:
            - connected
            - disconnected
        timestamp:
          type: string
          format: date-time
      required:
        - type
        - timestamp
    PlatformOracleErrorFrame:
      type: object
      properties:
        type:
          type: string
          const: error
        timestamp:
          type: string
          format: date-time
        message:
          type: string
      required:
        - type
        - timestamp
        - message
    PlatformOracleHeartbeatFrame:
      type: object
      properties:
        type:
          type: string
          const: heartbeat
        timestamp:
          type: string
          format: date-time
      required:
        - type
        - timestamp
    PlatformCandleTimeframe:
      type: string
      description: Candle width.
      enum:
        - 1s
        - 5s
        - 15s
        - 30s
        - 1m
        - 5m
        - 10m
        - 15m
    PlatformPricesChannel:
      type: object
      properties:
        symbol:
          type: string
          description: Bare asset ticker. The server accepts `BTC`, `ETH`, `SOL`, and `XRP`; the Joyride app subscribes to `BTC`, `ETH`, and `SOL`.
          enum:
            - BTC
            - ETH
            - SOL
            - XRP
        timeframe:
          $ref: "#/components/schemas/PlatformCandleTimeframe"
      required:
        - symbol
        - timeframe
    PlatformPricesSubscribeFrame:
      type: object
      properties:
        type:
          type: string
          const: subscribe
        channels:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/PlatformPricesChannel"
      required:
        - type
        - channels
    PlatformPricesUnsubscribeFrame:
      type: object
      properties:
        type:
          type: string
          const: unsubscribe
        channels:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/PlatformPricesChannel"
      required:
        - type
        - channels
    PlatformPricesConfirmationFrame:
      type: object
      properties:
        type:
          type: string
          enum:
            - subscription_confirmed
            - unsubscription_confirmed
        channels:
          type: array
          items:
            $ref: "#/components/schemas/PlatformPricesChannel"
        timestamp:
          type: string
          format: date-time
      required:
        - type
        - channels
        - timestamp
    PlatformPricesCandle:
      type: object
      description: OHLC as decimal strings; ISO 8601 open and close times. The service stores no volume.
      properties:
        symbol:
          type: string
          enum:
            - BTC
            - ETH
            - SOL
            - XRP
        timeframe:
          $ref: "#/components/schemas/PlatformCandleTimeframe"
        openTime:
          type: string
          format: date-time
        closeTime:
          type: string
          format: date-time
        open:
          type: string
        high:
          type: string
        low:
          type: string
        close:
          type: string
      required:
        - symbol
        - timeframe
        - openTime
        - closeTime
        - open
        - high
        - low
        - close
    PlatformPricesCandleUpdateFrame:
      type: object
      properties:
        type:
          type: string
          const: candle_update
        symbol:
          type: string
          enum:
            - BTC
            - ETH
            - SOL
            - XRP
        timeframe:
          $ref: "#/components/schemas/PlatformCandleTimeframe"
        candle:
          $ref: "#/components/schemas/PlatformPricesCandle"
      required:
        - type
        - symbol
        - timeframe
        - candle
    PlatformPricesTimestampedFrame:
      type: object
      properties:
        type:
          type: string
          enum:
            - ping
            - pong
        timestamp:
          type: string
          format: date-time
      required:
        - type
        - timestamp
    PlatformPricesErrorFrame:
      type: object
      properties:
        type:
          type: string
          const: error
        error:
          type: string
          description: Machine-readable code.
          enum:
            - INVALID_JSON
            - UNKNOWN_MESSAGE_TYPE
            - VALIDATION_ERROR
        message:
          type: string
      required:
        - type
        - error
        - message
