openapi: 3.1.0
info:
  title: CandleScript Charting Platform API
  version: 1.0.0
  description: |
    Partner-facing REST surface for market data, widgets, API keys, webhooks, and CandleScript.
    Session-authenticated endpoints require a logged-in user cookie or bearer session token.
servers:
  - url: https://api.candlescript.io
    description: Production
  - url: http://localhost:4000
    description: Local development
tags:
  - name: Market data
    description: Public API key endpoints under `/v1/public/*`
  - name: Streaming
    description: WebSocket quote stream
  - name: API keys
    description: Partner key management
  - name: Widgets
    description: Embeddable mini charts
  - name: Webhooks
    description: Outbound event delivery
  - name: CandleScript
    description: Compile, run, and backtest scripts (session auth)
  - name: User resources
    description: Watchlists, alerts, paper trading (session auth)
components:
  securitySchemes:
    ApiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: cpk_
    SessionAuth:
      type: apiKey
      in: cookie
      name: session
  schemas:
    Error:
      type: object
      properties:
        error:
          type: string
    Bar:
      type: object
      properties:
        ts: { type: string, format: date-time }
        open: { type: number }
        high: { type: number }
        low: { type: number }
        close: { type: number }
        volume: { type: number }
    KeyStatisticItem:
      type: object
      required: [id, value, format]
      properties:
        id: { type: string }
        value: { type: [number, "null"] }
        format:
          type: string
          enum: [number, decimal2, decimal4, compact, currency, compactCurrency, percentFraction]
        unit: { type: [string, "null"] }
        source: { type: [string, "null"] }
        asOf: { type: [string, "null"], format: date-time }
    KeyStatistics:
      type: object
      required: [assetClass, items]
      properties:
        assetClass: { type: string }
        currency: { type: string }
        asOf: { type: [string, "null"], format: date-time }
        source: { type: [string, "null"] }
        items:
          type: array
          items: { $ref: "#/components/schemas/KeyStatisticItem" }
    SymbolDetailContextSummary:
      type: object
      properties:
        newsCount: { type: integer }
        eventCount: { type: integer }
        ideaCount: { type: integer }
        tweetCount: { type: integer }
        nextEarningsAt: { type: [string, "null"], format: date-time }
        marketCap: { type: [number, "null"], deprecated: true, description: "Prefer keyStatistics.items[id=marketCap]." }
        peRatio: { type: [number, "null"], deprecated: true, description: "Prefer keyStatistics.items[id=peRatio]." }
        freeCashFlowYield: { type: [number, "null"] }
        keyStatistics: { $ref: "#/components/schemas/KeyStatistics" }
    SymbolDetail:
      type: object
      properties:
        symbol: { type: object, additionalProperties: true }
        quote: { type: object, additionalProperties: true }
        stats: { type: object, additionalProperties: true }
        technicals: { type: object, additionalProperties: true }
        context:
          type: object
          properties:
            summary: { $ref: "#/components/schemas/SymbolDetailContextSummary" }
            news: { type: array, items: { type: object, additionalProperties: true } }
            fundamentals: { type: [object, "null"], additionalProperties: true }
            ideas: { type: array, items: { type: object, additionalProperties: true } }
            tweets: { type: array, items: { type: object, additionalProperties: true } }
paths:
  /v1/public/symbols:
    get:
      tags: [Market data]
      summary: Search symbols
      security: [{ ApiKeyAuth: [] }]
      parameters:
        - in: query
          name: query
          schema: { type: string }
        - in: query
          name: assetClass
          schema: { type: string }
      responses:
        "200":
          description: Symbol matches
  /v1/public/quotes:
    get:
      tags: [Market data]
      summary: Latest quotes
      security: [{ ApiKeyAuth: [] }]
      parameters:
        - in: query
          name: symbols
          required: true
          schema: { type: string }
          description: Comma-separated symbol ids
      responses:
        "200":
          description: Quote snapshot
  /v1/public/history:
    get:
      tags: [Market data]
      summary: Historical OHLCV
      security: [{ ApiKeyAuth: [] }]
      parameters:
        - { in: query, name: symbol, required: true, schema: { type: string } }
        - { in: query, name: resolution, schema: { type: string, default: "1D" } }
        - { in: query, name: from, schema: { type: string, format: date-time } }
        - { in: query, name: to, schema: { type: string, format: date-time } }
      responses:
        "200":
          description: Bar series
  /v1/public/screeners:
    get:
      tags: [Market data]
      summary: Run screener
      security: [{ ApiKeyAuth: [] }]
      parameters:
        - { in: query, name: assetClass, schema: { type: string } }
        - { in: query, name: pageSize, schema: { type: integer } }
      responses:
        "200":
          description: Screener rows
  /v1/stream/quotes:
    get:
      tags: [Streaming]
      summary: WebSocket quote stream (upgrade)
      description: Connect with `wss://` and pass `symbols` query param. Requires Plus or higher and `quotes:read` scope.
      security: [{ ApiKeyAuth: [] }]
      parameters:
        - { in: query, name: symbols, required: true, schema: { type: string } }
      responses:
        "101":
          description: Switching Protocols
  /v1/public-api/keys:
    get:
      tags: [API keys]
      summary: List API keys
      security: [{ SessionAuth: [] }]
      responses:
        "200":
          description: Keys for the account
    post:
      tags: [API keys]
      summary: Create API key
      security: [{ SessionAuth: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name: { type: string }
                scopes:
                  type: array
                  items: { type: string }
                quotaPerDay: { type: integer }
      responses:
        "201":
          description: Created key (token shown once)
  /v1/public-api/keys/revoke:
    post:
      tags: [API keys]
      summary: Revoke API key
      security: [{ SessionAuth: [] }]
      responses:
        "200":
          description: Revoked
  /v1/public-api/usage:
    get:
      tags: [API keys]
      summary: Key usage for current quota window
      parameters:
        - { in: query, name: apiKey, schema: { type: string } }
      responses:
        "200":
          description: Usage counters
  /v1/widgets:
    get:
      tags: [Widgets]
      summary: List widget configs
      security: [{ SessionAuth: [] }]
      responses:
        "200":
          description: Widget list
    post:
      tags: [Widgets]
      summary: Create widget config
      security: [{ SessionAuth: [] }]
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                id: { type: string }
                symbolId: { type: string }
                theme: { type: string, enum: [dark, light] }
                resolution: { type: string }
      responses:
        "201":
          description: Widget created
  /widgets/mini-chart:
    get:
      tags: [Widgets]
      summary: Public mini-chart embed (query params)
      parameters:
        - { in: query, name: symbol, schema: { type: string } }
        - { in: query, name: theme, schema: { type: string } }
        - { in: query, name: resolution, schema: { type: string } }
      responses:
        "200":
          description: HTML embed document with SVG chart
  /widgets/{widgetId}:
    get:
      tags: [Widgets]
      summary: Render saved widget embed
      parameters:
        - { in: path, name: widgetId, required: true, schema: { type: string } }
      responses:
        "200":
          description: HTML embed document
  /v1/webhooks/endpoints:
    get:
      tags: [Webhooks]
      summary: List webhook endpoints
      security: [{ SessionAuth: [] }]
      responses:
        "200":
          description: Endpoints
    post:
      tags: [Webhooks]
      summary: Create webhook endpoint
      security: [{ SessionAuth: [] }]
      responses:
        "201":
          description: Created
  /v1/webhooks/deliveries:
    get:
      tags: [Webhooks]
      summary: List delivery attempts
      security: [{ SessionAuth: [] }]
      responses:
        "200":
          description: Deliveries
  /v1/scripts/compile:
    post:
      tags: [CandleScript]
      summary: Compile CandleScript source
      security: [{ SessionAuth: [] }]
      responses:
        "200":
          description: Compile result
  /v1/scripts/run:
    post:
      tags: [CandleScript]
      summary: Run compiled script on bars
      security: [{ SessionAuth: [] }]
      responses:
        "200":
          description: Paint output
  /v1/scripts/diagnostics:
    post:
      tags: [CandleScript]
      summary: List language builtins and diagnostics
      security: [{ SessionAuth: [] }]
      responses:
        "200":
          description: Diagnostics catalog
  /v1/backtests/run:
    post:
      tags: [CandleScript]
      summary: Run strategy backtest
      security: [{ SessionAuth: [] }]
      responses:
        "200":
          description: Backtest metrics and equity curve
  /v1/backtests/jobs:
    get:
      tags: [CandleScript]
      summary: List async backtest jobs
      security: [{ SessionAuth: [] }]
      responses:
        "200":
          description: Jobs
    post:
      tags: [CandleScript]
      summary: Queue async backtest job
      security: [{ SessionAuth: [] }]
      responses:
        "202":
          description: Job accepted
  /v1/watchlists/default:
    get:
      tags: [User resources]
      summary: Default watchlist
      security: [{ SessionAuth: [] }]
      responses:
        "200":
          description: Watchlist
  /v1/symbol-detail:
    get:
      tags: [User resources]
      summary: Symbol detail with quote, stats, and market context
      description: |
        Anonymous-safe aggregate for public symbol pages and richer aggregate consumers.
        `context.summary.keyStatistics` is the canonical asset-aware metrics block; legacy
        `marketCap` / `peRatio` fields are deprecated mirrors.
      parameters:
        - in: query
          name: symbol
          schema: { type: string }
          description: Symbol id (e.g. `NASDAQ:AAPL`, `COINBASE:BTCUSD`)
      responses:
        "200":
          description: Symbol detail payload
          content:
            application/json:
              schema: { $ref: "#/components/schemas/SymbolDetail" }
  /v1/context/key-statistics:
    get:
      tags: [Market context]
      summary: Asset-aware key statistics without the full symbol aggregate
      description: Anonymous-safe compact endpoint for workstation details and other frequently changing symbol surfaces.
      parameters:
        - in: query
          name: symbol
          schema: { type: string, default: "NASDAQ:AAPL" }
      responses:
        "200":
          description: Key statistics payload
          content:
            application/json:
              schema:
                type: object
                required: [keyStatistics]
                properties:
                  keyStatistics: { $ref: "#/components/schemas/KeyStatistics" }
  /v1/alerts:
    get:
      tags: [User resources]
      summary: List alerts
      security: [{ SessionAuth: [] }]
      responses:
        "200":
          description: Alerts
  /v1/trading/orders:
    post:
      tags: [User resources]
      summary: Submit paper order
      security: [{ SessionAuth: [] }]
      responses:
        "201":
          description: Order accepted
