openapi: 3.0.3
info:
  title: SecurePoint USA API
  description: |
    Enterprise visitor management and compliance screening API.
    
    All endpoints require authentication and enforce organization-level isolation via Row-Level Security (RLS).
  version: 1.0.0
  contact:
    name: SecurePoint USA API Support
    email: api-support@securepointusa.com
  license:
    name: Proprietary
    url: https://securepointusa.com/legal/terms-of-service

servers:
  - url: https://securepointusa.com/api
    description: Production
  - url: https://staging.securepointusa.com/api
    description: Staging
  - url: http://localhost:3000/api
    description: Local Development

tags:
  - name: Screening
    description: Real-time sanctions screening endpoints
  - name: Adjudication
    description: Adjudication queue and decision endpoints
  - name: Visitors
    description: Visitor management endpoints
  - name: Bulk Operations
    description: Bulk screening and CSV processing
  - name: Compliance
    description: Compliance reporting and evidence packs
  - name: Admin
    description: Administrative operations
  - name: Super Admin
    description: Super admin cross-tenant operations
  - name: V1 Screening
    description: V1 API screening endpoints (API key auth)
  - name: V1 Ownership
    description: V1 API BIS 50% ownership traversal
  - name: V1 Visitors
    description: V1 API visitor check-in and session management
  - name: V1 Audit
    description: V1 API audit log export
  - name: V1 Adjudication
    description: V1 API adjudication rule management

paths:
  /screening/check:
    post:
      tags:
        - Screening
      summary: Perform real-time sanctions screening
      description: |
        Screen a visitor against global sanctions databases (OFAC, BIS, UN, EU, UK, etc.).
        
        Returns risk level, risk score, and any matches found.
      operationId: screeningCheck
      security:
        - BearerAuth: []
        - DemoMode: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ScreeningRequest'
            examples:
              basic:
                summary: Basic screening
                value:
                  query: "John Smith"
                  company: "ACME Corp"
                  country: "US"
              withEmail:
                summary: Screening with email
                value:
                  query: "Jane Doe"
                  company: "XYZ Inc"
                  country: "CA"
                  email: "jane@example.com"
      responses:
        '200':
          description: Screening completed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScreeningResponse'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalError'

  /adjudication/{id}:
    get:
      tags:
        - Adjudication
      summary: Get adjudication case details
      description: Retrieve details for a specific adjudication case
      operationId: getAdjudicationCase
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Adjudication case ID
      responses:
        '200':
          description: Case details retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdjudicationCase'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalError'

  /adjudication/{id}/decide:
    post:
      tags:
        - Adjudication
      summary: Make adjudication decision
      description: |
        Record an adjudication decision (approved, denied, escalated, etc.).

        Requires compliance_manager, admin, or super_admin role.
      operationId: decideAdjudication
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Adjudication case ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AdjudicationDecision'
      responses:
        '200':
          description: Decision recorded successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdjudicationDecisionResponse'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalError'

  /kiosk/checkin:
    post:
      tags:
        - Visitors
      summary: Kiosk visitor check-in
      description: |
        Check in a visitor via kiosk interface.
        
        Validates site security requirements and performs automatic screening.
      operationId: kioskCheckin
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/KioskCheckinRequest'
      responses:
        '200':
          description: Visitor checked in successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VisitorSession'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/InternalError'

  /visitors/{id}/checkout:
    post:
      tags:
        - Visitors
      summary: Check out a visitor
      description: Record visitor checkout and update session status
      operationId: checkoutVisitor
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Visitor session ID
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                checkout_reason:
                  type: string
                  description: Reason for checkout
      responses:
        '200':
          description: Visitor checked out successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VisitorSession'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Visitor already checked out
        '500':
          $ref: '#/components/responses/InternalError'

  /bulk-screening:
    post:
      tags:
        - Bulk Operations
      summary: Bulk CSV screening
      description: |
        Upload CSV file for bulk screening.
        
        Requires compliance_manager or admin role.
      operationId: bulkScreening
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - file
              properties:
                file:
                  type: string
                  format: binary
                  description: CSV file with visitor data
      responses:
        '200':
          description: Bulk screening job created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BulkScreeningJob'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/InternalError'

  /admin/compliance/reports:
    post:
      tags:
        - Compliance
      summary: Generate compliance report
      description: |
        Generate compliance report (screening activity, adjudication decisions, audit trail).
        
        Requires compliance_manager, admin, or auditor_readonly role.
      operationId: generateComplianceReport
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ComplianceReportRequest'
      responses:
        '200':
          description: Report generated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComplianceReport'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/InternalError'

  /admin/overview:
    get:
      tags:
        - Admin
      summary: Get admin dashboard overview
      description: Retrieve overview statistics for admin dashboard
      operationId: getAdminOverview
      security:
        - BearerAuth: []
      responses:
        '200':
          description: Overview data retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdminOverview'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'

  # ────────────────────────────────────────────────────────────
  # V1 API Routes (API key or JWT auth, rate-limited)
  # ────────────────────────────────────────────────────────────

  /v1/prescreen:
    post:
      tags:
        - V1 Screening
      summary: Screen a subject against global sanctions databases
      description: |
        Primary screening endpoint for the V1 Screening API.
        Screens a subject name against OFAC, BIS, UN, EU, UK and other sanctions lists.
        Returns risk level, score, match count, and ITAR/EAR relevance flags.
      operationId: v1Prescreen
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/V1PrescreenRequest'
            examples:
              basic:
                summary: Basic screening
                value:
                  subject: "Acme Trading LLC"
                  country: "IR"
              withOptions:
                summary: With AI and site context
                value:
                  subject: "John Doe"
                  company: "Acme Corp"
                  country: "US"
                  enable_ai: true
                  site_id: "550e8400-e29b-41d4-a716-446655440000"
      responses:
        '200':
          description: Screening completed
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit per window
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Remaining requests in window
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix timestamp when the window resets
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/V1PrescreenResponse'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '402':
          $ref: '#/components/responses/EntitlementRequired'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalError'

  /v1/ownership/check:
    post:
      tags:
        - V1 Ownership
      summary: BIS 50% ownership rule traversal
      description: |
        Perform a BIS 50% ownership traversal for a given entity.
        Walks the ownership graph up to max_depth levels and determines
        whether aggregate sanctioned ownership exceeds the 50% threshold.
      operationId: v1OwnershipCheck
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/V1OwnershipCheckRequest'
      responses:
        '200':
          description: Ownership check completed
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
            X-RateLimit-Remaining:
              schema:
                type: integer
            X-RateLimit-Reset:
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/V1OwnershipCheckResponse'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '402':
          $ref: '#/components/responses/EntitlementRequired'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalError'

  /v1/screenings/{id}:
    get:
      tags:
        - V1 Screening
      summary: Retrieve a screening result by ID
      description: |
        Fetch the full screening result including risk level, matches,
        adjudication status, and original query metadata.
      operationId: v1GetScreening
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Screening record ID
      responses:
        '200':
          description: Screening result retrieved
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
            X-RateLimit-Remaining:
              schema:
                type: integer
            X-RateLimit-Reset:
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/V1ScreeningResult'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '402':
          $ref: '#/components/responses/EntitlementRequired'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalError'

  /v1/screenings/{id}/evidence:
    get:
      tags:
        - V1 Screening
      summary: Download screening evidence PDF
      description: |
        Returns an audit-oriented PDF for a screening result. Requires Evidence & Adjudication entitlement.
      operationId: v1GetScreeningEvidence
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: PDF document
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/EntitlementRequired'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /v1/adjudication/queue:
    get:
      tags:
        - V1 Adjudication
      summary: List adjudication queue cases
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      parameters:
        - name: status
          in: query
          schema:
            type: string
          description: Comma-separated queue statuses
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
        - name: from
          in: query
          schema:
            type: string
            format: date-time
        - name: to
          in: query
          schema:
            type: string
            format: date-time
      responses:
        '200':
          description: Queue page
        '402':
          $ref: '#/components/responses/EntitlementRequired'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /v1/adjudication/{id}:
    get:
      tags:
        - V1 Adjudication
      summary: Get adjudication case detail
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Case detail
        '404':
          $ref: '#/components/responses/NotFound'
        '402':
          $ref: '#/components/responses/EntitlementRequired'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v1/adjudication/{id}/disposition:
    post:
      tags:
        - V1 Adjudication
      summary: Apply adjudication disposition
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - disposition
                - reason_code
              properties:
                disposition:
                  type: string
                  enum: [CLEARED, ESCALATED, DENIED]
                reason_code:
                  type: string
                remarks:
                  type: string
                expires_at:
                  type: string
                  format: date-time
      responses:
        '200':
          description: Disposition recorded
        '409':
          $ref: '#/components/responses/ValidationError'
        '404':
          $ref: '#/components/responses/NotFound'
        '402':
          $ref: '#/components/responses/EntitlementRequired'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v1/vendors/screen:
    post:
      tags:
        - V1 Screening
      summary: Screen a vendor against sanctions databases
      description: |
        Screen a vendor entity by name, country, and optional registration number.
        Returns the same risk shape as the prescreen endpoint.
      operationId: v1VendorScreen
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/V1VendorScreenRequest'
      responses:
        '200':
          description: Vendor screening completed
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
            X-RateLimit-Remaining:
              schema:
                type: integer
            X-RateLimit-Reset:
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/V1PrescreenResponse'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalError'

  /v1/checkin:
    post:
      tags:
        - V1 Visitors
      summary: Check in a visitor
      description: |
        Create a new visitor session (check-in) for a pre-registered visitor.
        Optionally specify site, method, notes, and arbitrary metadata.
      operationId: v1Checkin
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/V1CheckinRequest'
      responses:
        '200':
          description: Visitor checked in
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
            X-RateLimit-Remaining:
              schema:
                type: integer
            X-RateLimit-Reset:
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/V1CheckinResponse'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalError'

  /v1/sessions/{id}/update:
    post:
      tags:
        - V1 Visitors
      summary: Update a visitor session
      description: |
        Update status, notes, or metadata on an existing visitor session.
        Set checkout: true to check out the visitor.
      operationId: v1UpdateSession
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Visitor session ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/V1UpdateSessionRequest'
      responses:
        '200':
          description: Session updated
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
            X-RateLimit-Remaining:
              schema:
                type: integer
            X-RateLimit-Reset:
              schema:
                type: integer
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalError'

  /v1/audit/export:
    get:
      tags:
        - V1 Audit
      summary: Export audit logs
      description: |
        Export audit log records for the authenticated organization.
        Supports time-range filtering and pagination via limit.
      operationId: v1AuditExport
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      parameters:
        - name: start
          in: query
          required: false
          schema:
            type: string
            format: date-time
          description: Start of time range (ISO 8601)
        - name: end
          in: query
          required: false
          schema:
            type: string
            format: date-time
          description: End of time range (ISO 8601)
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 1000
          description: Maximum number of records to return
      responses:
        '200':
          description: Audit logs exported
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
            X-RateLimit-Remaining:
              schema:
                type: integer
            X-RateLimit-Reset:
              schema:
                type: integer
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/V1AuditLogRecord'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalError'

  /v1/adjudication/rules:
    put:
      tags:
        - V1 Adjudication
      summary: Manage adjudication rules
      description: |
        Replace the set of adjudication rules for the authenticated organization.
        Each rule specifies a priority, condition, action, and reason code.
      operationId: v1PutAdjudicationRules
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: array
              items:
                $ref: '#/components/schemas/V1AdjudicationRule'
      responses:
        '200':
          description: Rules updated
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
            X-RateLimit-Remaining:
              schema:
                type: integer
            X-RateLimit-Reset:
              schema:
                type: integer
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/V1AdjudicationRule'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalError'

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: JWT token from Supabase Auth
    ApiKeyAuth:
      type: apiKey
      in: header
      name: Authorization
      description: API key with spusa_ prefix (e.g. "Bearer spusa_xxx")
    DemoMode:
      type: apiKey
      in: header
      name: X-Demo-Mode
      description: Demo mode header (limited endpoints only)

  schemas:
    APIResponse:
      type: object
      required:
        - success
        - meta
      properties:
        success:
          type: boolean
        data:
          type: object
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string
            details:
              type: object
        meta:
          type: object
          required:
            - requestId
            - timestamp
            - version
          properties:
            requestId:
              type: string
              format: uuid
            timestamp:
              type: string
              format: date-time
            version:
              type: string

    ScreeningRequest:
      type: object
      required:
        - query
      properties:
        query:
          type: string
          minLength: 1
          description: Visitor name or entity name to screen
        company:
          type: string
          description: Company name
        country:
          type: string
          description: Country code (ISO 3166-1 alpha-2)
        email:
          type: string
          format: email
        address:
          type: string
        enable_ai:
          type: boolean
          default: false
        source:
          type: string
          enum: [single, bulk, kiosk, api, demo]
        visitor_id:
          type: string
          format: uuid

    ScreeningResponse:
      allOf:
        - $ref: '#/components/schemas/APIResponse'
        - type: object
          properties:
            data:
              type: object
              properties:
                screening_id:
                  type: string
                  format: uuid
                risk_level:
                  type: string
                  enum: [LOW_RISK, MEDIUM_RISK, HIGH_RISK, CRITICAL_RISK]
                risk_score:
                  type: number
                  minimum: 0
                  maximum: 1
                matches:
                  type: array
                  items:
                    $ref: '#/components/schemas/ScreeningMatch'
                screened_at:
                  type: string
                  format: date-time

    ScreeningMatch:
      type: object
      properties:
        source:
          type: string
          enum: [OFAC_SDN, BIS_DENIED, UN_SANCTIONS, EU_SANCTIONS, UK_SANCTIONS]
        match_score:
          type: number
        entity_name:
          type: string
        entity_type:
          type: string

    AdjudicationCase:
      allOf:
        - $ref: '#/components/schemas/APIResponse'
        - type: object
          properties:
            data:
              type: object
              properties:
                id:
                  type: string
                  format: uuid
                screening_id:
                  type: string
                  format: uuid
                visitor_id:
                  type: string
                  format: uuid
                status:
                  type: string
                  enum: [pending, approved, denied, escalated, on_hold]
                risk_level:
                  type: string
                created_at:
                  type: string
                  format: date-time

    AdjudicationDecision:
      type: object
      required:
        - decision
        - reason_code
      properties:
        decision:
          type: string
          enum: [approved, denied, escalated, cleared, on_hold]
        reason_code:
          type: string
        remarks:
          type: string
        itar_applies:
          type: boolean
        ear_applies:
          type: boolean
        export_classification_ids:
          type: array
          items:
            type: string
            format: uuid
        site_id:
          type: string
          format: uuid
        program_name:
          type: string
        additional_checks_performed:
          type: object
          additionalProperties: true

    AdjudicationDecisionResponse:
      allOf:
        - $ref: '#/components/schemas/APIResponse'
        - type: object
          properties:
            data:
              type: object
              properties:
                adjudication_id:
                  type: string
                  format: uuid
                decision:
                  type: string
                decided_at:
                  type: string
                  format: date-time

    KioskCheckinRequest:
      type: object
      required:
        - visitor_name
        - host_id
        - site_id
      properties:
        visitor_name:
          type: string
        company:
          type: string
        email:
          type: string
          format: email
        phone:
          type: string
        host_id:
          type: string
          format: uuid
        site_id:
          type: string
          format: uuid
        expected_out_at:
          type: string
          format: date-time
        id_scan_data:
          type: object
        nda_accepted:
          type: boolean

    VisitorSession:
      allOf:
        - $ref: '#/components/schemas/APIResponse'
        - type: object
          properties:
            data:
              type: object
              properties:
                visitor_session_id:
                  type: string
                  format: uuid
                status:
                  type: string
                  enum: [checked_in, checked_out, under_review]
                check_in_at:
                  type: string
                  format: date-time
                checkout_at:
                  type: string
                  format: date-time

    BulkScreeningJob:
      allOf:
        - $ref: '#/components/schemas/APIResponse'
        - type: object
          properties:
            data:
              type: object
              properties:
                job_id:
                  type: string
                  format: uuid
                total_rows:
                  type: integer
                processed:
                  type: integer
                status:
                  type: string
                  enum: [processing, completed, failed]

    ComplianceReportRequest:
      type: object
      required:
        - report_type
        - date_from
        - date_to
      properties:
        report_type:
          type: string
          enum: [screening_activity, adjudication_decisions, audit_trail, evidence_pack]
        date_from:
          type: string
          format: date
        date_to:
          type: string
          format: date
        format:
          type: string
          enum: [csv, json]
          default: csv
        filters:
          type: object

    ComplianceReport:
      allOf:
        - $ref: '#/components/schemas/APIResponse'
        - type: object
          properties:
            data:
              type: object
              properties:
                report_id:
                  type: string
                  format: uuid
                download_url:
                  type: string
                  format: uri
                expires_at:
                  type: string
                  format: date-time

    AdminOverview:
      allOf:
        - $ref: '#/components/schemas/APIResponse'
        - type: object
          properties:
            data:
              type: object
              properties:
                organizations:
                  type: object
                  properties:
                    total:
                      type: integer
                    active:
                      type: integer
                visitors:
                  type: object
                  properties:
                    today:
                      type: integer
                    last_7d:
                      type: integer
                screenings:
                  type: object
                  properties:
                    today:
                      type: integer
                    pending:
                      type: integer

    # ──────────────────────────────────────────────────────────
    # V1 API Schemas
    # ──────────────────────────────────────────────────────────

    V1PrescreenRequest:
      type: object
      required:
        - subject
      properties:
        subject:
          type: string
          maxLength: 500
          description: Name of the individual or entity to screen
        company:
          type: string
          maxLength: 500
          description: Company or organization name
        country:
          type: string
          maxLength: 100
          description: Country name or ISO code
        site_id:
          type: string
          format: uuid
          description: Site context for the screening
        enable_ai:
          type: boolean
          description: Enable AI-assisted analysis
        visitor_id:
          type: string
          format: uuid
          description: Link screening to an existing visitor record
        source:
          type: string
          enum: [api, bulk, kiosk, demo]
          description: Source channel for the screening request
        include_list_freshness:
          type: boolean
          description: When true, response includes aggregate sanctions list freshness metadata

    V1PrescreenResponse:
      type: object
      properties:
        screening_id:
          type: string
          format: uuid
        risk_level:
          type: string
          enum: [APPROVED, CAUTION, HIGH_RISK, BLOCKED, UNKNOWN]
          description: Engine risk band (not a sanctions list name)
        risk_score:
          type: number
          minimum: 0
          maximum: 100
          description: Numeric risk score (0-100)
        list_freshness:
          type: object
          nullable: true
          description: Present when include_list_freshness was true on the request
          properties:
            overall:
              type: string
              enum: [fresh, warning, stale, unknown]
            stale_sources:
              type: integer
            warning_sources:
              type: integer
        adjudication_status:
          type: string
          enum: [pending, approved, denied, escalated, cleared, on_hold]
        adjudication_required:
          type: boolean
        matches:
          type: integer
          description: Number of sanctions matches found
        itar_relevant:
          type: boolean
          description: Whether ITAR regulations may apply
        ear_relevant:
          type: boolean
          description: Whether EAR regulations may apply
        ownership_check:
          type: object
          nullable: true
          description: BIS 50% ownership result if triggered

    V1OwnershipCheckRequest:
      type: object
      required:
        - entity_name
      properties:
        entity_name:
          type: string
          maxLength: 500
          description: Entity name to check in ownership graph
        max_depth:
          type: integer
          minimum: 1
          maximum: 10
          default: 10
          description: Maximum traversal depth
        entity_sector:
          type: string
          maxLength: 200
          description: Business sector of the entity
        entity_jurisdiction:
          type: string
          maxLength: 200
          description: Jurisdiction (country) of the entity

    V1OwnershipCheckResponse:
      type: object
      properties:
        entity_name:
          type: string
        in_ownership_graph:
          type: boolean
          description: Whether the entity was found in the ownership graph
        total_ownership_pct:
          type: number
          description: Aggregate sanctioned ownership percentage
        should_block:
          type: boolean
          description: Whether the 50% threshold is exceeded
        rule_trigger:
          type: string
          nullable: true
          description: Which rule triggered the block (if any)
        sanctioned_owners:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              ownership_pct:
                type: number
              source:
                type: string
        paths:
          type: array
          items:
            type: object
            description: Ownership path from sanctioned entity to target
        red_flags:
          type: object
          description: Compliance risk indicators (opaque jurisdictions, manual review, etc.)
          properties:
            unknown_ownership_detected:
              type: boolean
            opaque_jurisdictions:
              type: array
              items:
                type: string
            minority_sensitive_stake:
              type: boolean
            manual_review_required:
              type: boolean
            flag_reasons:
              type: array
              items:
                type: string

    V1ScreeningResult:
      type: object
      properties:
        id:
          type: string
          format: uuid
        risk_level:
          type: string
          enum: [APPROVED, CAUTION, HIGH_RISK, BLOCKED, UNKNOWN]
        risk_score:
          type: number
          minimum: 0
          maximum: 100
          description: Always expressed on the 0-100 scale in V1 API responses
        adjudication_status:
          type: string
          enum: [pending, approved, denied, escalated, cleared, on_hold]
        screening_type:
          type: string
        created_at:
          type: string
          format: date-time
        matches:
          type: array
          items:
            $ref: '#/components/schemas/ScreeningMatch'
        metadata:
          type: object
          nullable: true
        query:
          description: Original query parameters used for this screening (shape varies by channel)
          oneOf:
            - type: object
            - type: string

    V1VendorScreenRequest:
      type: object
      required:
        - vendor_name
      properties:
        vendor_name:
          type: string
          maxLength: 500
          description: Vendor entity name to screen
        country:
          type: string
          maxLength: 100
        registration:
          type: string
          maxLength: 200
          description: Business registration or DUNS number
        contact_email:
          type: string
          format: email
        enable_ai:
          type: boolean
          description: Enable AI-assisted analysis

    V1CheckinRequest:
      type: object
      required:
        - visitor_id
      properties:
        visitor_id:
          type: string
          format: uuid
          description: ID of the visitor to check in
        site_id:
          type: string
          format: uuid
          description: Site where check-in occurs
        method:
          type: string
          maxLength: 64
          description: Check-in method (e.g. kiosk, manual, api)
        notes:
          type: string
          maxLength: 500
        metadata:
          type: object
          description: Arbitrary metadata (max 4KB)

    V1CheckinResponse:
      type: object
      properties:
        session_id:
          type: string
          format: uuid
        status:
          type: string
        visitor_id:
          type: string
          format: uuid
        checked_in_at:
          type: string
          format: date-time

    V1UpdateSessionRequest:
      type: object
      properties:
        status:
          type: string
          enum: [checked_in, checked_out, under_review, on_hold]
        notes:
          type: string
          maxLength: 500
        metadata:
          type: object
          description: Arbitrary metadata (max 4KB)
        checkout:
          type: boolean
          description: Set to true to check out the visitor

    V1AuditLogRecord:
      type: object
      properties:
        id:
          type: string
          format: uuid
        action:
          type: string
        actor_id:
          type: string
          format: uuid
          nullable: true
        actor_name:
          type: string
        actor_role:
          type: string
        ip_address:
          type: string
        user_agent:
          type: string
        status:
          type: string
        created_at:
          type: string
          format: date-time
        metadata:
          type: object
          nullable: true

    V1AdjudicationRule:
      type: object
      required:
        - name
        - priority
        - condition
        - action
        - reason_code
      properties:
        name:
          type: string
          description: Human-readable rule name
        priority:
          type: integer
          description: Evaluation order (lower = higher priority)
        condition:
          type: string
          description: Rule condition expression
        action:
          type: string
          description: Action to take when condition matches
        reason_code:
          type: string
          description: Reason code for audit trail
        active:
          type: boolean
          default: true
          description: Whether the rule is active

  responses:
    ValidationError:
      description: Request validation failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/APIResponse'
          example:
            success: false
            error:
              code: VALIDATION_ERROR
              message: Invalid request body
              details:
                field: query
                reason: Required field missing

    Unauthorized:
      description: Authentication required or invalid
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/APIResponse'
          example:
            success: false
            error:
              code: UNAUTHORIZED
              message: Authentication required

    Forbidden:
      description: Insufficient permissions
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/APIResponse'
          example:
            success: false
            error:
              code: FORBIDDEN
              message: Insufficient permissions

    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/APIResponse'
          example:
            success: false
            error:
              code: NOT_FOUND
              message: Resource not found

    RateLimitExceeded:
      description: Rate limit exceeded
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/APIResponse'
          example:
            success: false
            error:
              code: RATE_LIMIT_EXCEEDED
              message: Rate limit exceeded
      headers:
        X-RateLimit-Limit:
          schema:
            type: integer
        X-RateLimit-Remaining:
          schema:
            type: integer
        X-RateLimit-Reset:
          schema:
            type: integer

    EntitlementRequired:
      description: Plan does not include this product (Screening API add-on required)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/APIResponse'
          example:
            success: false
            error:
              code: ENTITLEMENT_REQUIRED
              message: Active subscription does not include this feature.

    InternalError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/APIResponse'
          example:
            success: false
            error:
              code: INTERNAL_ERROR
              message: An internal error occurred
