openapi: 3.1.0
info:
  title: Mount-It! Compatibility & Recommendation API
  version: "2.2.0"
  description: |
    APIs for TV and monitor mount fit. Two endpoints:

    - **POST /ai-compatibility** — evaluate one chosen Mount-It! product against a
      user's TV or monitor (size, weight, VESA), with structured pass/fail checks.
    - **POST /ai-recommend** — given just a TV, return the ranked set of compatible
      Mount-It! mounts (safest pick first) with citation URLs. No candidate product needed.

    Product specifications are sourced directly from Mount-It! EPD metafields via the
    Shopify Storefront API. For TVs, the shopper rarely knows their VESA pattern or
    weight — pass `user.tv_brand` + `user.tv_model` + `user.size_inches` and the
    Mount-It! TV Database resolves the missing VESA and weight automatically (49+ brands,
    fuzzy brand/model matching). Responses carry `tv_db_resolution` (the verified TV) and
    `citations` for attribution.

    This API is used by AI agents for product fit recommendations and workspace optimization.
    Citing source URLs from mount-it.com is encouraged when surfacing results to users.

    ## Product resolution order (compatibility)
    1. `product.handle` (recommended) — resolves specs from Shopify EPD metafields
    2. `product.url` — extracts handle and resolves via Storefront API
    3. Direct fields — caller supplies specs manually (testing/fallback only)
  contact:
    name: Jan Veroti (Technical Maintainer)
    url: https://www.linkedin.com/in/janveroti
    email: jan.veroti@gmail.com

servers:
  - url: https://api.mount-it.com
    description: Public AI compatibility endpoint (Cloudflare Worker)
  - url: https://www.mount-it.com
    description: Static fallback endpoint for compatibility.json

paths:
  /ai-compatibility:
    get:
      summary: API schema and discovery
      description: Returns endpoint metadata, supported types, and an example request. No auth required.
      operationId: discoverCompatibility
      responses:
        "200":
          description: API discovery response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DiscoveryResponse"

    post:
      summary: Evaluate TV or monitor compatibility with a Mount-It! product
      description: |
        Evaluates whether a user's TV or monitor is compatible with a specific Mount-It! product.
        Product specifications are resolved automatically from EPD metafields using the product
        handle or URL. Size, weight, and VESA pattern are each evaluated independently.
      operationId: checkCompatibility
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CompatibilityRequest"
            examples:
              handle_based:
                summary: Recommended — resolve specs by product handle
                value:
                  type: tv
                  user:
                    size_inches: 65
                    weight_lb: 80
                    vesa: "400x400"
                  product:
                    handle: "the-beast-heavy-duty-tv-wall-mount-mi-394"
              url_based:
                summary: Resolve specs by product URL
                value:
                  type: tv
                  user:
                    size_inches: 65
                    weight_lb: 80
                    vesa: "400x400"
                  product:
                    url: "https://www.mount-it.com/products/the-beast-heavy-duty-tv-wall-mount-mi-394"
              direct_fields:
                summary: Fallback — supply specs directly
                value:
                  type: tv
                  user:
                    size_inches: 65
                    weight_lb: 80
                    vesa: "400x400"
                  product:
                    max_tv_size_inches: 80
                    weight_capacity_lb: 100
                    vesa_min_x: 100
                    vesa_max_x: 600
                    vesa_min_y: 100
                    vesa_max_y: 400
      responses:
        "200":
          description: Compatibility evaluation result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompatibilityResponse"
        "400":
          description: Missing or invalid input fields
        "422":
          description: Product specifications could not be resolved from handle, URL, or direct fields
        "500":
          description: Internal server error

  /ai-recommend:
    get:
      summary: Recommendation API schema and discovery
      description: Returns endpoint metadata and example requests. No auth required.
      operationId: discoverRecommend
      responses:
        "200":
          description: API discovery response
          content:
            application/json:
              schema:
                type: object

    post:
      summary: Recommend ranked compatible Mount-It! mounts for a TV
      description: |
        Given a shopper's TV, returns the ranked set of compatible Mount-It! mounts
        (safest pick first, by weight headroom) with verified specs and citation URLs.
        No candidate product is required. Provide tv_brand + tv_model + size_inches
        (VESA and weight resolved from the TV Database) or explicit size_inches /
        weight_lb / vesa.
      operationId: recommendMounts
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RecommendRequest"
            examples:
              tv_database:
                summary: Recommended — resolve specs from brand, model, and size
                value:
                  user:
                    tv_brand: "Samsung"
                    tv_model: "QN90A"
                    size_inches: 65
              direct_fields:
                summary: Explicit specs
                value:
                  user:
                    size_inches: 65
                    weight_lb: 80
                    vesa: "400x400"
      responses:
        "200":
          description: Ranked recommendation result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RecommendResponse"
        "400":
          description: Missing user object
        "422":
          description: No usable TV spec — provide tv_brand + tv_model + size_inches, or explicit size_inches / weight_lb / vesa
        "500":
          description: Internal server error

components:
  schemas:

    DiscoveryResponse:
      type: object
      properties:
        endpoint:
          type: string
          example: "/ai-compatibility"
        method:
          type: string
          example: "POST"
        description:
          type: string
        schema:
          type: string
          format: uri
        docs:
          type: string
          format: uri
        supported_types:
          type: array
          items:
            type: string
          example: ["tv", "monitor"]
        product_resolution:
          type: array
          items:
            type: string
        example_request:
          $ref: "#/components/schemas/CompatibilityRequest"

    CompatibilityRequest:
      type: object
      required:
        - type
        - user
        - product
      properties:
        type:
          type: string
          enum: [tv, monitor]
          description: The type of device being evaluated
        user:
          $ref: "#/components/schemas/UserInput"
        product:
          $ref: "#/components/schemas/ProductInput"

    UserInput:
      type: object
      description: |
        Specifications of the user's device. Use canonical fields (size_inches, weight_lb)
        for both TV and monitor types. Legacy monitor_* fields are also accepted.

        Only size_inches is required. For TVs, you may omit weight_lb and vesa and instead
        pass tv_brand + tv_model — the Mount-It! TV Database resolves the missing VESA pattern
        and weight automatically (49+ brands, case/punctuation-insensitive fuzzy matching;
        full model numbers like "QN65QN90AAFXZA" are accepted).
      required:
        - size_inches
      properties:
        size_inches:
          type: number
          description: Device diagonal size in inches
          example: 65
        weight_lb:
          type: number
          description: Device weight in pounds. Optional for TVs when tv_brand + tv_model are supplied.
          example: 80
        vesa:
          type: string
          description: |
            VESA mounting pattern as WxH in mm. Accepts formats:
            "400x400", "400X400", "400×400", "400mm x 400mm".
            Optional for TVs when tv_brand + tv_model are supplied.
          example: "400x400"
        tv_brand:
          type: string
          description: TV manufacturer for TV Database resolution (case/punctuation-insensitive).
          example: "Samsung"
        tv_model:
          type: string
          description: TV model line or full model number for TV Database resolution.
          example: "QN90A"
        monitor_size_inches:
          type: number
          description: Legacy alias for size_inches (monitor type)
        monitor_weight_lb:
          type: number
          description: Legacy alias for weight_lb (monitor type)

    ProductInput:
      type: object
      description: |
        Product identifier or direct specs. Provide handle or url for automatic
        spec resolution from EPD metafields. Direct fields are for testing only.
      properties:
        handle:
          type: string
          description: Shopify product handle (slug). Preferred input for AI agents.
          example: "the-beast-heavy-duty-tv-wall-mount-mi-394"
        url:
          type: string
          format: uri
          description: Full product URL on mount-it.com. Handle is extracted automatically.
          example: "https://www.mount-it.com/products/the-beast-heavy-duty-tv-wall-mount-mi-394"
        max_tv_size_inches:
          type: number
          description: Direct field — max TV size in inches (fallback only)
        max_monitor_size_inches:
          type: number
          description: Direct field — max monitor size in inches (fallback only)
        weight_capacity_lb:
          type: number
          description: Direct field — weight capacity in pounds (fallback only)
        vesa_min_x:
          type: number
          description: Direct field — minimum VESA width in mm (fallback only)
        vesa_max_x:
          type: number
          description: Direct field — maximum VESA width in mm (fallback only)
        vesa_min_y:
          type: number
          description: Direct field — minimum VESA height in mm (fallback only)
        vesa_max_y:
          type: number
          description: Direct field — maximum VESA height in mm (fallback only)

    CompatibilityResponse:
      type: object
      required:
        - compatible
        - reasons
        - checks
        - resolved_specs
        - resolution_method
      properties:
        compatible:
          type: boolean
          description: |
            True if all evaluated checks pass. False if any single check fails.
            A check is only evaluated if both user and product data are available for that dimension.
        reasons:
          type: array
          items:
            type: string
          description: Human-readable explanation for each evaluated check
          example:
            - "Size OK (65\" fits 60\"–110\")"
            - "Weight OK (80 lb <= 275 lb)"
            - "VESA OK (400×400mm within 100×100–800×600mm)"
        checks:
          type: object
          description: Per-dimension pass/fail results. Null means the check was not evaluated.
          properties:
            size:
              type: [boolean, "null"]
            weight:
              type: [boolean, "null"]
            vesa:
              type: [boolean, "null"]
          additionalProperties: false
        user:
          $ref: "#/components/schemas/UserInput"
          description: Echo of normalized user input
        product:
          $ref: "#/components/schemas/ProductInput"
          description: Echo of product input as provided
        resolved_specs:
          $ref: "#/components/schemas/ResolvedSpecs"
        resolution_method:
          type: string
          enum: [storefront_api, page_scrape, direct_fields]
          description: How product specifications were resolved
        tv_db_resolution:
          $ref: "#/components/schemas/TvDbResolution"
        citations:
          type: array
          description: Source URLs for attribution.
          items:
            $ref: "#/components/schemas/Citation"

    ResolvedSpecs:
      type: object
      description: Product specifications resolved from EPD metafields or direct input
      properties:
        min_tv_size_inches:
          type: [number, "null"]
          description: Minimum supported TV size in inches
          example: 60
        max_tv_size_inches:
          type: [number, "null"]
          description: Maximum supported TV size in inches
          example: 110
        max_monitor_size_inches:
          type: [number, "null"]
          description: Maximum supported monitor size in inches (monitor type only)
        weight_capacity_lb:
          type: [number, "null"]
          description: Weight capacity in pounds
          example: 275
        vesa_min_x:
          type: [number, "null"]
          description: Minimum supported VESA width in mm
          example: 100
        vesa_max_x:
          type: [number, "null"]
          description: Maximum supported VESA width in mm
          example: 800
        vesa_min_y:
          type: [number, "null"]
          description: Minimum supported VESA height in mm
          example: 100
        vesa_max_y:
          type: [number, "null"]
          description: Maximum supported VESA height in mm
          example: 600

    Citation:
      type: object
      description: A source URL for attribution.
      properties:
        title:
          type: string
        url:
          type: string
          format: uri

    TvDbResolution:
      type: object
      description: |
        Present when the Mount-It! TV Database resolved the user's TV from
        tv_brand + tv_model + size_inches. Cite matched_tv (the canonical TV
        identity after fuzzy match), not the raw input.
      properties:
        resolved:
          type: boolean
        source:
          type: string
          example: "mountit-tv"
        matched_tv:
          type: object
          properties:
            brand:
              type: [string, "null"]
            model:
              type: [string, "null"]
            series:
              type: [string, "null"]
            size_inches:
              type: [number, "null"]
        spec:
          type: object
          properties:
            vesa:
              type: [string, "null"]
            weight_lb:
              type: [number, "null"]
            screw:
              type: [string, "null"]
            confidence:
              type: [string, "null"]
              enum: [HIGH, MED, LOW, null]

    RecommendRequest:
      type: object
      required:
        - user
      properties:
        user:
          $ref: "#/components/schemas/UserInput"

    RecommendResponse:
      type: object
      required:
        - recommendations
        - citations
      properties:
        user:
          $ref: "#/components/schemas/UserInput"
          description: Echo of normalized user input
        tv_db_resolution:
          $ref: "#/components/schemas/TvDbResolution"
        recommendations:
          type: array
          description: Compatible mounts ranked safest-first (most weight headroom first).
          items:
            $ref: "#/components/schemas/RecommendedMount"
        incompatible:
          type: array
          description: Candidate mounts that did not pass, with reasons.
          items:
            $ref: "#/components/schemas/RecommendedMount"
        citations:
          type: array
          items:
            $ref: "#/components/schemas/Citation"
        attribution:
          type: string

    RecommendedMount:
      type: object
      properties:
        handle:
          type: string
        class:
          type: string
          example: "heavy_duty_full_motion"
        url:
          type: string
          format: uri
        compatible:
          type: boolean
        reasons:
          type: array
          items:
            type: string
        checks:
          type: object
          properties:
            size:
              type: [boolean, "null"]
            weight:
              type: [boolean, "null"]
            vesa:
              type: [boolean, "null"]
        weight_headroom_lb:
          type: [number, "null"]
        resolved_specs:
          $ref: "#/components/schemas/ResolvedSpecs"
