openapi: 3.1.0
info:
  title: DecisioQ Public API
  version: 7.6.3
  description: Public server-to-server authentication, validation, discovery, usage, and decision-execution contract.
servers:
  - url: https://dde.vinquery.com
    description: Production DDE
tags:
  - name: Authentication
  - name: Decision Validation
  - name: Decision Execution
  - name: Discovery
  - name: Usage
paths:
  /connect/token:
    servers:
      - url: https://identity.vinquery.com
    post:
      tags: [Authentication]
      operationId: issueToken
      summary: Issue an audience-specific API Consumer JWT
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenRequest'
      responses:
        '200':
          description: JWT issued
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          description: Invalid or disabled API Consumer credentials
  /health:
    get:
      summary: Service health
      security: []
      responses:
        '200':
          description: DDE is responding
  /api/v1/validate:
    post:
      tags: [Decision Validation]
      operationId: validateDecision
      summary: Validate and prepare business data without executing or charging
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BusinessDecisionRequest'
      responses:
        '200':
          description: Validation completed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DecisionValidationResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /api/v1/decide:
    post:
      tags: [Decision Execution]
      operationId: executeDecision
      summary: Execute a business-data or Prepared Decision Input request
      parameters:
        - $ref: '#/components/parameters/CorrelationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/BusinessDecisionRequest'
                - $ref: '#/components/schemas/PreparedDecisionRequest'
      responses:
        '200':
          description: Decision executed
          headers:
            X-Correlation-Id:
              schema: { type: string }
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DecisionResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/v1/usage/summary:
    get:
      tags: [Usage]
      operationId: getUsageSummary
      summary: Get non-billable discovery and billable execution counters
      responses:
        '200':
          description: Usage summary
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Usage account not found
  /api/v1/industry-profiles:
    get:
      tags: [Discovery]
      operationId: listIndustryProfiles
      summary: List published, Production, certified Industry Profiles
      responses:
        '200':
          description: Industry Profile list
        '401':
          $ref: '#/components/responses/Unauthorized'
  /api/v1/industry-profiles/{profileId}:
    get:
      tags: [Discovery]
      operationId: getIndustryProfile
      parameters:
        - name: profileId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Industry Profile }
        '404': { description: Industry Profile not found }
  /api/v1/mapping-profiles:
    get:
      tags: [Discovery]
      operationId: listMappingProfiles
      summary: List governed Mapping Profile versions
      responses:
        '200': { description: Mapping Profile list }
        '401':
          $ref: '#/components/responses/Unauthorized'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
  parameters:
    CorrelationId:
      name: X-Correlation-Id
      in: header
      required: false
      schema:
        type: string
        maxLength: 200
  schemas:
    TokenRequest:
      type: object
      additionalProperties: false
      required: [clientId, clientSecret, audience]
      properties:
        clientId: { type: string, minLength: 1 }
        clientSecret: { type: string, minLength: 1, writeOnly: true }
        audience:
          type: string
          const: vinquery:api:decisioq
    TokenResponse:
      type: object
      required: [service, version, requestId, operation, jwtToken, tokenType, audience, expiresIn, expiresUtc]
      properties:
        service: { type: string, example: decisioq }
        version: { type: string, example: 7.6.3 }
        requestId: { type: string }
        operation: { type: string, example: Authenticate }
        date: { type: string }
        timestampUtc: { type: string, format: date-time }
        jwtToken: { type: string, writeOnly: true }
        tokenType: { type: string, example: Bearer }
        audience: { type: string, example: 'vinquery:api:decisioq' }
        expiresIn: { type: integer, minimum: 1 }
        expiresUtc: { type: string, format: date-time }
        userId: { type: string }
        consumerName: { type: string }
        apiConsumerId: { type: string, format: uuid }
    RequestContext:
      type: object
      properties:
        correlationId: { type: string, maxLength: 200 }
        locale: { type: string }
        measurementSystem: { type: string }
        currency: { type: string }
        timeZone: { type: string }
        jurisdiction:
          type: object
          properties:
            countryCode: { type: string }
            regionCode: { type: string }
        attributes:
          type: object
          additionalProperties: true
    BusinessDecisionRequest:
      type: object
      required: [businessData]
      properties:
        industry: { type: string }
        decisionType: { type: string }
        decisionVersion: { type: string }
        decisionId: { type: string }
        businessData:
          type: object
          additionalProperties: true
        requestContext:
          $ref: '#/components/schemas/RequestContext'
      anyOf:
        - required: [decisionId]
        - required: [decisionType]
    PreparedDecisionOption:
      type: object
      required: [name]
      properties:
        optionId: { type: string }
        name: { type: string, minLength: 1 }
        values:
          type: object
          additionalProperties: { type: number }
        scores:
          type: object
          deprecated: true
          additionalProperties: { type: number }
    PreparedDecisionRequest:
      type: object
      required: [decisionId, options]
      properties:
        decisionId: { type: string, minLength: 1 }
        profileId: { type: [string, 'null'], default: balanced }
        scenarioId: { type: [string, 'null'] }
        requestContext:
          $ref: '#/components/schemas/RequestContext'
        options:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/PreparedDecisionOption'
        runSensitivity: { type: boolean, default: false }
        algorithm: { type: string, enum: [TOPSIS, WSM], default: TOPSIS }
        weightStrategy: { type: string, enum: [Expert, AHP], default: Expert }
        constraintOverrides: { type: array, items: { type: object } }
    DecisionValidationResponse:
      type: object
      required: [service, version, requestId, operation, success, decisionId, warnings, diagnostics]
      properties:
        service: { type: string }
        version: { type: string }
        requestId: { type: string }
        operation: { type: string }
        success: { type: boolean }
        decisionId: { type: string }
        decisionInput: {}
        warnings: { type: array, items: { type: string } }
        diagnostics: { type: array, items: { type: object } }
    DecisionResponse:
      type: object
      required: [service, version, requestId, operation, success, timestampUtc, warnings]
      properties:
        service: { type: string, example: decisioq }
        version: { type: string, example: 7.6.3 }
        requestId: { type: string }
        operation: { type: string, example: Decide }
        success: { type: boolean, const: true }
        industryId: { type: [string, 'null'] }
        decisionType: { type: [string, 'null'] }
        decisionVersion: { type: [string, 'null'] }
        timestampUtc: { type: string, format: date-time }
        decisionResult: { type: [object, 'null'], additionalProperties: true }
        sensitivityResult: { type: [object, 'null'], additionalProperties: true }
        confidence: { type: [number, 'null'] }
        explanation:
          oneOf:
            - $ref: '#/components/schemas/DecisionExplanation'
            - type: 'null'
        executionTrace: { type: [object, 'null'], additionalProperties: true }
        constraintSummary: { type: [object, 'null'], additionalProperties: true }
        warnings: { type: array, items: { type: string } }
        ahp: { type: [object, 'null'], additionalProperties: true }
        requestContext: { type: [object, 'null'], additionalProperties: true }
    DecisionExplanation:
      type: object
      additionalProperties: false
      required: [summary, whyRecommended, keyDrivers, tradeoffs, competitors, sensitivitySummary, scenarioSummary, risks, nextSteps, assumptions]
      properties:
        summary: { type: string }
        whyRecommended: { type: string }
        keyDrivers: { type: array, items: { type: string } }
        tradeoffs: { type: array, items: { type: string } }
        competitors: { type: array, items: { type: string } }
        sensitivitySummary: { type: string }
        scenarioSummary: { type: string }
        risks: { type: array, items: { type: string } }
        nextSteps: { type: array, items: { type: string } }
        assumptions: { type: array, items: { type: string } }
    ApiError:
      type: object
      additionalProperties: true
      properties:
        service: { type: string }
        version: { type: string }
        requestId: { type: string }
        operation: { type: string }
        success: { type: boolean, const: false }
        error: { type: object, additionalProperties: true }
  responses:
    BadRequest:
      description: Request validation failed
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ApiError' }
    Unauthorized:
      description: JWT missing, expired, invalid, or has the wrong audience
    Forbidden:
      description: Authenticated caller is not permitted
    TooManyRequests:
      description: Applicable rate limit exceeded
      headers:
        Retry-After:
          schema: { type: integer }
security:
  - bearerAuth: []
