{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://www.ironscout.ai/ids/schemas/search_results_v1.schema.json",
  "title": "search_results_v1",
  "description": "IDS v1 response shape for GET /api/ids/search. Canonically grouped products with current offers per retailer. Descriptive only: no purchase verdicts, deal scores, or 'best buy' language exist anywhere in this schema (ADR-003/006, IDS §2/§5.2). See https://www.ironscout.ai/.well-known/ids.md.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "schemaVersion",
    "computedAt",
    "query",
    "pagination",
    "products"
  ],
  "properties": {
    "schemaVersion": { "type": "string", "const": "search_results_v1" },
    "requestId": {
      "description": "Opaque per-request identifier. May be null when there is no active request context.",
      "type": ["string", "null"]
    },
    "computedAt": {
      "type": "string",
      "format": "date-time",
      "description": "When the served data was assembled. For live offer queries this is request time."
    },
    "query": {
      "type": "object",
      "additionalProperties": false,
      "description": "Echo of the resolved query parameters used to produce these results.",
      "required": ["sort"],
      "properties": {
        "q": { "type": ["string", "null"] },
        "caliber": { "type": ["string", "null"] },
        "brand": { "type": ["string", "null"] },
        "grain": { "type": ["integer", "null"] },
        "case": { "type": ["string", "null"] },
        "roundCountMin": { "type": ["integer", "null"] },
        "roundCountMax": { "type": ["integer", "null"] },
        "inStock": { "type": ["boolean", "null"] },
        "sort": {
          "type": "string",
          "enum": ["pricePerRoundAsc", "pricePerRoundDesc", "observedAtDesc"],
          "description": "Mechanical ordering only. Never implies endorsement (IDS §2.2)."
        }
      }
    },
    "pagination": { "$ref": "#/$defs/pagination" },
    "products": {
      "type": "array",
      "items": { "$ref": "#/$defs/productWithOffers" }
    }
  },
  "$defs": {
    "pagination": {
      "type": "object",
      "additionalProperties": false,
      "required": ["page", "pageSize", "totalProducts", "totalPages"],
      "properties": {
        "page": { "type": "integer", "minimum": 1 },
        "pageSize": { "type": "integer", "minimum": 1, "maximum": 50 },
        "totalProducts": { "type": "integer", "minimum": 0 },
        "totalPages": { "type": "integer", "minimum": 0 }
      }
    },
    "productWithOffers": {
      "type": "object",
      "additionalProperties": false,
      "required": ["canonicalProductId", "title", "caliberSlug", "offers"],
      "properties": {
        "canonicalProductId": { "type": "string" },
        "title": { "type": "string" },
        "brand": { "type": ["string", "null"] },
        "caliberSlug": { "type": ["string", "null"] },
        "grain": { "type": ["integer", "null"] },
        "caseMaterial": { "type": ["string", "null"] },
        "roundCount": { "type": ["integer", "null"] },
        "imageUrl": { "type": ["string", "null"], "format": "uri" },
        "offers": {
          "type": "array",
          "items": { "$ref": "#/$defs/offer" }
        }
      }
    },
    "offer": {
      "type": "object",
      "additionalProperties": false,
      "description": "A retailer's current offer for a canonical product (IDS §6.2). No verdict/score/recommendation field exists by construction.",
      "required": [
        "offerId",
        "retailerId",
        "retailerName",
        "inStock",
        "currentPrice",
        "observedAt",
        "provenance",
        "outUrl"
      ],
      "properties": {
        "offerId": { "type": "string" },
        "retailerId": { "type": "string" },
        "retailerName": { "type": "string" },
        "inStock": { "type": "boolean" },
        "availability": {
          "type": ["string", "null"],
          "enum": ["IN_STOCK", "OUT_OF_STOCK", "BACKORDER", "UNKNOWN", null]
        },
        "currentPrice": {
          "type": "object",
          "additionalProperties": false,
          "required": ["amount", "currency", "pricePerRound", "unitQuantity"],
          "properties": {
            "amount": { "type": "number", "minimum": 0 },
            "currency": { "type": "string", "description": "ISO 4217 code." },
            "pricePerRound": { "type": ["number", "null"], "minimum": 0 },
            "unitQuantity": { "type": ["integer", "null"], "minimum": 0, "description": "Rounds per pack." }
          }
        },
        "observedAt": { "type": "string", "format": "date-time" },
        "provenance": { "$ref": "#/$defs/provenance" },
        "outUrl": {
          "type": "string",
          "format": "uri",
          "description": "IronScout-provided signed outbound URL. MUST be used exactly as provided (IDS §5.5)."
        },
        "priceContext": { "$ref": "#/$defs/priceContext" }
      }
    },
    "provenance": {
      "type": "object",
      "additionalProperties": false,
      "description": "Offer-level attribution (IDS §4.4). sourceId is pseudonymous, never human-readable (ADR-033 §2).",
      "required": ["sourceType", "sourceId", "observationId"],
      "properties": {
        "sourceType": {
          "type": ["string", "null"],
          "enum": ["AFFILIATE_FEED", "APPROVED_SCRAPE", "DIRECT_FEED", null]
        },
        "sourceId": { "type": ["string", "null"], "description": "Stable pseudonymous hash of the source. Not the internal source id." },
        "observationId": { "type": "string" },
        "ingestedAt": { "type": ["string", "null"], "format": "date-time" }
      }
    },
    "priceContext": {
      "type": "object",
      "additionalProperties": false,
      "description": "Non-prescriptive context signals (IDS §6.3). No deal score / buy-wait-hold verdict / internal confidence hint may appear here.",
      "properties": {
        "relativePricePct": { "type": ["number", "null"] },
        "positionInRange": { "type": ["number", "null"], "minimum": 0, "maximum": 1 },
        "contextBand": { "type": ["string", "null"], "enum": ["LOW", "TYPICAL", "HIGH", null] },
        "meta": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "windowDays": { "type": ["integer", "null"], "minimum": 1 },
            "sampleCount": { "type": ["integer", "null"], "minimum": 0 },
            "asOf": { "type": ["string", "null"], "format": "date-time" }
          }
        }
      }
    }
  }
}
