openapi: 3.1.0
info:
  title: CYBEXO Pulse Public Scan API
  version: 1.0.0
  description: Public runtime verification API for CYBEXO Pulse.
servers:
  - url: https://pulse.cybexo.com
paths:
  /v2/public-scan/healthz:
    get:
      summary: Check public scan service health.
      operationId: getPulsePublicScanHealth
      responses:
        "200":
          description: Service is available.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthResponse"
  /v2/public-scan/scan:
    post:
      summary: Scan a public domain for CYBEXO runtime verification.
      operationId: createPulsePublicScan
      parameters:
        - name: domain
          in: query
          required: false
          schema:
            type: string
          description: Public hostname or URL to scan. Prefer the JSON request body for new integrations.
        - name: maxPages
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
          description: Optional page crawl limit. Service-side caps still apply.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PublicScanRequest"
            examples:
              scanDomain:
                summary: Scan a production domain
                value:
                  domain: cmp.cybexo.com
                  maxPages: 1
      responses:
        "200":
          description: Scan completed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublicScanResponse"
        "400":
          description: Invalid scan request.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Public scan service error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /v2/public-scan/report/{token}:
    get:
      summary: Retrieve a public scan report by token.
      operationId: getPulsePublicScanReport
      parameters:
        - name: token
          in: path
          required: true
          schema:
            type: string
            pattern: "^[a-fA-F0-9]{32}$"
          description: Public report token returned by a scan request.
      responses:
        "200":
          description: Report found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublicReportResponse"
        "404":
          description: Report was not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
components:
  schemas:
    HealthResponse:
      type: object
      required:
        - ok
        - data
        - meta
      properties:
        ok:
          type: boolean
          const: true
        data:
          type: object
          required:
            - status
            - service
          properties:
            status:
              type: string
              example: ok
            service:
              type: string
              example: public_scan
        meta:
          $ref: "#/components/schemas/ResponseMeta"
    PublicScanRequest:
      type: object
      required:
        - domain
      properties:
        domain:
          type: string
          description: Public hostname or URL to scan.
          examples:
            - cmp.cybexo.com
            - https://example.com
        maxPages:
          type: integer
          minimum: 1
          description: Optional page crawl limit. Service-side caps still apply.
        token:
          type: string
          pattern: "^[a-fA-F0-9]{32}$"
          description: Optional caller-supplied public report token. If omitted, Pulse generates one.
      additionalProperties: false
    PublicScanResponse:
      type: object
      required:
        - ok
        - data
        - meta
      properties:
        ok:
          type: boolean
          const: true
        data:
          type: object
          required:
            - scan
            - report
          properties:
            scan:
              $ref: "#/components/schemas/PublicScan"
            report:
              $ref: "#/components/schemas/PublicReportReference"
        meta:
          $ref: "#/components/schemas/ResponseMeta"
    PublicReportResponse:
      type: object
      required:
        - ok
        - data
        - meta
      properties:
        ok:
          type: boolean
          const: true
        data:
          type: object
          required:
            - client
            - scan
            - generatedAt
          properties:
            client:
              type: object
              required:
                - domain
              properties:
                domain:
                  type: string
            scan:
              $ref: "#/components/schemas/PublicScan"
            generatedAt:
              type: string
              format: date-time
        meta:
          $ref: "#/components/schemas/ResponseMeta"
    PublicReportReference:
      type: object
      required:
        - token
        - url
        - json_url
      properties:
        token:
          type: string
          pattern: "^[a-fA-F0-9]{32}$"
        url:
          type: string
          format: uri
          description: Human-readable public report URL.
        json_url:
          type: string
          description: Relative JSON report endpoint.
    PublicScan:
      type: object
      required:
        - timestamp
        - domain
        - pagesTested
        - okCount
        - issueCount
        - issues
        - pages
      properties:
        timestamp:
          type: string
          format: date-time
        domain:
          type: string
        pagesTested:
          type: integer
          minimum: 0
        okCount:
          type: integer
          minimum: 0
        issueCount:
          type: integer
          minimum: 0
        issues:
          type: array
          items:
            $ref: "#/components/schemas/PublicScanIssue"
        pages:
          type: array
          items:
            $ref: "#/components/schemas/PublicScanPage"
    PublicScanPage:
      type: object
      required:
        - url
        - ok
      properties:
        url:
          type: string
          format: uri
        ok:
          type: boolean
        runtime_contract:
          $ref: "#/components/schemas/RuntimeContract"
        issues:
          type: array
          items:
            $ref: "#/components/schemas/PublicScanIssue"
    RuntimeContract:
      type: object
      properties:
        status:
          type: string
          enum:
            - pass
            - warn
            - fail
            - unknown
        brand:
          type: string
          example: cybexo
        loader_present:
          type: boolean
        loader_url:
          type: string
          format: uri
        loader_id:
          type: string
          example: cybexo-cmp
        settings_id:
          type: string
        install_surfaces:
          type: array
          items:
            type: string
            examples:
              - direct
              - gtm
              - wordpress
              - shopify
        source_markers:
          $ref: "#/components/schemas/SourceMarkers"
        artifact:
          $ref: "#/components/schemas/LoaderArtifact"
        checks:
          type: array
          items:
            $ref: "#/components/schemas/RuntimeCheck"
      additionalProperties: true
    SourceMarkers:
      type: object
      properties:
        wordpress:
          type: boolean
        google_tag_manager:
          type: boolean
        cybexo_loader_url:
          type: boolean
        cybexo_loader_id:
          type: boolean
        cybexo_cdn_url:
          type: boolean
        cybexo_api_url:
          type: boolean
        cybexo_wordpress_plugin:
          type: boolean
        cybexo_preferences_marker:
          type: boolean
        cybexo_runtime_storage:
          type: boolean
        cybexo_event_namespace:
          type: boolean
        cybexo_debug_namespace:
          type: boolean
      additionalProperties:
        type: boolean
    LoaderArtifact:
      type: object
      properties:
        fetched:
          type: boolean
        status:
          type: integer
        version:
          type: string
        build:
          type: string
        channel:
          type: string
        commit:
          type: string
        built:
          type: string
          format: date-time
        payload_sha256:
          type: string
        artifact:
          type: string
      additionalProperties: true
    RuntimeCheck:
      type: object
      required:
        - key
        - status
        - detail
      properties:
        key:
          type: string
        status:
          type: string
          enum:
            - pass
            - warn
            - fail
            - unknown
        detail:
          type: string
        evidence:
          type: string
      additionalProperties: true
    PublicScanIssue:
      type: object
      properties:
        code:
          type: string
        message:
          type: string
        severity:
          type: string
        url:
          type: string
          format: uri
      additionalProperties: true
    ResponseMeta:
      type: object
      required:
        - request_id
        - timestamp
      properties:
        request_id:
          type: string
        timestamp:
          type: string
          format: date-time
        rate_limit:
          $ref: "#/components/schemas/RateLimit"
      additionalProperties: true
    RateLimit:
      type: object
      properties:
        limit:
          type: integer
        remaining:
          type: integer
      additionalProperties: true
    ErrorResponse:
      type: object
      required:
        - ok
        - error
        - meta
      properties:
        ok:
          type: boolean
          const: false
        error:
          oneOf:
            - $ref: "#/components/schemas/ErrorObject"
            - type: string
        reason:
          type: string
        retry_after_seconds:
          type:
            - integer
            - "null"
        meta:
          $ref: "#/components/schemas/ResponseMeta"
      additionalProperties: true
    ErrorObject:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
        message:
          type: string
      additionalProperties: true
