# =============================================================================
# Conyr API — OpenAPI 3.1 spec
#
# This spec covers the full live /v1 surface. Every implemented REST route is
# documented below with its access tier (see the x-tier extension on each
# operation, and the per-tier security model in the description). The only
# planned operations are intentionally absent so generated agent clients only
# contain callable production routes.
#
# Removed:
#   getWalletTaxonomy → use getWalletLabels
#   getWalletBundles  → use getTokenBundles + client-side filter
#   getWalletSecurity → use WS wallet:{addr}:security
# =============================================================================

openapi: 3.1.0
info:
  title: CONYR API
  version: 1.4.0
  description: |
    The Solana intelligence API built for autonomous agents. The live surface exposes
    39 /v1 operations for position-correct wallet skill, FIFO positions and realized PnL,
    funding provenance, 30-dimensional behavioral similarity, bundle state, actor-level
    coordination, holder concentration, liquidity exit capacity, and organic OHLCV.

    This spec documents the full live /v1 surface. Each operation carries an `x-tier`
    extension declaring its minimum access tier (Free, Layer 1, Layer 2, or Layer 3);
    requesting an operation above your key's tier returns 403. Planned operations
    remain out of this contract so generated clients only expose callable routes.

    Thirteen live WebSocket channel families are available at wss://api.conyr.ai/ws.
    They cover trade ticks, candles, wallet activity, security, coordination, raw BundleV2
    state, and compact dump alerts. REST-only analytical reads are identified separately.

    The /ws endpoint requires a Bearer key; the Free tier is rejected at upgrade (HTTP 403).
    Per-key concurrent connection caps apply (Free 0 / Layer 1 5 / Layer 2 20 / Layer 3 50 /
    Enterprise unlimited), with 50 subscriptions per connection on paid tiers; exceeding the
    connection cap closes the socket with WS close code 1008. WebSocket channels are themselves
    tier-gated — subscribing to a channel above your key's tier returns a non-fatal op:error
    while the connection and other channels in the batch stay live:
      Layer 1: token:{mint}:trades, token:{mint}:ticks, token:{mint}:ohlcv:{view}:{tf}
      Layer 2: wallet:{addr}:pnl, wallet:{addr}:positions, wallet:{addr}:labels
      Layer 3: token:{mint}:security, token:{mint}:campaign,
               token:{mint}:bundles, token:{mint}:dump_alert,
               wallet:{addr}:security, wallet:{addr}:follow, wallet:{addr}:entity
    See WsChannelFormat schema for details.
  contact:
    name: CONYR
    url: https://conyr.ai
servers:
  - url: https://api.conyr.ai
    description: Production
security:
  - BearerAuth: []

tags:
  - name: Operations
    description: System liveness, readiness, and metrics (no auth)
  - name: Wallets
    description: Wallet performance, trades, positions, labels, quality, profile, similarity, provenance
  - name: Tokens
    description: Token info, OHLCV, trades, liquidity, holders, bundles, and security signals
  - name: Leaderboard
    description: Ranked wallet leaderboard
  - name: Coordination
    description: coordination-v2 follows, entities, campaigns, crowd state, and health
  - name: Intel
    description: Cross-cutting coordination intelligence composites

paths:
  /health:
    get:
      operationId: getHealth
      summary: Liveness check
      tags: [Operations]
      security: []
      responses:
        "200":
          description: Service is alive
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum: [ok]

  /ready:
    get:
      operationId: getReadiness
      summary: Readiness check
      tags: [Operations]
      security: []
      responses:
        "200":
          description: Service ready
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReadinessResponse"
        "503":
          description: Service not ready
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReadinessResponse"

  /metrics:
    get:
      operationId: getMetrics
      summary: Prometheus metrics
      tags: [Operations]
      security: []
      responses:
        "200":
          description: Prometheus text format
          content:
            text/plain:
              schema:
                type: string

  /v1/wallet/{address}/performance:
    get:
      operationId: getWalletPerformance
      summary: Wallet trading performance summary
      tags: [Wallets]
      x-tier: Layer 2
      parameters:
        - $ref: "#/components/parameters/WalletAddress"
        - name: window
          in: query
          description: Time window for performance calculation
          schema:
            type: string
            enum: [1d, 7d, 30d]
            default: "7d"
      responses:
        "200":
          description: Performance summary
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WalletPerformance"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/wallet/{address}/trades:
    get:
      operationId: getWalletTrades
      summary: Wallet trade history (realized PnL with FIFO cost-basis)
      description: |
        Returns paginated realized-PnL history including FIFO cost-basis,
        entry/exit signatures and prices, hold time, position/exit type, and DEXes.
        The wallet:{address}:pnl stream carries a richer live event payload.
      tags: [Wallets]
      x-tier: Layer 2
      parameters:
        - $ref: "#/components/parameters/WalletAddress"
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
        - name: offset
          in: query
          schema:
            type: integer
            minimum: 0
            default: 0
        - name: token
          in: query
          description: Filter trades by token mint address
          schema:
            type: string
      responses:
        "200":
          description: Paginated trade list
          content:
            application/json:
              schema:
                type: object
                properties:
                  trades:
                    type: array
                    items:
                      $ref: "#/components/schemas/WalletTrade"
                  limit:
                    type: integer
                  offset:
                    type: integer
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/wallet/{address}/positions:
    get:
      operationId: getWalletPositions
      summary: Wallet token positions (lot-level FIFO)
      description: |
        Returns wallet positions tracked with lot-level FIFO accounting.
        Supports fragment tracking for partial fills and DCA entries.
        Live stream: wallet:{address}:positions fires on open/update/close.
      tags: [Wallets]
      x-tier: Layer 2
      parameters:
        - $ref: "#/components/parameters/WalletAddress"
        - name: status
          in: query
          description: Filter by position status
          schema:
            type: string
            enum: [open, closed, all]
            default: all
      responses:
        "200":
          description: Position list
          content:
            application/json:
              schema:
                type: object
                properties:
                  positions:
                    type: array
                    items:
                      $ref: "#/components/schemas/WalletPosition"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/wallet/{address}/labels:
    get:
      operationId: getWalletLabels
      summary: Focused wallet classification, automation, tool use, and badges
      description: |
        Returns the focused classification contract: trader type, performance
        level, size class, exit style, automation and tool-use labels, fresh-funding
        flags, badges, description, and label hash. Use /quality for quantitative
        performance evidence such as Sharpe, profit factor, disposition, and ROI
        percentiles.
      tags: [Wallets]
      x-tier: Layer 2
      parameters:
        - $ref: "#/components/parameters/WalletAddress"
      responses:
        "200":
          description: Wallet labels (null if not yet classified)
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/WalletLabel"
                  - type: "null"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/leaderboard:
    get:
      operationId: getLeaderboard
      summary: Ranked wallet leaderboard
      tags: [Leaderboard]
      x-tier: Layer 2
      parameters:
        - name: window
          in: query
          description: Time window
          schema:
            type: string
            enum: [1d, 7d, 30d]
            default: "7d"
        - name: sort
          in: query
          description: Sort metric
          schema:
            type: string
            enum: [pnl_usd, roi, win_rate]
            default: pnl_usd
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 100
        - name: offset
          in: query
          schema:
            type: integer
            minimum: 0
            default: 0
        - name: include_hft
          in: query
          description: >-
            Include HFT/MEV bots (wallets with a median hold time under 60s).
            Defaults to false — they are excluded so the board reflects real
            traders rather than tick-scalpers. Malicious wallets are always
            excluded.
          schema:
            type: boolean
            default: false
      responses:
        "200":
          description: Leaderboard results
          content:
            application/json:
              schema:
                type: object
                properties:
                  leaderboard:
                    type: array
                    items:
                      $ref: "#/components/schemas/LeaderboardEntry"
                  window:
                    type: string
                  sort:
                    type: string
                  limit:
                    type: integer
                  offset:
                    type: integer
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/token/{mint}/info:
    get:
      operationId: getTokenInfo
      summary: Token validation status
      description: |
        Returns token validation state and any validation reason currently attached
        to the token.
      tags: [Tokens]
      x-tier: Layer 1
      parameters:
        - $ref: "#/components/parameters/TokenMint"
      responses:
        "200":
          description: Token validation status
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TokenInfo"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/token/{mint}/ohlcv:
    get:
      operationId: getTokenOhlcv
      summary: OHLCV candlestick data (full + filtered views)
      description: |
        Returns OHLCV candles with buy/sell volume split, VWAP, trade count,
        and approximate unique buyer/seller counts. The "filtered" view removes
        bot and wash trades for organic price discovery.
      tags: [Tokens]
      x-tier: Layer 1
      parameters:
        - $ref: "#/components/parameters/TokenMint"
        - name: timeframe
          in: query
          description: Candle interval
          schema:
            type: string
            enum: [1s, 1m, 5m, 15m, 1h]
            default: "1m"
        - name: view
          in: query
          description: Full includes all trades; filtered removes bot/wash activity
          schema:
            type: string
            enum: [full, filtered]
            default: full
        - name: from
          in: query
          description: Start time (unix milliseconds)
          schema:
            type: integer
            format: int64
        - name: to
          in: query
          description: End time (unix milliseconds)
          schema:
            type: integer
            format: int64
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 2000
            default: 500
      responses:
        "200":
          description: OHLCV candles
          content:
            application/json:
              schema:
                type: object
                properties:
                  candles:
                    type: array
                    items:
                      $ref: "#/components/schemas/OhlcvCandle"
                  token_mint:
                    type: string
                  timeframe:
                    type: string
                  view:
                    type: string
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/token/{mint}/trades:
    get:
      operationId: getTokenTrades
      summary: Recent trades for a token
      tags: [Tokens]
      x-tier: Layer 1
      parameters:
        - $ref: "#/components/parameters/TokenMint"
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
      responses:
        "200":
          description: Trade list
          content:
            application/json:
              schema:
                type: object
                properties:
                  trades:
                    type: array
                    items:
                      $ref: "#/components/schemas/TokenTrade"
                  token_mint:
                    type: string
                  limit:
                    type: integer
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/token/{mint}/liquidity:
    get:
      operationId: getTokenLiquidity
      summary: Current pool depth, LP state, dump impact, and exit capacity
      description: |
        Returns the latest supported reserve snapshot, LP burn/lock verdict,
        coordinated-dump impact, and the SOL sell capacity before 10%, 25%,
        and 50% price impact. Depth is null when it is missing, stale, or unusable;
        USD capacity is null because this route does not invent a SOL/USD quote.
      tags: [Tokens]
      x-tier: Layer 1
      parameters:
        - $ref: "#/components/parameters/TokenMint"
      responses:
        "200":
          description: Liquidity intelligence snapshot
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LiquidityResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/token/{mint}/liquidity/events:
    get:
      operationId: getTokenLiquidityEvents
      summary: Latest LP add/remove events with bundle and flag attribution
      description: |
        Returns the newest 100 liquidity events for the token. Each event reports
        pool, DEX, wallet, side, vault deltas, share of pool moved when known,
        full-close status, bundle membership, and malicious-wallet attribution.
      tags: [Tokens]
      x-tier: Layer 3
      parameters:
        - $ref: "#/components/parameters/TokenMint"
      responses:
        "200":
          description: Newest-first liquidity event tape
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LiquidityEventsResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/token/{mint}/holders/summary:
    get:
      operationId: getTokenHoldersSummary
      summary: Holder distinct count, concentration, and hydration status
      description: |
        Distinct holder count, total non-zero balance, top-N concentration
        (top_10_pct / top_50_pct / top_100_pct), and hydration lifecycle status.
        The "Top 10 hold X%" flagship signal.
      tags: [Tokens]
      x-tier: Layer 1
      parameters:
        - $ref: "#/components/parameters/TokenMint"
      responses:
        "200":
          description: Holder summary
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HoldersSummary"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/token/{mint}/holders/top:
    get:
      operationId: getTokenHoldersTop
      summary: Ordered top-N holders
      description: |
        Summary fields plus the ordered top-N holders with per-holder
        percentage of total balance.
      tags: [Tokens]
      x-tier: Layer 1
      parameters:
        - $ref: "#/components/parameters/TokenMint"
        - name: n
          in: query
          description: Number of holders to return (default 10, max 500)
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 10
      responses:
        "200":
          description: Top holders
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HoldersTop"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/token/{mint}/holders:
    get:
      operationId: getTokenHolders
      summary: Paginated full holder list
      description: |
        Top fields plus pagination (limit, offset, next_offset) over the full
        holder list ordered by balance descending.
      tags: [Tokens]
      x-tier: Layer 1
      parameters:
        - $ref: "#/components/parameters/TokenMint"
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 1000
            default: 100
        - name: offset
          in: query
          schema:
            type: integer
            minimum: 0
            default: 0
      responses:
        "200":
          description: Holder list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HoldersList"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/token/{mint}/buyer-quality:
    get:
      operationId: getBuyerQuality
      summary: Aggregate buyer-quality signal for a token
      description: |
        Aggregate quality of the wallets currently buying a token over the
        given lookback window, derived from buyer skill labels and outcomes.
      tags: [Tokens]
      x-tier: Free
      parameters:
        - $ref: "#/components/parameters/TokenMint"
        - name: window_hours
          in: query
          description: Lookback window in hours
          schema:
            type: integer
            minimum: 1
            maximum: 24
            default: 1
      responses:
        "200":
          description: Buyer-quality summary
          content:
            application/json:
              schema:
                type: object
                description: Aggregate buyer-quality metrics for the token.
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/token/{mint}/bundles/summary:
    get:
      operationId: getTokenBundleSummary
      summary: Bundle summary for a token
      description: |
        Counts and aggregate supply/risk of coordinated bundles detected on a
        token, without the per-bundle detail.
      tags: [Tokens]
      x-tier: Free
      parameters:
        - $ref: "#/components/parameters/TokenMint"
      responses:
        "200":
          description: Bundle summary
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BundleSummary"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/token/{mint}/bundles:
    get:
      operationId: getTokenBundles
      summary: Detected coordinated bundles for a token
      description: |
        Full list of coordinated bundles (BundleV2) detected on a token, with
        membership, status, supply held, and evidence.
      tags: [Tokens]
      x-tier: Layer 3
      parameters:
        - $ref: "#/components/parameters/TokenMint"
      responses:
        "200":
          description: Bundle list
          content:
            application/json:
              schema:
                type: object
                properties:
                  bundles:
                    type: array
                    items:
                      $ref: "#/components/schemas/Bundle"
                  token_mint:
                    type: string
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/token/{mint}/bundles/{bundle_id}:
    get:
      operationId: getBundleDetail
      summary: Detail for a single bundle
      tags: [Tokens]
      x-tier: Layer 3
      parameters:
        - $ref: "#/components/parameters/TokenMint"
        - name: bundle_id
          in: path
          required: true
          description: Bundle identifier
          schema:
            type: string
      responses:
        "200":
          description: Bundle detail
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BundleFullResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/token/{mint}/wash-trades:
    get:
      operationId: getWashTrades
      summary: Detected wash-trading activity for a token
      tags: [Tokens]
      x-tier: Layer 3
      parameters:
        - $ref: "#/components/parameters/TokenMint"
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
      responses:
        "200":
          description: Wash-trade signals
          content:
            application/json:
              schema:
                type: object
                description: Detected wash-trading clusters and offending wallets.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/token/{mint}/funding-abuse:
    get:
      operationId: getFundingAbuse
      summary: Funding-abuse detection for early buyers of a token
      description: |
        Early buyers funded from shared/farm sources within the early window,
        surfacing coordinated funding abuse around launch.
      tags: [Tokens]
      x-tier: Layer 3
      parameters:
        - $ref: "#/components/parameters/TokenMint"
        - name: early_window_minutes
          in: query
          description: Early-buy window in minutes
          schema:
            type: integer
            minimum: 10
            maximum: 240
            default: 60
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
      responses:
        "200":
          description: Funding-abuse signals
          content:
            application/json:
              schema:
                type: object
                description: Early buyers with shared/farm funding provenance.
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/token/{mint}/suspicious-activity:
    get:
      operationId: getSuspiciousActivity
      summary: Aggregate suspicious-activity signals for a token
      tags: [Tokens]
      x-tier: Layer 3
      parameters:
        - $ref: "#/components/parameters/TokenMint"
      responses:
        "200":
          description: Suspicious-activity summary
          content:
            application/json:
              schema:
                type: object
                description: Composite suspicious-activity flags and evidence for the token.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/token/{mint}/campaigns:
    get:
      operationId: getTokenCampaigns
      summary: Coordinated campaigns on a token
      tags: [Coordination]
      x-tier: Layer 3
      parameters:
        - $ref: "#/components/parameters/TokenMint"
      responses:
        "200":
          description: Token campaigns
          content:
            application/json:
              schema:
                type: object
                description: Coordinated campaigns (coordination-v2) active on the token.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/token/{mint}/crowd:
    get:
      operationId: getTokenCrowd
      summary: Per-minute crowd state for a token
      tags: [Coordination]
      x-tier: Layer 3
      parameters:
        - $ref: "#/components/parameters/TokenMint"
      responses:
        "200":
          description: Crowd state
          content:
            application/json:
              schema:
                type: object
                description: Coordination-v2 crowd-state snapshot for the token.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/wallet/{address}/quality:
    get:
      operationId: getWalletQuality
      summary: Wallet quality — position-correct skill signal
      description: |
        Composite skill score and the position-correct vs commodity win-rate
        delta. `rated` is true once the wallet has at least 10 closed positions.
        Returns the object below, or literal null when no snapshot row exists.
      tags: [Wallets]
      x-tier: Layer 2
      parameters:
        - $ref: "#/components/parameters/WalletAddress"
      responses:
        "200":
          description: Wallet quality (null if no snapshot row)
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/WalletQuality"
                  - type: "null"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/wallet/{address}/profile:
    get:
      operationId: getWalletProfile
      summary: 30-dimensional behavioral profile for a wallet
      description: |
        Behavioral profile derived from the wallet's 30-dim embedding —
        hold timing, exit discipline, performance, velocity/sizing, and
        infrastructure/provenance axes.
      tags: [Wallets]
      x-tier: Layer 2
      parameters:
        - $ref: "#/components/parameters/WalletAddress"
      responses:
        "200":
          description: Wallet behavioral profile
          content:
            application/json:
              schema:
                type: object
                description: 30-dim behavioral profile and derived axes for the wallet.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/wallet/{address}/similar:
    get:
      operationId: getWalletSimilar
      summary: Wallets behaviorally similar to a given wallet
      description: |
        k-nearest-neighbour lookup over the 30-dim behavioral vector space,
        optionally filtered by minimum trade count and performance level.
      tags: [Wallets]
      x-tier: Layer 2
      parameters:
        - $ref: "#/components/parameters/WalletAddress"
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 20
        - name: min_trades
          in: query
          description: Only return wallets with at least this many trades
          schema:
            type: integer
        - name: performance_level
          in: query
          description: Filter similar wallets by performance level
          schema:
            type: string
            enum: [EARNER, BREAK_EVEN, UNPROFITABLE, UNRATED, DORMANT]
      responses:
        "200":
          description: Similar wallets
          content:
            application/json:
              schema:
                type: object
                description: Behaviorally similar wallets with cosine similarity scores.
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/wallet/{address}/provenance:
    get:
      operationId: getWalletProvenance
      summary: Funding provenance for a wallet
      description: |
        Funding provenance graph for a wallet — root funding source, hop
        distance, and farm/CEX classification (e.g. ROOT_CEX, FUNDED_BY_FARM).
      tags: [Wallets]
      x-tier: Layer 2
      parameters:
        - $ref: "#/components/parameters/WalletAddress"
      responses:
        "200":
          description: Wallet provenance
          content:
            application/json:
              schema:
                type: object
                description: Funding root, hop count, and provenance classification.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/wallet/{address}/entanglement:
    get:
      operationId: getWalletEntanglement
      summary: Coordination entanglement for a wallet
      description: |
        Wallets entangled with the given wallet through shared funding or
        shared execution, with the strength of each link.
      tags: [Wallets]
      x-tier: Layer 3
      parameters:
        - $ref: "#/components/parameters/WalletAddress"
      responses:
        "200":
          description: Wallet entanglement
          content:
            application/json:
              schema:
                type: object
                description: Entangled wallets and the basis (shared funder/execution) of each link.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/wallet/{address}/follows:
    get:
      operationId: getWalletFollows
      summary: Follow edges for a wallet
      description: |
        coordination-v2 follow edges where the wallet is a leader or follower,
        with TF-IDF support and confidence.
      tags: [Coordination]
      x-tier: Layer 3
      parameters:
        - $ref: "#/components/parameters/WalletAddress"
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
      responses:
        "200":
          description: Follow edges
          content:
            application/json:
              schema:
                type: object
                description: Follow edges involving the wallet, with confidence and support.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/wallet/{address}/entity:
    get:
      operationId: getWalletEntity
      summary: Entity membership for a wallet
      description: |
        The coordination-v2 entity (shared-funder / shared-execution cluster)
        the wallet belongs to, if any.
      tags: [Coordination]
      x-tier: Layer 3
      parameters:
        - $ref: "#/components/parameters/WalletAddress"
      responses:
        "200":
          description: Entity membership
          content:
            application/json:
              schema:
                type: object
                description: Entity the wallet belongs to (coordination-v2), or null.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/entity/{entity_id}:
    get:
      operationId: getEntityDetail
      summary: Detail for a coordination entity
      tags: [Coordination]
      x-tier: Layer 3
      parameters:
        - name: entity_id
          in: path
          required: true
          description: Entity identifier
          schema:
            type: string
      responses:
        "200":
          description: Entity detail
          content:
            application/json:
              schema:
                type: object
                description: Coordination-v2 entity detail (kind, root wallet, member count, confidence).
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/entity/{entity_id}/members:
    get:
      operationId: getEntityMembers
      summary: Members of a coordination entity
      tags: [Coordination]
      x-tier: Layer 3
      parameters:
        - name: entity_id
          in: path
          required: true
          description: Entity identifier
          schema:
            type: string
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: page_size
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 100
      responses:
        "200":
          description: Entity members
          content:
            application/json:
              schema:
                type: object
                description: Paginated member wallets of the entity.
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/coordination/health:
    get:
      operationId: getCoordinationHealth
      summary: coordination-v2 engine health
      description: |
        Runtime health of the coordination-v2 engine (freshness, queue depth,
        and per-shard liveness).
      tags: [Coordination]
      x-tier: Layer 3
      responses:
        "200":
          description: Coordination health
          content:
            application/json:
              schema:
                type: object
                description: Coordination-v2 runtime health metrics.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/intel/coordinated-pushes/forming:
    get:
      operationId: getFormingCoordinatedPushes
      summary: Coordinated token pushes currently forming
      description: |
        Cross-cutting coord_v2 composite — tokens with an active coordinated
        push (regime shift + campaign + participant entities).
      tags: [Intel]
      x-tier: Layer 3
      responses:
        "200":
          description: Forming coordinated pushes
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/CoordinatedPushCandidate"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/intel/wallet/{leader}/cascade:
    get:
      operationId: getFollowerCascade
      summary: Follower cascade behind a leader on a token
      description: |
        Followers that bought the given token shortly after the leader, with
        confidence and lag in seconds.
      tags: [Intel]
      x-tier: Layer 3
      parameters:
        - name: leader
          in: path
          required: true
          description: Leader wallet address (base58)
          schema:
            type: string
        - name: mint
          in: query
          required: true
          description: Token mint to scope the cascade to (required)
          schema:
            type: string
        - name: window_s
          in: query
          description: Lookback window in seconds
          schema:
            type: integer
            minimum: 1
            maximum: 3600
            default: 300
      responses:
        "200":
          description: Follower cascade
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/CascadeFollower"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/intel/entities/emerging:
    get:
      operationId: getEmergingEntities
      summary: Recently emerged wallet entities
      tags: [Intel]
      x-tier: Layer 3
      parameters:
        - name: hours
          in: query
          description: Lookback window in hours
          schema:
            type: integer
            minimum: 1
            maximum: 168
            default: 24
      responses:
        "200":
          description: Emerging entities
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/EmergingEntity"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/intel/token/{mint}/early-buyers:
    get:
      operationId: getTokenEarlyBuyers
      summary: Coordinated entities present in the early-buy window
      tags: [Intel]
      x-tier: Layer 3
      parameters:
        - $ref: "#/components/parameters/TokenMint"
        - name: window_s
          in: query
          description: Early-buy window in seconds
          schema:
            type: integer
            minimum: 1
            maximum: 3600
            default: 300
      responses:
        "200":
          description: Early-buy clusters
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/EarlyBuyCluster"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/intel/token/{mint}/copy-originator:
    get:
      operationId: getTokenCopyOriginator
      summary: Candidate copy-trade originators within a time window
      description: |
        Candidate leader wallets whose buys were copied by followers in the
        given window. The window is bounded to a maximum of 60 minutes.
      tags: [Intel]
      x-tier: Layer 3
      parameters:
        - $ref: "#/components/parameters/TokenMint"
        - name: start_ts_ms
          in: query
          required: true
          description: Window start (unix milliseconds, required)
          schema:
            type: integer
            format: int64
        - name: end_ts_ms
          in: query
          required: true
          description: Window end (unix milliseconds, required; must be > start_ts_ms, max 60 minutes wide)
          schema:
            type: integer
            format: int64
      responses:
        "200":
          description: Originator candidates
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/OriginatorCandidate"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/intel/token/{mint}/entity-concentration:
    get:
      operationId: getTokenEntityConcentration
      summary: Entity-level volume concentration for a token
      tags: [Intel]
      x-tier: Layer 3
      parameters:
        - $ref: "#/components/parameters/TokenMint"
        - name: hours
          in: query
          description: Lookback window in hours
          schema:
            type: integer
            minimum: 1
            maximum: 168
            default: 24
      responses:
        "200":
          description: Entity concentration
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EntityConcentration"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /v1/intel/token/{mint}/late-retail:
    get:
      operationId: getTokenLateRetail
      summary: Late-retail entry behind elite exits
      description: |
        Late buyers entering after elite wallets have sold, with volume, average
        lag, and the share that ended up unprofitable.
      tags: [Intel]
      x-tier: Layer 3
      parameters:
        - $ref: "#/components/parameters/TokenMint"
        - name: hours
          in: query
          description: Lookback window in hours
          schema:
            type: integer
            minimum: 1
            maximum: 72
            default: 24
      responses:
        "200":
          description: Late-retail summary
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LateRetail"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: API key passed as Bearer token in Authorization header

  parameters:
    WalletAddress:
      name: address
      in: path
      required: true
      description: Solana wallet address (base58)
      schema:
        type: string
        example: "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU"

    TokenMint:
      name: mint
      in: path
      required: true
      description: Solana token mint address (base58)
      schema:
        type: string
        example: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"

  responses:
    BadRequest:
      description: Invalid request parameters
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"

    Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"

    Forbidden:
      description: API key tier is insufficient for this endpoint
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"

    InternalError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"

  schemas:
    Error:
      type: object
      required: [error, code]
      properties:
        error:
          type: string
        code:
          type: integer

    ReadinessResponse:
      type: object
      properties:
        status:
          type: string
          enum: [ready, not ready]

    WalletPerformance:
      type: object
      properties:
        wallet_address:
          type: string
        total_trades:
          type: integer
          format: uint64
        wins:
          type: integer
          format: uint64
        losses:
          type: integer
          format: uint64
        win_rate:
          type: number
          format: double
          description: Value between 0.0 and 1.0
        total_pnl_usd:
          type: number
          format: double
        total_volume_usd:
          type: number
          format: double
        total_fees_usd:
          type: number
          format: double
        avg_hold_time_seconds:
          type: number
          format: double
        cash_pnl_usd:
          type: number
          format: double
          description: Net cash flow (SOL in/out) in USD terms
        sharpe_ratio:
          type: number
          format: double
          description: Present but currently always 0.0 on this endpoint; use /quality for the populated metric.
        profit_factor:
          type: number
          format: double
          description: Present but currently always 0.0 on this endpoint; use /quality for the populated metric.
        disposition_index:
          type: number
          format: double
          description: Present but currently always 0.0 on this endpoint; use /quality for the populated metric.

    WalletTrade:
      type: object
      properties:
        wallet_address:
          type: string
        token_mint:
          type: string
        entry_signature:
          type: string
        exit_signature:
          type: string
        entry_timestamp:
          type: string
          format: date-time
        exit_timestamp:
          type: string
          format: date-time
        hold_time_seconds:
          type: integer
        entry_price_usd:
          type: number
          format: double
        exit_price_usd:
          type: number
          format: double
        realized_pnl_usd:
          type: number
          format: double
        realized_pnl_sol:
          type: number
          format: double
        percentage_gain:
          type: number
          format: double
        position_type:
          type: string
        exit_type:
          type: string
        exit_dex:
          type: string
        entry_dex:
          type: string

    WalletPosition:
      type: object
      properties:
        wallet_address:
          type: string
        token_mint:
          type: string
        entry_signature:
          type: string
        entry_timestamp:
          type: integer
          format: int64
        entry_price_usd:
          type: number
          format: double
        original_amount:
          type: number
          format: double
        remaining_amount:
          type: number
          format: double
        price_per_token:
          type: number
          format: double
        status:
          type: string
          enum: [OPEN, PARTIAL, CLOSED]
        exit_count:
          type: integer
        entry_dex:
          type: string

    WalletLabel:
      type: object
      properties:
        wallet:
          type: string
        trader_type:
          type: string
          enum: [SCALPER, DAY_TRADER, SWING_TRADER, POSITION_TRADER, MALICIOUS]
        performance_level:
          type: string
          enum: [BANNED, UNRATED, DORMANT, EARNER, UNPROFITABLE, BREAK_EVEN]
        size_class:
          type: string
          enum: [SHRIMP, FISH, SHARK, WHALE]
        exit_style:
          type: string
          enum: [FULL_EXITER, DCA_OUT, PARTIAL_TAKER, MIXED]
        automation_label:
          type: string
          description: "BOT:HFT, BOT:FLASH, BOT:ARB, BOT:WASH, BOT:MULTI, MIXED, ORGANIC, UNKNOWN"
        primary_terminal:
          type: string
          description: Primary trading tool (photon, bullx, axiom, etc.)
        primary_tool:
          type: string
        primary_tool_pct:
          type: number
          format: double
        is_fresh_6h:
          type: integer
        is_fresh_24h:
          type: integer
        funded_by_farm:
          type: integer
          description: "1 if funded by a known farm wallet, 0 otherwise"
        labels:
          type: array
          items:
            type: string
        trade_badges:
          type: array
          items:
            type: string
          description: "RUNNER_KEEPER, CONFIDENCE_SIZER, STOP_LOSS_USER, CONSISTENT_WINNER, BAG_HOLDER, LOSS_CUTTER, LOTTO_PLAYER, DIAMOND_HANDS, PAPER_HANDS, ELITE_TRADER, VETERAN_WINNER"
        identity_badges:
          type: array
          items:
            type: string
          description: "HFT_BOT, FLASH_BOT, ARB_BOT, WASH_TRADER, TOOL_*, FRESH_FUNDED_*, FUNDED_BY_FARM, DEX_SPECIALIST_*, ROOT_CEX, etc."
        special_labels:
          type: array
          items:
            type: string
        description:
          type: [string, "null"]
        label_hash:
          type: [string, "null"]

    BundleSummary:
      type: object
      properties:
        token_mint:
          type: string
        bundle_count:
          type: integer
        open_bundle_count:
          type: integer
        bundled_supply_pct:
          type: number
          format: double
          description: Fraction of total token supply held by surfaced bundles (0..1).
        bundled_value_usd:
          type: number
          format: double
        bundled_realized_pnl_usd:
          type: number
          format: double
        dominant_bundle_type:
          type: [string, "null"]

    Bundle:
      type: object
      properties:
        bundle_id:
          type: string
        token_mint:
          type: string
        bundle_type:
          type: string
        status:
          type: string
          enum: [active, dormant, dumping, derisked, exited, rugged, merged]
        leader_wallet:
          type: string
        member_count_total:
          type: integer
        member_count_active:
          type: integer
        chain_balance_ui:
          type: number
          format: double
          description: Current token balance held by all bundle members
        total_usd_invested:
          type: number
          format: double
        total_usd_returned:
          type: number
          format: double
        total_tokens_bought_ui:
          type: number
          format: double
        total_tokens_sold_ui:
          type: number
          format: double
        realized_pnl_usd:
          type: number
          format: double
        risk_score:
          type: number
          format: double
          description: 0.0 to 1.0
        evidence:
          type: array
          items:
            type: string
          description: Human-readable evidence strings
        sold_pct:
          type: number
          format: double
        created_at:
          type: string
        linked_entity:
          $ref: "#/components/schemas/BundleLinkedEntity"

    BundleLinkedEntity:
      type: object
      properties:
        entityId:
          type: string
        entityKind:
          type: string
        memberCount:
          type: integer
        bundleMemberShare:
          type: number
          format: double
          description: Fraction of bundle members sharing this entity.

    BundleMember:
      type: object
      properties:
        wallet_address:
          type: string
        bundle_id:
          type: string
        is_leader:
          type: boolean
        is_active:
          type: boolean
        chain_balance_ui:
          type: number
          format: double
        realized_pnl_usd:
          type: number
          format: double
        avg_entry_price_usd:
          type: number
          format: double

    BundleFullResponse:
      type: object
      properties:
        bundle:
          $ref: "#/components/schemas/Bundle"
        members:
          type: array
          items:
            $ref: "#/components/schemas/BundleMember"

    LeaderboardEntry:
      type: object
      properties:
        rank:
          type: integer
          format: uint64
        wallet_address:
          type: string
        total_trades:
          type: integer
          format: uint64
        wins:
          type: integer
          format: uint64
        losses:
          type: integer
          format: uint64
        win_rate:
          type: number
          format: double
        total_pnl_usd:
          type: number
          format: double
        total_volume_usd:
          type: number
          format: double
        roi:
          type: number
          format: double
          description: Return on investment as percentage
        cash_pnl_usd:
          type: number
          format: double

    TokenInfo:
      type: object
      properties:
        token_mint:
          type: string
        is_validated:
          type: boolean
        is_suspicious:
          type: boolean
        is_high_risk:
          type: boolean
        validation_reason:
          type: string
          nullable: true

    OhlcvCandle:
      type: object
      properties:
        bucket:
          type: string
          format: date-time
          description: Candle start timestamp
        open:
          type: number
          format: double
        high:
          type: number
          format: double
        low:
          type: number
          format: double
        close:
          type: number
          format: double
        volume_token:
          type: number
          format: double
        volume_usd:
          type: number
          format: double
        num_trades:
          type: integer
          format: int64
        buy_volume:
          type: number
          format: double
        sell_volume:
          type: number
          format: double
        vwap:
          type: number
          format: double
          nullable: true
          description: Volume-weighted average price

    TokenTrade:
      type: object
      properties:
        signature:
          type: string
        slot:
          type: integer
          format: uint64
        timestamp:
          type: string
          format: date-time
        wallet_address:
          type: string
        token_amount:
          type: number
          format: double
        direction:
          type: string
          enum: [buy, sell]
        dex_name:
          type: string
        base_amount:
          type: number
          format: double
          description: SOL amount
        base_price_usd:
          type: number
          format: double
        total_fees_usd:
          type: number
          format: double

    LiquidityResponse:
      type: object
      required: [token_mint, depth, lock, dump_impact_pct, exit_capacity]
      properties:
        token_mint:
          type: string
        depth:
          oneOf:
            - $ref: "#/components/schemas/LiquidityDepth"
            - type: "null"
          description: Null when no current supported reserve snapshot is available.
        lock:
          oneOf:
            - $ref: "#/components/schemas/LiquidityLock"
            - type: "null"
          description: Null until LP lock/burn state has been measured.
        dump_impact_pct:
          type: [number, "null"]
          format: double
          description: Fractional price impact if every coordinated wallet sold.
        exit_capacity:
          oneOf:
            - $ref: "#/components/schemas/ExitCapacity"
            - type: "null"
          description: Null when depth is unknown or the venue curve is unsupported.

    LiquidityDepth:
      type: object
      required: [venue, token_reserve_ui, quote_reserve_sol, liquidity_usd, reserve_slot, updated_at_ms]
      properties:
        venue:
          type: string
          enum: [curve_virtual, amm_actual]
        token_reserve_ui:
          type: number
          format: double
        quote_reserve_sol:
          type: number
          format: double
        liquidity_usd:
          type: [number, "null"]
          format: double
          description: Null in the current implementation; convert with the agent's own SOL/USD source.
        reserve_slot:
          type: integer
          format: int64
        updated_at_ms:
          type: [integer, "null"]
          format: int64

    LiquidityLock:
      type: object
      required: [status, burn_pct, lock_pct, verified_at]
      properties:
        status:
          type: string
          enum: [burned, locked, unlocked, unknown]
        burn_pct:
          type: [number, "null"]
          format: double
        lock_pct:
          type: [number, "null"]
          format: double
        verified_at:
          type: [string, "null"]

    ExitCapacity:
      type: object
      required: [usd_at_10, usd_at_25, usd_at_50, sol_at_10, sol_at_25, sol_at_50]
      properties:
        usd_at_10:
          type: [number, "null"]
          format: double
        usd_at_25:
          type: [number, "null"]
          format: double
        usd_at_50:
          type: [number, "null"]
          format: double
        sol_at_10:
          type: number
          format: double
        sol_at_25:
          type: number
          format: double
        sol_at_50:
          type: number
          format: double

    LiquidityEventsResponse:
      type: object
      required: [token_mint, events]
      properties:
        token_mint:
          type: string
        events:
          type: array
          items:
            $ref: "#/components/schemas/LiquidityEvent"

    LiquidityEvent:
      type: object
      required: [signature, ts_ms, slot, pool, dex, wallet, side, token_ui, quote_ui, pct_of_pool, is_full_close, is_bundle_member, is_flagged]
      properties:
        signature:
          type: string
        ts_ms:
          type: integer
          format: int64
        slot:
          type: integer
          format: int64
        pool:
          type: string
        dex:
          type: string
        wallet:
          type: string
        side:
          type: string
          enum: [add, remove]
        token_ui:
          type: number
          format: double
          description: Zero means vault deltas could not be resolved, not that no tokens moved.
        quote_ui:
          type: number
          format: double
        pct_of_pool:
          type: [number, "null"]
          format: double
          description: Fraction of the quote vault moved.
        is_full_close:
          type: boolean
        is_bundle_member:
          type: boolean
        is_flagged:
          type: boolean

    HoldersSummary:
      type: object
      properties:
        mint:
          type: string
        distinctCount:
          type: integer
          format: uint64
        totalBalanceRaw:
          type: string
          description: Sum of all non-zero holder balances, raw token units (string for u64 precision)
        supplyRaw:
          type: string
          description: On-chain mint supply, raw units. Omitted when unknown.
        top10Pct:
          type: number
          format: double
          description: Percentage of total balance held by the top 10 wallets. Omitted when fewer than 10 holders.
        top50Pct:
          type: number
          format: double
        top100Pct:
          type: number
          format: double
        hydrationStatus:
          type: string
          description: "born_forward | hydrating | hydrated | partial_forward"
        hydratedAtSlot:
          type: integer
          format: uint64
        lastUpdatedSlot:
          type: integer
          format: uint64
        lastUpdatedTsMs:
          type: integer
          format: int64
        asOfTsMs:
          type: integer
          format: int64
          description: Server-side response timestamp (ms-epoch)

    HolderEntry:
      type: object
      properties:
        rank:
          type: integer
          format: uint32
        wallet:
          type: string
        balanceRaw:
          type: string
          description: Holder balance, raw token units (string for u64 precision)
        pctOfTotal:
          type: number
          format: double
          description: Percentage of total balance. Omitted when total is zero.
        firstSeenSlot:
          type: integer
          format: uint64

    HoldersTop:
      type: object
      properties:
        mint:
          type: string
        distinctCount:
          type: integer
          format: uint64
        totalBalanceRaw:
          type: string
        hydrationStatus:
          type: string
        holders:
          type: array
          items:
            $ref: "#/components/schemas/HolderEntry"
        asOfTsMs:
          type: integer
          format: int64

    HoldersList:
      type: object
      properties:
        mint:
          type: string
        distinctCount:
          type: integer
          format: uint64
        totalBalanceRaw:
          type: string
        hydrationStatus:
          type: string
        holders:
          type: array
          items:
            $ref: "#/components/schemas/HolderEntry"
        limit:
          type: integer
          format: uint32
        offset:
          type: integer
          format: uint32
        nextOffset:
          type: integer
          format: uint32
          nullable: true
          description: Offset of the next page, or null when no more pages
        asOfTsMs:
          type: integer
          format: int64

    WalletQuality:
      type: object
      properties:
        wallet:
          type: string
        rated:
          type: boolean
          description: True when closed_positions >= 10
        skill_score:
          type: number
          format: double
          description: Composite skill score in [0, 100]
        performance_level:
          type: string
        trader_type:
          type: string
        exit_style:
          type: string
        size_class:
          type: string
        closed_positions:
          type: integer
          format: uint64
        position_win_rate_pct:
          type: number
          format: double
          description: Position-correct win rate — the differentiated metric
        raw_exit_win_rate_pct:
          type: number
          format: double
          description: Commodity per-exit win rate, for contrast
        win_rate_inflation_pts:
          type: number
          format: double
          description: How much the commodity number overstates skill (raw - position), in pts
        disposition_index:
          type: number
          format: double
          nullable: true
        conviction_sizing:
          type: number
          format: double
          nullable: true
        profit_factor:
          type: number
          format: double
        sharpe_ratio:
          type: number
          format: double
          nullable: true
        runner_score:
          type: number
          format: double
        avg_loss_roi_pct:
          type: number
          format: double
        loss_consistency_stddev:
          type: number
          format: double
        roi_p10_pct:
          type: number
          format: float
        roi_p50_pct:
          type: number
          format: float
        roi_p90_pct:
          type: number
          format: float
        big_wins:
          type: integer
          format: uint64
        big_losses:
          type: integer
          format: uint64
        realized_pnl_usd:
          type: number
          format: double
        trade_badges:
          type: array
          items:
            type: string

    CoordinatedPushCandidate:
      type: object
      properties:
        tokenMint:
          type: string
        latestRegime:
          type: string
        priorRegime:
          type: string
        campaignId:
          type: string
        campaignStatus:
          type: string
        participantWallets:
          type: integer
          format: uint32
        participantEntities:
          type: integer
          format: uint32
        confidence:
          type: number
          format: double
        tokenAgeMinutes:
          type: integer
          format: uint32

    CascadeFollower:
      type: object
      properties:
        follower:
          type: string
        confidence:
          type: number
          format: double
        lagSeconds:
          type: integer
          format: int32

    EmergingEntity:
      type: object
      properties:
        entityId:
          type: string
        entityKind:
          type: string
        rootWallet:
          type: string
        memberCount:
          type: integer
          format: uint32
        confidence:
          type: number
          format: double
        firstSeenTsMs:
          type: integer
          format: uint64
        lastSeenTsMs:
          type: integer
          format: uint64

    EarlyBuyCluster:
      type: object
      properties:
        entityId:
          type: string
        entityKind:
          type: string
        rootWallet:
          type: string
        confidence:
          type: number
          format: double
        membersInEarlyBuy:
          type: integer
          format: uint32
        memberWallets:
          type: array
          items:
            type: string

    OriginatorCandidate:
      type: object
      properties:
        candidateLeader:
          type: string
        followerBuysInCluster:
          type: integer
          format: uint32
        meanConfidence:
          type: number
          format: double

    EntityConcentration:
      type: object
      properties:
        totalWallets:
          type: integer
          format: uint64
        distinctActors:
          type: integer
          format: uint64
        totalVolumeUsd:
          type: number
          format: double
        entityVolumeUsd:
          type: number
          format: double
        entityVolumeShare:
          type: number
          format: double
        topActors:
          type: array
          items:
            type: string

    LateRetail:
      type: object
      properties:
        eliteSells:
          type: integer
          format: uint64
        lateBuyers:
          type: integer
          format: uint64
        lateBuyerVolumeUsd:
          type: number
          format: double
        avgLagSeconds:
          type: number
          format: double
        shareUnprofitable:
          type: number
          format: double

    # WebSocket schemas (not REST, documented for completeness)
    WsClientMessage:
      discriminator:
        propertyName: op
      oneOf:
        - type: object
          properties:
            op:
              type: string
              const: subscribe
            channels:
              type: array
              items:
                type: string
          required: [op, channels]
        - type: object
          properties:
            op:
              type: string
              const: unsubscribe
            channels:
              type: array
              items:
                type: string
          required: [op, channels]
        - type: object
          properties:
            op:
              type: string
              const: ping
          required: [op]

    WsServerMessage:
      oneOf:
        - type: object
          description: Data event
          properties:
            channel:
              type: string
            data:
              type: object
          required: [channel, data]
        - type: object
          description: Control message
          properties:
            op:
              type: string
              enum: [subscribed, unsubscribed, pong, error]
            channels:
              type: array
              items:
                type: string
            message:
              type: string

    WsChannelFormat:
      type: string
      description: |
        Channel naming convention. Market data:
        - token:<mint>:trades — live trade tape
        - token:<mint>:ticks — chart tick events
        - token:<mint>:ohlcv:<view>:<tf> — OHLCV updates (view: full|filtered, tf: 1s|1m|5m|15m|1h)

        Wallet activity:
        - wallet:<address>:pnl — realized PnL events (sub-second)
        - wallet:<address>:positions — position updates
        - wallet:<address>:labels — label and badge state changes

        Security:
        - token:<mint>:security — token security events (bundle, wash trade, rug, coordination alert)
        - token:<mint>:bundles — raw BundleV2 token updates
        - token:<mint>:dump_alert — compact qualifying coordinated-dump alerts
        - wallet:<address>:security — wallet-scoped security events

        Coordination v2:
        - wallet:<address>:follow — follow-edge activations
        - wallet:<address>:entity — entity-membership changes
        - token:<mint>:campaign — coordinated campaign activations

      examples:
        - "token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v:trades"
        - "token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v:ohlcv:full:1m"
        - "wallet:7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU:pnl"
        - "wallet:7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU:follow"
