openapi: 3.0.3
info:
  title: QuantDB Agent Safe Access API
  version: 0.1.0
  description: >
    Safe, audited, API-mediated access contract for agents that need approved
    read-only QuantDB-backed data. This contract forbids direct PostgreSQL
    access, arbitrary SQL execution, secrets in documentation, and PII examples.
servers:
  - url: https://data.quantclaw.org
    description: Production QuantDB API
security:
  - serviceAuth: []
paths:
  /health:
    get:
      summary: Service health
      description: Returns service health only. No row data.
      security: []
      responses:
        '200':
          description: Healthy or degraded service status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthStatus'
  /docs:
    get:
      summary: Existing QuantDB API Swagger UI
      description: Human-readable documentation for the existing QuantDB API endpoints.
      responses:
        '200':
          description: Swagger UI HTML
  /api/v1/schema:
    get:
      summary: Approved metadata schema explorer API
      description: Returns approved metadata only. Do not include row samples or PII values.
      security:
        - serviceAuth: [quantdb.read.metadata]
      responses:
        '200':
          description: Approved schema metadata
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
  /v2/instruments:
    get:
      summary: List instruments
      description: Bounded instrument metadata endpoint. Use filters and limits where supported.
      security:
        - serviceAuth: [quantdb.read.market_data]
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Offset'
      responses:
        '200':
          description: Instrument metadata response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
  /v2/prices/history:
    get:
      summary: Historical price bars
      description: Point-in-time market price history. Use explicit instruments and date bounds.
      security:
        - serviceAuth: [quantdb.read.market_data]
      parameters:
        - name: symbol
          in: query
          schema: { type: string }
        - name: instrument_id
          in: query
          schema: { type: integer }
        - name: start_date
          in: query
          schema: { type: string, format: date }
        - name: end_date
          in: query
          schema: { type: string, format: date }
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Bounded historical bars
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
  /v2/factors:
    get:
      summary: Factor scores and factor metadata
      description: Use as-of dates for research to avoid look-ahead bias.
      security:
        - serviceAuth: [quantdb.read.market_data]
      parameters:
        - name: as_of
          in: query
          schema: { type: string, format: date }
        - name: symbol
          in: query
          schema: { type: string }
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Bounded factor response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
  /v2/signals:
    get:
      summary: Signals
      description: Read-only signal data. Requires point-in-time handling for research/backtests.
      security:
        - serviceAuth: [quantdb.read.market_data]
      parameters:
        - name: as_of
          in: query
          schema: { type: string, format: date }
        - name: symbol
          in: query
          schema: { type: string }
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Bounded signals response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
  /v2/portfolio/universe:
    get:
      summary: Point-in-time eligible portfolio universe
      description: API-owned read-only portfolio universe. Not a personal/private portfolio endpoint.
      security:
        - serviceAuth: [quantdb.read.market_data]
      parameters:
        - name: as_of
          in: query
          schema: { type: string, format: date }
        - name: require_price
          in: query
          schema: { type: boolean, default: true }
        - name: min_positive_factors
          in: query
          schema: { type: integer }
        - name: min_quant_score
          in: query
          schema: { type: number }
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Offset'
      responses:
        '200':
          description: Bounded portfolio universe response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
  /v1/quantdb/named-query/{query_id}:
    post:
      summary: Execute a pre-approved read-only named query
      description: >
        No arbitrary SQL. Query must be pre-registered, parameterized,
        field-allowlisted, row-limited, redacted, audited, and PII-gated.
      security:
        - serviceAuth: [quantdb.read.named_query]
      parameters:
        - name: query_id
          in: path
          required: true
          schema:
            type: string
            pattern: '^[a-z0-9_:-]{3,80}$'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NamedQueryRequest'
      responses:
        '200':
          description: Redacted, bounded result set
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NamedQueryResponse'
        '403':
          description: Scope denied or PII gate failed
        '422':
          description: Unsupported query or unsafe parameter
components:
  securitySchemes:
    serviceAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: Service API key or gateway-issued token. Never place secrets in Swagger examples.
  parameters:
    Limit:
      name: limit
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 1000
        default: 100
    Offset:
      name: offset
      in: query
      schema:
        type: integer
        minimum: 0
        default: 0
  schemas:
    HealthStatus:
      type: object
      properties:
        status: { type: string, enum: [ok, degraded] }
        database: { type: string, enum: [connected, disconnected] }
        redis: { type: string, enum: [connected, disconnected] }
        version: { type: string }
        uptime: { type: string }
    NamedQueryRequest:
      type: object
      additionalProperties: false
      required: [request_id, caller_claw_id, purpose, expected_output_shape, params]
      properties:
        request_id:
          type: string
          description: Caller-generated request identifier for audit correlation.
        caller_claw_id:
          type: string
          description: Calling agent/claw identity.
        purpose:
          type: string
          minLength: 10
          description: Why the data is needed.
        expected_output_shape:
          type: string
          description: Expected fields, bounds, and aggregation level.
        params:
          type: object
          description: Query-specific typed parameters. No SQL strings.
          additionalProperties: true
    NamedQueryResponse:
      type: object
      properties:
        request_id: { type: string }
        query_id: { type: string }
        row_count: { type: integer }
        redaction_mode: { type: string }
        data:
          type: array
          maxItems: 1000
          items:
            type: object
            additionalProperties: true
        audit:
          type: object
          properties:
            caller_claw_id: { type: string }
            duration_ms: { type: integer }
            status: { type: string }
