openapi: 3.1.1
info:
  title: TrustRails API
  version: 0.1.0
  description: >
    UK electronics product search API for AI assistants.
    Covers 26,000+ products from major UK retailers across 19 categories.
    All prices are in GBP.


    HOW TO CALL searchProducts — read before every call:

    Decompose the user's request into filters first. Only what's left over goes in query.
    STEP 1: brand name → brand filter. STEP 2: product category → category filter. STEP 3: price → min_price/max_price. STEP 4: what remains → query.

    BAD: query='Sony headphones under £200' | GOOD: brand='Sony', category='Headphones', max_price=200, no query.
    BAD: query='tablet' | GOOD: category='Tablets', no query.
    BAD: query='smartwatch' | GOOD: category='Wearables', no query.
    BAD: query='macbook neo' | GOOD: brand='Apple', category='Laptops', query='neo'.
    BAD: query='Samsung QLED TV' | GOOD: brand='Samsung', category='TVs', query='qled'.

    Only put differentiating terms in query: model lines (neo, ultra, oled), variants, model numbers (WH-1000XM5, s25 ultra).
    If brand+category alone cover what the user wants, omit query entirely.
    Always set lite=true. If 0 results, broaden the query or drop filters.
    Use getProduct for full specs — do not assume specs from titles.

    Valid categories: Laptops, Desktops, Tablets, Phones, TVs, Monitors, Headphones, Speakers,
    Cameras, Keyboards, Mice, Printers, Networking, Storage, Gaming, Wearables, Drones, Audio, Cables & Chargers.
    'Smartphones' is not valid — use 'Phones'. 'Televisions' is not valid — use 'TVs'.

    For spec-based queries (RAM, ports, screen size, weight etc.), search first then call getProduct on top 3-5 results.
    Do not assume technical specs from titles alone.

servers:
  - url: https://trustrails.app

paths:
  /api/search:
    get:
      operationId: searchProducts
      summary: Search UK electronics products
      description: >
        Search across 26,000+ UK electronics products. Returns summary data: title, brand,
        price, availability, category, and purchase link. Specs are minimal — for full
        technical specifications, use getProduct with the product ID.
      parameters:
        - in: query
          name: query
          description: >
            Refinement terms ONLY — model lines, series, variants, model numbers (e.g. 'neo', 'ultra', 'oled', 'WH-1000XM5', 's25 ultra').
            NEVER a category name: BAD query='tablet', query='smartwatch', query='laptop'. Set the category filter instead.
            NEVER a brand name: BAD query='Sony'. Set the brand filter instead.
            NEVER a price.
            Omit entirely when browsing a category or brand.
          schema:
            type: string
            example: neo
        - in: query
          name: min_price
          description: Minimum price in GBP. Use this instead of putting prices in the query.
          schema:
            type: number
            example: 400
        - in: query
          name: max_price
          description: Maximum price in GBP. Use this instead of putting prices in the query.
          schema:
            type: number
            example: 800
        - in: query
          name: brand
          description: >
            Filter by brand name (exact match, case-insensitive).
            Use this instead of putting brand names in the query.
            Examples: Apple, Samsung, Sony, HP, Dell, Lenovo, Anker, Bose, LG.
          schema:
            type: string
            example: HP
        - in: query
          name: category
          description: >
            Filter by product category. Use ONLY these exact values:
            Laptops, Desktops, Tablets, Phones, TVs, Monitors,
            Headphones, Speakers, Cameras, Keyboards, Mice, Printers, Networking,
            Storage, Gaming, Wearables, Drones, Audio, Cables & Chargers.
            NOTE: 'Smartphones' is not valid — use 'Phones'. 'Televisions' is not valid — use 'TVs'.
          schema:
            type: string
            example: Laptops
        - in: query
          name: limit
          description: Maximum number of products to return (default 50, max 100).
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
            example: 50
        - in: query
          name: sort
          description: >
            Sort order for results. Use 'price_asc' for cheapest first, 'price_desc' for most expensive first.
            Default is 'relevance' (best match first).
            When comparing prices or finding the best deal, use 'price_asc'.
          schema:
            type: string
            enum: [relevance, price_asc, price_desc]
            default: relevance
            example: price_asc
        - in: query
          name: lite
          description: >
            Return trimmed product objects with only essential fields
            (id, title, brand, price, currency, availability, image_url, purchase_url, offer_count).
            Always set to true unless the user specifically needs full product objects.
          schema:
            type: boolean
            default: false
            example: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  products:
                    type: array
                    items:
                      $ref: '#/components/schemas/Product'
                  total:
                    type: integer
                    description: Total number of matching products (may exceed the returned limit).
      security:
        - ApiKeyAuth: []

  /api/product/{id}:
    get:
      operationId: getProduct
      summary: Get full product details by ID
      description: >
        Returns full specs (specs.description has processor, RAM, storage, display, ports),
        stock, delivery, and all retailer offers with per-retailer pricing.
        Use after searchProducts for detailed specs or price comparison.
        Accepts canonical product IDs or retailer offer IDs.
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            example: '43740928768'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Product'
        '404':
          description: Product not found
      security:
        - ApiKeyAuth: []

  /api/health:
    get:
      operationId: getHealth
      summary: Service health
      description: Quick status check showing product count and last sync time.
      responses:
        '200':
          description: Basic service info
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
                  items:
                    type: integer
                    example: 26000
                  last_update:
                    type: string
                    format: date-time
                    example: '2026-02-23T10:00:00Z'

components:
  securitySchemes:
    ApiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: API_KEY
      description: 'Use Bearer token authentication with your API key'

  schemas:
    Product:
      type: object
      required:
        - id
        - title
        - price
        - currency
        - availability
        - purchase_url
      properties:
        id:
          type: string
          example: '43740928768'
        title:
          type: string
          example: Apple iPhone 16 Plus 256GB Ultramarine
        brand:
          type: string
          example: Apple
        price:
          type: number
          description: Price in GBP
          example: 923.99
        currency:
          type: string
          example: GBP
        availability:
          type: string
          enum: [in_stock, low_stock, out_of_stock]
          example: in_stock
        stock:
          type: integer
          example: 34
        delivery_time:
          type: string
          example: '3-5 working days'
        image_url:
          type: string
          format: uri
          description: Product image URL
        category:
          type: string
          description: >
            Canonical product category. One of: Laptops, Desktops, Tablets, Phones, TVs,
            Monitors, Headphones, Speakers, Cameras, Keyboards, Mice, Printers, Networking,
            Storage, Gaming, Wearables, Drones, Audio, Cables & Chargers.
          example: Laptops
        product_type:
          type: string
          enum: [product, accessory]
          description: Whether this is a primary product or an accessory (e.g. a phone case).
          example: product
        specs:
          type: object
          description: >
            Product specifications. Always check specs.description for full technical
            details (processor, RAM, storage, display size, ports, etc.) before
            making recommendations. Other fields: model_number, dimensions.
          properties:
            description:
              type: string
              description: Full prose spec text from the retailer feed. Contains technical attributes such as processor, RAM, storage, display specs, connectivity, and more.
              example: 'Intel Core Ultra 7 266V, 16GB LPDDR5x, 512GB SSD, 15" 2496x1664 display, Intel Arc 140V graphics, Wi-Fi 7, Windows 11 Pro'
            model_number:
              type: string
              example: MXY23QN/A
            dimensions:
              type: string
              example: '340 x 240 x 17mm'
        provenance:
          type: object
          description: Data source and freshness information
          required: [source, last_updated]
          properties:
            source:
              type: string
              example: AO
            last_updated:
              type: string
              format: date-time
              example: '2026-02-23T10:00:00Z'
        purchase_url:
          type: string
          format: uri
          description: Direct link to purchase from the cheapest retailer
          example: 'https://trustrails.app/go/43740928768'
        offer_count:
          type: integer
          description: "Number of retailer offers available for this product. MANDATORY: If >1, you MUST call getProduct before responding — never mention multiple offers without presenting the full comparison. Show: cheapest retailer, all other prices with the difference, exact savings amount. A response is incomplete if it references offer_count > 1 without retailer pricing details."
          example: 3
        offers:
          type: array
          description: Per-retailer offers sorted by price (only in full mode / getProduct response)
          items:
            $ref: '#/components/schemas/Offer'

    Offer:
      type: object
      required:
        - id
        - source
        - title
        - price
        - purchase_url
      properties:
        id:
          type: string
          example: '43740928768'
        source:
          type: string
          description: Retailer name
          example: AO.com
        title:
          type: string
        price:
          type: number
          description: Price in GBP at this retailer
          example: 889.00
        currency:
          type: string
          example: GBP
        availability:
          type: string
          enum: [in_stock, low_stock, out_of_stock]
        stock:
          type: integer
        delivery_time:
          type: string
        purchase_url:
          type: string
          format: uri
          description: Direct link to purchase from this retailer
        image_url:
          type: string
          format: uri
        last_updated:
          type: string
          format: date-time
