openapi: 3.0.3
info:
  title: Panoptic Scans API
  description: |
    Complete API for Panoptic Scans - a comprehensive security scanning platform.
    
    ## Authentication
    All endpoints (except `/api/token`) require Bearer token authentication.
    
    1. Obtain your API key from your account settings
    2. Exchange it for a JWT token using `/api/token`
    3. Include the token in the Authorization header: `Bearer {token}`
    4. Tokens can be refreshed using `/api/refresh`
    
    ## Rate Limits
    API requests are subject to rate limiting based on your subscription tier.
    
    ## Pagination
    List endpoints support pagination with `per_page` parameter (max 100).
  version: 1.0.0
  contact:
    name: Panoptic Scans Support
    url: https://panopticscans.com
  license:
    name: Proprietary
servers:
  - url: https://panopticscans.com
    description: Production server
  - url: http://localhost:8000
    description: Local development server

tags:
  - name: Authentication
    description: Authentication and token management
  - name: Scans
    description: Security scan management
  - name: Vulnerabilities
    description: Vulnerability tracking and management
  - name: Targets
    description: Target asset management

paths:
  /api/token:
    post:
      tags:
        - Authentication
      summary: Get API Token
      description: Exchange your API key for a JWT authentication token
      operationId: getToken
      parameters:
        - name: key
          in: query
          required: true
          description: Your API key from account settings
          schema:
            type: string
          example: YOUR_API_KEY_HERE
      responses:
        '200':
          description: Token generated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  token:
                    type: string
                    description: JWT authentication token
                  expires_in:
                    type: integer
                    description: Token expiration time in seconds
        '401':
          description: Invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /api/refresh:
    post:
      tags:
        - Authentication
      summary: Refresh Token
      description: Refresh your authentication token
      operationId: refreshToken
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Token refreshed successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  token:
                    type: string
                  expires_in:
                    type: integer
        '401':
          $ref: '#/components/responses/UnauthorizedError'

  /api/user:
    get:
      tags:
        - Authentication
      summary: Get Authenticated User
      description: Get current user information
      operationId: getUser
      security:
        - bearerAuth: []
      responses:
        '200':
          description: User information retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '401':
          $ref: '#/components/responses/UnauthorizedError'

  /api/scans:
    get:
      tags:
        - Scans
      summary: List All Scans
      description: Retrieve all scans for the authenticated user
      operationId: listScans
      security:
        - bearerAuth: []
      responses:
        '200':
          description: List of scans
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Scan'
        '401':
          $ref: '#/components/responses/UnauthorizedError'

  /api/new-scan:
    post:
      tags:
        - Scans
      summary: Create New Scan
      description: |
        Create a new security scan. Target format varies by scan type:
        - **Nmap/OpenVAS/Nuclei**: IP addresses, hostnames, or CIDR ranges (without http/https)
        - **ZAP**: Full URLs with http:// or https://
      operationId: createScan
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - scan_name
                - scan_type
                - scan_frequency
                - scan_time_zone
                - scan_email
                - scan_target
              properties:
                scan_name:
                  type: string
                  maxLength: 255
                  description: Name for the scan
                  example: API Test Scan
                scan_type:
                  type: string
                  enum: [Nmap, OpenVas, ZAP, Nuclei]
                  description: Type of security scan
                  example: Nmap
                scan_frequency:
                  type: string
                  enum: [One-time, Daily, Weekly, Monthly, Quarterly, Yearly]
                  description: Scan frequency
                  example: One-time
                scan_start_date:
                  type: string
                  maxLength: 25
                  description: Start date (for scheduled scans)
                  example: September 23, 2025
                scan_start_time:
                  type: string
                  maxLength: 10
                  description: Start time (for scheduled scans)
                  example: 8:45 PM
                scan_time_zone:
                  type: string
                  maxLength: 35
                  description: Timezone for scheduled scan
                  example: America/New_York
                scan_email:
                  type: string
                  format: email
                  maxLength: 100
                  description: Email address for scan results
                  example: user@example.com
                scan_target:
                  type: string
                  description: Target(s) - format depends on scan_type. Multiple targets separated by commas, spaces, or newlines
                  example: scanme.nmap.org
      responses:
        '200':
          description: Scan created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    description: Scan UUID
                    example: e510ab1c-1dec-41a5-86c9-9499220912ac
        '422':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'

  /api/scans/{uuid}:
    put:
      tags:
        - Scans
      summary: Rename Scan
      description: Update the name of an existing scan
      operationId: updateScan
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/ScanUUID'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - scan_name
              properties:
                scan_name:
                  type: string
                  maxLength: 255
                  example: Updated Scan Name
      responses:
        '200':
          description: Scan name updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Successfully updated scan name.
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/ServerError'

    delete:
      tags:
        - Scans
      summary: Delete Scan
      description: Delete a scan and all associated data
      operationId: deleteScan
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/ScanUUID'
      responses:
        '200':
          description: Scan deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Successfully deleted scan.
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/ServerError'

  /api/scans/{uuid}/runs:
    get:
      tags:
        - Scans
      summary: List Scan Runs
      description: Get all available runs for a scan with available report formats
      operationId: getScanRuns
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/ScanUUID'
      responses:
        '200':
          description: List of scan runs
          content:
            application/json:
              schema:
                type: object
                properties:
                  scan:
                    type: object
                    properties:
                      uuid:
                        type: string
                      name:
                        type: string
                      type:
                        type: string
                      current_progress:
                        type: string
                      run_count:
                        type: integer
                      total_runs:
                        type: integer
                  runs:
                    type: array
                    items:
                      type: object
                      properties:
                        run_index:
                          type: integer
                        run_folder:
                          type: integer
                        status:
                          type: string
                          enum: [current, completed]
                        completed_at:
                          type: string
                          format: date-time
                        scan_progress:
                          type: string
                        available_formats:
                          type: array
                          items:
                            type: string
                            enum: [pdf, csv, html, txt, xml]
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  /api/scans/pdf/{uuid}:
    get:
      tags:
        - Scans
      summary: Get PDF Report
      description: Download scan results as PDF (optionally for a specific run)
      operationId: getPDF
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/ScanUUID'
        - $ref: '#/components/parameters/RunIndex'
      responses:
        '200':
          description: PDF file
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  /api/scans/csv/{uuid}:
    get:
      tags:
        - Scans
      summary: Get CSV Report
      description: Download scan results as CSV (optionally for a specific run)
      operationId: getCSV
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/ScanUUID'
        - $ref: '#/components/parameters/RunIndex'
      responses:
        '200':
          description: CSV file
          content:
            text/csv:
              schema:
                type: string
                format: binary
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  /api/scans/html/{uuid}:
    get:
      tags:
        - Scans
      summary: Get HTML Report
      description: Download scan results as HTML (optionally for a specific run)
      operationId: getHTML
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/ScanUUID'
        - $ref: '#/components/parameters/RunIndex'
      responses:
        '200':
          description: HTML file
          content:
            text/html:
              schema:
                type: string
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  /api/scans/txt/{uuid}:
    get:
      tags:
        - Scans
      summary: Get TXT Report
      description: Download scan results as plain text (optionally for a specific run)
      operationId: getTXT
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/ScanUUID'
        - $ref: '#/components/parameters/RunIndex'
      responses:
        '200':
          description: Text file
          content:
            text/plain:
              schema:
                type: string
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  /api/scans/xml/{uuid}:
    get:
      tags:
        - Scans
      summary: Get XML Report
      description: Download scan results as XML (optionally for a specific run)
      operationId: getXML
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/ScanUUID'
        - $ref: '#/components/parameters/RunIndex'
      responses:
        '200':
          description: XML file
          content:
            application/xml:
              schema:
                type: string
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  /api/vulnerabilities:
    get:
      tags:
        - Vulnerabilities
      summary: List All Vulnerabilities
      description: Get all vulnerabilities with optional filtering, sorting, and pagination
      operationId: listVulnerabilities
      security:
        - bearerAuth: []
      parameters:
        - name: severity
          in: query
          description: Filter by severity level
          schema:
            type: string
            enum: [Critical, High, Medium, Low, Informational]
          example: Critical
        - name: status
          in: query
          description: Filter by status
          schema:
            type: string
            enum: [Unresolved, Accepted, False Positive]
          example: Unresolved
        - name: target
          in: query
          description: Filter by target hostname or IP
          schema:
            type: string
          example: example.com
        - name: search
          in: query
          description: Search in vulnerability name, hostname, IP, or scan name
          schema:
            type: string
          example: SQL
        - name: sort
          in: query
          description: Field to sort by
          schema:
            type: string
            enum: [name, threat, cvss_base, hostname, host_ip, scan_type, status, created_at]
            default: cvss_base
          example: cvss_base
        - name: direction
          in: query
          description: Sort direction
          schema:
            type: string
            enum: [asc, desc]
            default: desc
          example: desc
        - name: per_page
          in: query
          description: Items per page (max 100)
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 10
          example: 25
      responses:
        '200':
          description: List of vulnerabilities
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Vulnerability'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/UnauthorizedError'

  /api/vulnerabilities/{id}:
    get:
      tags:
        - Vulnerabilities
      summary: Get Vulnerability Details
      description: Get detailed information about a specific vulnerability
      operationId: getVulnerability
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Vulnerability ID
          schema:
            type: integer
          example: 123
      responses:
        '200':
          description: Vulnerability details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Vulnerability'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  /api/vulnerabilities/{id}/accept:
    post:
      tags:
        - Vulnerabilities
      summary: Accept Vulnerability
      description: Mark a vulnerability as accepted (risk acknowledged)
      operationId: acceptVulnerability
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Vulnerability ID
          schema:
            type: integer
          example: 123
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Vulnerability marked as accepted
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Vulnerability marked as accepted
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  /api/vulnerabilities/{id}/false-positive:
    post:
      tags:
        - Vulnerabilities
      summary: Mark as False Positive
      description: Mark a vulnerability as a false positive
      operationId: falsePositiveVulnerability
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Vulnerability ID
          schema:
            type: integer
          example: 123
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Vulnerability marked as false positive
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Vulnerability marked as false positive
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  /api/vulnerabilities/{id}/undo:
    post:
      tags:
        - Vulnerabilities
      summary: Reset Vulnerability Status
      description: Reset vulnerability status back to unresolved
      operationId: undoVulnerability
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Vulnerability ID
          schema:
            type: integer
          example: 123
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Vulnerability status reset to unresolved
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Vulnerability status reset to unresolved
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  /api/vulnerabilities/export:
    get:
      tags:
        - Vulnerabilities
      summary: Export Vulnerabilities CSV
      description: Export vulnerabilities to CSV with optional filtering
      operationId: exportVulnerabilities
      security:
        - bearerAuth: []
      parameters:
        - name: severity
          in: query
          description: Filter by severity level
          schema:
            type: string
            enum: [Critical, High, Medium, Low, Informational]
          example: High
        - name: status
          in: query
          description: Filter by status
          schema:
            type: string
            enum: [Unresolved, Accepted, False Positive]
          example: Unresolved
        - name: target
          in: query
          description: Filter by target hostname or IP
          schema:
            type: string
          example: example.com
        - name: search
          in: query
          description: Search term
          schema:
            type: string
          example: XSS
      responses:
        '200':
          description: CSV file with vulnerabilities
          content:
            text/csv:
              schema:
                type: string
                format: binary
        '401':
          $ref: '#/components/responses/UnauthorizedError'

  /api/targets:
    get:
      tags:
        - Targets
      summary: List All Targets
      description: Get all targets with optional filtering, sorting, and pagination
      operationId: listTargets
      security:
        - bearerAuth: []
      parameters:
        - name: search
          in: query
          description: Search in target, description, or type
          schema:
            type: string
          example: example.com
        - name: type
          in: query
          description: Filter by target type
          schema:
            type: string
            enum: [Domain, Ip]
          example: Domain
        - name: status
          in: query
          description: Filter by status
          schema:
            type: string
            enum: [Active, Inactive, Unknown]
          example: Active
        - name: sort
          in: query
          description: Field to sort by
          schema:
            type: string
            enum: [target, type, status, last_scan, created_at, vulnerabilities]
            default: last_scan
          example: last_scan
        - name: direction
          in: query
          description: Sort direction
          schema:
            type: string
            enum: [asc, desc]
            default: desc
          example: desc
        - name: per_page
          in: query
          description: Items per page (max 100)
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 10
          example: 25
      responses:
        '200':
          description: List of targets
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Target'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/UnauthorizedError'

  /api/targets/{id}:
    get:
      tags:
        - Targets
      summary: Get Target Details
      description: Get detailed information about a specific target
      operationId: getTarget
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Target ID
          schema:
            type: integer
          example: 456
      responses:
        '200':
          description: Target details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Target'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'

    delete:
      tags:
        - Targets
      summary: Delete Target
      description: Delete a target and all associated vulnerabilities
      operationId: deleteTarget
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Target ID
          schema:
            type: integer
          example: 456
      responses:
        '200':
          description: Target deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Target example.com has been deleted successfully. 5 associated vulnerabilities were also removed.
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  /api/targets/{id}/vulnerabilities:
    get:
      tags:
        - Targets
      summary: Get Target Vulnerabilities
      description: Get all vulnerabilities for a specific target with pagination
      operationId: getTargetVulnerabilities
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Target ID
          schema:
            type: integer
          example: 456
        - name: sort
          in: query
          description: Field to sort by
          schema:
            type: string
            enum: [name, threat, cvss_base, scan_type, created_at]
            default: cvss_base
          example: cvss_base
        - name: direction
          in: query
          description: Sort direction
          schema:
            type: string
            enum: [asc, desc]
            default: desc
          example: desc
        - name: per_page
          in: query
          description: Items per page (max 100)
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 10
          example: 25
      responses:
        '200':
          description: List of vulnerabilities for the target
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Vulnerability'
                  target:
                    $ref: '#/components/schemas/Target'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: JWT token obtained from /api/token endpoint

  parameters:
    ScanUUID:
      name: uuid
      in: path
      required: true
      description: Scan UUID
      schema:
        type: string
        format: uuid
      example: e510ab1c-1dec-41a5-86c9-9499220912ac

    RunIndex:
      name: run_index
      in: query
      required: false
      description: Specific run index to retrieve (omit for latest run)
      schema:
        type: integer
        minimum: 0
      example: 0

  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        email:
          type: string
          format: email
        role:
          type: object
          properties:
            name:
              type: string
              enum: [trial, basic, premium, pro, admin]

    Scan:
      type: object
      properties:
        id:
          type: integer
        uuid:
          type: string
          format: uuid
        user_id:
          type: integer
        scan_name:
          type: string
        scan_type:
          type: string
          enum: [Nmap, OpenVas, ZAP, Nuclei]
        scan_target:
          type: string
        scan_frequency:
          type: string
          enum: [One-time, Daily, Weekly, Monthly, Quarterly, Yearly]
        scan_progress:
          type: string
        run_count:
          type: integer
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    Vulnerability:
      type: object
      properties:
        id:
          type: integer
        user_id:
          type: integer
        name:
          type: string
          description: Vulnerability name
        threat:
          type: string
          enum: [Critical, High, Medium, Low, Informational]
          description: Severity level
        cvss_base:
          type: number
          format: float
          minimum: 0
          maximum: 10
          description: CVSS base score
        cvss_vector:
          type: string
          description: CVSS scoring vector
        hostname:
          type: string
          nullable: true
          description: Target hostname
        host_ip:
          type: string
          nullable: true
          description: Target IP address
        port:
          type: string
          nullable: true
          description: Affected port
        description:
          type: string
          description: Vulnerability description
        solution:
          type: string
          description: Recommended solution
        impact:
          type: string
          nullable: true
          description: Potential impact
        evidence:
          type: string
          nullable: true
          description: Evidence of vulnerability
        references:
          type: string
          nullable: true
          description: External references
        scan_name:
          type: string
          description: Name of scan that found this vulnerability
        scan_type:
          type: string
          enum: [Nmap, OpenVas, ZAP, Nuclei]
          description: Type of scan
        status:
          type: string
          enum: [Unresolved, Accepted, False Positive]
          description: Current status
        status_updated_at:
          type: string
          format: date-time
          nullable: true
          description: When status was last updated
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    Target:
      type: object
      properties:
        id:
          type: integer
        user_id:
          type: integer
        target:
          type: string
          description: Original target string
        normalized_target:
          type: string
          description: Normalized target for comparison
        type:
          type: string
          enum: [Domain, Ip]
          description: Target type
        description:
          type: string
          description: Target description
        status:
          type: string
          enum: [Active, Inactive, Unknown]
          description: Target status
        last_scan:
          type: string
          format: date-time
          nullable: true
          description: Last scan date
        vulnerability_counts:
          type: object
          properties:
            total:
              type: integer
            critical:
              type: integer
            high:
              type: integer
            medium:
              type: integer
            low:
              type: integer
            informational:
              type: integer
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    PaginationMeta:
      type: object
      properties:
        current_page:
          type: integer
          description: Current page number
        per_page:
          type: integer
          description: Items per page
        total:
          type: integer
          description: Total number of items
        last_page:
          type: integer
          description: Last page number

    Error:
      type: object
      properties:
        message:
          type: string
          description: Error message

    ValidationError:
      type: object
      properties:
        message:
          type: string
          description: Error message
        errors:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
          description: Field-specific validation errors

  responses:
    UnauthorizedError:
      description: Authentication required or token invalid
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            message: Unauthenticated

    NotFoundError:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            message: Resource not found

    ServerError:
      description: Server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            message: Internal server error

security:
  - bearerAuth: []

