asyncapi: 3.0.0
info:
  title: Blink WebSocket API
  version: 0.1.0
  description: >
    Real-time market and account streams served by the Blink indexer. Values are
    integers (prices in ticks, sizes in base lots, balances in atoms), serialized
    as decimal strings where they can exceed 2^53. All data is on-chain and
    public; no authentication is required.

servers:
  indexer:
    host: 'api.blink.trade'
    protocol: wss
    description: Blink WebSocket API.

channels:
  client:
    address: '/'
    description: Client control messages.
    messages:
      subscribe: { $ref: '#/components/messages/Subscribe' }
      unsubscribe: { $ref: '#/components/messages/Unsubscribe' }
      ping: { $ref: '#/components/messages/Ping' }
  orderbook:
    address: orderbook
    messages:
      update: { $ref: '#/components/messages/OrderbookMessage' }
  trades:
    address: trades
    messages:
      update: { $ref: '#/components/messages/TradesMessage' }
  funding:
    address: funding
    messages:
      update: { $ref: '#/components/messages/FundingMessage' }
  mark_price:
    address: mark_price
    messages:
      update: { $ref: '#/components/messages/MarkPriceMessage' }
  candles:
    address: candles
    messages:
      update: { $ref: '#/components/messages/CandleMessage' }
  mark_price_candles:
    address: mark_price_candles
    messages:
      update: { $ref: '#/components/messages/CandleMessage' }
  market_stats:
    address: market_stats
    description: Per-market stats; subscribe without market_id for all markets.
    messages:
      update: { $ref: '#/components/messages/MarketStatsMessage' }
  all_mids:
    address: all_mids
    messages:
      update: { $ref: '#/components/messages/AllMidsMessage' }
  bbo:
    address: bbo
    description: Best bid/offer; sent only when the top of book changes.
    messages:
      update: { $ref: '#/components/messages/BboMessage' }
  vaults:
    address: vaults
    description: Vault operations and share totals; subscribe without vault_id for all vaults.
    messages:
      update: { $ref: '#/components/messages/VaultsMessage' }
  blp:
    address: blp
    description: BLP operations, status, and share totals.
    messages:
      update: { $ref: '#/components/messages/BlpMessage' }
  account:
    address: account
    description: >
      Account-scoped streams (orders, fills, positions, balances, liquidations,
      funding payments, subaccount snapshots).
    messages:
      update: { $ref: '#/components/messages/AccountMessage' }

operations:
  sendControl:
    action: send
    channel: { $ref: '#/channels/client' }
    summary: Subscribe, unsubscribe, or ping.
  receiveOrderbook:
    action: receive
    channel: { $ref: '#/channels/orderbook' }
  receiveTrades:
    action: receive
    channel: { $ref: '#/channels/trades' }
  receiveFunding:
    action: receive
    channel: { $ref: '#/channels/funding' }
  receiveMarkPrice:
    action: receive
    channel: { $ref: '#/channels/mark_price' }
  receiveCandles:
    action: receive
    channel: { $ref: '#/channels/candles' }
  receiveMarkPriceCandles:
    action: receive
    channel: { $ref: '#/channels/mark_price_candles' }
  receiveMarketStats:
    action: receive
    channel: { $ref: '#/channels/market_stats' }
  receiveAllMids:
    action: receive
    channel: { $ref: '#/channels/all_mids' }
  receiveBbo:
    action: receive
    channel: { $ref: '#/channels/bbo' }
  receiveVaults:
    action: receive
    channel: { $ref: '#/channels/vaults' }
  receiveBlp:
    action: receive
    channel: { $ref: '#/channels/blp' }
  receiveAccount:
    action: receive
    channel: { $ref: '#/channels/account' }

components:
  messages:
    Subscribe:
      name: subscribe
      payload:
        type: object
        required: [method, subscription]
        properties:
          method: { const: subscribe }
          subscription: { $ref: '#/components/schemas/Subscription' }
    Unsubscribe:
      name: unsubscribe
      payload:
        type: object
        required: [method, subscription_id]
        properties:
          method: { const: unsubscribe }
          subscription_id: { type: string }
    Ping:
      name: ping
      payload:
        type: object
        required: [method, timestamp]
        properties:
          method: { const: ping }
          timestamp: { type: integer, format: int64 }
    OrderbookMessage:
      name: orderbook
      payload: { $ref: '#/components/schemas/OrderbookUpdate' }
    TradesMessage:
      name: trades
      payload: { $ref: '#/components/schemas/TradesUpdate' }
    FundingMessage:
      name: funding
      payload: { $ref: '#/components/schemas/FundingUpdate' }
    MarkPriceMessage:
      name: mark_price
      payload: { $ref: '#/components/schemas/MarkPriceUpdate' }
    CandleMessage:
      name: candles
      payload: { $ref: '#/components/schemas/CandleUpdate' }
    MarketStatsMessage:
      name: market_stats
      payload: { $ref: '#/components/schemas/MarketStatsUpdate' }
    AllMidsMessage:
      name: all_mids
      payload: { $ref: '#/components/schemas/AllMidsUpdate' }
    BboMessage:
      name: bbo
      payload: { $ref: '#/components/schemas/BboUpdate' }
    VaultsMessage:
      name: vaults
      payload: { $ref: '#/components/schemas/VaultsUpdate' }
    BlpMessage:
      name: blp
      payload: { $ref: '#/components/schemas/BlpUpdate' }
    AccountMessage:
      name: account
      payload: { $ref: '#/components/schemas/AccountUpdate' }

  schemas:
    IntStr:
      type: string
      pattern: '^-?[0-9]+$'
      description: Integer as a decimal string.
    Level:
      type: array
      description: '[price_ticks, size_base_lots]'
      items: { $ref: '#/components/schemas/IntStr' }
      minItems: 2
      maxItems: 2
    Subscription:
      type: object
      required: [channel]
      properties:
        channel:
          type: string
          enum: [orderbook, trades, funding, mark_price, candles, mark_price_candles, market_stats, all_mids, bbo, vaults, blp, orders, fills, positions, balances, liquidations, funding_payments, subaccount]
        market_id: { type: integer, description: Omit for all_mids; optional for market_stats (omit = all markets). }
        is_perps: { type: boolean }
        snapshot: { type: boolean }
        interval:
          type: string
          enum: ['1m', '5m', '15m', '1h', '4h', '1d']
          description: Candle channels only.
        vault_id: { type: integer, description: 'vaults channel; omit for all vaults.' }
        address: { type: string }
        subaccount_id: { type: integer }
    OrderbookUpdate:
      type: object
      required: [type, market_id, bids, asks, sequence, timestamp_ms]
      properties:
        type: { const: orderbook }
        market_id: { type: integer }
        is_perps: { type: boolean }
        snapshot: { type: boolean }
        bids: { type: array, items: { $ref: '#/components/schemas/Level' } }
        asks: { type: array, items: { $ref: '#/components/schemas/Level' } }
        sequence: { $ref: '#/components/schemas/IntStr' }
        timestamp_ms: { type: integer, format: int64 }
    TradesUpdate:
      type: object
      required: [type, market_id, trades]
      properties:
        type: { const: trades }
        market_id: { type: integer }
        is_perps: { type: boolean }
        trades:
          type: array
          items:
            type: object
            properties:
              price: { $ref: '#/components/schemas/IntStr' }
              quantity: { $ref: '#/components/schemas/IntStr' }
              taker_is_bid: { type: boolean }
              timestamp_ms: { type: integer, format: int64 }
              seq: { $ref: '#/components/schemas/IntStr' }
    FundingUpdate:
      type: object
      required: [type, market_id, funding_rate, timestamp_ms]
      properties:
        type: { const: funding }
        market_id: { type: integer }
        funding_rate: { type: integer, description: 100ths of a bp. }
        cumulative_funding_index_long: { $ref: '#/components/schemas/IntStr' }
        cumulative_funding_index_short: { $ref: '#/components/schemas/IntStr' }
        timestamp_ms: { type: integer, format: int64 }
    MarkPriceUpdate:
      type: object
      required: [type, market_id, mark_price, timestamp_ms]
      properties:
        type: { const: mark_price }
        market_id: { type: integer }
        mark_price: { $ref: '#/components/schemas/IntStr' }
        oracle_price: { $ref: '#/components/schemas/IntStr' }
        timestamp_ms: { type: integer, format: int64 }
    CandleUpdate:
      type: object
      description: One message per candle update; `candle` mirrors the REST candle resource.
      required: [type, market_id, interval, candle]
      properties:
        type: { type: string, enum: [candles, mark_price_candles] }
        market_id: { type: integer }
        interval: { type: string, enum: ['1m', '5m', '15m', '1h', '4h', '1d'] }
        candle:
          type: object
          properties:
            open_time_ms: { type: integer, format: int64 }
            open: { $ref: '#/components/schemas/IntStr' }
            high: { $ref: '#/components/schemas/IntStr' }
            low: { $ref: '#/components/schemas/IntStr' }
            close: { $ref: '#/components/schemas/IntStr' }
            volume_base: { $ref: '#/components/schemas/IntStr' }
            volume_quote: { $ref: '#/components/schemas/IntStr' }
            trades: { type: integer }
    MarketStatsUpdate:
      type: object
      description: Mirrors the REST market-stats resource.
      required: [type, market_id, stats, timestamp_ms]
      properties:
        type: { const: market_stats }
        market_id: { type: integer }
        stats: { type: object, description: The REST MarketStats shape. }
        timestamp_ms: { type: integer, format: int64 }
    AllMidsUpdate:
      type: object
      description: Mark price per market, keyed by market id.
      required: [type, mids, timestamp_ms]
      properties:
        type: { const: all_mids }
        mids:
          type: object
          additionalProperties: { $ref: '#/components/schemas/IntStr' }
          description: market_id (as a string key) to mark price in ticks.
        timestamp_ms: { type: integer, format: int64 }
    BboUpdate:
      type: object
      description: Sent only when the top of book changes.
      required: [type, market_id, timestamp_ms]
      properties:
        type: { const: bbo }
        market_id: { type: integer }
        is_perps: { type: boolean }
        bid: { $ref: '#/components/schemas/Level' }
        ask: { $ref: '#/components/schemas/Level' }
        sequence: { $ref: '#/components/schemas/IntStr' }
        timestamp_ms: { type: integer, format: int64 }
    VaultOperation:
      type: object
      required: [kind, address, amount, shares]
      properties:
        kind: { type: string, enum: [deposit, withdrawal] }
        address: { type: string }
        amount: { allOf: [{ $ref: '#/components/schemas/IntStr' }], description: Token atoms. }
        shares: { allOf: [{ $ref: '#/components/schemas/IntStr' }], description: Shares minted or burned. }
    VaultsUpdate:
      type: object
      description: One message per vault operation, with updated share totals.
      required: [type, vault_id, operation, total_shares, timestamp_ms]
      properties:
        type: { const: vaults }
        vault_id: { type: integer }
        operation: { $ref: '#/components/schemas/VaultOperation' }
        total_shares: { $ref: '#/components/schemas/IntStr' }
        timestamp_ms: { type: integer, format: int64 }
    BlpUpdate:
      type: object
      description: One message per BLP operation or status change, with updated share totals.
      required: [type, timestamp_ms]
      properties:
        type: { const: blp }
        operation: { $ref: '#/components/schemas/VaultOperation' }
        status: { type: string, description: Present on status changes. }
        total_shares: { $ref: '#/components/schemas/IntStr' }
        timestamp_ms: { type: integer, format: int64 }
    AccountUpdate:
      type: object
      description: >
        Account-scoped update. `type` names the channel; the matching field
        carries the REST resource shape.
      required: [type, address, subaccount_id]
      properties:
        type: { type: string, enum: [orders, fills, positions, balances, liquidations, funding_payments, subaccount] }
        address: { type: string }
        subaccount_id: { type: integer }
        order: { type: object }
        fill: { type: object }
        position: { type: object }
        balance: { type: object }
        liquidation: { type: object }
        funding_payment: { type: object }
        subaccount:
          type: object
          description: >
            Indexed subaccount snapshot (cross_margin_balance,
            total_isolated_margin, status). Computed values such as equity and
            margin requirements are served by REST.
    ErrorFrame:
      type: object
      description: >
        Protocol error. `code` is a stable machine-readable identifier;
        `error` is human-readable detail and may be reworded.
        INVALID_MESSAGE / UNKNOWN_CHANNEL / INVALID_SUBSCRIPTION leave the
        connection open; CONNECTION_LAGGED and FEED_UNAVAILABLE precede a
        server-initiated close (reconnect and re-subscribe — deltas are not
        replayed).
      required: [type, code, error]
      properties:
        type: { const: error }
        code:
          type: string
          enum:
            - INVALID_MESSAGE
            - UNKNOWN_CHANNEL
            - INVALID_SUBSCRIPTION
            - CONNECTION_LAGGED
            - FEED_UNAVAILABLE
        error: { type: string }
