openapi: 3.1.0
info:
  title: Platphorm Ready API
  version: 1.0.0
  description: Evidence-backed conformity, readiness, and certification surfaces for the PlatPhorm network.
servers:
  - url: https://platphormready.platphormnews.com
paths:
  /api/self-assessment:
    get:
      operationId: getSelfAssessment
      description: Run bounded, read-only live checks against the published PlatPhorm Ready contract and registries.
      responses: { '200': { description: Self-assessment result } }
  /api/v1/self-assessment:
    get:
      operationId: getSelfAssessmentVersioned
      responses: { '200': { description: Versioned self-assessment result } }
  /api/health:
    get:
      operationId: getHealth
      responses:
        '200': { description: Service health }
  /api/v1/controls:
    get:
      operationId: listControls
      responses:
        '200': { description: Versioned control registry }
  /api/v1/certificates:
    get:
      operationId: listCertificates
      responses:
        '200': { description: Public active certificates }
  /api/v1/profiles:
    get:
      operationId: listVerificationProfiles
      responses:
        '200': { description: Versioned readiness profiles }
  /api/v1/adapters:
    get:
      operationId: listSpecialistAdapters
      description: Runtime-backed scanner status, authority rationale, bounded live capability discovery, health evidence, and source-labeled discovery evidence; unavailable scanners remain UNKNOWN.
      responses:
        '200': { description: Source-backed specialist scanner authorities }
  /api/v1/tests:
    get:
      operationId: listReadyTests
      responses:
        '200': { description: Versioned tests and execution surfaces }
  /api/v1/verification/plan:
    post:
      operationId: createVerificationPlan
      requestBody:
        required: true
        content:
          application/json: { schema: { $ref: '#/components/schemas/VerificationRequest' } }
      responses:
        '200': { description: Bounded read-only verification plan }
  /api/v1/verification/schema:
    get:
      operationId: getVerificationSchema
      responses:
        '200': { description: Versioned verification report schema }
  /api/v1/network/assessments:
    post:
      operationId: queueNetworkAssessment
      security: [{ PlatPhormApiKey: [] }]
      requestBody:
        required: true
        content:
          application/json: { schema: { $ref: '#/components/schemas/NetworkAssessmentRequest' } }
      responses:
        '202': { description: Bounded trusted-network assessment queued }
        '400': { description: Invalid network request }
        '401': { description: Authentication required }
  /api/v1/network/assessments/{id}:
    get:
      operationId: getNetworkAssessment
      security: [{ PlatPhormApiKey: [] }]
      parameters:
        - { name: id, in: path, required: true, schema: { type: string } }
      responses:
        '200': { description: Network parent and child-run matrix }
        '401': { description: Authentication required }
        '404': { description: Network assessment not found }
  /api/v1/verification/contracts:
    get:
      operationId: getAssessmentContracts
      responses:
        '200': { description: Versioned common assessment request/result and orchestration contracts }
  /api/v1/verification/runs:
    get:
      operationId: listVerificationRuns
      description: List durable verification runs from Cloudflare D1 by requester and status; full reports remain in R2.
      security: [{ PlatPhormApiKey: [] }]
      parameters:
        - { name: requested_by, in: query, schema: { type: string } }
        - { name: status, in: query, schema: { type: string, description: 'Comma-separated queued, running, completed, or failed states.' } }
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100 } }
      responses:
        '200': { description: Durable verification run summaries }
        '401': { description: Authentication required }
    post:
      operationId: queueVerificationRun
      security: [{ PlatPhormApiKey: [] }]
      requestBody:
        required: true
        content:
          application/json: { schema: { $ref: '#/components/schemas/VerificationRequest' } }
      responses:
        '202': { description: Verification queued with a run ID and result URL }
        '401': { description: Authentication required }
  /api/v1/verification/runs/{id}:
    get:
      operationId: getVerificationRun
      security: [{ PlatPhormApiKey: [] }]
      parameters:
        - { name: id, in: path, required: true, schema: { type: string } }
      responses:
        '200': { description: Verification state or evidence-backed report }
        '401': { description: Authentication required }
        '404': { description: Run not found }
  /api/v1/verification/runs/{id}/specialists:
    post:
      operationId: ingestSpecialistResult
      security: [{ PlatPhormApiKey: [] }]
      parameters:
        - { name: id, in: path, required: true, schema: { type: string } }
      responses:
        '202': { description: Specialist result accepted }
        '401': { description: Authentication required }
  /api/v1/repository-assessments:
    post:
      operationId: queueRepositoryAssessment
      security: [{ PlatPhormApiKey: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RepositoryAssessmentRequest'
      responses:
        '202': { description: Repository assessment queued with a run ID and result URL }
        '400': { description: Invalid repository input }
        '401': { description: Authentication required }
  /api/repository-assessments:
    post:
      operationId: queueRepositoryAssessmentStablePath
      security: [{ PlatPhormApiKey: [] }]
      requestBody:
        required: true
        content:
          application/json: { schema: { $ref: '#/components/schemas/RepositoryAssessmentRequest' } }
      responses:
        '202': { description: Repository assessment queued with a run ID and result URL }
        '400': { description: Invalid repository input }
        '401': { description: Authentication required }
  /api/network-assessments:
    post:
      operationId: queueNetworkAssessmentStablePath
      security: [{ PlatPhormApiKey: [] }]
      requestBody:
        required: true
        content:
          application/json: { schema: { $ref: '#/components/schemas/NetworkAssessmentRequest' } }
      responses:
        '202': { description: Bounded trusted-network assessment queued }
        '400': { description: Invalid network request }
        '401': { description: Authentication required }
  /api/network-assessments/{id}:
    get:
      operationId: getNetworkAssessmentStablePath
      security: [{ PlatPhormApiKey: [] }]
      parameters:
        - { name: id, in: path, required: true, schema: { type: string } }
      responses:
        '200': { description: Network parent and child-run matrix with evidence-backed gap queries }
        '401': { description: Authentication required }
        '404': { description: Network assessment not found }
  /api/assessments:
    post:
      operationId: createAssessment
      security: [{ PlatPhormApiKey: [] }]
      requestBody:
        required: true
        content:
          application/json: { schema: { $ref: '#/components/schemas/VerificationRequest' } }
      responses:
        '202': { description: Assessment queued }
        '401': { description: Authentication required }
  /api/assessments/{id}:
    get:
      operationId: getAssessment
      security: [{ PlatPhormApiKey: [] }]
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses:
        '200': { description: Assessment state or report }
        '401': { description: Authentication required }
        '404': { description: Assessment not found }
  /api/assessments/{id}/report:
    get:
      operationId: getAssessmentReport
      description: Return the JSON report by default or an escaped human-readable HTML report with format=html or Accept text/html.
      security: [{ PlatPhormApiKey: [] }]
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }, { name: format, in: query, required: false, schema: { type: string, enum: [json, html] } }]
      responses: { '200': { description: Complete evidence-backed report }, '401': { description: Authentication required } }
  /api/v1/verification/ui/report:
    get:
      operationId: getPublicAssessmentHtmlReport
      description: Return a human-readable HTML report for a public dashboard run. The report is escaped and public-safe.
      parameters: [{ name: id, in: query, required: true, schema: { type: string } }]
      responses: { '200': { description: Human-readable HTML report }, '401': { description: Authentication required } }
  /api/assessments/{id}/evidence:
    get:
      operationId: getAssessmentEvidence
      description: Observations, specialist evidence, source-preserving evidence edges, deduplicated evidence clusters, scanner contributions, scanner coverage, and requirement coverage.
      security: [{ PlatPhormApiKey: [] }]
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses: { '200': { description: Persisted observations and specialist evidence }, '401': { description: Authentication required } }
  /api/assessments/{id}/conformity:
    get:
      operationId: getAssessmentConformity
      security: [{ PlatPhormApiKey: [] }]
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses: { '200': { description: Independent conformity and readiness gates }, '401': { description: Authentication required } }
  /api/assessments/{id}/drift:
    get:
      operationId: getAssessmentDrift
      security: [{ PlatPhormApiKey: [] }]
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses: { '200': { description: Drift findings }, '401': { description: Authentication required } }
  /api/assessments/{id}/contradictions:
    get:
      operationId: getAssessmentContradictions
      security: [{ PlatPhormApiKey: [] }]
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses: { '200': { description: Contradictory observations }, '401': { description: Authentication required } }
  /api/assessments/{id}/repair-plan:
    get:
      operationId: getAssessmentRepairPlan
      security: [{ PlatPhormApiKey: [] }]
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses: { '200': { description: Evidence-derived repair plan }, '401': { description: Authentication required } }
  /api/comparisons:
    post:
      operationId: createComparison
      security: [{ PlatPhormApiKey: [] }]
      requestBody:
        required: true
        content:
          application/json: { schema: { $ref: '#/components/schemas/ComparisonRequest' } }
      responses:
        '202': { description: Baseline/candidate comparison queued }
        '400': { description: Invalid comparison request }
        '401': { description: Authentication required }
  /api/comparisons/{id}:
    get:
      operationId: getComparison
      security: [{ PlatPhormApiKey: [] }]
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses:
        '200': { description: Evidence-backed migration comparison }
        '401': { description: Authentication required }
        '404': { description: Comparison not found }
  /api/tests:
    get: { operationId: listTests, responses: { '200': { description: Versioned tests } } }
  /api/probes:
    get: { operationId: listProbes, responses: { '200': { description: Versioned probe definitions } } }
  /api/assertions:
    get: { operationId: listAssertions, responses: { '200': { description: Versioned assertion definitions } } }
  /api/profiles:
    get: { operationId: listProfiles, responses: { '200': { description: Versioned readiness profiles } } }
  /api/standards:
    get: { operationId: listStandards, responses: { '200': { description: External and PlatPhorm standard references } } }
  /api/invariants:
    get: { operationId: listInvariants, responses: { '200': { description: Applicability-aware invariant registry } } }
  /api/archetypes:
    get: { operationId: listArchetypes, responses: { '200': { description: Applicability-aware service archetypes } } }
  /api/drift-types:
    get: { operationId: listDriftTypes, responses: { '200': { description: Drift type registry } } }
  /api/v1/assessments/rollup:
    post:
      operationId: rollupAssessments
      security: [{ PlatPhormApiKey: [] }]
      requestBody:
        required: true
        content:
          application/json: { schema: { $ref: '#/components/schemas/RollupRequest' } }
      responses:
        '200': { description: Validated and reconciled federated rollup }
        '400': { description: Invalid supplied assessment }
        '401': { description: Authentication required }
  /api/assessments/rollup:
    post:
      operationId: rollupAssessmentsLegacyPath
      security: [{ PlatPhormApiKey: [] }]
      requestBody:
        required: true
        content:
          application/json: { schema: { $ref: '#/components/schemas/RollupRequest' } }
      responses:
        '200': { description: Validated and reconciled federated rollup }
        '400': { description: Invalid supplied assessment }
        '401': { description: Authentication required }
  /api/v1/assessment-events:
    post:
      operationId: ingestAssessmentEvent
      security: [{ PlatPhormApiKey: [] }]
      requestBody:
        required: true
        content:
          application/json: { schema: { $ref: '#/components/schemas/AssessmentEvent' } }
      responses:
        '200': { description: Event accepted and completed events rolled up }
        '400': { description: Invalid assessment event }
        '401': { description: Authentication required }
  /api/assessment-events:
    post:
      operationId: ingestAssessmentEventLegacyPath
      security: [{ PlatPhormApiKey: [] }]
      requestBody:
        required: true
        content:
          application/json: { schema: { $ref: '#/components/schemas/AssessmentEvent' } }
      responses:
        '200': { description: Event accepted and completed events rolled up }
        '400': { description: Invalid assessment event }
        '401': { description: Authentication required }
  /api/mcp:
    get: { operationId: getMcpMetadata, responses: { '200': { description: MCP metadata } } }
    post: { operationId: callMcp, responses: { '200': { description: JSON-RPC response } } }
components:
  securitySchemes:
    PlatPhormApiKey:
      type: http
      scheme: bearer
      bearerFormat: PLATPHORM_API_KEY
      description: Shared PlatPhorm API key. X-PlatPhorm-API-Key is also accepted.
  schemas:
    VerificationRequest:
      type: object
      required: [target]
      properties:
        target: { type: string, format: uri, pattern: '^https://' }
        mode: { type: string, enum: [WEB_ONLY, WEB_BROWSER, AGENT_REPOSITORY, AGENT_PROVIDER, FULL] }
        scan_level: { type: string, enum: [QUICK, STANDARD, FULL, MIGRATION, REPOSITORY, NETWORK], description: 'Requested scan depth. NETWORK uses bounded graph and sitemap fan-out through the network assessment endpoint.' }
        profiles: { type: array, items: { type: string } }
        repository: { type: string, description: 'GitHub owner/repository or URL; accepted only through API or MCP' }
        idempotency_key: { type: string, maxLength: 200, description: 'Reuse to retrieve the existing queued run instead of creating a duplicate' }
        orchestration_mode: { type: string, enum: [AUTO, LOCAL_ONLY, SPECIALISTS_ONLY, CONSUME_SUPPLIED] }
        specialist_results: { type: array, items: { type: object }, description: 'Fresh normalized specialist results supplied for reuse' }
        provider_project: { type: string, description: 'Vercel project id/name reference; never a token' }
        cloudflare_zone_id: { type: string }
        cloudflare_account_id: { type: string }
        execution_id: { type: string }
        parent_run_id: { type: string }
        root_orchestrator: { type: string }
        visited_services: { type: array, items: { type: string } }
        depth: { type: integer, minimum: 0, maximum: 10 }
    ComparisonRequest:
      type: object
      required: [baseline_target, candidate_target]
      properties:
        baseline_target: { type: string, format: uri, pattern: '^https://' }
        candidate_target: { type: string, format: uri, pattern: '^https://' }
        production_target: { type: string, format: uri, pattern: '^https://' }
        comparison_mode: { type: string, enum: [STRICT_PARITY, SEMANTIC_PARITY, CAPABILITY_PARITY] }
        mode: { type: string, enum: [WEB_ONLY, WEB_BROWSER, AGENT_REPOSITORY, AGENT_PROVIDER, FULL] }
        profiles: { type: array, items: { type: string } }
        idempotency_key: { type: string, maxLength: 200 }
        orchestration_mode: { type: string, enum: [AUTO, LOCAL_ONLY, SPECIALISTS_ONLY, CONSUME_SUPPLIED] }
    RepositoryAssessmentRequest:
      type: object
      required: [repository]
      properties:
        repository: { type: string, description: 'GitHub owner/repository or https://github.com/owner/repository URL' }
        target: { type: string, format: uri, pattern: '^https://', description: 'Optional associated public deployment URL; defaults to the repository URL' }
        mode: { type: string, enum: [AGENT_REPOSITORY, FULL] }
        scan_level: { type: string, enum: [REPOSITORY, FULL], description: 'Repository-focused scan depth; NETWORK is not supported by the single-target queue.' }
        profiles: { type: array, items: { type: string } }
        idempotency_key: { type: string, maxLength: 200 }
        requested_by: { type: string }
        provider_project: { type: string, description: 'Vercel project reference; never a token' }
        cloudflare_zone_id: { type: string }
        cloudflare_account_id: { type: string }
    NetworkAssessmentRequest:
      type: object
      properties:
        root_url: { type: string, format: uri, pattern: '^https://platphormnews\.com$' }
        source: { type: string, enum: [graph, sitemap, both] }
        max_targets: { type: integer, minimum: 1, maximum: 100 }
        concurrency: { type: integer, minimum: 1, maximum: 8 }
        scan_level: { type: string, enum: [QUICK, STANDARD, FULL] }
        mode: { type: string, enum: [WEB_ONLY, WEB_BROWSER, FULL] }
        requested_by: { type: string }
        idempotency_key: { type: string, maxLength: 200 }
        execution_id: { type: string }
    SelfAssessment:
      type: object
      required: [schema, target, status, generatedAt, summary, assertions, evidence]
      properties:
        schema: { const: platphorm.ready.self-assessment@1 }
        target: { type: string, format: uri }
        status: { type: string, enum: [PASS, FAIL, CONDITIONAL, UNKNOWN] }
        generatedAt: { type: string, format: date-time }
        summary: { type: object }
        assertions: { type: array, items: { type: object } }
        evidence: { type: array, items: { type: object } }
        limitations: { type: array, items: { type: string } }
    RollupRequest:
      type: object
      required: [target, assessments]
      properties:
        target: { type: string, format: uri, pattern: '^https://' }
        assessments: { type: array, minItems: 1, maxItems: 20, items: { type: object } }
        mode: { type: string, enum: [WEB_ONLY, WEB_BROWSER, AGENT_REPOSITORY, AGENT_PROVIDER, FULL] }
        execution_id: { type: string }
        orchestration_mode: { type: string, enum: [AUTO, LOCAL_ONLY, SPECIALISTS_ONLY, CONSUME_SUPPLIED] }
        rubric: { type: string }
    AssessmentEvent:
      type: object
      required: [id, type, source, subject, data]
      properties:
        specversion: { type: string, enum: ['1.0'] }
        id: { type: string }
        type: { type: string, enum: [platphorm.assessment.started@1, platphorm.assessment.completed@1, platphorm.assessment.failed@1, platphorm.assessment.expired@1, assessment.started, assessment.completed, assessment.failed, assessment.expired] }
        source: { type: string }
        subject: { type: string }
        time: { type: string, format: date-time }
        datacontenttype: { type: string, enum: [application/json] }
        data: { type: object }
