openapi: 3.1.0
info:
  title: Orrery Probability Intelligence API (x402)
  version: 1.0.0
  description: |
    Paid per-call intelligence layer over public Polymarket data.

    Built for autonomous AI agents discovering services on Agentic Market.
    Every endpoint is account-less and stateless: agents pay USDC per HTTP
    request via the x402 protocol (HTTP 402 + on-chain settlement on Base).
    No API keys, no user records, no rate-cards — just per-call pricing
    quoted in the response challenge.

    All responses use the same envelope:
      {
        "data": { ... endpoint-specific payload ... },
        "meta": {
          "endpoint": "/api/x402/v1/...",
          "fetched_at": "ISO-8601",
          "cache_seconds": 120,
          "payment_status": "preview" | "settled",
          "usdc_per_call": 0.005,
          "sources": ["Polymarket Gamma", "Polymarket Data"],
          "not_trade_advice": true
        }
      }

    During public preview, all endpoints are free and tagged
    `payment_status: "preview"`. When settlement is enforced,
    callers without a valid `X-PAYMENT` header receive HTTP 402 with
    a JSON challenge listing the price + accepted networks.

    Discovery files:
      • Public llms.txt:        https://orrery.me/llms.txt
      • Agent plugin manifest:  https://orrery.me/.well-known/ai-plugin.json
      • x402 service catalog:   https://orrery.me/.well-known/x402-services.json
  contact:
    name: Orrery
    email: hello@orrery.me
    url: https://orrery.me/contact
  license:
    name: Public read-only data — see https://orrery.me/terms
servers:
  - url: https://orrery.me
    description: Production (preview pricing)
tags:
  - name: brief
    description: Daily prediction-market brief
  - name: markets
    description: Per-market intelligence
  - name: events
    description: Event clusters
  - name: signals
    description: Live signal feed
  - name: portfolio
    description: Portfolio + watchlist intelligence
components:
  securitySchemes:
    x402:
      type: apiKey
      in: header
      name: X-PAYMENT
      description: |
        On-chain settlement proof for x402 payment. During preview the header
        is optional; once enforcement is enabled, requests without it receive
        HTTP 402 with a JSON challenge specifying price + accepted networks.
  schemas:
    EnvelopeMeta:
      type: object
      required: [endpoint, fetched_at, cache_seconds, payment_status, usdc_per_call, sources, not_trade_advice]
      properties:
        endpoint: { type: string }
        fetched_at: { type: string, format: date-time }
        cache_seconds: { type: integer }
        payment_status:
          type: string
          enum: [preview, settled, missing, rate_limited]
        usdc_per_call: { type: number, format: float }
        sources:
          type: array
          items: { type: string }
        not_trade_advice:
          type: boolean
          enum: [true]
    PaymentChallenge:
      type: object
      properties:
        error:
          type: string
          enum: [payment_required]
        accepts:
          type: array
          items:
            type: object
            properties:
              scheme: { type: string, example: x402-onchain }
              network: { type: string, example: base }
              asset: { type: string, example: USDC }
              amount: { type: number, example: 0.01 }
              endpoint: { type: string }
        description: { type: string }
        docs: { type: string }
paths:
  /api/x402/v1/brief/today:
    get:
      tags: [brief]
      summary: Today's machine-readable daily brief
      description: |
        Compact JSON edition of the public Daily Brief: biggest moves,
        unusual volume, research signals, resolution watch, smart-money
        flow, headline, and next-action hints pointing at sister
        endpoints. Cache 5 min.
      security:
        - x402: []
      responses:
        '200':
          description: OK — brief envelope
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      date: { type: string, example: "2026-04-30" }
                      headline: { type: string }
                      biggest_moves: { type: array, items: { type: object } }
                      unusual_volume: { type: array, items: { type: object } }
                      research_signals: { type: array, items: { type: object } }
                      resolution_watch: { type: array, items: { type: object } }
                      smart_money_flow: { type: array, items: { type: object } }
                      next_actions: { type: array, items: { type: string } }
                  meta:
                    $ref: '#/components/schemas/EnvelopeMeta'
        '402':
          description: Payment required — agent must settle on-chain and replay
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PaymentChallenge' }
        '429':
          description: Rate-limited (60 req/min/IP)
  /api/x402/v1/markets/movers:
    get:
      tags: [markets]
      summary: Biggest 24h probability movers
      description: Ranked by absolute 24h delta, filtered to live markets only.
      security:
        - x402: []
      parameters:
        - in: query
          name: limit
          schema: { type: integer, default: 10, maximum: 50 }
        - in: query
          name: category
          schema: { type: string }
          description: Case-insensitive category match (e.g. "Crypto").
        - in: query
          name: min_liquidity
          schema: { type: number, default: 5000 }
        - in: query
          name: direction
          schema: { type: string, enum: [up, down] }
      responses:
        '200': { description: OK — movers envelope }
        '402': { description: Payment required }
        '429': { description: Rate-limited }
  /api/x402/v1/markets/{id}/snapshot:
    get:
      tags: [markets]
      summary: Compact per-market snapshot
      description: |
        Probability, deltas, volume, status, resolution source, active
        signals, related markets, in one payload.
      security:
        - x402: []
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
          description: Polymarket slug (e.g. "btc-above-100k").
      responses:
        '200': { description: OK — snapshot envelope }
        '402': { description: Payment required }
        '404': { description: Market not found }
  /api/x402/v1/markets/{id}/why:
    get:
      tags: [markets]
      summary: Why did this market move? — interpreted explanation
      description: |
        Returns a list of factors with structured evidence + per-factor
        confidence, plus a what-to-verify checklist. Deterministic — no
        LLM call. Same logic as the in-app "Why did it move?" panel.
      security:
        - x402: []
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      responses:
        '200': { description: OK — why envelope }
        '402': { description: Payment required }
        '404': { description: Market not found }
  /api/x402/v1/markets/{id}/resolution-risk:
    get:
      tags: [markets]
      summary: Resolution-risk analysis
      description: |
        Source extraction + UMA dispute status + ambiguity hints +
        what-to-verify list. The single most important call before
        treating any market move as news.
      security:
        - x402: []
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      responses:
        '200': { description: OK — resolution-risk envelope }
        '402': { description: Payment required }
        '404': { description: Market not found }
  /api/x402/v1/events/{slug}/cluster:
    get:
      tags: [events]
      summary: All markets around one event
      description: |
        Aggregates every market under a Polymarket event: cluster volume,
        average move, top movers, resolution risks, and a one-line summary.
      security:
        - x402: []
      parameters:
        - in: path
          name: slug
          required: true
          schema: { type: string }
      responses:
        '200': { description: OK — event cluster envelope }
        '402': { description: Payment required }
        '404': { description: Event not found }
  /api/x402/v1/signals:
    get:
      tags: [signals]
      summary: Live signal feed across top markets
      security:
        - x402: []
      parameters:
        - in: query
          name: limit
          schema: { type: integer, default: 20, maximum: 60 }
        - in: query
          name: kind
          schema: { type: string }
          description: Comma-separated. Allowed — flow,momentum,divergence,resolution_risk
        - in: query
          name: min_evidence
          schema: { type: string, enum: [low, medium, high] }
        - in: query
          name: category
          schema: { type: string }
      responses:
        '200': { description: OK — signals envelope (returns empty array when no signals match the filter — this endpoint never 404s) }
        '402': { description: Payment required }
        '429': { description: Rate-limited (60 req/min/IP) }
  /api/x402/v1/watchlist/summary:
    post:
      tags: [portfolio]
      summary: Compose intelligence over a custom watchlist
      description: |
        Pass a set of slugs, themes (category slugs), and wallet
        addresses; receive movers, signals, resolution changes, whale
        flow, and a priority-scored items list back.
      security:
        - x402: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                markets:
                  type: array
                  items: { type: string }
                  maxItems: 50
                themes:
                  type: array
                  items: { type: string }
                  maxItems: 10
                wallets:
                  type: array
                  items: { type: string, pattern: '^0x[a-fA-F0-9]{40}$' }
                  maxItems: 10
      responses:
        '200': { description: OK — watchlist summary envelope }
        '402': { description: Payment required }
        '400': { description: Empty or invalid request }
  /api/x402/v1/portfolio/risk:
    post:
      tags: [portfolio]
      summary: Portfolio-risk cockpit for one or more wallets
      description: |
        Read-only aggregation across Polymarket addresses: open
        notional, unrealized PnL, largest positions, upcoming
        resolutions, single-position concentration, stale-pricing
        exposure. No wallet signatures.
      security:
        - x402: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [addresses]
              properties:
                addresses:
                  type: array
                  items: { type: string, pattern: '^0x[a-fA-F0-9]{40}$' }
                  minItems: 1
                  maxItems: 10
      responses:
        '200': { description: OK — portfolio risk envelope }
        '402': { description: Payment required }
        '400': { description: Empty or invalid request }
  /api/x402/v1/share-card/{slug}:
    get:
      tags: [markets]
      summary: Pre-formatted share artefact for a market move
      description: |
        Returns the OG image URL plus ready-to-publish copy variants
        for X (single post + thread), Telegram, Discord, and
        newsletter. Generated deterministically from the live
        market state — no LLM call.
      security:
        - x402: []
      parameters:
        - in: path
          name: slug
          required: true
          schema: { type: string }
      responses:
        '200': { description: OK — share-card envelope }
        '402': { description: Payment required }
        '404': { description: Market not found }
  /api/x402/v1/wallets/{address}:
    get:
      tags: [portfolio]
      summary: Per-wallet intelligence with dimensional scoring
      description: |
        Open notional, PnL, win rate, top category, plus the five
        independent dimensions: activity / performance confidence /
        specialization / early-entry / copy-risk. Same scoring
        engine as the in-app /wallets/{address} surface.
      security:
        - x402: []
      parameters:
        - in: path
          name: address
          required: true
          schema: { type: string, pattern: '^0x[a-fA-F0-9]{40}$' }
      responses:
        '200': { description: OK — wallet intelligence envelope }
        '402': { description: Payment required }
        '400': { description: Invalid address }
  /api/x402/v1/category/{slug}/intelligence:
    get:
      tags: [markets]
      summary: Category-level intelligence dashboard
      description: |
        Aggregate metrics for one category slug: total volume, avg
        volatility, top movers count, source-risk count, whale
        flow USD, resolving-soon count, and a one-line summary.
        Known slugs — crypto, ai, politics, geopolitics, macro,
        sports, tech, entertainment, science, business, weather.
      security:
        - x402: []
      parameters:
        - in: path
          name: slug
          required: true
          schema: { type: string }
      responses:
        '200': { description: OK — category intelligence envelope }
        '402': { description: Payment required }
        '404': { description: Unknown category slug }
  /api/x402/v1/backtest/{kind}:
    get:
      tags: [signals]
      summary: Backtest verdict for one signal kind
      description: |
        Live backtest run over the top-15 highest-volume markets
        for `momentum` and `divergence`. For `flow`,
        `resolution_risk`, and `news_lag`, returns a
        `forward-only` verdict with the documented reason — no
        fabricated numbers.
      security:
        - x402: []
      parameters:
        - in: path
          name: kind
          required: true
          schema:
            type: string
            enum: [momentum, divergence, flow, resolution_risk, news_lag]
      responses:
        '200': { description: OK — backtest envelope }
        '402': { description: Payment required }
        '404': { description: Unknown signal kind }
  /api/x402/v1/search:
    get:
      tags: [markets]
      summary: Free-text market search
      description: |
        Find a Polymarket market by free-text query. Useful as the
        first call when an agent has the user's prose ("Bitcoin
        $100k") but not the slug. Same algorithm as the in-app
        command palette — agents and humans get identical results.
      security:
        - x402: []
      parameters:
        - in: query
          name: q
          required: true
          schema: { type: string, minLength: 1, maxLength: 200 }
        - in: query
          name: limit
          schema: { type: integer, default: 10, maximum: 20 }
        - in: query
          name: category
          schema: { type: string }
      responses:
        '200': { description: OK — search envelope }
        '402': { description: Payment required }
        '400': { description: Missing or invalid query }
  /api/x402/v1/events:
    get:
      tags: [events]
      summary: Top events by 24h volume
      description: |
        Discover event slugs to fan out to
        `/api/x402/v1/events/{slug}/cluster`. The first call an
        agent should make when it doesn't yet know which events
        are alive.
      security:
        - x402: []
      parameters:
        - in: query
          name: limit
          schema: { type: integer, default: 20, maximum: 50 }
        - in: query
          name: category
          schema: { type: string }
      responses:
        '200': { description: OK — events list envelope }
        '402': { description: Payment required }
  /api/x402/v1/signals/{kind}:
    get:
      tags: [signals]
      summary: Live signals filtered to one kind
      description: |
        Drill-down per signal kind (faster + simpler than the
        generic /signals endpoint with `?kind=` when an agent
        only ever cares about one kind, e.g. a Discord bot that
        only re-broadcasts resolution-risk warnings).
      security:
        - x402: []
      parameters:
        - in: path
          name: kind
          required: true
          schema:
            type: string
            enum: [momentum, divergence, flow, resolution_risk, news_lag]
        - in: query
          name: limit
          schema: { type: integer, default: 20, maximum: 60 }
        - in: query
          name: min_evidence
          schema: { type: string, enum: [low, medium, high] }
        - in: query
          name: category
          schema: { type: string }
      responses:
        '200': { description: OK — kind-specific signals envelope }
        '402': { description: Payment required }
        '404': { description: Unknown signal kind }
  /api/x402/v1/trades/recent:
    get:
      tags: [markets]
      summary: Raw recent whale trades feed
      description: |
        Last N $5k+ whale trades with side, size, wallet, market.
        Designed for trading bots and Discord/Telegram channel bots
        that re-broadcast significant flow.
      security:
        - x402: []
      parameters:
        - in: query
          name: limit
          schema: { type: integer, default: 20, maximum: 50 }
        - in: query
          name: min_usd
          schema: { type: number, default: 5000, minimum: 1000 }
        - in: query
          name: side
          schema: { type: string, enum: [BUY, SELL] }
      responses:
        '200': { description: OK — trades feed envelope }
        '402': { description: Payment required }
  /api/x402/v1/health:
    get:
      tags: [markets]
      summary: Free health check + endpoint inventory
      description: |
        Always free (`usdc_per_call: 0`, `payment_status: "preview"`).
        Returns the full X402_CATALOG with current pricing,
        whether payment enforcement is on, and live upstream
        Polymarket health. Agents ping this before deciding which
        paid endpoints to call.
      responses:
        '200': { description: OK — health + inventory envelope }
