KuCoin · AsyncAPI Specification

KuCoin Public WebSocket API

Version 1.0.0

AsyncAPI 2.6 description of KuCoin's public WebSocket streaming API for the Classic (Spot/Margin) account. ## Obtaining the connection endpoint The WebSocket endpoint and a short-lived bearer token are NOT statically documented. A client must first call the REST endpoint: `POST https://api.kucoin.com/api/v1/bullet-public` The response contains a `token` and an `instanceServers[]` array. Each `instanceServer` includes: | Field | Type | Notes | |-------|------|-------| | `endpoint` | string | The wss URL (e.g. `wss://ws-api-spot.kucoin.com/`) | | `protocol` | string | `websocket` | | `encrypt` | boolean | `true` | | `pingInterval` | integer | Milliseconds between client pings (e.g. 18000) | | `pingTimeout` | integer | Milliseconds to wait for pong before disconnecting (e.g. 10000) | Example response (truncated): ```json { "code": "200000", "data": { "token": "2neAi...", "instanceServers": [ { "pingInterval": 18000, "endpoint": "wss://ws-api-spot.kucoin.com/", "protocol": "websocket", "encrypt": true, "pingTimeout": 10000 } ] } } ``` The client then opens the WebSocket using the URL: `{endpoint}?token={token}&connectId={uniqueId}` The connection becomes active only after the server sends a `welcome` message. The client must send a `ping` envelope every `pingInterval` milliseconds and consider the connection dead if no `pong` arrives within `pingTimeout`. Tokens are valid for 24 hours; clients should refresh and reconnect before expiry. Sources: - https://www.kucoin.com/docs/websocket/basic-info/introduction - https://www.kucoin.com/docs-new/websocket-api/base-info/introduction - https://www.kucoin.com/docs/rest/websocket/get-public-token

View Spec View on GitHub CryptocurrencyPublic APIsAsyncAPIWebhooksEvents

Channels

/
publish clientToServer
Control and heartbeat messages sent by the client.
Single multiplexed WebSocket connection. All KuCoin streaming traffic flows over one connection; topics are subscribed/unsubscribed via JSON control messages and inbound messages are routed by their `topic` and `subject` fields.

Messages

SubscribeMessage
Subscribe
Subscribe to a topic. Some topics support a comma-separated list of symbols (up to 100).
UnsubscribeMessage
Unsubscribe
Unsubscribe from a topic.
PingMessage
Ping
Heartbeat. Must be sent every `pingInterval` ms returned by /api/v1/bullet-public.
WelcomeMessage
Welcome
First message from server after the WebSocket handshake. The connection is only usable after this message is received.
AckMessage
Subscribe/Unsubscribe Acknowledgement
Sent when the client requested `"response": true` on subscribe/unsubscribe.
PongMessage
Pong
Reply to a client ping.
ErrorMessage
Error
Server-side error envelope.
SymbolTicker
Public: Symbol Ticker
Topic: `/market/ticker:{symbol}` — pushed at most once every 100ms.
AllSymbolsTicker
Public: All Symbols Ticker
Topic: `/market/ticker:all` — ticker updates for every symbol, subject is the symbol code.
SymbolSnapshot
Public: Symbol Snapshot
Topic: `/market/snapshot:{symbol}` — pushed every 2s.
MarketSnapshot
Public: Market Snapshot
Topic: `/market/snapshot:{market}` — pushed every 2s for every symbol in a market (e.g. `BTC`, `USDS`).
Level1BBO
Public: Orderbook - Best Bid/Ask (Level 1)
Topic: `/spotMarket/level1:{symbol}` — pushed at most once every 10ms when top-of-book changes.
Level2Update
Public: Orderbook - Incremental Updates (Level 2)
Topic: `/market/level2:{symbol}` — real-time incremental updates. Subject `trade.l2update`.
Level2Depth5
Public: Orderbook - Top 5 (Level 2 Depth 5)
Topic: `/spotMarket/level2Depth5:{symbol}` — pushed at most once every 100ms.
Level2Depth50
Public: Orderbook - Top 50 (Level 2 Depth 50)
Topic: `/spotMarket/level2Depth50:{symbol}` — pushed at most once every 100ms.
Klines
Public: Klines (Candles)
Topic: `/market/candles:{symbol}_{type}` — real-time OHLCV updates. Type is one of 1min,3min,15min,30min,1hour,2hour,4hour,6hour,8hour,12hour,1day,1week.
Match
Public: Match Execution Data
Topic: `/market/match:{symbol}` — real-time Level 3 trade matches. Subject `trade.l3match`.
IndexPrice
Public: Index Price
Topic: `/indicator/index:{symbol}` — pushed once per second.
MarkPrice
Public: Mark Price
Topic: `/indicator/markPrice:{symbol}` — pushed once per second.
TradeOrders
Private: Trade Orders V1
Topic: `/spotMarket/tradeOrders` — real-time order lifecycle events. Requires private token.
TradeOrdersV2
Private: Trade Orders V2
Topic: `/spotMarket/tradeOrdersV2` — real-time order lifecycle events with additional update events. Requires private token.
AccountBalance
Private: Account Balance
Topic: `/account/balance` — real-time balance changes. Requires private token.
CrossMarginPosition
Private: Cross Margin Position
Topic: `/margin/position` — debt ratio and position status updates. Requires private token.
IsolatedMarginPosition
Private: Isolated Margin Position
Topic: `/margin/isolatedPosition:{symbol}` — isolated margin position changes. Requires private token.

Servers

wss
bulletPublic ws-api-spot.kucoin.com
Classic Spot/Margin public WebSocket endpoint. The actual endpoint is returned by `POST /api/v1/bullet-public` and must be appended with `?token={token}&connectId={uniqueId}`.

AsyncAPI Specification

Raw ↑
asyncapi: '2.6.0'
info:
  title: KuCoin Public WebSocket API
  version: '1.0.0'
  description: |
    AsyncAPI 2.6 description of KuCoin's public WebSocket streaming API
    for the Classic (Spot/Margin) account.

    ## Obtaining the connection endpoint

    The WebSocket endpoint and a short-lived bearer token are NOT statically
    documented. A client must first call the REST endpoint:

    `POST https://api.kucoin.com/api/v1/bullet-public`

    The response contains a `token` and an `instanceServers[]` array. Each
    `instanceServer` includes:

    | Field | Type | Notes |
    |-------|------|-------|
    | `endpoint` | string | The wss URL (e.g. `wss://ws-api-spot.kucoin.com/`) |
    | `protocol` | string | `websocket` |
    | `encrypt` | boolean | `true` |
    | `pingInterval` | integer | Milliseconds between client pings (e.g. 18000) |
    | `pingTimeout` | integer | Milliseconds to wait for pong before disconnecting (e.g. 10000) |

    Example response (truncated):

    ```json
    {
      "code": "200000",
      "data": {
        "token": "2neAi...",
        "instanceServers": [
          {
            "pingInterval": 18000,
            "endpoint": "wss://ws-api-spot.kucoin.com/",
            "protocol": "websocket",
            "encrypt": true,
            "pingTimeout": 10000
          }
        ]
      }
    }
    ```

    The client then opens the WebSocket using the URL:

    `{endpoint}?token={token}&connectId={uniqueId}`

    The connection becomes active only after the server sends a `welcome`
    message. The client must send a `ping` envelope every `pingInterval`
    milliseconds and consider the connection dead if no `pong` arrives within
    `pingTimeout`.

    Tokens are valid for 24 hours; clients should refresh and reconnect before
    expiry.

    Sources:
      - https://www.kucoin.com/docs/websocket/basic-info/introduction
      - https://www.kucoin.com/docs-new/websocket-api/base-info/introduction
      - https://www.kucoin.com/docs/rest/websocket/get-public-token
  contact:
    name: KuCoin API Support
    url: https://www.kucoin.com/docs-new
  license:
    name: KuCoin Terms of Service
    url: https://www.kucoin.com/legal/terms-of-use
  termsOfService: https://www.kucoin.com/legal/terms-of-use

defaultContentType: application/json

servers:
  bulletPublic:
    url: 'ws-api-spot.kucoin.com'
    protocol: wss
    description: |
      Classic Spot/Margin public WebSocket endpoint.
      The actual endpoint is returned by `POST /api/v1/bullet-public` and
      must be appended with `?token={token}&connectId={uniqueId}`.
    variables:
      token:
        description: Short-lived public token issued by /api/v1/bullet-public.
        default: REPLACE_WITH_TOKEN
      connectId:
        description: Unique client-generated connection identifier.
        default: REPLACE_WITH_CONNECT_ID

channels:
  '/':
    description: |
      Single multiplexed WebSocket connection. All KuCoin streaming traffic
      flows over one connection; topics are subscribed/unsubscribed via JSON
      control messages and inbound messages are routed by their `topic` and
      `subject` fields.
    publish:
      operationId: clientToServer
      summary: Control and heartbeat messages sent by the client.
      message:
        oneOf:
          - $ref: '#/components/messages/SubscribeMessage'
          - $ref: '#/components/messages/UnsubscribeMessage'
          - $ref: '#/components/messages/PingMessage'
    subscribe:
      operationId: serverToClient
      summary: Server-pushed envelopes (welcome, ack, pong, market data, private data).
      message:
        oneOf:
          - $ref: '#/components/messages/WelcomeMessage'
          - $ref: '#/components/messages/AckMessage'
          - $ref: '#/components/messages/PongMessage'
          - $ref: '#/components/messages/ErrorMessage'
          - $ref: '#/components/messages/SymbolTicker'
          - $ref: '#/components/messages/AllSymbolsTicker'
          - $ref: '#/components/messages/SymbolSnapshot'
          - $ref: '#/components/messages/MarketSnapshot'
          - $ref: '#/components/messages/Level1BBO'
          - $ref: '#/components/messages/Level2Update'
          - $ref: '#/components/messages/Level2Depth5'
          - $ref: '#/components/messages/Level2Depth50'
          - $ref: '#/components/messages/Klines'
          - $ref: '#/components/messages/Match'
          - $ref: '#/components/messages/IndexPrice'
          - $ref: '#/components/messages/MarkPrice'
          - $ref: '#/components/messages/TradeOrders'
          - $ref: '#/components/messages/TradeOrdersV2'
          - $ref: '#/components/messages/AccountBalance'
          - $ref: '#/components/messages/CrossMarginPosition'
          - $ref: '#/components/messages/IsolatedMarginPosition'

components:
  messages:

    SubscribeMessage:
      name: subscribe
      title: Subscribe
      summary: Subscribe to a topic. Some topics support a comma-separated list of symbols (up to 100).
      payload:
        $ref: '#/components/schemas/SubscribeEnvelope'

    UnsubscribeMessage:
      name: unsubscribe
      title: Unsubscribe
      summary: Unsubscribe from a topic.
      payload:
        $ref: '#/components/schemas/UnsubscribeEnvelope'

    PingMessage:
      name: ping
      title: Ping
      summary: Heartbeat. Must be sent every `pingInterval` ms returned by /api/v1/bullet-public.
      payload:
        $ref: '#/components/schemas/PingEnvelope'

    WelcomeMessage:
      name: welcome
      title: Welcome
      summary: First message from server after the WebSocket handshake. The connection is only usable after this message is received.
      payload:
        $ref: '#/components/schemas/WelcomeEnvelope'

    AckMessage:
      name: ack
      title: Subscribe/Unsubscribe Acknowledgement
      summary: 'Sent when the client requested `"response": true` on subscribe/unsubscribe.'
      payload:
        $ref: '#/components/schemas/AckEnvelope'

    PongMessage:
      name: pong
      title: Pong
      summary: Reply to a client ping.
      payload:
        $ref: '#/components/schemas/PongEnvelope'

    ErrorMessage:
      name: error
      title: Error
      summary: Server-side error envelope.
      payload:
        $ref: '#/components/schemas/ErrorEnvelope'

    SymbolTicker:
      name: symbolTicker
      title: 'Public: Symbol Ticker'
      summary: 'Topic: `/market/ticker:{symbol}` — pushed at most once every 100ms.'
      payload:
        $ref: '#/components/schemas/SymbolTickerEnvelope'

    AllSymbolsTicker:
      name: allSymbolsTicker
      title: 'Public: All Symbols Ticker'
      summary: 'Topic: `/market/ticker:all` — ticker updates for every symbol, subject is the symbol code.'
      payload:
        $ref: '#/components/schemas/AllSymbolsTickerEnvelope'

    SymbolSnapshot:
      name: symbolSnapshot
      title: 'Public: Symbol Snapshot'
      summary: 'Topic: `/market/snapshot:{symbol}` — pushed every 2s.'
      payload:
        $ref: '#/components/schemas/SymbolSnapshotEnvelope'

    MarketSnapshot:
      name: marketSnapshot
      title: 'Public: Market Snapshot'
      summary: 'Topic: `/market/snapshot:{market}` — pushed every 2s for every symbol in a market (e.g. `BTC`, `USDS`).'
      payload:
        $ref: '#/components/schemas/MarketSnapshotEnvelope'

    Level1BBO:
      name: level1
      title: 'Public: Orderbook - Best Bid/Ask (Level 1)'
      summary: 'Topic: `/spotMarket/level1:{symbol}` — pushed at most once every 10ms when top-of-book changes.'
      payload:
        $ref: '#/components/schemas/Level1Envelope'

    Level2Update:
      name: level2
      title: 'Public: Orderbook - Incremental Updates (Level 2)'
      summary: 'Topic: `/market/level2:{symbol}` — real-time incremental updates. Subject `trade.l2update`.'
      payload:
        $ref: '#/components/schemas/Level2Envelope'

    Level2Depth5:
      name: level2Depth5
      title: 'Public: Orderbook - Top 5 (Level 2 Depth 5)'
      summary: 'Topic: `/spotMarket/level2Depth5:{symbol}` — pushed at most once every 100ms.'
      payload:
        $ref: '#/components/schemas/Level2DepthEnvelope'

    Level2Depth50:
      name: level2Depth50
      title: 'Public: Orderbook - Top 50 (Level 2 Depth 50)'
      summary: 'Topic: `/spotMarket/level2Depth50:{symbol}` — pushed at most once every 100ms.'
      payload:
        $ref: '#/components/schemas/Level2DepthEnvelope'

    Klines:
      name: klines
      title: 'Public: Klines (Candles)'
      summary: 'Topic: `/market/candles:{symbol}_{type}` — real-time OHLCV updates. Type is one of 1min,3min,15min,30min,1hour,2hour,4hour,6hour,8hour,12hour,1day,1week.'
      payload:
        $ref: '#/components/schemas/KlinesEnvelope'

    Match:
      name: match
      title: 'Public: Match Execution Data'
      summary: 'Topic: `/market/match:{symbol}` — real-time Level 3 trade matches. Subject `trade.l3match`.'
      payload:
        $ref: '#/components/schemas/MatchEnvelope'

    IndexPrice:
      name: indexPrice
      title: 'Public: Index Price'
      summary: 'Topic: `/indicator/index:{symbol}` — pushed once per second.'
      payload:
        $ref: '#/components/schemas/IndexPriceEnvelope'

    MarkPrice:
      name: markPrice
      title: 'Public: Mark Price'
      summary: 'Topic: `/indicator/markPrice:{symbol}` — pushed once per second.'
      payload:
        $ref: '#/components/schemas/MarkPriceEnvelope'

    TradeOrders:
      name: tradeOrders
      title: 'Private: Trade Orders V1'
      summary: 'Topic: `/spotMarket/tradeOrders` — real-time order lifecycle events. Requires private token.'
      payload:
        $ref: '#/components/schemas/TradeOrdersEnvelope'

    TradeOrdersV2:
      name: tradeOrdersV2
      title: 'Private: Trade Orders V2'
      summary: 'Topic: `/spotMarket/tradeOrdersV2` — real-time order lifecycle events with additional update events. Requires private token.'
      payload:
        $ref: '#/components/schemas/TradeOrdersV2Envelope'

    AccountBalance:
      name: accountBalance
      title: 'Private: Account Balance'
      summary: 'Topic: `/account/balance` — real-time balance changes. Requires private token.'
      payload:
        $ref: '#/components/schemas/AccountBalanceEnvelope'

    CrossMarginPosition:
      name: crossMarginPosition
      title: 'Private: Cross Margin Position'
      summary: 'Topic: `/margin/position` — debt ratio and position status updates. Requires private token.'
      payload:
        $ref: '#/components/schemas/CrossMarginPositionEnvelope'

    IsolatedMarginPosition:
      name: isolatedMarginPosition
      title: 'Private: Isolated Margin Position'
      summary: 'Topic: `/margin/isolatedPosition:{symbol}` — isolated margin position changes. Requires private token.'
      payload:
        $ref: '#/components/schemas/IsolatedMarginPositionEnvelope'

  schemas:

    # ---------- Control / heartbeat envelopes ----------

    SubscribeEnvelope:
      type: object
      required: [id, type, topic]
      properties:
        id:
          type: string
          description: Client-generated message id (echoed in the ack).
        type:
          type: string
          const: subscribe
        topic:
          type: string
          description: Topic to subscribe to (e.g. `/market/ticker:BTC-USDT`).
          example: /market/ticker:BTC-USDT
        privateChannel:
          type: boolean
          default: false
          description: Set to true for private topics that require a private token.
        response:
          type: boolean
          default: false
          description: 'If true, the server returns an `ack` envelope with the same id.'

    UnsubscribeEnvelope:
      type: object
      required: [id, type, topic]
      properties:
        id:
          type: string
        type:
          type: string
          const: unsubscribe
        topic:
          type: string
        privateChannel:
          type: boolean
        response:
          type: boolean

    PingEnvelope:
      type: object
      required: [id, type]
      properties:
        id:
          type: string
          description: Arbitrary client id used to correlate with the pong.
        type:
          type: string
          const: ping

    WelcomeEnvelope:
      type: object
      required: [id, type]
      properties:
        id:
          type: string
        type:
          type: string
          const: welcome
      example:
        id: hQvf8jkno
        type: welcome

    AckEnvelope:
      type: object
      required: [id, type]
      properties:
        id:
          type: string
        type:
          type: string
          const: ack
      example:
        id: '1545910660739'
        type: ack

    PongEnvelope:
      type: object
      required: [id, type]
      properties:
        id:
          type: string
        type:
          type: string
          const: pong
        timestamp:
          type: integer
          description: Server timestamp (microseconds).
      example:
        id: '1545910590801'
        type: pong
        timestamp: 1764215232226553

    ErrorEnvelope:
      type: object
      required: [id, type]
      properties:
        id:
          type: string
        type:
          type: string
          const: error
        code:
          type: integer
        data:
          type: string

    # ---------- Generic data envelope ----------

    DataEnvelopeBase:
      type: object
      required: [type, topic, subject, data]
      properties:
        id:
          type: string
        type:
          type: string
          const: message
        topic:
          type: string
        subject:
          type: string
        sn:
          type: integer
          description: Sequence number, present on some channels.

    # ---------- Public market data payloads ----------

    SymbolTickerEnvelope:
      allOf:
        - $ref: '#/components/schemas/DataEnvelopeBase'
        - type: object
          properties:
            topic:
              type: string
              example: /market/ticker:BTC-USDT
            subject:
              type: string
              example: trade.ticker
            data:
              $ref: '#/components/schemas/SymbolTickerData'

    SymbolTickerData:
      type: object
      properties:
        sequence: { type: string }
        price: { type: string, description: Most recent transaction price. }
        size: { type: string, description: Most recent transaction size. }
        bestAsk: { type: string }
        bestAskSize: { type: string }
        bestBid: { type: string }
        bestBidSize: { type: string }
        time: { type: integer, description: Match timestamp (milliseconds). }

    AllSymbolsTickerEnvelope:
      allOf:
        - $ref: '#/components/schemas/DataEnvelopeBase'
        - type: object
          properties:
            topic:
              type: string
              example: /market/ticker:all
            subject:
              type: string
              description: Symbol code, e.g. `BTC-USDT`.
              example: BTC-USDT
            data:
              $ref: '#/components/schemas/SymbolTickerData'

    SymbolSnapshotEnvelope:
      allOf:
        - $ref: '#/components/schemas/DataEnvelopeBase'
        - type: object
          properties:
            topic:
              type: string
              example: /market/snapshot:BTC-USDT
            subject:
              type: string
              example: trade.snapshot
            data:
              type: object
              properties:
                sequence: { type: string }
                data:
                  $ref: '#/components/schemas/SnapshotData'

    MarketSnapshotEnvelope:
      allOf:
        - $ref: '#/components/schemas/DataEnvelopeBase'
        - type: object
          properties:
            topic:
              type: string
              example: /market/snapshot:BTC
            subject:
              type: string
              example: trade.snapshot
            data:
              type: object
              properties:
                sequence: { type: integer }
                data:
                  $ref: '#/components/schemas/SnapshotData'

    SnapshotData:
      type: object
      description: Snapshot detail object pushed every 2 seconds.
      properties:
        askSize: { type: number }
        averagePrice: { type: number }
        baseCurrency: { type: string }
        bidSize: { type: number }
        board: { type: integer }
        buy: { type: number }
        changePrice: { type: number }
        changeRate: { type: number }
        close: { type: number }
        datetime: { type: integer, description: Timestamp (milliseconds). }
        high: { type: number }
        lastTradedPrice: { type: number }
        low: { type: number }
        makerCoefficient: { type: number }
        makerFeeRate: { type: number }
        marginTrade: { type: boolean }
        mark: { type: integer }
        market: { type: string }
        marketChange1h:
          $ref: '#/components/schemas/MarketChangeWindow'
        marketChange4h:
          $ref: '#/components/schemas/MarketChangeWindow'
        marketChange24h:
          $ref: '#/components/schemas/MarketChangeWindow'
        markets:
          type: array
          items: { type: string }
        open: { type: number }
        quoteCurrency: { type: string }
        sell: { type: number }
        siteTypes:
          type: array
          items: { type: string }
        sort: { type: integer }
        symbol: { type: string }
        symbolCode: { type: string }
        takerCoefficient: { type: number }
        takerFeeRate: { type: number }
        trading: { type: boolean }
        vol: { type: number }
        volValue: { type: number }

    MarketChangeWindow:
      type: object
      properties:
        changePrice: { type: number }
        changeRate: { type: number }
        high: { type: number }
        low: { type: number }
        open: { type: number }
        vol: { type: number }
        volValue: { type: number }

    Level1Envelope:
      allOf:
        - $ref: '#/components/schemas/DataEnvelopeBase'
        - type: object
          properties:
            topic:
              type: string
              example: /spotMarket/level1:BTC-USDT
            subject:
              type: string
              example: level1
            data:
              type: object
              properties:
                asks:
                  type: array
                  description: '[price, size] for the best ask.'
                  items: { type: string }
                  minItems: 2
                  maxItems: 2
                bids:
                  type: array
                  description: '[price, size] for the best bid.'
                  items: { type: string }
                  minItems: 2
                  maxItems: 2
                timestamp: { type: integer }

    Level2Envelope:
      allOf:
        - $ref: '#/components/schemas/DataEnvelopeBase'
        - type: object
          properties:
            topic:
              type: string
              example: /market/level2:BTC-USDT
            subject:
              type: string
              example: trade.l2update
            data:
              type: object
              properties:
                symbol: { type: string }
                sequenceStart: { type: integer }
                sequenceEnd: { type: integer }
                time: { type: integer }
                changes:
                  type: object
                  properties:
                    asks:
                      type: array
                      description: '[price, size, sequence] tuples. A size of 0 means the price level was removed.'
                      items:
                        type: array
                        items: { type: string }
                        minItems: 3
                        maxItems: 3
                    bids:
                      type: array
                      items:
                        type: array
                        items: { type: string }
                        minItems: 3
                        maxItems: 3

    Level2DepthEnvelope:
      allOf:
        - $ref: '#/components/schemas/DataEnvelopeBase'
        - type: object
          properties:
            subject:
              type: string
              example: level2
            data:
              type: object
              properties:
                asks:
                  type: array
                  items:
                    type: array
                    items: { type: string }
                    minItems: 2
                    maxItems: 2
                bids:
                  type: array
                  items:
                    type: array
                    items: { type: string }
                    minItems: 2
                    maxItems: 2
                timestamp: { type: integer }

    KlinesEnvelope:
      allOf:
        - $ref: '#/components/schemas/DataEnvelopeBase'
        - type: object
          properties:
            topic:
              type: string
              example: /market/candles:BTC-USDT_1hour
            subject:
              type: string
              example: trade.candles.update
            data:
              type: object
              properties:
                symbol:
                  type: string
                candles:
                  type: array
                  description: '[startTime, open, close, high, low, volume, amount] as strings.'
                  items: { type: string }
                  minItems: 7
                  maxItems: 7
                time:
                  type: integer
                  description: Push timestamp (nanoseconds).

    MatchEnvelope:
      allOf:
        - $ref: '#/components/schemas/DataEnvelopeBase'
        - type: object
          properties:
            topic:
              type: string
              example: /market/match:BTC-USDT
            subject:
              type: string
              example: trade.l3match
            data:
              type: object
              properties:
                symbol: { type: string }
                sequence: { type: string }
                side:
                  type: string
                  enum: [buy, sell]
                size: { type: string }
                price: { type: string }
                takerOrderId: { type: string }
                makerOrderId: { type: string }
                tradeId: { type: string }
                type:
                  type: string
                  description: Always `match` for this stream.
                time:
                  type: string
                  description: Match time (nanoseconds, as a string).

    IndexPriceEnvelope:
      allOf:
        - $ref: '#/components/schemas/DataEnvelopeBase'
        - type: object
          properties:
            topic:
              type: string
              example: /indicator/index:USDT-BTC
            subject:
              type: string
              example: tick
            data:
              type: object
              properties:
                symbol: { type: string }
                granularity: { type: integer, description: Granularity in milliseconds. }
                timestamp: { type: integer }
                value: { type: number }

    MarkPriceEnvelope:
      allOf:
        - $ref: '#/components/schemas/DataEnvelopeBase'
        - type: object
          properties:
            topic:
              type: string
              example: /indicator/markPrice:USDT-BTC
            subject:
              type: string
              example: tick
            data:
              type: object
              properties:
                symbol: { type: string }
                granularity: { type: integer }
                timestamp: { type: integer }
                value: { type: number }

    # ---------- Private user data payloads ----------

    PrivateEnvelopeBase:
      allOf:
        - $ref: '#/components/schemas/DataEnvelopeBase'
        - type: object
          properties:
            userId: { type: string }
            channelType:
              type: string
              const: private

    TradeOrdersEnvelope:
      allOf:
        - $ref: '#/components/schemas/PrivateEnvelopeBase'
        - type: object
          properties:
            topic:
              type: string
              example: /spotMarket/tradeOrders
            subject:
              type: string
              example: orderChange
            data:
              $ref: '#/components/schemas/TradeOrderData'

    TradeOrdersV2Envelope:
      allOf:
        - $ref: '#/components/schemas/PrivateEnvelopeBase'
        - type: object
          properties:
            topic:
              type: string
              example: /spotMarket/tradeOrdersV2
            subject:
              type: string
              example: orderChange
            data:
              $ref: '#/components/schemas/TradeOrderData'

    TradeOrderData:
      type: object
      properties:
        orderId: { type: string }
        clientOid: { type: string }
        symbol: { type: string }
        side:
          type: string
          enum: [buy, sell]
        orderType:
          type: string
          enum: [limit, market]
        price: { type: string }
        size: { type: string }
        originSize: { type: string }
        filledSize: { type: string }
        canceledSize: { type: string }
        remainSize: { type: string }
        remainFunds: { type: string }
        status:
          type: string
          enum: [new, open, match, done]
        type:
          type: string
          description: Event type.
          enum: [received, open, match, update, filled, canceled]
        orderTime:
          type: integer
          description: Order creation timestamp (milliseconds).
        ts:
          type: integer
          description: Event timestamp (nanoseconds).
        matchPrice: { type: string }
        matchSize: { type: string }
        tradeId: { type: string }
        liquidity:
          type: string
          enum: [taker, maker]
        feeType: { type: string }
        oldSize: { type: string }

    AccountBalanceEnvelope:
      allOf:
        - $ref: '#/components/schemas/PrivateEnvelopeBase'
        - type: object
          properties:
            topic:
              type: string
              example: /account/balance
            subject:
              type: string
              example: account.balance
            data:
              type: object
              properties:
                accountId: { type: string }
                currency: { type: string }
                total: { type: string }
                available: { type: string }
                hold: { type: string }
                availableChange: { type: string }
                holdChange: { type: string }
                relationContext:
                  type: object
                  properties:
                    symbol: { type: string }
                    orderId: { type: string }
                    tradeId: { type: string }
                relationEvent:
                  type: string
                  description: Event type (deposit, transfer, hold, etc.).
                relationEventId: { type: string }
                time:
                  type: string
                  description: Event timestamp (milliseconds).

    CrossMarginPositionEnvelope:
      allOf:
        - $ref: '#/components/schemas/PrivateEnvelopeBase'
        - type: object
          properties:
            topic:
              type: string
              example: /margin/position
            subject:
              type: string
              description: Either `debt.ratio` or `position.status`.
              enum: [debt.ratio, position.status]
            data:
              oneOf:
                - $ref: '#/components/schemas/MarginDebtRatioData'
                - $ref: '#/components/schemas/MarginPositionStatusData'

    MarginDebtRatioData:
      type: object
      properties:
        debtRatio: { type: number }
        totalAsset: { type: string }
        marginCoefficientTotalAsset: { type: string }
        totalDebt: { type: string }
        assetList:
          type: object
          additionalProperties:
            type: object
            properties:
              total: { type: string }
              available: { type: string }
              hold: { type: string }
        debtList:
          type: object
          additionalProperties:
            type: string
        timestamp: { type: integer }

    MarginPositionStatusData:
      type: object
      properties:
        type:
          type: string
          description: Position status (e.g. `FROZEN_FL`).
        timestamp: { type: integer }

    IsolatedMarginPositionEnvelope:
      allOf:
        - $ref: '#/components/schemas/PrivateEnvelopeBase'
        - type: object
          properties:
            topic:
              type: string
              example: /margin/isolatedPosition:BTC-USDT
            subject:
              type: string
              example: positionChange
            data:
              type: object
              properties:
                tag:
                  type: string
                  description: Isolated trading pair symbol.
                status:
                  type: string
                  description: Position status (e.g. `DEBT`).
                statusBizType:
                  type: string
                accumulatedPrincipal: { type: string }
                changeAssets:
                  type: object
                  additionalProperties:
                    type: object
                    properties:
                      total: { type: string }
                      hold: { type: string }
                      liabilityPrincipal: { type: string }
                      liabilityInterest: { type: string }
                timestamp: { type: integer }