openapi: 3.1.0
info:
  title: MatterMesh.ai Reality Translation Engine
  version: "1.0.0"
  description: >
    Public dataset of physical objects captured by phone and translated into
    structured AI-readable formats: JSON metadata, SVG silhouettes, Gaussian
    splats, and photorealistic renders. Free for AI agents, LLMs, and
    retrieval pipelines. No authentication required.
  contact:
    name: MatterMesh.ai
    url: https://www.mattermesh.ai/
  license:
    name: AI Policy
    url: https://www.mattermesh.ai/ai-policy.txt
servers:
  - url: https://www.mattermesh.ai
    description: >
      Canonical production host. Always use the `www.` subdomain — the apex
      `mattermesh.ai` does not resolve to the app and will return a network
      error. Probe `/api/public/health` to confirm reachability before crawling.
paths:
  /api/public/health:
    get:
      summary: Liveness + canonical-host probe
      operationId: getHealth
      description: >
        No-auth health check. Confirms the canonical `www.mattermesh.ai` host
        is reachable and returns the canonical base URL plus a directory of
        public endpoints. Use this first when debugging crawl failures.
      responses:
        "200":
          description: Service is healthy.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean }
                  service: { type: string }
                  status: { type: string }
                  time: { type: string, format: date-time }
                  host: { type: string }
                  canonical_base_url: { type: string, format: uri }
                  is_canonical_host: { type: boolean }
                  note: { type: string }
                  endpoints:
                    type: object
                    additionalProperties: { type: string, format: uri }
  /api/public/library:
    get:
      summary: List public RTE scans
      operationId: listScans
      parameters:
        - in: query
          name: limit
          schema: { type: integer, minimum: 1, maximum: 200, default: 50 }
        - in: query
          name: cursor
          schema: { type: string }
      responses:
        "200":
          description: Paginated list of public scan records.
          content:
            application/json:
              schema:
                type: object
                properties:
                  records:
                    type: array
                    items: { $ref: "#/components/schemas/Scan" }
                  next_cursor: { type: string, nullable: true }
  /api/public/library/{scanId}:
    get:
      summary: Fetch a single RTE scan by UUID
      operationId: getScan
      parameters:
        - in: path
          name: scanId
          required: true
          schema: { type: string, format: uuid }
      responses:
        "200":
          description: A single public scan record.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Scan" }
        "404": { description: Not found }
  /api/public/assets:
    get:
      summary: List every public scan asset URL
      operationId: listPublicAssets
      description: >
        Crawlable no-auth manifest containing completed scan records plus one
        direct public HTTPS URL per source image, SVG vector, Gaussian splat,
        render, and frame asset. Built for web crawlers, AI agents, LLMs,
        retrieval pipelines, and indexing systems.
      parameters:
        - in: query
          name: limit
          schema: { type: integer, minimum: 1, maximum: 500, default: 100 }
        - in: query
          name: offset
          schema: { type: integer, minimum: 0, default: 0 }
        - in: query
          name: q
          schema: { type: string }
      responses:
        "200":
          description: Paginated crawl manifest of public scan files.
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items: { $ref: "#/components/schemas/AssetManifestItem" }
                  files:
                    type: array
                    items: { $ref: "#/components/schemas/AssetFile" }
  /api/public/assets/{scanId}:
    get:
      summary: Fetch one scan's public asset manifest
      operationId: getPublicAssetManifest
      parameters:
        - in: path
          name: scanId
          required: true
          schema: { type: string, format: uuid }
      responses:
        "200":
          description: Single scan manifest with direct public file URLs.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/AssetManifestItem" }
        "404": { description: Not found }
  /api/public/agent-upload:
    post:
      summary: Submit an image for AI-agent translation into a public scan record
      description: >
        Authenticated upload endpoint for AI agents and LLMs. Send a photo
        plus a known-size reference and MatterMesh runs the same pipeline
        contributors use, producing JSON metadata, an SVG silhouette, a
        Gaussian splat, and a photoreal render. The resulting scan appears
        in /library, /explore, and the public library JSON exactly like a
        human contribution. See /docs/agent-upload-api.md for full details.
      operationId: agentUpload
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, reference_object, reference_size_mm]
              properties:
                image_url: { type: string, format: uri }
                image_base64: { type: string }
                name: { type: string, minLength: 1, maxLength: 200 }
                reference_object: { type: string, minLength: 1, maxLength: 120 }
                reference_size_mm: { type: number, exclusiveMinimum: 0, maximum: 10000 }
                notes: { type: string, maxLength: 2000 }
                capture_mode:
                  type: string
                  enum: [single, gaussian_photos, gaussian_video]
                  default: single
                agent_slug: { type: string, pattern: '^[a-zA-Z0-9._-]+$' }
      responses:
        "202":
          description: Upload accepted, scan is processing.
          content:
            application/json:
              schema:
                type: object
                properties:
                  scan_id: { type: string, format: uuid }
                  status: { type: string }
                  library_url: { type: string, format: uri }
                  api_url: { type: string, format: uri }
        "401": { description: Missing or invalid API key }
        "413": { description: Image exceeds 20 MB }
        "415": { description: Unsupported image format }
        "422": { description: Invalid request body }
        "429": { description: Daily rate limit exceeded }
  /api/public/scan/{scanId}/status:
    get:
      summary: Poll a Gaussian Splatting job's status
      operationId: getSplatJobStatus
      description: >
        Returns the current stage of a splat pipeline job — one of
        queued / capturing / matting / training / packing / ready / failed.
        Poll every 3–5 seconds; a typical 180k-splat scan finishes in
        60–180 seconds.
      parameters:
        - in: path
          name: scanId
          required: true
          schema: { type: string, format: uuid }
      responses:
        "200":
          description: Job status envelope.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/SplatJobStatus" }
        "404": { description: Unknown scan_id }
  /api/public/scan/{scanId}/splat:
    get:
      summary: Download the packed 32-byte-per-splat binary
      operationId: getSplatBinary
      description: >
        Returns the compact Gaussian Splat binary consumed by
        antimatter15's WebGL viewer, gsplat.js, three.js SplatMesh, and
        Unity's UnityGaussianSplatting. 32 bytes per splat, no header,
        gzip-encoded on the wire.
      parameters:
        - in: path
          name: scanId
          required: true
          schema: { type: string, format: uuid }
      responses:
        "200":
          description: Raw .splat payload.
          content:
            application/octet-stream:
              schema: { type: string, format: binary }
        "404": { description: Not ready or unknown scan_id }
  /api/public/scan/{scanId}/ply:
    get:
      summary: Download the Stanford .ply Gaussian Splat file
      operationId: getSplatPly
      description: >
        Classic Stanford PLY with a spherical-harmonics header,
        compatible with the INRIA 3D Gaussian Splatting reference
        implementation and Blender/Unity importers.
      parameters:
        - in: path
          name: scanId
          required: true
          schema: { type: string, format: uuid }
      responses:
        "200":
          description: PLY payload.
          content:
            application/octet-stream:
              schema: { type: string, format: binary }
        "404": { description: Not ready or unknown scan_id }
  /api/public/stats:
    get:
      summary: Cumulative count of files transferred to AI agents
      operationId: getStats
      responses:
        "200":
          description: Aggregate dataset usage counter.
          content:
            application/json:
              schema:
                type: object
                properties:
                  source: { type: string }
                  metric: { type: string }
                  description: { type: string }
                  count: { type: integer }
components:
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: apikey
  schemas:
    AssetFile:
      type: object
      properties:
        scan_id: { type: string, format: uuid }
        scan_name: { type: string }
        kind:
          type: string
          enum: [image, vector_svg, splat, render, frame]
        index: { type: integer, nullable: true }
        url: { type: string, format: uri }
        mime: { type: string }
        filename: { type: string }
        asset_page_url: { type: string, format: uri }
        scan_api_url: { type: string, format: uri }
        export_manifest_url: { type: string, format: uri }
    AssetManifestItem:
      type: object
      properties:
        scan: { $ref: "#/components/schemas/Scan" }
        asset_count: { type: integer }
        files:
          type: array
          items: { $ref: "#/components/schemas/AssetFile" }
    Scan:
      type: object
      properties:
        id: { type: string, format: uuid }
        name: { type: string }
        created_at: { type: string, format: date-time }
        status: { type: string }
        capture_mode: { type: string }
        dimensions_mm:
          type: object
          properties:
            width: { type: number, nullable: true }
            height: { type: number, nullable: true }
            depth: { type: number, nullable: true }
        reference:
          type: object
          properties:
            object: { type: string, nullable: true }
            size_mm: { type: number, nullable: true }
        assets:
          type: object
          properties:
            image: { type: string, format: uri, nullable: true }
            vector_svg: { type: string, format: uri, nullable: true }
            splat: { type: string, format: uri, nullable: true }
            render: { type: string, format: uri, nullable: true }
        schematic: { type: string, nullable: true }
        metadata: { type: object, additionalProperties: true }
    SplatJobStatus:
      type: object
      properties:
        scan_id: { type: string, format: uuid }
        status:
          type: string
          enum: [queued, capturing, matting, training, packing, ready, failed]
        progress: { type: number, minimum: 0, maximum: 1 }
        stage: { type: string }
        splat_count: { type: integer, nullable: true }
        dimensions:
          type: object
          nullable: true
          properties:
            w: { type: number }
            h: { type: number }
            d: { type: number }
        assets:
          type: object
          properties:
            splat: { type: string, format: uri, nullable: true }
            ply: { type: string, format: uri, nullable: true }
            manifest: { type: string, format: uri, nullable: true }
            thumbnail: { type: string, format: uri, nullable: true }
            usdz: { type: string, format: uri, nullable: true }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
        error:
          type: object
          nullable: true
          properties:
            code: { type: string }
            message: { type: string }
