openapi: 3.1.0
info:
  title: PORTIQA Portfolio Analysis API
  version: "2026-03-07-v5"
  description: >-
    Evaluate portfolio structure, concentration, component evidence, exposure,
    diagnostics, insights, and suggestions through one authenticated endpoint.
  contact:
    name: PORTIQA Support
    email: support@portiqa.com
    url: https://portiqa.ai/docs/
  license:
    name: PORTIQA Terms of Service
    url: https://portiqa.ai/legal/terms.html
servers:
  - url: https://portiqa.ai
    description: Production
paths:
  /evaluate:
    post:
      operationId: evaluatePortfolio
      summary: Evaluate a portfolio
      description: >-
        Resolves supplied tickers, enriches the positions from PORTIQA's data
        store, and returns portfolio scores, diagnostics, and suggestions.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EvaluationRequest"
            examples:
              marketValues:
                summary: Three positions and cash
                value:
                  risk_level: medium
                  cash_balance: 5000
                  positions:
                    - ticker: AAPL
                      market_value: 25000
                    - ticker: MSFT
                      market_value: 22000
                    - ticker: NVDA
                      market_value: 8000
      responses:
        "200":
          description: Portfolio evaluated successfully.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EvaluationEnvelope"
        "400":
          description: Invalid JSON, missing positions, or no evaluable positions.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Error" }
        "401":
          description: API key is missing or invalid.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Error" }
        "402":
          description: Subscription is inactive.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Error" }
        "413":
          description: Unique ticker count exceeds the plan limit.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Error" }
        "429":
          description: Monthly request limit has been reached.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Error" }
        "500":
          description: Evaluation failed.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Error" }
        "503":
          description: Authentication database is unavailable.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Error" }
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: PORTIQA API key
      description: Generate a ptq_ bearer token in the PORTIQA dashboard.
  schemas:
    EvaluationRequest:
      type: object
      required: [positions]
      properties:
        risk_level:
          type: string
          enum: [low, medium, high]
          default: medium
        cash_balance:
          type: number
          minimum: 0
          default: 0
          description: Cash in the same currency as position values.
        positions:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/Position"
      additionalProperties: false
    Position:
      type: object
      required: [ticker]
      properties:
        ticker:
          type: string
          minLength: 1
          examples: [AAPL]
        market_value:
          type: number
          minimum: 0
          description: Preferred when the client already knows the position value.
        amount:
          type: number
          minimum: 0
          description: Used with the latest available close price when market_value is absent.
      anyOf:
        - required: [market_value]
        - required: [amount]
      additionalProperties: false
    EvaluationEnvelope:
      type: object
      required: [ok, evaluation]
      properties:
        ok:
          type: boolean
          const: true
        evaluation:
          $ref: "#/components/schemas/Evaluation"
    Evaluation:
      type: object
      required:
        - engine_version
        - score
        - status
        - max_position_weight
        - effective_positions
        - score_components
        - coverage
        - diagnostics
        - portfolio_input
      properties:
        engine_version: { type: string }
        score: { type: number, minimum: 0, maximum: 100 }
        health_score: { type: number, minimum: 0, maximum: 100 }
        status: { type: string }
        status_key: { type: string }
        count: { type: integer, minimum: 0 }
        weighted_score: { type: number }
        max_position_weight: { type: number, minimum: 0, maximum: 100 }
        max_position_ticker: { type: string }
        effective_positions: { type: number, minimum: 0 }
        weighted_beta: { type: [number, "null"] }
        top_sector: { type: [string, "null"] }
        top_sector_weight: { type: number, minimum: 0, maximum: 100 }
        score_components:
          type: object
          additionalProperties: { type: number }
        coverage:
          type: object
          additionalProperties: { type: number }
        trajectory: { type: object, additionalProperties: true }
        context_alignment: { type: object, additionalProperties: true }
        exposure_plan: { type: object, additionalProperties: true }
        holding_analysis:
          type: array
          items: { type: object, additionalProperties: true }
        diagnostics: { type: object, additionalProperties: true }
        insights:
          type: array
          items: { type: object, additionalProperties: true }
        suggestions:
          type: array
          items: { type: object, additionalProperties: true }
        portfolio_input:
          type: object
          properties:
            cash_balance: { type: number }
            holdings_value: { type: number }
            total_value: { type: number }
            cash_pct: { type: number }
            invested_pct: { type: number }
            missing_tickers:
              type: array
              items: { type: string }
          additionalProperties: true
      additionalProperties: true
    Error:
      type: object
      required: [ok, error]
      properties:
        ok:
          type: boolean
          const: false
        error: { type: string }
        message: { type: string }
      additionalProperties: true
externalDocs:
  description: PORTIQA API documentation
  url: https://portiqa.ai/docs/api-reference.html
