openapi: "3.0.3"
info:
  title: DevSecure Intelligence API
  version: "1.0.0"
  description: >
    Vulnerability Intelligence API providing enriched CVE data, proprietary RPS
    risk scoring, KEV enrichment, EPSS trends, and verified patch intelligence.
    Data refreshes daily at 04:00 UTC. Freshness is reported via
    /api/v1/status.data_freshness; stale is greater than 30 hours since
    last_data_refresh_utc.
  contact:
    name: DevSecure
    url: https://intelligence.devsecure.io
    email: api@devsecure.io
  license:
    name: Proprietary
    url: https://pre.intelligence.devsecure.io/terms

servers:
  - url: https://api.intelligence.devsecure.io
    description: Production API (Cloudflare Worker to rps-api-production)

x-tagGroups:
  - name: Public (no key required)
    tags: [System, KEV, Waitlist]
  - name: Authenticated (API key required)
    tags: [CVE, RPS, CWE, Stats, Patches]

tags:
  - name: CVE
    description: Core vulnerability intelligence lookups
  - name: RPS
    description: Risk Priority Score (lightweight)
  - name: CWE
    description: Search by weakness classification
  - name: KEV
    description: CISA Known Exploited Vulnerabilities (free tier)
  - name: Patches
    description: Verified patch intelligence (enterprise tier)
  - name: Stats
    description: Corpus statistics and data quality
  - name: System
    description: Health and status monitoring
  - name: Waitlist
    description: Access request queue

security:
  - BearerAuth: []

paths:
  # ══════════════════════════════════════════════════════════════════════
  # CVE Endpoints
  # ══════════════════════════════════════════════════════════════════════
  /api/v1/cve/{cve_id}:
    get:
      tags: [CVE]
      summary: Full enriched CVE lookup
      description: Returns all intelligence signals for a single CVE.
      parameters:
        - name: cve_id
          in: path
          required: true
          schema: { type: string }
          examples:
            log4shell:
              summary: Log4Shell (CVSS 10.0, KEV)
              value: CVE-2021-44228
            zerologon:
              summary: Zerologon (low CVSS, KEV)
              value: CVE-2020-1472
            windows_tcp:
              summary: Windows TCP/IP RCE (CVSS 9.8)
              value: CVE-2024-38063
            xz_utils:
              summary: xz-utils backdoor
              value: CVE-2024-3094
      responses:
        "200":
          description: RPS score and components
          content:
            application/json:
              schema: { $ref: "#/components/schemas/RpsResponse" }
        "400":
          description: Invalid CVE format
        "401":
          description: Missing or invalid API key
        "404":
          description: CVE not found
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CveNotFoundResponse" }

  # ══════════════════════════════════════════════════════════════════════
  # CWE Endpoint
  # ══════════════════════════════════════════════════════════════════════
  /api/v1/cwe/{cwe_id}/cves:
    get:
      tags: [CWE]
      summary: Search CVEs by CWE
      description: Returns CVEs matching a CWE ID with optional filters.
      parameters:
        - name: cwe_id
          in: path
          required: true
          schema: { type: string, example: "CWE-502" }
        - name: severity
          in: query
          required: false
          schema: { type: string, enum: [CRITICAL, HIGH, MEDIUM, LOW, NONE] }
        - name: has_patch
          in: query
          required: false
          schema: { type: boolean }
        - name: is_kev
          in: query
          required: false
          schema: { type: boolean }
        - name: epss_min
          in: query
          required: false
          schema: { type: number, minimum: 0, maximum: 1 }
      responses:
        "200":
          description: Array of matching CVEs (max 1,000)
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CweSearchResponse" }
        "400":
          description: Invalid CWE format
        "401":
          description: Missing or invalid API key

  # ══════════════════════════════════════════════════════════════════════
  # KEV Endpoint (Free Tier — No Auth)
  # ══════════════════════════════════════════════════════════════════════
  /api/v1/kev:
    get:
      tags: [KEV]
      summary: Free KEV+ catalog
      description: >
        Full CISA KEV catalog enriched with RPS scores. **No authentication required.**
        Rate limited by IP (20 req/min, 200/day). Cached at edge (Cache-Control: public, max-age=3600).
      security: []
      responses:
        "200":
          description: KEV catalog with RPS scores
          headers:
            Cache-Control:
              schema: { type: string, example: "public, max-age=3600" }
          content:
            application/json:
              schema: { $ref: "#/components/schemas/KevResponse" }
        "429":
          description: Free tier rate limit exceeded (20 req/min, 200/day per IP)

  # ══════════════════════════════════════════════════════════════════════
  # Patches Endpoint (Enterprise)
  # ══════════════════════════════════════════════════════════════════════
  /api/v1/patches/{cve_id}:
    get:
      tags: [Patches]
      summary: Patch intelligence
      description: >
        Verified before/after code pairs for a CVE. **Enterprise tier only.**
        Requires patch_access flag on API key.
      parameters:
        - name: cve_id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Patch data (or empty if none found)
          content:
            application/json:
              schema: { $ref: "#/components/schemas/PatchResponse" }
        "400":
          description: Invalid CVE format
        "401":
          description: Missing or invalid API key
        "403":
          description: Tier does not include patch access (enterprise only)
        "404":
          description: CVE not found

  # ══════════════════════════════════════════════════════════════════════
  # Stats Endpoint
  # ══════════════════════════════════════════════════════════════════════
  /api/v1/stats:
    get:
      tags: [Stats]
      summary: Corpus statistics
      description: Returns aggregate corpus metrics including total CVEs, enrichment coverage, and last refresh timestamp. Public — no auth required.
      security: []
      responses:
        "200":
          description: Corpus stats
          content:
            application/json:
              schema: { $ref: "#/components/schemas/StatsResponse" }

  # ══════════════════════════════════════════════════════════════════════
  # Trends Endpoints (Public)
  # ══════════════════════════════════════════════════════════════════════
  /api/v1/trends:
    get:
      tags: [System]
      summary: Aggregated threat landscape data
      description: Returns CVE volume by year, top CVEs this week, KEV additions, CWE distribution, EPSS movers, and patch coverage. Public — no auth required.
      security: []
      responses:
        "200":
          description: Aggregated trends data

  /api/v1/trends/scatter:
    get:
      tags: [System]
      summary: RPS vs CVSS scatter plot data
      description: Returns sampled CVEs for visualising how RPS re-prioritises CVEs relative to CVSS alone. Includes all KEV CVEs and stratified non-KEV sample. Public — no auth required.
      security: []
      responses:
        "200":
          description: Scatter plot data points

  # ══════════════════════════════════════════════════════════════════════
  # POST /api/v1/prioritize (API release: v2.7.0, RPS scoring: v2.6.8)
  # ══════════════════════════════════════════════════════════════════════
  /api/v1/prioritize:
    post:
      tags: [CVE]
      summary: Ranked remediation order
      description: >
        Submit a list of CVE IDs and receive them ranked by RPS score with
        signal-based guidance. Transforms scanner output into an actionable
        remediation plan. Free tier not available.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/PrioritizeRequest" }
      responses:
        "200":
          description: Ranked results with guidance
          content:
            application/json:
              schema: { $ref: "#/components/schemas/PrioritizeResponse" }
        "400":
          description: Invalid input
        "401":
          description: Missing or invalid API key
        "403":
          description: Tier does not include this endpoint
        "429":
          description: Rate limit exceeded

  # ══════════════════════════════════════════════════════════════════════
  # System Endpoints (Unauthenticated)
  # ══════════════════════════════════════════════════════════════════════
  /api/v1/health:
    get:
      tags: [System]
      summary: Service health check
      description: Returns service status. Unauthenticated — for monitoring probes.
      security: []
      responses:
        "200":
          description: Service healthy
          content:
            application/json:
              schema: { $ref: "#/components/schemas/HealthResponse" }

  /api/v1/status:
    get:
      tags: [System]
      summary: Data freshness check
      description: Returns API status, cache status, BigQuery connection, user-facing data freshness, and separate pipeline diagnostics.
      security: []
      responses:
        "200":
          description: Data freshness status
          content:
            application/json:
              schema: { $ref: "#/components/schemas/StatusResponse" }

  # ══════════════════════════════════════════════════════════════════════
  /api/v1/waitlist:
    post:
      tags: [Waitlist]
      summary: Request API access
      description: Queues an access request. This does not provision an API key.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/WaitlistRequest" }
      responses:
        "202":
          description: Access request queued
          content:
            application/json:
              schema: { $ref: "#/components/schemas/WaitlistResponse" }
        "400":
          description: Invalid request body
        "429":
          description: Waitlist request rate limit exceeded
        "503":
          description: Waitlist storage is unavailable

  # Admin
  # ══════════════════════════════════════════════════════════════════════
  /api/v1/cache/flush:
    post:
      tags: [System]
      summary: Flush Redis cache
      description: Admin endpoint — flushes all cached CVE entries.
      responses:
        "200":
          description: Cache flushed
        "401":
          description: Missing or invalid API key

# ══════════════════════════════════════════════════════════════════════════
# Components
# ══════════════════════════════════════════════════════════════════════════
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: |
        API key or JWT Bearer token for authenticated access.
        Request access at https://intelligence.devsecure.io/signup.

  schemas:
    ErrorResponse:
      type: object
      properties:
        error:
          type: string
          description: Human-readable error message
        status:
          type: string
          enum: [bad_request, not_found, error, rate_limited, service_unavailable]
          description: Machine-readable status code

    CveNotFoundResponse:
      type: object
      required: [error, status, source_record_url]
      properties:
        error:
          type: string
          example: CVE not found: CVE-2026-9522
        status:
          type: string
          enum: [not_found]
        corpus_last_refresh_utc:
          type: string
          nullable: true
          format: date-time
          example: "2026-06-02T03:00:32.793027+00:00"
        source_record_url:
          type: string
          format: uri
          example: https://nvd.nist.gov/vuln/detail/CVE-2026-9522
        note:
          type: string
          example: This CVE may exist in NVD but not yet in the DevSecure corpus. Check the NVD source record for the upstream entry.

    WaitlistRequest:
      type: object
      required: [email]
      properties:
        email:
          type: string
          format: email
          example: security@example.com
        company:
          type: string
          maxLength: 200
        use_case:
          type: string
          maxLength: 1000
        source:
          type: string
          maxLength: 80
          example: signup

    WaitlistResponse:
      type: object
      required: [status]
      properties:
        status:
          type: string
          enum: [queued]
          example: queued

    PrioritizeRequest:
      type: object
      required: [cve_ids]
      properties:
        cve_ids:
          type: array
          items: { type: string }
          minItems: 1
          maxItems: 1000
          example:
            - CVE-2021-44228
            - CVE-2020-1472

    PrioritizeResponse:
      type: object
      required: [total, found, not_found, ranked, status]
      properties:
        total: { type: integer, example: 2 }
        found: { type: integer, example: 1 }
        not_found:
          type: array
          items: { type: string }
          example: [CVE-2099-0001]
        ranked:
          type: array
          items: { $ref: "#/components/schemas/PrioritizeRankedItem" }
        status:
          type: string
          enum: [success]

    PrioritizeRankedItem:
      type: object
      required:
        - cve_id
        - rps_score
        - priority_band
        - rank_in_request
        - confidence
        - evidence
        - data_quality
        - reason
        - sla_guidance
        - recommended_next_step
      properties:
        cve_id: { type: string, example: CVE-2021-44228 }
        rps_score: { type: number, example: 99.7 }
        priority_band:
          type: string
          enum: [critical_priority, high_priority, medium_priority, low_priority, informational, unknown]
          example: critical_priority
        rank_in_request: { type: integer, example: 1 }
        confidence:
          type: string
          example: high
        evidence:
          type: object
          required: [cvss, epss, kev, exploit_evidence, patch_available]
          properties:
            cvss:
              type: object
              nullable: true
              properties:
                score: { type: number, nullable: true, example: 10.0 }
                version: { type: string, nullable: true, example: "3.1" }
                source: { type: string, nullable: true, example: NVD }
            epss:
              type: object
              nullable: true
              properties:
                score: { type: number, nullable: true, example: 0.97554 }
                percentile: { type: number, nullable: true, example: 0.99987 }
                source: { type: string, nullable: true, example: FIRST }
            kev:
              type: object
              nullable: true
              properties:
                listed: { type: boolean, nullable: true, example: true }
                date_added: { type: string, nullable: true, example: "2021-12-10" }
            exploit_evidence:
              type: array
              items: { type: string }
            patch_available: { type: boolean, example: true }
        data_quality:
          type: object
          required: [completeness]
          properties:
            completeness:
              type: string
              enum: [complete, partial, sparse]
            stale_signals:
              type: array
              items: { type: string }
            notes:
              type: array
              items: { type: string }
        reason: { type: string }
        sla_guidance:
          type: string
          example: Apply your organisation's SLA for critical-priority vulnerabilities.
        recommended_next_step:
          type: string
          enum: [review_against_internal_sla, verify_applicability, monitor, no_action_recommended]
          example: review_against_internal_sla

    CveResponse:
      type: object
      properties:
        cache: { type: string, enum: [hit, miss] }
        data:
          type: object
          properties:
            cve_id: { type: string, example: "CVE-2021-44228" }
            rps_score: { type: number, example: 104.98 }
            rps: { $ref: "#/components/schemas/RpsBlock" }
            signals: { $ref: "#/components/schemas/SignalsBlock" }
            explanation: { $ref: "#/components/schemas/ExplanationBlock" }
            data_quality: { $ref: "#/components/schemas/DataQualityBlock" }
            severity: { type: string, example: "CRITICAL" }
            published_date: { type: string }
            last_modified: { type: string }
            data_sources: { type: array, items: { type: string } }
        status: { type: string, enum: [success] }

    BatchResponse:
      type: object
      properties:
        results: { type: array, items: { type: object } }
        cache_stats:
          type: object
          properties:
            total_requested: { type: integer }
            cache_hits: { type: integer }
            cache_misses: { type: integer }
            not_found: { type: integer }
        not_found_cves: { type: array, items: { type: string } }
        status: { type: string }

    RecentResponse:
      type: object
      properties:
        since: { type: string }
        count: { type: integer }
        results: { type: array, items: { type: object } }
        status: { type: string }

    RpsResponse:
      type: object
      properties:
        cache: { type: string }
        data:
          type: object
          properties:
            cve_id: { type: string, example: "CVE-2021-44228" }
            rps: { $ref: "#/components/schemas/RpsBlock" }
            signals: { $ref: "#/components/schemas/SignalsBlock" }
            explanation: { $ref: "#/components/schemas/ExplanationBlock" }
        status: { type: string }

    RpsBlock:
      type: object
      properties:
        score: { type: number, example: 104.98 }
        band: { type: string, example: "critical_priority" }
        version: { type: string, example: "2.6.8" }
        formula: { type: string, example: "additive_base_kev_amplifier" }
        scored_at: { type: string, example: "2026-05-20T04:00:00Z" }

    SignalsBlock:
      type: object
      properties:
        cvss:
          type: object
          properties:
            score: { type: number, example: 10.0 }
            version: { type: string, example: "3.1" }
            weight: { type: number, example: 0.4 }
            contribution: { type: number, example: 0.4 }
            source: { type: string, example: "NVD" }
        epss:
          type: object
          properties:
            score: { type: number, example: 0.944 }
            percentile: { type: number, example: 0.999 }
            weight: { type: number, example: 0.3 }
            contribution: { type: number, example: 0.283 }
            source: { type: string, example: "FIRST" }
        kev:
          type: object
          properties:
            in_catalog: { type: boolean, example: true }
            date_added: { type: string, example: "2021-12-10" }
            amplifier: { type: number, example: 1.5 }
            source: { type: string, example: "CISA KEV" }
        exploit:
          type: object
          properties:
            public_exploit_available: { type: boolean, example: true }
            sources: { type: array, items: { type: string }, example: ["ExploitDB"] }
        patch:
          type: object
          properties:
            verified_patch_available: { type: boolean, example: true }
            patch_count: { type: integer, example: 12 }
            source: { type: string, example: "MoreFixes" }

    ExplanationBlock:
      type: object
      properties:
        summary: { type: string, example: "Critical priority: CISA KEV confirmed exploitation, EPSS 99th percentile..." }
        top_drivers: { type: array, items: { type: string } }
        guidance: { type: string, example: "Highest external threat. Active exploitation confirmed." }

    DataQualityBlock:
      type: object
      properties:
        completeness: { type: number, example: 1.0 }
        missing_fields: { type: array, items: { type: string }, example: [] }
        last_source_refresh:
          type: object
          properties:
            nvd: { type: string, example: "2026-05-20T03:10:00Z" }
            epss: { type: string, example: "2026-05-20T02:00:00Z" }
            kev: { type: string, example: "2026-05-20T01:30:00Z" }
            morefixes: { type: string, example: "2026-05-19T22:00:00Z" }

    CweSearchResponse:
      type: object
      properties:
        cwe_id: { type: string }
        filters: { type: object }
        count: { type: integer }
        results: { type: array, items: { type: object } }
        status: { type: string }

    KevResponse:
      type: object
      properties:
        count: { type: integer }
        last_updated: { type: string }
        results: { type: array, items: { type: object } }
        status: { type: string }
        note: { type: string }

    PatchResponse:
      type: object
      properties:
        cve_id: { type: string }
        patch_available: { type: boolean }
        total_commits: { type: integer }
        total_files: { type: integer }
        commits: { type: array, items: { type: object } }
        status: { type: string }

    StatsResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            total_cves: { type: integer }
            cves_with_cvss: { type: integer }
            pct_cvss: { type: number }
            cves_with_epss: { type: integer }
            pct_epss: { type: number }
            cves_with_kev: { type: integer }
            cves_with_cwe: { type: integer }
            cves_with_patch: { type: integer }
            cves_fully_enriched: { type: integer }
            last_data_refresh_utc: { type: string }
            last_refresh:
              type: string
              deprecated: true
              description: Deprecated alias for last_data_refresh_utc. Removed in v2.8.
            _deprecation_warnings:
              type: array
              items: { type: string }
        status: { type: string }

    HealthResponse:
      type: object
      properties:
        service: { type: string }
        status: { type: string }
        version: { type: string }
        cache_status: { type: string }
        timestamp: { type: string }

    StatusResponse:
      type: object
      properties:
        api_status: { type: string }
        cache_status: { type: string }
        bigquery_status: { type: string }
        data_freshness:
          type: object
          properties:
            last_data_refresh_utc: { type: string }
            hours_since_refresh: { type: number }
            is_stale: { type: boolean }
        pipeline_diagnostics:
          type: object
          properties:
            last_dbt_run_utc: { type: string }
        version: { type: string }
        timestamp: { type: string }
