openapi: 3.1.0
info:
  title: Omni x402 Agent Data Products
  version: "0.4.0-testnet"
  summary: Pay-per-request AI market context and Hyperliquid trader intelligence for agents.
  description: |
    Omni's x402 v2 gateway sells seven bounded AI News and Hyperliquid intelligence
    products without requiring an Omni account or API key. All seven paid route
    templates have completed funded Base Sepolia CDP settlements and are cataloged
    in Coinbase Bazaar.
    Base mainnet support is implemented but remains disabled
    until the protected CDP credential is rotated and the production gate passes.

    Responses are versioned allowlist projections. Raw news messages, source
    inventory, prompts, full provider payloads, orders, fills, ledger rows, WebData,
    private account state, API keys, and trading authority are never returned.
  contact:
    name: Omni Terminal
    url: https://omniterminal.app/x402
servers:
  - url: https://omniterminal.app
    description: Canonical production-domain Base Sepolia service
  - url: https://dev.omniterminal.app
    description: Development-domain Base Sepolia service
tags:
  - name: Health
    description: Free readiness and payment-configuration endpoints.
  - name: AI News Pulse
    description: Enriched events plus a market-wide context rollup.
  - name: Hyperliquid Trader Profile
    description: Bounded public wallet statistics; never trading authority.
  - name: Hyperliquid Liquidations
    description: Bounded liquidation summary, bucket, cluster, and flow projections.
  - name: Hyperliquid Trader Leaderboard
    description: Capped rankings for one allowlisted market and ranking mode.
  - name: Market Risk
    description: Joined liquidation and enriched-news context with component freshness.
  - name: Hyperliquid Market Snapshot
    description: Bounded OHLCV candles plus an optional liquidation overlay.
paths:
  /api/x402/v1/news/health:
    get:
      tags: [Health]
      operationId: getX402NewsHealth
      summary: News product readiness
      security: []
      responses:
        "200":
          description: Gateway and seller configuration state.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Health" }
  /api/x402/v1/trader-profile/health:
    get:
      tags: [Health]
      operationId: getX402TraderProfileHealth
      summary: Trader Profile readiness
      security: []
      responses:
        "200":
          description: Gateway and Trader Profile configuration state.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Health" }
  /api/x402/v1/news/{symbol}:
    get:
      tags: [AI News Pulse]
      operationId: buySymbolNewsPulse
      summary: Buy News Pulse for one symbol
      description: Unpaid calls receive HTTP 402 and `PAYMENT-REQUIRED`. A valid paid retry receives the bounded product and `PAYMENT-RESPONSE`.
      parameters:
        - name: symbol
          in: path
          required: true
          schema: { type: string, enum: [BTC, ETH, SOL, HYPE] }
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/LookbackHours"
        - $ref: "#/components/parameters/EventWindowMinutes"
        - $ref: "#/components/parameters/NewsMode"
        - $ref: "#/components/parameters/NewsOrder"
        - $ref: "#/components/parameters/NewsOffset"
        - $ref: "#/components/parameters/NearestTimestamp"
        - $ref: "#/components/parameters/Sentiment"
        - $ref: "#/components/parameters/Impact"
        - $ref: "#/components/parameters/MinConfidence"
      responses:
        "200": { $ref: "#/components/responses/PaidNewsPulse" }
        "400": { description: Unsupported symbol or invalid bounded query. }
        "402": { $ref: "#/components/responses/PaymentRequired" }
        "503": { description: Payment service or bounded upstream unavailable. }
  /api/x402/v1/news:
    get:
      tags: [AI News Pulse]
      operationId: buyMarketNewsPulse
      summary: Buy market-wide News Pulse
      parameters:
        - name: market
          in: query
          required: false
          schema: { type: string, enum: [crypto, macro, equities, forex], default: crypto }
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/LookbackHours"
        - $ref: "#/components/parameters/EventWindowMinutes"
        - $ref: "#/components/parameters/NewsMode"
        - $ref: "#/components/parameters/NewsOrder"
        - $ref: "#/components/parameters/NewsOffset"
        - $ref: "#/components/parameters/NearestTimestamp"
        - $ref: "#/components/parameters/Sentiment"
        - $ref: "#/components/parameters/Impact"
        - $ref: "#/components/parameters/MinConfidence"
      responses:
        "200": { $ref: "#/components/responses/PaidNewsPulse" }
        "400": { description: Unsupported market or invalid bounded query. }
        "402": { $ref: "#/components/responses/PaymentRequired" }
        "503": { description: Payment service or bounded upstream unavailable. }
  /api/x402/v1/trader-profile/{address}:
    get:
      tags: [Hyperliquid Trader Profile]
      operationId: buyHyperliquidTraderProfile
      summary: Buy a bounded public Hyperliquid trader profile
      description: Returns analytical public-wallet statistics only. It does not authenticate the wallet owner or grant execution rights.
      parameters:
        - name: address
          in: path
          required: true
          schema:
            type: string
            pattern: "^0x[0-9a-fA-F]{40}$"
            example: "0x0ddf9bae2af4b874b96d287a5ad42eb47138a902"
        - name: range
          in: query
          required: false
          schema: { type: string, enum: [1d, 7d, 30d, all], default: 30d }
        - name: view
          in: query
          required: false
          schema: { type: string, enum: [summary, positions, balances, full], default: full }
        - name: symbol
          in: query
          required: false
          description: Optional allowlisted open-position filter.
          schema: { type: string, enum: [BTC, ETH, SOL, HYPE] }
        - name: limit
          in: query
          required: false
          schema: { type: integer, minimum: 1, maximum: 20, default: 20 }
      responses:
        "200":
          description: Paid bounded Trader Profile.
          headers:
            PAYMENT-RESPONSE: { $ref: "#/components/headers/PaymentResponse" }
          content:
            application/json:
              schema: { $ref: "#/components/schemas/TraderProfile" }
        "400": { description: Invalid EVM address or unsupported range. }
        "402": { $ref: "#/components/responses/PaymentRequired" }
        "503": { description: Payment service or bounded upstream unavailable. }
  /api/x402/v1/liquidations/{symbol}:
    get:
      tags: [Hyperliquid Liquidations]
      operationId: buyHyperliquidLiquidationMap
      summary: Buy a bounded liquidation map
      description: Select a summary, heatmap-bucket, cluster, or cumulative-flow projection. Raw position exports are not returned.
      parameters:
        - $ref: "#/components/parameters/AnalyticsSymbol"
        - $ref: "#/components/parameters/Scope"
        - name: view
          in: query
          schema: { type: string, enum: [summary, buckets, clusters, flow], default: summary }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 50, default: 20 }
        - name: order
          in: query
          schema: { type: string, enum: [strongest, nearest, price], default: strongest }
        - name: around_price
          in: query
          schema: { type: number, exclusiveMinimum: 0 }
        - name: side
          in: query
          schema: { type: string, enum: [all, long, short], default: all }
      responses:
        "200": { $ref: "#/components/responses/PaidLiquidationMap" }
        "400": { description: Unsupported symbol or invalid bounded query. }
        "402": { $ref: "#/components/responses/PaymentRequired" }
        "503": { description: Payment service or bounded upstream unavailable. }
  /api/x402/v1/traders/{symbol}:
    get:
      tags: [Hyperliquid Trader Leaderboard]
      operationId: buyHyperliquidTraderLeaderboard
      summary: Buy a bounded trader leaderboard
      description: Rank capped public rows by PnL, position value, position size, wallet size, risk, or liquidation proximity.
      parameters:
        - $ref: "#/components/parameters/AnalyticsSymbol"
        - $ref: "#/components/parameters/Scope"
        - name: rank
          in: query
          schema:
            type: string
            enum: [best, worst, largest, largest_size, wallet_size, risk, closest]
            default: best
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 20, default: 10 }
      responses:
        "200": { $ref: "#/components/responses/PaidTraderLeaderboard" }
        "400": { description: Unsupported symbol or invalid bounded query. }
        "402": { $ref: "#/components/responses/PaymentRequired" }
        "503": { description: Payment service or bounded upstream unavailable. }
  /api/x402/v1/market-risk/{symbol}:
    get:
      tags: [Market Risk]
      operationId: buyMarketRiskSnapshot
      summary: Buy a joined market-risk snapshot
      description: Returns liquidation risk plus the current enriched AI News pulse with independent component freshness.
      parameters:
        - $ref: "#/components/parameters/AnalyticsSymbol"
        - $ref: "#/components/parameters/Scope"
      responses:
        "200": { $ref: "#/components/responses/PaidMarketRisk" }
        "400": { description: Unsupported symbol or invalid bounded query. }
        "402": { $ref: "#/components/responses/PaymentRequired" }
        "503": { description: Payment service or bounded upstream unavailable. }
  /api/x402/v1/market-snapshot/{symbol}:
    get:
      tags: [Hyperliquid Market Snapshot]
      operationId: buyHyperliquidMarketSnapshot
      summary: Buy bounded candles and an optional liquidation overlay
      description: Uses Hyperliquid's public candle snapshot and Omni's bounded liquidation projection. It does not expose order entry or private account data.
      parameters:
        - $ref: "#/components/parameters/AnalyticsSymbol"
        - name: interval
          in: query
          schema: { type: string, enum: [1m, 5m, 15m, 1h, 2h, 4h, 8h, 1d, 3d, 1w, 1M], default: 1h }
        - name: limit
          in: query
          schema: { type: integer, minimum: 20, maximum: 200, default: 120 }
        - name: scope
          in: query
          schema: { type: string, enum: [current, aggregate], default: aggregate }
        - name: include_liquidations
          in: query
          schema: { type: boolean, default: true }
      responses:
        "200": { $ref: "#/components/responses/PaidMarketSnapshot" }
        "400": { description: Unsupported symbol or invalid bounded query. }
        "402": { $ref: "#/components/responses/PaymentRequired" }
        "503": { description: Payment service, Hyperliquid, or bounded upstream unavailable. }
components:
  parameters:
    AnalyticsSymbol:
      name: symbol
      in: path
      required: true
      schema: { type: string, enum: [BTC, ETH, SOL, HYPE] }
    Scope:
      name: scope
      in: query
      required: false
      schema: { type: string, enum: [current, aggregate], default: current }
    Limit:
      name: limit
      in: query
      required: false
      schema: { type: integer, minimum: 1, maximum: 20, default: 10 }
    LookbackHours:
      name: lookback_hours
      in: query
      required: false
      schema: { type: integer, minimum: 1, maximum: 168, default: 24 }
    EventWindowMinutes:
      name: event_window_minutes
      in: query
      required: false
      description: Recent event slice. Market context remains a 24-hour rollup refreshed every 15 minutes.
      schema: { type: integer, enum: [15, 60], default: 60 }
    NewsMode:
      name: mode
      in: query
      required: false
      schema: { type: string, enum: [latest, window, context], default: latest }
    NewsOrder:
      name: order
      in: query
      required: false
      schema: { type: string, enum: [recent, impact], default: recent }
    NewsOffset:
      name: offset
      in: query
      required: false
      description: Zero-based item selection after filters and ordering.
      schema: { type: integer, minimum: 0, maximum: 19, default: 0 }
    NearestTimestamp:
      name: nearest_timestamp
      in: query
      required: false
      description: Select the closest filtered event to a 13-digit Unix timestamp in milliseconds.
      schema: { type: integer, format: int64 }
    Sentiment:
      name: sentiment
      in: query
      required: false
      schema: { type: string, enum: [bullish, bearish, neutral] }
    Impact:
      name: impact
      in: query
      required: false
      schema: { type: string, enum: [high, medium, low] }
    MinConfidence:
      name: min_confidence
      in: query
      required: false
      schema: { type: number, minimum: 0, maximum: 1 }
  headers:
    PaymentResponse:
      description: Base64-encoded x402 v2 settlement response.
      schema: { type: string }
  responses:
    PaidNewsPulse:
      description: Paid News Pulse.
      headers:
        PAYMENT-RESPONSE: { $ref: "#/components/headers/PaymentResponse" }
      content:
        application/json:
          schema: { $ref: "#/components/schemas/NewsPulse" }
    PaymentRequired:
      description: x402 v2 payment required.
      headers:
        PAYMENT-REQUIRED:
          description: Base64-encoded payment requirements and Bazaar discovery metadata.
          required: true
          schema: { type: string }
    PaidLiquidationMap:
      description: Paid bounded liquidation product.
      headers:
        PAYMENT-RESPONSE: { $ref: "#/components/headers/PaymentResponse" }
      content:
        application/json:
          schema: { $ref: "#/components/schemas/LiquidationMap" }
    PaidTraderLeaderboard:
      description: Paid bounded trader leaderboard.
      headers:
        PAYMENT-RESPONSE: { $ref: "#/components/headers/PaymentResponse" }
      content:
        application/json:
          schema: { $ref: "#/components/schemas/TraderLeaderboard" }
    PaidMarketRisk:
      description: Paid joined market-risk snapshot.
      headers:
        PAYMENT-RESPONSE: { $ref: "#/components/headers/PaymentResponse" }
      content:
        application/json:
          schema: { $ref: "#/components/schemas/MarketRisk" }
    PaidMarketSnapshot:
      description: Paid bounded market snapshot.
      headers:
        PAYMENT-RESPONSE: { $ref: "#/components/headers/PaymentResponse" }
      content:
        application/json:
          schema: { $ref: "#/components/schemas/MarketSnapshot" }
  schemas:
    Freshness:
      type: object
      required: [status, age_seconds, max_age_seconds]
      properties:
        status: { type: string, enum: [fresh, stale, historical, unknown] }
        data_as_of: { type: [string, "null"], format: date-time }
        age_seconds: { type: [number, "null"] }
        max_age_seconds: { type: number }
    Health:
      type: object
      required: [status, service, protocol, network, payment_enabled]
      properties:
        status: { type: string, enum: [ready, configuration_required] }
        service: { type: string, const: omni-x402-gateway }
        product: { type: string }
        protocol: { type: string, const: x402-v2 }
        network: { type: string, example: "eip155:84532" }
        payment_enabled: { type: boolean }
        bazaar_metadata: { type: boolean }
        discovery_catalog: { type: string }
    AnalysisLineage:
      type: object
      additionalProperties: false
      required: [schema_version, prompt_version, model, validation_status]
      properties:
        schema_version: { type: string }
        prompt_version: { type: string }
        model: { type: string }
        validation_status: { type: string }
    NewsItem:
      type: object
      additionalProperties: false
      required: [id, timestamp, headline, summary, direction, sentiment, impact, importance, confidence, tickers, topics, theme, analysis_type, analysis]
      properties:
        id: { type: string, maxLength: 160 }
        timestamp: { type: integer, format: int64 }
        headline: { type: string, maxLength: 320 }
        summary: { type: string, maxLength: 1200 }
        direction: { type: string, maxLength: 32 }
        sentiment: { type: number, minimum: -10, maximum: 10 }
        impact: { type: string, maxLength: 32 }
        importance: { type: number }
        confidence: { type: number }
        tickers: { type: array, maxItems: 20, items: { type: string } }
        topics: { type: array, maxItems: 20, items: { type: string } }
        theme: { type: string, maxLength: 80 }
        analysis_type: { type: string, maxLength: 80 }
        analysis: { $ref: "#/components/schemas/AnalysisLineage" }
    ContextEvent:
      type: object
      additionalProperties: false
      properties:
        summary: { type: string, maxLength: 500 }
        direction: { type: string, maxLength: 32 }
        sentiment_rating: { type: number, minimum: -10, maximum: 10 }
        confidence: { type: number }
        category: { type: string, maxLength: 48 }
        tickers: { type: array, maxItems: 12, items: { type: string } }
    MarketContext:
      type: object
      additionalProperties: false
      required: [object, generated_at, cadence_minutes, source_window_hours, summary, direction, sentiment_rating, confidence, notable_tickers, topics, timeline]
      properties:
        object: { type: string, const: market_context.v1 }
        generated_at: { type: string, format: date-time }
        cadence_minutes: { type: integer, const: 15 }
        source_window_hours: { type: [number, "null"], maximum: 24 }
        summary: { type: string, maxLength: 1500 }
        direction: { type: string, maxLength: 32 }
        sentiment_rating: { type: number, minimum: -10, maximum: 10 }
        confidence: { type: number }
        notable_tickers: { type: array, maxItems: 20, items: { type: string } }
        topics: { type: array, maxItems: 20, items: { type: string } }
        timeline: { type: array, maxItems: 8, items: { $ref: "#/components/schemas/ContextEvent" } }
    NewsPulse:
      type: object
      additionalProperties: false
      required: [service, product_version, schema, market, generated_at, data_as_of, freshness, market_context, items, usage]
      properties:
        service: { type: string, const: omni.ai_news_pulse }
        product_version: { type: string, const: v1 }
        schema: { type: string, const: news_pulse.v1 }
        symbol: { type: string }
        market: { type: string }
        generated_at: { type: string, format: date-time }
        data_as_of: { type: string, format: date-time }
        freshness: { $ref: "#/components/schemas/Freshness" }
        market_context:
          oneOf:
            - { $ref: "#/components/schemas/MarketContext" }
            - { type: "null" }
        items: { type: array, maxItems: 20, items: { $ref: "#/components/schemas/NewsItem" } }
        usage:
          type: object
          required: [item_count, lookback_hours, event_window_minutes]
          properties:
            item_count: { type: integer, maximum: 20 }
            lookback_hours: { type: integer, maximum: 24 }
            event_window_minutes: { type: integer, enum: [15, 60] }
    ScalarMap:
      type: object
      additionalProperties:
        type: [string, number, boolean, "null"]
    Position:
      type: object
      additionalProperties: false
      properties:
        coin: { type: [string, "null"] }
        size: { type: [string, number, "null"] }
        side: { type: [string, "null"] }
        entry_price: { type: [string, number, "null"] }
        position_value: { type: [string, number, "null"] }
        unrealized_pnl: { type: [string, number, "null"] }
        return_on_equity: { type: [string, number, "null"] }
        liquidation_price: { type: [string, number, "null"] }
        margin_used: { type: [string, number, "null"] }
        leverage:
          oneOf:
            - { $ref: "#/components/schemas/ScalarMap" }
            - { type: [string, number, "null"] }
        dex: { type: [string, "null"] }
    SpotBalance:
      type: object
      additionalProperties: false
      properties:
        coin: { type: [string, "null"] }
        total: { type: [string, number, "null"] }
        held: { type: [string, number, "null"] }
        mark_price: { type: [string, number, "null"] }
        usd_value: { type: [string, number, "null"] }
    TraderProfile:
      type: object
      additionalProperties: false
      required: [service, product_version, schema, address, range, scope, view, generated_at, data_as_of, freshness, summary, components, positions, spot_balances, activity, quality, usage]
      properties:
        service: { type: string, const: omni.trader_profile }
        product_version: { type: string, const: v1 }
        schema: { type: string, const: trader_profile.v1 }
        address: { type: string, pattern: "^0x[0-9a-f]{40}$" }
        range: { type: string, enum: [1d, 7d, 30d, all] }
        scope: { type: string, const: wallet }
        view: { type: string, enum: [summary, positions, balances, full] }
        symbol: { type: string }
        generated_at: { type: string, format: date-time }
        data_as_of: { type: [string, number, "null"] }
        freshness: { $ref: "#/components/schemas/Freshness" }
        summary: { $ref: "#/components/schemas/ScalarMap" }
        components: { $ref: "#/components/schemas/ScalarMap" }
        positions: { type: array, maxItems: 20, items: { $ref: "#/components/schemas/Position" } }
        spot_balances: { type: array, maxItems: 20, items: { $ref: "#/components/schemas/SpotBalance" } }
        activity: { $ref: "#/components/schemas/ScalarMap" }
        quality:
          type: object
          properties:
            degraded_sources: { type: array, maxItems: 12, items: { type: string } }
            portfolio_history_used: { type: [boolean, string, number, "null"] }
        usage:
          type: object
          required: [position_count, spot_balance_count]
          properties:
            position_count: { type: integer, maximum: 20 }
            spot_balance_count: { type: integer, maximum: 20 }
    LiquidationMap:
      type: object
      required: [service, product_version, schema, symbol, scope, view, generated_at, data_as_of, freshness, summary, usage]
      properties:
        service: { type: string, const: omni.hyperliquid_liquidation_map }
        product_version: { type: string, const: v1 }
        schema: { type: string, const: hyperliquid_liquidation_map.v1 }
        symbol: { type: string }
        scope: { type: string, enum: [current, aggregate] }
        view: { type: string, enum: [summary, buckets, clusters, flow] }
        generated_at: { type: string, format: date-time }
        data_as_of: { type: [string, number, "null"] }
        freshness: { $ref: "#/components/schemas/Freshness" }
        summary: { type: object, additionalProperties: true }
        nearest: { type: array, maxItems: 10, items: { type: object, additionalProperties: true } }
        largest: { type: array, maxItems: 10, items: { type: object, additionalProperties: true } }
        buckets: { type: array, maxItems: 50, items: { type: object, additionalProperties: true } }
        clusters: { type: object, additionalProperties: true }
        flow: { type: array, maxItems: 50, items: { type: object, additionalProperties: true } }
        usage: { type: object, additionalProperties: true }
    TraderLeaderboard:
      type: object
      required: [service, product_version, schema, symbol, scope, rank, generated_at, data_as_of, freshness, rows, usage]
      properties:
        service: { type: string, const: omni.hyperliquid_trader_leaderboard }
        product_version: { type: string, const: v1 }
        schema: { type: string, const: hyperliquid_trader_leaderboard.v1 }
        symbol: { type: string }
        scope: { type: string, enum: [current, aggregate] }
        rank: { type: string, enum: [best, worst, largest, largest_size, wallet_size, risk, closest] }
        generated_at: { type: string, format: date-time }
        data_as_of: { type: [string, number, "null"] }
        freshness: { $ref: "#/components/schemas/Freshness" }
        rows: { type: array, maxItems: 20, items: { type: object, additionalProperties: true } }
        usage: { type: object, additionalProperties: true }
    MarketRisk:
      type: object
      required: [service, product_version, schema, symbol, scope, generated_at, data_as_of, freshness, liquidations, news]
      properties:
        service: { type: string, const: omni.market_risk_snapshot }
        product_version: { type: string, const: v1 }
        schema: { type: string, const: market_risk_snapshot.v1 }
        symbol: { type: string }
        scope: { type: string, enum: [current, aggregate] }
        generated_at: { type: string, format: date-time }
        data_as_of: { type: [string, "null"], format: date-time }
        freshness: { type: object, additionalProperties: true }
        liquidations: { $ref: "#/components/schemas/LiquidationMap" }
        news: { $ref: "#/components/schemas/NewsPulse" }
    Candle:
      type: object
      additionalProperties: false
      required: [open_time, close_time, open, high, low, close, volume, trades]
      properties:
        open_time: { type: [integer, "null"], format: int64 }
        close_time: { type: [integer, "null"], format: int64 }
        open: { type: [number, "null"] }
        high: { type: [number, "null"] }
        low: { type: [number, "null"] }
        close: { type: [number, "null"] }
        volume: { type: [number, "null"] }
        trades: { type: [number, "null"] }
    MarketSnapshot:
      type: object
      required: [service, product_version, schema, symbol, interval, scope, generated_at, data_as_of, freshness, candles, usage]
      properties:
        service: { type: string, const: omni.hyperliquid_market_snapshot }
        product_version: { type: string, const: v1 }
        schema: { type: string, const: hyperliquid_market_snapshot.v1 }
        symbol: { type: string }
        interval: { type: string }
        scope: { type: string, enum: [current, aggregate] }
        generated_at: { type: string, format: date-time }
        data_as_of: { type: [string, "null"], format: date-time }
        freshness: { $ref: "#/components/schemas/Freshness" }
        candles: { type: array, maxItems: 200, items: { $ref: "#/components/schemas/Candle" } }
        liquidation_overlay:
          oneOf:
            - { $ref: "#/components/schemas/LiquidationMap" }
            - { type: "null" }
        usage: { type: object, additionalProperties: true }
