openapi: 3.1.0
info:
  title: willhappen public API
  version: "1.0.0"
  description: >
    Read-only open data for willhappen, a real-money prediction-market venue on
    Hyperliquid HIP-4. Odds, order-book liquidity, forecaster skill scores, and
    cross-market no-arbitrage dislocations. Authenticate with a public `pub_`
    key (`X-API-Key`). Analytics endpoints also accept `?format=csv`.
  license:
    name: MIT
servers:
  - url: https://willhappen.io/v1
    description: Production
  - url: http://127.0.0.1:8091/v1
    description: Local mock gateway
security:
  - PubKey: []
tags:
  - name: markets
  - name: analytics
  - name: forecasters
components:
  securitySchemes:
    PubKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: A public open-data key, prefixed `pub_`. Read-only; cannot reach write routes.
  parameters:
    Format:
      name: format
      in: query
      required: false
      description: Response format. `csv` renders the primary tabular array as CSV.
      schema:
        type: string
        enum: [json, csv]
        default: json
    Coin:
      name: coin
      in: query
      required: true
      description: Outcome coin ticker, e.g. `#8130`.
      schema:
        type: string
  responses:
    NotFound:
      description: Unknown coin or address.
      content:
        application/json:
          schema:
            type: object
            properties:
              code: { type: string }
    Unavailable:
      description: Artifact not yet built.
      content:
        application/json:
          schema:
            type: object
            properties:
              code: { type: string }
  schemas:
    Json:
      description: Endpoint-specific JSON envelope.
      type: object
      additionalProperties: true
    Csv:
      type: string
paths:
  /questions:
    get:
      tags: [markets]
      summary: Markets catalogue — outcomes, live mids, 24h volume.
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items: { $ref: "#/components/schemas/Json" }
  /mids:
    get:
      tags: [markets]
      summary: Coin → current mid price map.
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Json" }
  /history:
    get:
      tags: [markets]
      summary: Per-coin probability history.
      parameters:
        - name: coins
          in: query
          required: true
          schema: { type: string }
          description: Comma-separated coin tickers.
        - name: hours
          in: query
          schema: { type: integer, default: 24 }
        - name: interval
          in: query
          schema: { type: string, default: "1h" }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Json" }
  /trades:
    get:
      tags: [markets]
      summary: Public trade tape for a coin.
      parameters:
        - $ref: "#/components/parameters/Coin"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items: { $ref: "#/components/schemas/Json" }
        "404": { $ref: "#/components/responses/NotFound" }
  /book:
    get:
      tags: [markets]
      summary: Priced L2 order book for a coin.
      parameters:
        - $ref: "#/components/parameters/Coin"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Json" }
        "404": { $ref: "#/components/responses/NotFound" }
  /leaderboard:
    get:
      tags: [forecasters]
      summary: Forecasters ranked by rating.
      parameters:
        - name: window
          in: query
          schema: { type: string }
        - name: category
          in: query
          schema: { type: string }
        - name: limit
          in: query
          schema: { type: integer }
        - $ref: "#/components/parameters/Format"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Json" }
            text/csv:
              schema: { $ref: "#/components/schemas/Csv" }
  /score/{address}:
    get:
      tags: [forecasters]
      summary: A wallet's foresight scorecard.
      parameters:
        - name: address
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Json" }
        "404": { $ref: "#/components/responses/NotFound" }
  /market-calibration:
    get:
      tags: [analytics]
      summary: Market's own calibration (Brier) by horizon.
      parameters:
        - $ref: "#/components/parameters/Format"
      responses:
        "200":
          description: OK
          content:
            application/json: { schema: { $ref: "#/components/schemas/Json" } }
            text/csv: { schema: { $ref: "#/components/schemas/Csv" } }
  /liquidity:
    get:
      tags: [analytics]
      summary: Spread, depth, uptime, cost-to-move per coin/day.
      parameters:
        - $ref: "#/components/parameters/Coin"
        - name: days
          in: query
          schema: { type: integer }
        - $ref: "#/components/parameters/Format"
      responses:
        "200":
          description: OK
          content:
            application/json: { schema: { $ref: "#/components/schemas/Json" } }
            text/csv: { schema: { $ref: "#/components/schemas/Csv" } }
        "404": { $ref: "#/components/responses/NotFound" }
        "503": { $ref: "#/components/responses/Unavailable" }
  /flow:
    get:
      tags: [analytics]
      summary: Trade counts, notional, taker mix per coin.
      parameters:
        - $ref: "#/components/parameters/Coin"
        - $ref: "#/components/parameters/Format"
      responses:
        "200":
          description: OK
          content:
            application/json: { schema: { $ref: "#/components/schemas/Json" } }
            text/csv: { schema: { $ref: "#/components/schemas/Csv" } }
        "404": { $ref: "#/components/responses/NotFound" }
  /smart:
    get:
      tags: [analytics]
      summary: Where top forecasters lean on a market.
      parameters:
        - $ref: "#/components/parameters/Coin"
        - $ref: "#/components/parameters/Format"
      responses:
        "200":
          description: OK
          content:
            application/json: { schema: { $ref: "#/components/schemas/Json" } }
        "404": { $ref: "#/components/responses/NotFound" }
  /dislocations:
    get:
      tags: [analytics]
      summary: No-arbitrage violations across markets.
      parameters:
        - $ref: "#/components/parameters/Format"
      responses:
        "200":
          description: OK
          content:
            application/json: { schema: { $ref: "#/components/schemas/Json" } }
            text/csv: { schema: { $ref: "#/components/schemas/Csv" } }
  /taker-cost:
    get:
      tags: [analytics]
      summary: Realised taker slippage vs mid, per coin/day.
      parameters:
        - $ref: "#/components/parameters/Coin"
        - $ref: "#/components/parameters/Format"
      responses:
        "200":
          description: OK
          content:
            application/json: { schema: { $ref: "#/components/schemas/Json" } }
            text/csv: { schema: { $ref: "#/components/schemas/Csv" } }
        "404": { $ref: "#/components/responses/NotFound" }
  /deployers:
    get:
      tags: [analytics]
      summary: Settlement-punctuality scorecards (A–F) per operator.
      parameters:
        - $ref: "#/components/parameters/Format"
      responses:
        "200":
          description: OK
          content:
            application/json: { schema: { $ref: "#/components/schemas/Json" } }
            text/csv: { schema: { $ref: "#/components/schemas/Csv" } }
  /mqi:
    get:
      tags: [analytics]
      summary: Market Quality Index — composite 0–100 grade (A–F) per market.
      parameters:
        - name: coin
          in: query
          required: false
          description: One market's card; omit for the whole board.
          schema: { type: string }
        - $ref: "#/components/parameters/Format"
      responses:
        "200":
          description: OK
          content:
            application/json: { schema: { $ref: "#/components/schemas/Json" } }
            text/csv: { schema: { $ref: "#/components/schemas/Csv" } }
        "404": { $ref: "#/components/responses/NotFound" }
  /badge/{address}.svg:
    get:
      tags: [forecasters]
      summary: SVG rating badge (keyless, cacheable).
      security: []
      parameters:
        - name: address
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: SVG image
          content:
            image/svg+xml:
              schema: { type: string }
