openapi: 3.1.0
info:
  title: Robertium Data API
  version: 1.0.0
  summary: Static JSON endpoints for the Robertium hypothesis catalog.
  description: |
    Robertium exposes its hypothesis catalog, prospective tracking snapshot,
    per-hypothesis provenance bundles, and ClinicalTrials.gov indexes as
    publicly-hosted JSON files on the production site.

    **This is a static file API.** Every endpoint resolves to a JSON file
    served by Cloudflare Pages. There are no query parameters, no
    authentication, no rate limits, and no per-user quotas. Free to use,
    including for commercial research. If you find it useful in a
    publication, please cite Robertium (see contact).

    **No live querying.** Filters described on the
    [Research access](https://robertium.com/api) page (`?drug=X&mediator=Y`)
    are *planned*, not shipped. Today, clients must download the full
    document (e.g. `/data/hypotheses.json`, ~3 MB) and filter locally. For
    bulk graph access (Kuzu / Postgres dumps), email the contact below.

    **Schema stability.** Field names follow [snake_case]. Top-level
    documents include a `schema_version` or `metadata.schema_version` where
    available. Breaking changes will bump the spec `info.version` major.
  contact:
    name: Daniel Trofimov
    email: daniel@robertium.com
    url: https://github.com/routewise96/robertium/issues
  license:
    name: MIT
    url: https://github.com/routewise96/robertium/blob/main/LICENSE
  termsOfService: https://robertium.com/api

servers:
  - url: https://robertium.com
    description: Production (Cloudflare Pages)

security:
  - {}  # No authentication required. All endpoints are public, anonymous, and rate-limit-free.

tags:
  - name: Hypotheses
    description: Outreach-quality drug repurposing hypotheses generated by the cross-domain orchestrator.
  - name: Prospective
    description: Frozen hypothesis snapshot tracked against newly-published PubMed evidence.
  - name: Provenance
    description: Per-hypothesis evidence bundles — source papers, extracted claims, pipeline run metadata.
  - name: Trials
    description: ClinicalTrials.gov indexes by drug and by hypothesis.

paths:
  /data/hypotheses.json:
    get:
      tags: [Hypotheses]
      summary: Full hypothesis catalog
      description: |
        Returns the full set of outreach-quality cross-domain hypotheses
        (typically a few thousand items at `high` or `mid` quality tier)
        plus generation metadata. This is the same file the website renders
        on `/hypotheses` and `/drugs`. Roughly 3 MB; clients should cache.
      operationId: getHypotheses
      responses:
        '200':
          description: Hypothesis catalog with metadata.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HypothesesDocument'
        '404':
          description: File temporarily unavailable during a deploy window. Retry.

  /data/prospective.json:
    get:
      tags: [Prospective]
      summary: Prospective tracking snapshot
      description: |
        Returns a frozen hypothesis snapshot together with newly-discovered
        PubMed evidence accumulated since the freeze date. Used to power
        the `/predictions` page.
      operationId: getProspective
      responses:
        '200':
          description: Prospective snapshot with per-row update status.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProspectiveDocument'
        '404':
          description: File temporarily unavailable during a deploy window. Retry.

  /data/provenance/{hypothesis_id}.json:
    get:
      tags: [Provenance]
      summary: Provenance bundle for a single hypothesis
      description: |
        Returns the full provenance bundle for a single hypothesis — source
        papers (with PMIDs, DOIs, roles), extracted claim counts, and
        snapshots of the extraction, bridge, embedding, and scoring
        configurations used to produce it.

        **Availability:** This endpoint becomes live after the parallel
        `provenance` branch is merged into `main`. Until then, files exist
        in the working tree but are not yet served by production.
      operationId: getProvenance
      parameters:
        - $ref: '#/components/parameters/HypothesisId'
      responses:
        '200':
          description: Provenance bundle for the requested hypothesis.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProvenanceDocument'
        '404':
          description: No provenance bundle exists for this hypothesis id.

  /data/trials/summary.json:
    get:
      tags: [Trials]
      summary: Trials index summary
      description: |
        Returns counts and the canonical list of `drug_id` slugs for which
        per-drug clinical trial files are available. Counts (`drug_files`,
        `hypothesis_files`, `total_drug_trials`, `total_hypothesis_trials`)
        are derived from the index at generation time and may lag the
        actual file count on disk between rebuilds; treat `drug_ids` as
        authoritative for client-side enumeration.
      operationId: getTrialsSummary
      responses:
        '200':
          description: Trials index summary.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrialsSummary'
        '404':
          description: File temporarily unavailable during a deploy window. Retry.

  /data/trials/by_drug/{drug_slug}.json:
    get:
      tags: [Trials]
      summary: ClinicalTrials.gov results for a drug
      description: |
        Returns ClinicalTrials.gov registry entries matched to a single
        drug, regardless of indication. The list of valid slugs is in
        `/data/trials/summary.json#drug_ids`.
      operationId: getDrugTrials
      parameters:
        - $ref: '#/components/parameters/DrugSlug'
      responses:
        '200':
          description: Clinical trials for the requested drug.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DrugTrials'
        '404':
          description: Unknown drug slug.

  /data/trials/by_hypothesis/{hypothesis_id}.json:
    get:
      tags: [Trials]
      summary: ClinicalTrials.gov results scoped to a hypothesis
      description: |
        Returns ClinicalTrials.gov registry entries that match both the
        drug and the outcome of a specific hypothesis (intersection
        search). Empty `trials` array means no co-mentioning trial exists
        — common for novel cross-domain hypotheses.
      operationId: getHypothesisTrials
      parameters:
        - $ref: '#/components/parameters/HypothesisId'
      responses:
        '200':
          description: Clinical trials matching the hypothesis drug × outcome.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HypothesisTrials'
        '404':
          description: Unknown hypothesis id.

components:
  parameters:
    HypothesisId:
      name: hypothesis_id
      in: path
      required: true
      description: Integer hypothesis id, matching `hypotheses[].id` in `/data/hypotheses.json`.
      schema:
        type: integer
        minimum: 1
        examples: [37636, 10180]

    DrugSlug:
      name: drug_slug
      in: path
      required: true
      description: |
        Kebab-case drug identifier. Lowercase, ASCII, with apostrophes and
        whitespace replaced by hyphens (e.g. `2'-fucosyllactose` → `2-fucosyllactose`,
        `5-ASA` → `5-asa`). Enumerated in `/data/trials/summary.json#drug_ids`.
      schema:
        type: string
        pattern: '^[a-z0-9][a-z0-9-]*$'
        examples: ['auranofin', '2-fucosyllactose', '5-asa']

  schemas:
    # ── Hypothesis catalog ─────────────────────────────────────────────
    HypothesesDocument:
      type: object
      required: [metadata, hypotheses]
      properties:
        metadata:
          $ref: '#/components/schemas/HypothesesMetadata'
        hypotheses:
          type: array
          description: One entry per outreach-quality hypothesis.
          items:
            $ref: '#/components/schemas/Hypothesis'

    HypothesesMetadata:
      type: object
      required:
        - generated_at
        - domain_filter
        - quality_filter
        - total_count
        - domains
        - evidence_papers
        - quality_breakdown
        - validation_breakdown
        - schema_version
      properties:
        generated_at:
          type: string
          format: date-time
          description: ISO-8601 timestamp of the catalog generation run.
        domain_filter:
          type: string
          description: Source-domain selector applied at generation time.
          examples: ['cross_domain']
        quality_filter:
          type: array
          items: { type: string }
          description: Outreach-quality tiers included in this catalog.
          examples: [['high', 'mid']]
        total_count:
          type: integer
          description: Number of items in `hypotheses[]`. Varies between rebuilds.
        domains:
          type: array
          items: { type: string }
          description: Therapeutic domains the orchestrator drew from.
        evidence_papers:
          type: integer
          description: Distinct PubMed papers cited across all hypotheses' evidence chains.
        duplicate_count:
          type: integer
          description: Number of (drug, mediator, outcome) triples deduplicated during generation.
        quality_breakdown:
          $ref: '#/components/schemas/QualityBreakdown'
        validation_breakdown:
          $ref: '#/components/schemas/ValidationBreakdown'
        schema_version:
          type: string
          description: Schema version for the hypotheses document.
          examples: ['1.3']

    QualityBreakdown:
      type: object
      description: Counts of hypotheses by outreach-quality tier across the full pre-filter pool.
      properties:
        high: { type: integer }
        mid: { type: integer }
        low: { type: integer }
        excluded: { type: integer }

    ValidationBreakdown:
      type: object
      required: [clinical_trials, literature]
      properties:
        clinical_trials:
          $ref: '#/components/schemas/ClinicalTrialsBreakdown'
        literature:
          $ref: '#/components/schemas/LiteratureBreakdown'

    ClinicalTrialsBreakdown:
      type: object
      description: Hypothesis counts by ClinicalTrials.gov status of the drug × outcome pair.
      properties:
        active: { type: integer }
        completed: { type: integer }
        none: { type: integer }
        unknown: { type: integer }
        preclinical_only: { type: integer }

    LiteratureBreakdown:
      type: object
      description: Hypothesis counts by literature-validation status.
      properties:
        novel_signal: { type: integer }
        partial_evidence: { type: integer }
        reproduces_known: { type: integer }
        unknown: { type: integer }

    Hypothesis:
      type: object
      required:
        - id
        - drug
        - mediator
        - outcome
        - score
        - outreach_score
        - outreach_quality
        - clinical_trial_status
        - clinical_trial_nct_ids
        - literature_status
        - hypothesis_type
        - evidence
      properties:
        id:
          type: integer
          description: Stable hypothesis identifier. Use it to fetch `/data/provenance/{id}.json` and `/data/trials/by_hypothesis/{id}.json`.
        drug:
          type: string
          description: Drug as it appeared in the source literature.
        drug_type:
          type: string
          examples: ['drug', 'compound']
        drug_domain:
          type: string
          description: Therapeutic domain in which the drug was first surfaced.
        drug_canonical:
          type: string
          description: Lowercased canonical form of `drug` for cross-domain matching.
        predicate_ab:
          type: string
          description: Verb linking drug → mediator (A → B).
          examples: ['inhibits', 'activates', 'modulates']
        mediator:
          type: string
          description: Mechanistic mediator (protein, pathway, complex) bridging drug and outcome.
        mediator_type:
          type: string
          examples: ['protein', 'pathway', 'gene']
        mediator_canonical:
          type: string
        predicate_bc:
          type: string
          description: Verb linking mediator → outcome (B → C).
          examples: ['associated_with', 'causes', 'modulates']
        outcome:
          type: string
        outcome_type:
          type: string
          examples: ['disease', 'phenotype']
        outcome_domain:
          type: string
          description: Therapeutic domain to which `outcome` belongs.
        outcome_canonical:
          type: string
        score:
          type: number
          format: float
          description: Raw orchestrator score before outreach filtering.
        filtered_score:
          type: number
          format: float
          description: Score after repurposing-boost / penalty filters.
        filter_reason:
          type:
            - string
            - 'null'
          description: Tag explaining why `filtered_score` differs from `score`, when applicable.
          examples: ['boosted_repurposing']
        outreach_score:
          type: number
          format: float
        outreach_quality:
          type: string
          enum: [high, mid]
        outreach_reason:
          type:
            - string
            - 'null'
        clinical_trial_status:
          type: string
          enum: [active, completed, none, unknown, preclinical_only]
        clinical_trial_nct_ids:
          type: array
          items: { type: string, pattern: '^NCT[0-9]+$' }
        literature_status:
          type: string
          enum: [novel_signal, partial_evidence, reproduces_known, unknown]
        ab_count:
          type: integer
          description: Number of papers supporting the drug → mediator claim.
        bc_count:
          type: integer
          description: Number of papers supporting the mediator → outcome claim.
        direct_count:
          type: integer
          description: Number of papers asserting drug → outcome directly (usually 0 for novel cross-domain leads).
        hypothesis_type:
          type: string
          examples: ['cross_domain']
        evidence:
          $ref: '#/components/schemas/EvidenceCollection'

    EvidenceCollection:
      type: object
      required: [ab, bc]
      properties:
        ab:
          type: array
          description: Papers supporting drug → mediator.
          items: { $ref: '#/components/schemas/EvidencePaper' }
        bc:
          type: array
          description: Papers supporting mediator → outcome.
          items: { $ref: '#/components/schemas/EvidencePaper' }

    EvidencePaper:
      type: object
      required: [pmid, title]
      properties:
        pmid:
          type: string
          description: PubMed ID as a string (preserves leading zeros if any).
        title: { type: string }
        doi: { type: string }
        year: { type: integer }

    # ── Prospective tracking ───────────────────────────────────────────
    ProspectiveDocument:
      type: object
      required: [metadata, rows]
      properties:
        metadata:
          $ref: '#/components/schemas/ProspectiveMetadata'
        rows:
          type: array
          items: { $ref: '#/components/schemas/ProspectiveRow' }

    ProspectiveMetadata:
      type: object
      required: [snapshot_name, snapshot_date, frozen_at, total]
      properties:
        snapshot_name: { type: string, examples: ['v2_2026_05_12'] }
        snapshot_date: { type: string, format: date }
        frozen_at: { type: string, format: date-time }
        last_checked_at: { type: string, format: date-time }
        exported_at: { type: string, format: date-time }
        total: { type: integer }
        pending: { type: integer }
        partial_support: { type: integer }
        strong_support: { type: integer }
        high_quality: { type: integer }
        mid_quality: { type: integer }
        total_new_pmids: { type: integer }
        total_new_trials: { type: integer }
        methodology:
          type: object
          additionalProperties: { type: string }
          description: Free-form text fields documenting the tracking methodology (baseline, match criteria, etc.).

    ProspectiveRow:
      type: object
      required: [hypothesis_id, drug, mediator, outcome, status, frozen_at]
      properties:
        hypothesis_id: { type: integer }
        drug: { type: string }
        drug_normalized: { type: string }
        mediator: { type: string }
        outcome: { type: string }
        outcome_normalized:
          type:
            - string
            - 'null'
        domain_source: { type: string }
        domain_target: { type: string }
        quality_tier: { type: string, enum: [high, mid] }
        score: { type: number, format: float }
        status:
          type: string
          enum: [pending, partial_support, strong_support]
        frozen_at: { type: string, format: date-time }
        last_check_date: { type: string, format: date }
        baseline_pmid_count: { type: integer }
        current_pmid_count: { type: integer }
        new_pmids_since_freeze:
          type: array
          items: { $ref: '#/components/schemas/NewPmid' }

    NewPmid:
      type: object
      required: [pmid, year, title, pubdate, first_seen_check_date]
      properties:
        pmid: { type: string }
        year: { type: integer }
        title: { type: string }
        pubdate:
          type: string
          description: Free-form PubMed publication date (e.g. `2026 Dec`).
        first_seen_check_date:
          type: string
          format: date
          description: Date on which this PMID first appeared in the prospective check.

    # ── Provenance ─────────────────────────────────────────────────────
    ProvenanceDocument:
      type: object
      required:
        - schema_version
        - hypothesis_id
        - triple
        - domain
        - source_papers
        - generated_at
      properties:
        schema_version: { type: string, examples: ['1.0'] }
        hypothesis_id: { type: integer }
        triple: { $ref: '#/components/schemas/ProvenanceTriple' }
        domain: { $ref: '#/components/schemas/ProvenanceDomain' }
        provenance_completeness:
          type: string
          description: How fully the provenance bundle was reconstructed.
          examples: ['partial_backfill', 'complete']
        completeness_notes: { type: string }
        source_papers:
          type: array
          items: { $ref: '#/components/schemas/SourcePaper' }
        source_paper_count: { type: integer }
        extracted_claims:
          description: Sample of structured claims extracted from `source_papers`. Shape may vary across rebuilds; treat as documentation-only payload.
          oneOf:
            - type: array
              items: { type: object, additionalProperties: true }
            - type: object
              additionalProperties: true
        extracted_claim_count: { type: integer }
        extraction:
          type: object
          additionalProperties: true
          description: Snapshot of extraction-stage configuration (model, prompt version, parameters).
        bridge:
          type: object
          additionalProperties: true
          description: Snapshot of cross-domain bridge configuration.
        embedding:
          type: object
          additionalProperties: true
        scoring:
          type: object
          additionalProperties: true
        pipeline_run:
          type: object
          additionalProperties: true
          description: Identifier of the pipeline run that produced this bundle (may be a backfill record).
        generated_at: { type: string, format: date-time }

    ProvenanceTriple:
      type: object
      required: [drug, mediator, outcome]
      properties:
        drug: { type: string }
        drug_canonical: { type: string }
        mediator: { type: string }
        mediator_canonical: { type: string }
        outcome: { type: string }
        outcome_canonical: { type: string }

    ProvenanceDomain:
      type: object
      properties:
        domain:
          type: string
          examples: ['cross_domain']
        source:
          type: string
          description: Therapeutic domain the drug was originally surfaced in.
        target:
          type: string
          description: Therapeutic domain the outcome belongs to.

    SourcePaper:
      type: object
      required: [pmid, title]
      properties:
        pmid: { type: string }
        doi: { type: string }
        year: { type: integer }
        title: { type: string }
        journal: { type: string }
        publication_date: { type: string, format: date }
        work_id:
          type: integer
          description: Internal stable id for the paper inside Robertium's catalog.
        roles:
          type: array
          items:
            type: string
            enum: [drug_mediator, mediator_outcome, drug_outcome]
          description: Which leg(s) of the (A,B,C) triple this paper supports.

    # ── Trials ─────────────────────────────────────────────────────────
    TrialsSummary:
      type: object
      required: [generated_at, drug_ids]
      properties:
        generated_at: { type: string, format: date-time }
        drug_files:
          type: integer
          description: Number of `by_drug/*.json` files at index generation time. May lag the actual file count on disk between rebuilds.
        hypothesis_files:
          type: integer
          description: Number of `by_hypothesis/*.json` files at index generation time. May lag the actual file count on disk between rebuilds.
        total_drug_trials:
          type: integer
          description: Sum of `total_trials` across all drug files at generation time.
        total_hypothesis_trials:
          type: integer
          description: Sum of `total_trials` across all hypothesis files at generation time.
        drug_ids:
          type: array
          description: Canonical kebab-case slugs for which `/data/trials/by_drug/{slug}.json` exists at generation time.
          items: { type: string }

    DrugTrials:
      type: object
      required: [drug, drug_id, total_trials, trials]
      properties:
        drug:
          type: string
          description: Drug name as displayed (may include apostrophes, capitalisation).
        drug_id:
          type: string
          description: Kebab-case slug matching the path parameter.
        total_trials: { type: integer }
        fetched_at: { type: string, format: date-time }
        search_url:
          type: string
          format: uri
          description: ClinicalTrials.gov search URL that produced this result set.
        trials:
          type: array
          items: { $ref: '#/components/schemas/ClinicalTrial' }

    HypothesisTrials:
      type: object
      required: [hypothesis_id, drug, outcome, total_trials, trials]
      properties:
        hypothesis_id: { type: integer }
        drug: { type: string }
        outcome: { type: string }
        total_trials: { type: integer }
        fetched_at: { type: string, format: date-time }
        search_url: { type: string, format: uri }
        trials:
          type: array
          items: { $ref: '#/components/schemas/ClinicalTrial' }

    ClinicalTrial:
      type: object
      required: [nct_id, title, overall_status]
      properties:
        nct_id:
          type: string
          pattern: '^NCT[0-9]+$'
        title: { type: string }
        phases:
          type: array
          items:
            type: string
            description: Trial phase as reported by ClinicalTrials.gov; `NA` for non-phased studies.
            examples: ['PHASE1', 'PHASE2', 'PHASE3', 'PHASE4', 'NA']
        overall_status:
          type: string
          examples: ['RECRUITING', 'COMPLETED', 'TERMINATED', 'WITHDRAWN', 'ACTIVE_NOT_RECRUITING', 'UNKNOWN']
        study_type:
          type: string
          examples: ['INTERVENTIONAL', 'OBSERVATIONAL']
        start_date: { type: string, format: date }
        completion_date: { type: string, format: date }
        last_update_date: { type: string, format: date }
        conditions:
          type: array
          items: { type: string }
        interventions:
          type: array
          items: { $ref: '#/components/schemas/TrialIntervention' }
        sponsor: { type: string }

    TrialIntervention:
      type: object
      required: [name, type]
      properties:
        name: { type: string }
        type:
          type: string
          examples: ['DRUG', 'BIOLOGICAL', 'DIETARY_SUPPLEMENT', 'PROCEDURE', 'DEVICE', 'OTHER']
        other_names:
          type: array
          items: { type: string }
