asyncapi: 3.0.0
info:
  title: True Markets CeFi WebSocket
  description: |
    ## Overview
    This API allows clients to interact with the **CeFi trading platform** for real time instrument, market data and exchange transactions.

    ## Authentication
    Authenticated requests carry their credentials **in the subscription message payload** (not HTTP headers). Two signing schemes are supported:
    - **ES256** (EC P-256) signature over the canonical payload, using an API key created at [app.truemarkets.co](https://app.truemarkets.co) (public key only held server-side). This is the recommended scheme.
    - **HMAC-SHA256** over the same canonical payload, using a legacy API key (`key` id + shared `secret`). HMAC keys keep working while they are phased out.

    Both sign `timestamp + TRUEXWS + key + path`, where `path` is the WebSocket endpoint path — use the canonical `/v1/cefi/market`; the legacy `/api/v1` is also accepted but **deprecated**. See the security schemes below for exact rules.

    ### Timestamps
    Every subscription request (authenticated or not) carries a `timestamp`: a UTC Unix epoch in **whole seconds**. The server accepts timestamps between **15 seconds in the past** and **1 second in the future** relative to server UTC time; anything outside that window is rejected with an `ERROR` confirmation.

    The 1-second future allowance exists because whole-second timestamps are coarse: a client clock that rounds up to the next second reads ahead of the server's truncated clock even when both are perfectly synchronized. To avoid timestamp rejects:
    - Keep your clock synchronized via NTP; a clock more than ~1 second fast (or ~15 seconds slow) will cause rejects.
    - Truncate (round down) the current time to whole seconds rather than rounding to nearest or up.
    - Generate the timestamp (and signature) immediately before sending — not when the request is queued or built for retry.
    - On a timestamp reject, retry with a freshly generated timestamp and signature.

    ## Unauthenticated Endpoints
    Many endpoints have an unauthenticated version which only requires the user to supply a valid timestamp (no older than 15 seconds and at most 1 second in the future, relative to server UTC time). These endpoints batch updates and publish on an interval set by the exchange, typically 1 second.

    ## Authenticated-Only Channels
    Some channels require authentication and cannot be accessed without valid credentials:
    - **DROPCOPY**: Order drop copy channel that provides real-time order state updates. 

    ## Heartbeats
    A quiet book (no order activity) sends nothing after its snapshot, which a client cannot tell apart from a dead connection unless it can see WebSocket protocol pings (browsers cannot). Heartbeats are **opt-in**: include `HEARTBEAT` in `channels` of a `SUBSCRIBE` or `SUBSCRIBE_NO_AUTH` request (`item_names` is ignored for it, and it can share a request with other channels). The confirmation then lists `HEARTBEAT` among the subscriptions.

    Once subscribed, the connection receives a `HEARTBEAT` message only after **10 seconds with nothing else sent to it**; any other message resets that clock, so an active feed never carries heartbeats. Treat ~30 seconds of total silence as a dead connection and reconnect. Unsubscribe with `UNSUBSCRIBE`/`UNSUBSCRIBE_NO_AUTH` on the `HEARTBEAT` channel.

    ## Links
    - 📖 **Full API Documentation**: [https://docs.truemarkets.co/](https://docs.truemarkets.co/)
    - 📧 **Support**: [support@truemarkets.co](mailto:support@truemarkets.co)

    ## Change Log
    | Version   | Date       | Notes                                       |
    |-----------|------------|---------------------------------------------|
    | v1.0.12   | 2026-10-15 | Documented ES256 API keys as the recommended subscription auth and HMAC as legacy. |
    | v1.0.11   | 2026-10-14 | Added `INDEX` channel streaming the exchange's own view of a price index (`SNAPSHOT`/`UPDATE` + `SNAPSHOT_COMPLETE`), the level an up/down outcome market opens and resolves against. Items are observable ids as strings. |
    | v1.0.10   | 2026-10-12 | Added the opt-in `HEARTBEAT` channel: a `HEARTBEAT` message after 10 seconds with no other traffic on the connection. |
    | v1.0.9    | 2026-07-30 | Timestamp validation now allows a 1-second future grace window (accepted window: 15s past to 1s future of server time). Documented how to avoid timestamp-based subscription rejects. |
    | v1.0.8    | 2026-07-28 | ERROR confirmations may now carry a `message` explaining the failure. DROPCOPY subscriptions made while the exchange catalog is still syncing receive a retry message instead of being silently dropped. |
    | v1.0.7    | 2026-07-14 | Added `AUCTION` channel streaming the opening-auction indicative (`SNAPSHOT`/`UPDATE` + `SNAPSHOT_COMPLETE`). The payload carries a `type` discriminator (currently always `OPENING`). |
    | v1.0.6    | 2026-07-08 | Canonical endpoint moved to `wss://api.truemarkets.co/v1/cefi/market`; legacy `wss://api.truex.co/api/v1` deprecated (still supported). Authenticated signatures accept the canonical or legacy `path`. Documented retail **ES256** subscription auth. |
    | v1.0.5    | 2026-04-09 | Added `SNAPSHOT_COMPLETE` update type with `item` field — sent per-item after snapshot delivery. |
    | v1.0.4    | 2026-04-07 | Add text field to order reports.            |
    | v1.0.3    | 2026-02-23 | Added `UNSUBSCRIBE_NO_AUTH` and clarified no-auth request requirements on authenticated sessions. |
    | v1.0.2    | 2026-02-10 | Clarified authentication/signature rules and subscription field requirements. |
    | v1.0.1    | 2026-01-28 | Added DROPCOPY channel for order drop copy. |
    | v1.0.0    | 2025-10-16 | Initial Release.                            |

  contact:
    email: info@truemarkets.co
  termsOfService: https://truex.co/tos
  version: "v1.0.12"
servers:
  production:
    $ref: "#/components/servers/production"
  uat:
    $ref: "#/components/servers/uat"
channels:
  v1:
    address: /v1/cefi/market
    messages:
      subscription:
        security:
          - HMAC: []
          - ES256: []
        $ref: "#/components/messages/subscription"
      welcome:
        $ref: "#/components/messages/welcome"
      confirmation:
        $ref: "#/components/messages/confirmation"
      heartbeat:
        $ref: "#/components/messages/heartbeat"
      instrument:
        $ref: "#/components/messages/instrument"
      trade:
        $ref: "#/components/messages/trade"
      ebbo:
        $ref: "#/components/messages/ebbo"
      depth:
        $ref: "#/components/messages/depth"
      auction:
        $ref: "#/components/messages/auction"
      index:
        $ref: "#/components/messages/index"
      dropcopy:
        $ref: "#/components/messages/dropcopy"
operations:
  subscribe:
    action: send
    description: "Message sent to server indicating what channels and data the user would like to subscribe to."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/subscription"
  welcome:
    action: receive
    description: "Message received when connecting to the server and establishing a websocket connection."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/welcome"
  heartbeat:
    action: receive
    description: "Message sent to a connection subscribed to the HEARTBEAT channel after 10 seconds with nothing else sent to it, so clients can tell a quiet feed from a dead connection."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/heartbeat"
  confirmation:
    action: receive
    description: "Message sent from server containing channels and items the user is currently subscribed to."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/confirmation"
  instrument:
    action: receive
    description: "Message sent from server with instrument data."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/instrument"
  trade:
    action: receive
    description: "Message sent from server containing the latest trade information for a subscribed instrument."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/trade"
  ebbo:
    action: receive
    description: "Message sent from server containing the latest exchange best bid and offer information for a subscribed instrument."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/ebbo"
  depth:
    action: receive
    description: "Message sent from server containing the latest market data change for a subscribed instrument."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/depth"
  auction:
    action: receive
    description: "Message sent from server containing the latest opening-auction indicative for a subscribed instrument. A status of INVALID indicates the instrument is not currently in an opening auction; updates are emitted while an auction is in progress, plus one final zeroed update (status INVALID) when the auction completes."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/auction"
  index:
    action: receive
    description: "Message sent from server with the exchange's own view of a subscribed price index after each one-second bar applies: the bar, the feed state, and the settlement value on the bar that closes a settlement window. An up/down outcome market opens at the settlement value of the window ending at its open time (its `strike`) and resolves YES when the settlement value of the window ending at its expiry is strictly above it, so a client can check a verdict from this stream rather than trust it. Items are observable ids as strings; an outcome instrument's `terms.resolver_id` names its observable."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/index"
  dropcopy:
    action: receive
    description: "Message sent from server containing order drop copy data for subscribed client IDs. Provides real-time order state updates including new orders, fills, cancellations, and modifications. **Requires authentication** - unauthenticated connections will be rejected. A DROPCOPY subscription placed while the exchange is still syncing its client catalog (shortly after a restart) receives a confirmation without that subscription and a `message` asking you to retry — resend the subscription in a few moments."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/dropcopy"
components:
  servers:
    production:
      host: api.truemarkets.co
      protocol: wss
      description: Production CeFi market-data WebSocket (canonical). Connect at `/v1/cefi/market`.
      security:
        - $ref: "#/components/securitySchemes/NONE"
        - $ref: "#/components/securitySchemes/HMAC"
        - $ref: "#/components/securitySchemes/ES256"
    uat:
      host: api.uat.truemarkets.co
      protocol: wss
      description: UAT CeFi market-data WebSocket sandbox (canonical). Connect at `/v1/cefi/market`.
      security:
        - $ref: "#/components/securitySchemes/NONE"
        - $ref: "#/components/securitySchemes/HMAC"
        - $ref: "#/components/securitySchemes/ES256"
  schemas:
    MarketQuote:
      type: object
      properties:
        price:
          type: string
          example: "75.50"
        qty:
          type: string
          example: "1.5"
        timestamp:
          type: string
          example: ""
          description: "The Unix UTC timestamp (in nanoseconds) when the event occurred."
    MarketLevel:
      type: object
      properties:
        price:
          type: string
          example: "110865.50"
          description: "Price at this level."
        qty:
          type: string
          example: "0.00018"
          description: "Aggregate quantity resting at this level."
        depth:
          type: string
          example: "10"
          description: "Number of orders resting at this level."
    Trade:
      type: object
      properties:
        match_id:
          type: string
          description: "A unique identifier for the trade match event."
          example: "1234576970"
        taking_side:
          type: string
          description: "Which side initiated the trade."
          enum:
            - "BUY"
            - "SELL"
          example: "SELL"
        info:
          type: object
          $ref: "#/components/schemas/MarketQuote"
          description: "Last trade information."
        symbol:
          type: string
          maxLength: 31
          example: "BTC-PYUSD"
          description: "Human readable representation of the instrument."
    MarketData:
      type: object
      properties:
        id:
          type: string
          example: "0987654321"
          description: "Exchange assigned ID of the market data."
        symbol:
          type: string
          maxLength: 31
          example: "BTC-PYUSD"
          description: "Human readable representation of the instrument."
        info:
          type: object
          properties:
            last_trade:
              type: object
              $ref: "#/components/schemas/MarketQuote"
              description: "Last trade information."
            best_bid:
              type: object
              $ref: "#/components/schemas/MarketLevel"
              description: "Current exchange best bid."
            best_ask:
              type: object
              $ref: "#/components/schemas/MarketLevel"
              description: "Current exchange best ask."
            last_update:
              type: string
              example: "123478909"
              description: "A UTC Unix epoch in nanoseconds indicating when the book was last updated."
    AuctionIndicative:
      type: object
      properties:
        type:
          type: string
          description: "Kind of auction the payload describes, so the channel can carry other auction types (e.g. RFQ, flash) in the future. Currently always OPENING."
          enum:
            - "OPENING"
          example: "OPENING"
        indicative_price:
          type: string
          example: "75.50"
          description: "Price at which the opening auction would clear if it uncrossed now."
        matched_volume:
          type: string
          example: "1200.5"
          description: "Quantity that would match at the indicative price."
        projected_best_bid_price:
          type: string
          example: "75.25"
          description: "Best bid remaining after the projected uncross."
        projected_best_ask_price:
          type: string
          example: "75.75"
          description: "Best ask remaining after the projected uncross."
        timestamp:
          type: string
          example: "1741894773000000000"
          description: "The Unix UTC timestamp (in nanoseconds) when the indicative was calculated."
        status:
          type: string
          description: "Health of the indicative. INVALID means the instrument is not currently in an opening auction."
          enum:
            - "INVALID"
            - "NORMAL"
            - "INSUFFICIENT_BUY_INTEREST"
            - "INSUFFICIENT_SELL_INTEREST"
            - "NO_CROSS"
          example: "NORMAL"
        symbol:
          type: string
          maxLength: 31
          example: "BTC-PYUSD"
          description: "Human readable representation of the instrument."
    Index:
      type: object
      properties:
        observable_id:
          type: string
          example: "7383716248297537536"
          description: "Exchange id of the observable (the price index) this message describes; the subscribed item."
        interval_start:
          type: string
          example: "1789409099000000000"
          description: "Start of the applied bar, UTC Unix epoch in nanoseconds, exclusive. Zero on a feed-state message."
        interval_end:
          type: string
          example: "1789409100000000000"
          description: "End of the applied bar, UTC Unix epoch in nanoseconds, inclusive; the exchange's tail for the index."
        last:
          type: string
          example: "78959.72"
          description: "Last index print in the bar."
        twap:
          type: string
          example: "78959.70"
          description: "Duration-weighted mean of the index over the bar."
        low:
          type: string
          example: "78955.10"
        high:
          type: string
          example: "78962.00"
        settlement_value:
          type: string
          example: "78961.821151038181666666"
          description: 'When this bar closes a fully covered settlement window, the window''s duration-weighted mean, which is the value an up/down market opening or expiring at `interval_end` uses as its strike or verdict. "0" on every other bar.'
        state:
          type: string
          enum:
            - ACTIVE
            - STALE
          example: "ACTIVE"
          description: "Feed health as last reported by the exchange's ingest. STALE is sent the moment the upstream feed drops and clears on the next applied bar."
    InstrumentStat:
      type: object
      properties:
        last_24hr_notional:
          type: string
          example: "4325606.30380"
          description: "The notional (quantity * price) amount of REFERENCE_ASSET traded over the last 24hr period."
        last_24hr_quantity:
          type: string
          example: "123754.2345"
          description: "The quantity traded over the last 24 hour period."
    Instrument:
      type: object
      properties:
        id:
          type: string
          example: "123456789"
          description: "Exchange assigned ID of the instrument."
        status:
          enum:
            - DISABLED
            - OPENING
            - ACTIVE
            - HALTED
          type: string
          example: "ACTIVE"
          description: "Current instrument state."
        info:
          type: object
          properties:
            symbol:
              type: string
              maxLength: 31
              example: "BTC-PYUSD"
              description: "Human readable representation of the instrument."
            reference_price:
              type: string
              example: "75000.50"
              description: "Price used for initial price limit/band calcuations."
            base_asset_id:
              type: string
              example: "123456789"
              description: "ID of the base asset."
            quote_asset_id:
              type: string
              example: "123456789"
              description: "ID of the quote asset."
            price_limit_window_secs:
              type: string
              example: "3600"
              description: "Number of seconds in the price limit's sliding window."
            price_limit_percent:
              type: string
              description: "Specifies the percentage threshold for price movement beyond a reference price (e.g., the midpoint or previous close) at which the exchange will take action. If the price of a trade or order breaches this threshold, the exchange may halt trading, trigger a circuit breaker, or initiate other predefined actions to ensure market stability."
            price_bands_percent:
              type: string
              example: "0.025"
              description: "Defines the acceptable price range for order validation within the market. The price bands ensure that orders are priced within a range around the market's midpoint, adjusted for increments and thresholds."
        stats:
          type: object
          $ref: "#/components/schemas/InstrumentStat"
    Order:
      type: object
      description: "Order data representing the current state of an order."
      properties:
        order_id:
          type: string
          example: "987654321"
          description: "Exchange assigned unique identifier for the order."
        client_id:
          type: string
          example: "12345"
          description: "Client ID that owns this order."
        instrument_id:
          type: string
          example: "111222333"
          description: "Exchange assigned ID of the instrument being traded."
        external_id:
          type: string
          example: "my-order-001"
          description: "Client-supplied external order identifier."
        symbol:
          type: string
          maxLength: 31
          example: "BTC-PYUSD"
          description: "Human readable representation of the instrument."
        side:
          type: string
          enum:
            - BUY
            - SELL
          example: "BUY"
          description: "Order side."
        order_type:
          type: string
          enum:
            - LIMIT
            - MARKET
            - NOTIONAL_LIMIT
          example: "LIMIT"
          description: "Type of order. `NOTIONAL_LIMIT` is deprecated and appears only on orders entered before it became an alias: new notional orders are reported as `MARKET` (entered with the `QUOTE_QTY` flag)."
        time_in_force:
          type: string
          enum:
            - GTC
            - IOC
          example: "GTC"
          description: "Time in force for the order."
        status:
          type: string
          enum:
            - NEW_PENDING
            - ACTIVE
            - FILLED
            - CANCEL_PENDING
            - CANCELED
            - MODIFY_PENDING
            - REJECTED
          example: "ACTIVE"
          description: "Current state of the order."
        price:
          type: string
          example: "50000.00"
          description: "Order price (for limit orders)."
        quantity:
          type: string
          example: "1.5"
          description: "Original order quantity."
        leaves_qty:
          type: string
          example: "1.0"
          description: "Remaining quantity to be filled."
        executed_qty:
          type: string
          example: "0.5"
          description: "Total quantity that has been executed."
        executed_vwap:
          type: string
          example: "49500.00"
          description: "Volume weighted average price of executed quantity."
        event_type:
          type: string
          enum:
            - NEW
            - TRADE
            - CANCELED
            - MODIFIED
            - REJECTED
          example: "TRADE"
          description: "Type of event that triggered this update (only present on UPDATE messages)."
        text:
          type: string
          description: "Information provided by the exchange about an event.  Maybe empty."
        trade_price:
          type: string
          example: "50000.00"
          description: "Price of the last trade (only present on TRADE events)."
        trade_qty:
          type: string
          example: "0.5"
          description: "Quantity of the last trade (only present on TRADE events)."
        timestamp:
          type: string
          example: "1737469200000000"
          description: "UTC Unix timestamp in nanoseconds when the event occurred."
  messages:
    subscription:
      payload:
        type: object
        required:
          - type
          - item_names
          - channels
          - timestamp
        oneOf:
          - description: Authenticated request (`SUBSCRIBE` or `UNSUBSCRIBE`) requiring credentials.
            properties:
              type:
                enum:
                  - SUBSCRIBE
                  - UNSUBSCRIBE
              organization_id:
                type: string
              key:
                type: string
              signature:
                type: string
            required:
              - organization_id
              - key
              - signature
          - description: Unauthenticated request (`SUBSCRIBE_NO_AUTH` or `UNSUBSCRIBE_NO_AUTH`) with no auth credential fields.
            properties:
              type:
                enum:
                  - SUBSCRIBE_NO_AUTH
                  - UNSUBSCRIBE_NO_AUTH
              organization_id:
                type: string
              key:
                type: string
              signature:
                type: string
            not:
              anyOf:
                - required:
                    - organization_id
                  properties:
                    organization_id:
                      type: string
                - required:
                    - key
                  properties:
                    key:
                      type: string
                - required:
                    - signature
                  properties:
                    signature:
                      type: string
        properties:
          type:
            type: string
            description: "Action the request is intending to take."
            enum:
              - SUBSCRIBE
              - SUBSCRIBE_NO_AUTH
              - UNSUBSCRIBE
              - UNSUBSCRIBE_NO_AUTH
            example: "SUBSCRIBE"
          item_names:
            type: array
            description: "List of items the request is intending to act upon. For market data channels (INSTRUMENT, EBBO, DEPTH, TRADE, AUCTION), this contains instrument symbols. For the INDEX channel, observable ids as strings. For the DROPCOPY channel, this contains client IDs as strings."
            items:
              type: string
              example: "BTC-PYUSD"
          channels:
            type: array
            description: "List of channels the request is intending to interact with. Note: DROPCOPY channel requires authentication. HEARTBEAT applies to the connection and ignores item_names."
            items:
              type: string
              enum:
                - INSTRUMENT
                - EBBO
                - DEPTH
                - TRADE
                - AUCTION
                - INDEX
                - DROPCOPY
                - HEARTBEAT
              example: "INSTRUMENT"
          timestamp:
            description: |
              Required field on all requests; a UTC Unix epoch in seconds. Must not be in the future and must be no older than 15 seconds relative to server time.
            type: string
            example: "1741894773"
          organization_id:
            description: "Exchange designation ID will be UUIDv4. Must be supplied in human readable form. [RFC 9562](https://datatracker.ietf.org/doc/html/rfc9562#name-uuid-version-4)."
            type: string
            example: "30d17c5b-5d7b-4f06-bdc5-d4b3b12bdd4"
          key:
            description: "Exchange supplied API key will be in UUIDv4. Must be supplied in human readable form. [RFC 9562](https://datatracker.ietf.org/doc/html/rfc9562#name-uuid-version-4)."
            type: string
            example: "6398805c-f369-40f6-ac07-94ce683056e4"
          signature:
            description: "The computed HMAC signature."
            type: string
            example: "gLoCBQaBqjfBx+Z5o113nUUNgWHaliIhQGISguSLVKY="
    welcome:
      payload:
        type: object
        properties:
          connections:
            type: string
            description: "Current number of active connections from the source IP."
            example: "3"
          channel:
            type: string
            description: "Channel this message originated from."
            enum:
              - WEBSOCKET
            example: "WEBSOCKET"
          update:
            type: string
            description: "Type of update for the current message."
            enum:
              - WELCOME
            example: "WELCOME"
          message:
            type: string
            description: "Message supplied when initiating a connection with the server."
            example: "Welcome to True Markets!"
          version:
            type: string
            description: "Current version of the websocket API."
            example: "1.0.0"
          datetime:
            description: "Current datetime of when the server sent the message,"
            type: string
            example: "2025-01-01 17:00:00"
    heartbeat:
      payload:
        type: object
        properties:
          timestamp:
            type: string
            description: "Server UTC time the heartbeat was sent, in nanoseconds since the Unix epoch."
            example: "1791602585352888679"
          channel:
            type: string
            description: "Channel this message originated from."
            enum:
              - HEARTBEAT
            example: "HEARTBEAT"
          update:
            type: string
            description: "Type of update for the current message."
            enum:
              - HEARTBEAT
            example: "HEARTBEAT"
    confirmation:
      payload:
        type: object
        properties:
          timestamp:
            type: string
            description: "Server timestamp when the confirmation was generated."
            example: "1741894944"
          channel:
            type: string
            description: "Channel this message originated from."
            enum:
              - WEBSOCKET
            example: "WEBSOCKET"
          update:
            type: string
            description: "Type of update for the current message."
            enum:
              - SNAPSHOT
              - UPDATE
            example: "SNAPSHOT"
          status:
            type: string
            description: "Current status of the websocket connection."
            enum:
              - INVALID
              - ERROR
              - UNAUTHORIZED
              - UNAUTHENTICATED
              - AUTHENTICATED
            example: "AUTHENTICATED"
          message:
            type: string
            description: "Optional detail present when a request failed or when one or more requested items could not be subscribed (compare `subscriptions` against what you requested). A message asking you to retry in a few moments indicates a transient condition (e.g. the exchange is still syncing its client catalog after a restart), not a denial — resend the same subscription shortly."
            example: "Service temporarily unavailable; retry subscription in a few moments"
          subscriptions:
            type: array
            description: "List of current subscriptions. For DROPCOPY channel, item_names contains client IDs. HEARTBEAT is listed with empty item_names."
            items:
              type: object
              properties:
                channel:
                  type: string
                  enum:
                    - INSTRUMENT
                    - EBBO
                    - DEPTH
                    - TRADE
                    - AUCTION
                    - INDEX
                    - DROPCOPY
                    - HEARTBEAT
                  example: "EBBO"
                item_names:
                  type: array
                  description: "Subscribed items. For market data channels, these are instrument symbols. For DROPCOPY, these are client IDs."
                  items:
                    type: string
                    example: "BTC-PYUSD"
    instrument:
      payload:
        oneOf:
          - type: object
            required: [seqnum, channel, update, data]
            properties:
              seqnum:
                type: string
                description: "Current sequence number of the channel."
                example: "12345"
              channel:
                type: string
                enum: [INSTRUMENT]
              update:
                type: string
                enum: [SNAPSHOT, UPDATE]
                example: "SNAPSHOT"
              data:
                $ref: "#/components/schemas/Instrument"
          - type: object
            required: [seqnum, channel, update, item]
            properties:
              seqnum:
                type: string
                description: "Current sequence number of the channel."
              channel:
                type: string
                enum: [INSTRUMENT]
              update:
                type: string
                enum: [SNAPSHOT_COMPLETE]
              item:
                type: string
                description: "The subscribed item whose snapshot is complete. For single-instrument subscriptions this is the symbol (e.g. BTC-PYUSD). For all-instruments subscriptions this is ALL."
                example: "BTC-PYUSD"
    trade:
      payload:
        oneOf:
          - type: object
            required: [seqnum, channel, update, data]
            properties:
              seqnum:
                type: string
                description: "Current sequence number of the channel."
                example: "100001"
              channel:
                type: string
                enum: [TRADE]
              update:
                type: string
                enum: [SNAPSHOT, UPDATE]
                example: "UPDATE"
              data:
                $ref: "#/components/schemas/Trade"
          - type: object
            required: [seqnum, channel, update, item]
            properties:
              seqnum:
                type: string
                description: "Current sequence number of the channel."
              channel:
                type: string
                enum: [TRADE]
              update:
                type: string
                enum: [SNAPSHOT_COMPLETE]
              item:
                type: string
                description: "The subscribed item whose snapshot is complete."
                example: "BTC-PYUSD"
    ebbo:
      payload:
        oneOf:
          - type: object
            required: [seqnum, channel, update, data]
            properties:
              seqnum:
                type: string
                description: "Current sequence number of the channel."
                example: "20210209"
              channel:
                type: string
                enum: [EBBO]
              update:
                type: string
                enum: [SNAPSHOT, UPDATE]
                example: "SNAPSHOT"
              data:
                $ref: "#/components/schemas/MarketData"
          - type: object
            required: [seqnum, channel, update, item]
            properties:
              seqnum:
                type: string
                description: "Current sequence number of the channel."
              channel:
                type: string
                enum: [EBBO]
              update:
                type: string
                enum: [SNAPSHOT_COMPLETE]
              item:
                type: string
                description: "The subscribed item whose snapshot is complete."
                example: "BTC-PYUSD"
    depth:
      payload:
        oneOf:
          - type: object
            required: [seqnum, channel, update, data]
            properties:
              seqnum:
                type: string
                description: "Current sequence number of the channel."
                example: "20210209"
              channel:
                type: string
                enum: [DEPTH]
              update:
                type: string
                enum: [SNAPSHOT, UPDATE]
                example: "SNAPSHOT"
              data:
                type: object
                properties:
                  symbol:
                    type: string
                    maxLength: 31
                    example: "BTC-PYUSD"
                    description: "Human readable representation of the instrument."
                  bids:
                    type: array
                    items:
                      $ref: "#/components/schemas/MarketLevel"
                  asks:
                    type: array
                    items:
                      $ref: "#/components/schemas/MarketLevel"
          - type: object
            required: [seqnum, channel, update, item]
            properties:
              seqnum:
                type: string
                description: "Current sequence number of the channel."
              channel:
                type: string
                enum: [DEPTH]
              update:
                type: string
                enum: [SNAPSHOT_COMPLETE]
              item:
                type: string
                description: "The subscribed item whose snapshot is complete."
                example: "BTC-PYUSD"
    auction:
      payload:
        oneOf:
          - type: object
            required: [seqnum, channel, update, data]
            properties:
              seqnum:
                type: string
                description: "Current sequence number of the channel."
                example: "20210209"
              channel:
                type: string
                enum: [AUCTION]
              update:
                type: string
                description: "SNAPSHOT is sent upon subscription with the current indicative. UPDATE is sent while an opening auction is in progress, on a timer and after each book change, plus once with a zeroed payload (status INVALID) when the auction completes."
                enum: [SNAPSHOT, UPDATE]
                example: "UPDATE"
              data:
                $ref: "#/components/schemas/AuctionIndicative"
          - type: object
            required: [seqnum, channel, update, item]
            properties:
              seqnum:
                type: string
                description: "Current sequence number of the channel."
              channel:
                type: string
                enum: [AUCTION]
              update:
                type: string
                enum: [SNAPSHOT_COMPLETE]
              item:
                type: string
                description: "The subscribed item whose snapshot is complete."
                example: "BTC-PYUSD"
    index:
      payload:
        oneOf:
          - type: object
            required: [seqnum, channel, update, data]
            properties:
              seqnum:
                type: string
                description: "Current sequence number of the channel."
                example: "20210209"
              channel:
                type: string
                enum: [INDEX]
              update:
                type: string
                description: "SNAPSHOT is sent upon subscription with the exchange's last view of the index. UPDATE is sent once per applied bar (about once a second) and once when the feed drops."
                enum: [SNAPSHOT, UPDATE]
                example: "UPDATE"
              data:
                $ref: "#/components/schemas/Index"
          - type: object
            required: [seqnum, channel, update, item]
            properties:
              seqnum:
                type: string
                description: "Current sequence number of the channel."
              channel:
                type: string
                enum: [INDEX]
              update:
                type: string
                enum: [SNAPSHOT_COMPLETE]
              item:
                type: string
                description: "The subscribed item whose snapshot is complete."
                example: "7383716248297537536"
    dropcopy:
      payload:
        oneOf:
          - type: object
            description: "Order drop copy message containing order state information. This channel requires authentication."
            required: [seqnum, channel, update, data]
            properties:
              seqnum:
                type: string
                description: "Per-connection sequence number for the DROPCOPY channel."
                example: "42"
              channel:
                type: string
                enum: [DROPCOPY]
              update:
                type: string
                description: "SNAPSHOT is sent upon initial subscription with all active orders. UPDATE is sent for subsequent order state changes."
                enum: [SNAPSHOT, UPDATE]
                example: "UPDATE"
              data:
                $ref: "#/components/schemas/Order"
          - type: object
            required: [seqnum, channel, update, item]
            properties:
              seqnum:
                type: string
                description: "Per-connection sequence number for the DROPCOPY channel."
              channel:
                type: string
                enum: [DROPCOPY]
              update:
                type: string
                enum: [SNAPSHOT_COMPLETE]
              item:
                type: string
                description: "The subscribed item whose snapshot is complete. For client subscriptions: CLIENT/{clientId}. For organization subscriptions: ORG/{orgUuid}."
                example: "CLIENT/12345"
  securitySchemes:
    NONE:
      type: httpApiKey
      in: query
      name: x-no-auth
      description: |
        This security scheme documents message-level behavior. Authentication fields are carried in the subscription message payload, not as HTTP query parameters.

        Clients may subscribe to endpoints without an API key or computing an HMAC signature. These endpoints are rate limited by the server, coalescing and only sending data at a predetermined rate. Non-authenticated connections can be upgraded by supplying an API key and computed HMAC signature on the next request sent to the server.

        Unauthenticated requests must have type `SUBSCRIBE_NO_AUTH` or `UNSUBSCRIBE_NO_AUTH` and must not include the following fields in the request: `organization_id`, `key`, `signature`.

        `*_NO_AUTH` requests are only valid on unauthenticated sessions. If a connection is already authenticated, no-auth requests are rejected and do not modify authenticated subscriptions.

        ### Required members
          * `timestamp`: A UTC Unix epoch in seconds.
    HMAC:
      type: httpApiKey
      in: query
      name: x-api-key
      description: |
        This security scheme documents message-level behavior. Authentication fields are carried in the subscription message payload, not as HTTP query parameters.

        Clients must compute and send an HMAC signature in the `signature` member of the request structure.

        Compute the payload exactly as:

        `timestamp + TRUEXWS + key + path`

        Then compute the signature as:

        `Base64( HMAC_SHA256(secret, payload) )`

        Rules:
        * `timestamp` is a Unix epoch in **seconds**.
        * `TRUEXWS` is the literal string `TRUEXWS`.
        * `key` is the API key UUID string.
        * `path` is the WebSocket endpoint path in the signed payload. Use the canonical `/v1/cefi/market`; the legacy `/api/v1` is also accepted but **deprecated**. The server accepts either, regardless of the host/URL you connect through.
        * Timestamp must be no older than 15 seconds and at most 1 second in the future relative to server time (see **Timestamps** in the overview for how to avoid rejects).

        Example payload:
        * `1700000000TRUEXWS11112222-3333-4444-8555-666677778888/v1/cefi/market`

        Example signature (shell):
        * `printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" -binary | base64`

        ### Required members
        * `signature`: The signature generated from processing the payload with the HMAC key.
        * `key`: The ID of the HMAC key being used.
        * `timestamp`: A UTC Unix epoch in seconds.
    ES256:
      type: httpApiKey
      in: query
      name: x-api-key
      description: |
        This security scheme documents message-level behavior. Authentication fields are carried in the subscription message payload, not as HTTP query parameters.

        Clients authenticate with an asymmetric **EC P-256 (ES256)** API key created at app.truemarkets.co (public key only is held server-side). The client signs with its private key.

        Compute the same canonical payload as the HMAC scheme:

        `timestamp + TRUEXWS + key + path`

        Then sign it as **ES256** (ECDSA over SHA-256) and encode the raw `r || s` (64 bytes) as **base64url**:

        `signature = base64url( ECDSA_P256_SHA256( private_key, payload ).r_concat_s )`

        Rules:
        * `timestamp` is a Unix epoch in **seconds**; must be no older than 15 seconds and at most 1 second in the future relative to server time (see **Timestamps** in the overview for how to avoid rejects).
        * `TRUEXWS` is the literal string `TRUEXWS`.
        * `key` is the API key UUID string (the ES256 key id).
        * `path` is the WebSocket endpoint path — use the canonical `/v1/cefi/market`; the legacy `/api/v1` is also accepted but **deprecated** (same as HMAC).
        * The signature is the raw 64-byte `r || s`, base64url-encoded (no padding) — DER-encoded signatures are not accepted.

        Example payload:
        * `1700000000TRUEXWS11112222-3333-4444-8555-666677778888/v1/cefi/market`

        An ES256 key whose account is not enabled for CeFi trading (no bound `client_id`) is rejected.

        ### Required members
        * `signature`: The base64url `r || s` ES256 signature over the payload.
        * `key`: The ES256 API key id.
        * `timestamp`: A UTC Unix epoch in seconds.
