Skip to main content
Glama

WEM Price Compare

Compare retailer offers for one product

compare_offers
Read-only

Provide at least one of: gtin, slug, wem_id, or title. Lists the retailer offers WEM's catalogue holds for one product: available current prices cheapest first, then current offers last seen out of stock and older prices, with WEM's 90-day price low (null when the product is held only in another currency). The product is found by barcode, catalogue slug or wem3.ai/pl/ URL, WEM ID, or a title (a model name, ASIN, MPN or merchant SKU) that must pass a relevance check; there is no live retailer search. A barcode, slug or WEM ID match is the product itself; a title match is inferred and marked so in identity. A marketplace offer priced far below the product's other offers is withheld and counted in filtered. When WEM does not hold the product, found is false and nextTool names search_products with a query.

Input Schema

NameRequiredDescriptionDefault
gtinNoProduct barcode: EAN-13, UPC-A, EAN-8 or GTIN-14. The strongest identifier.
slugNoWEM catalogue slug, or a wem3.ai/pl/{slug} URL / host path. Hosts may pass either form.
titleNoProduct or model name, or an ASIN, MPN or merchant SKU (e.g. AF400UK). The weakest identifier: used only when no barcode, slug or WEM ID is given, and only if it passes a relevance check; otherwise `found` is false rather than a guess.
wem_idNoWEM ID (W + 10 Crockford characters + check). Active catalogue products only.
currencyNoISO 4217 code for the shopper's market. Default GBP. Decides which market's offers WEM returns from its catalogue, not just how the answer reads ; WEM does not infer the market from IP. When WEM holds the product only in another currency it says so rather than presenting a foreign listing as the answer.

Output Schema

NameRequiredDescriptionDefault
nextNoPresent when WEM's set of offers is thin. chrome_extension is a page that compares the same product on any retailer page, including shops without an affiliate programme.
foundYesFalse when the product is not in WEM’s catalogue. The rest of the fields below are then absent ; that does not mean the product is unavailable or unpriced. `unavailable` distinguishes an outage from a miss. `nextTool`, when present, names the next lookup.
linksNoThe links from `offers` in the same order, pre-formatted: current prices cheapest first, then any out of stock when last checked or not current, each saying so in its markdown. Give these to the user when you name a price from this result. Each opens the retailer’s page for the offer WEM priced; a retailer URL found elsewhere may be a different listing or price. The same links are in this array and, when a host renders one, in the card.
offersNoCurrent prices first (`priceStatus`), then current prices from a shop out of stock when last checked (`inStock: false`), then stale or undated rows, each group ascending by price. Only the first group is compared: an out-of-stock, stale or undated row is listed, never the cheapest. Barcode, slug and WEM ID rows are the product. A title match is inferred; `identity.strength` says which.
reasonNoPresent only when `found` is false: which identifier missed, and why. When `unavailable` is true this describes the outage rather than a missing product ; it is the reason the lookup failed, not a fact about the product.
searchNoPresent when this result names no offer. `url` is WEM's results page for the query and `markdown` is the same link, pre-formatted. This tool reads the catalogue only, so an empty result here is not evidence the product is unavailable or unpriced.
sourceNo
productNo
rankingNoWhy the offers are in this order. Available current offers are ordered by price, lowest first; current offers last seen out of stock and older prices follow. Affiliate commission is not an input.
coverageNoPresent when surviving rows include a marketplace cluster at similar prices. That is not a retail floor — the cheapest marketplace listing is not the deal. The listings stay in the payload, each with its url. `next`, when present, is the Chrome extension page for shops WEM does not yet hold as partners.
currencyNo
degradedNoPresent when this call hit its time budget and skipped enrichment. The offers returned are COMPLETE — only the extras were dropped. A missing catalogue block or missing observed retailers does not mean WEM holds nothing, and no retry is needed.
filteredNoOffers of this product WEM held back, tallied by reason. "Priced far below this product’s other offers" is a marketplace listing under 40% of what the product sells for elsewhere — the shape of a counterfeit, a part or a mislisted variant. Those prices are not in `offers`, `lowPrice`, `links`, `summary` or `priceHistory.low`: none is the cheapest, a deal or the 90-day low. The tally says how many were held back.
identityNoHow the shopper’s words reached this product. On `inferred`, WEM matched it by name (not by barcode) and the identity is not confirmed, even when `identityBasis` is `barcode`; on `exact`, the barcode itself resolved it.
lowPriceNoLowest CURRENT price in `offers` (read within 14 days) from a shop not out of stock when last checked. Null when there is none, or WEM holds no price it may quote; it is never taken from a stale or undated row, a row with `inStock: false`, or `unpriced`.
nextToolNoPresent only when `found` is false. It names `search_products` and the query. A named model that misses the catalogue is the ordinary case.
unpricedNoListings whose price WEM may not show — today, Amazon rows Amazon’s API did not price just now. The row carries `priceNote` (e.g. "See price at Amazon") in place of a figure. No price is stated, estimated or compared for one; it is not the cheapest and is not counted in a lowest price or a saving.
highPriceNoHighest current price in `offers`; null with lowPrice.
sameModelNoPresent only when the maker's own page says another model code is this product and a person at WEM has checked it. These are NOT offers for the product asked about: they are not part of `offers`, and none is this product's lowest price or the best deal. On verify_offer they are not part of the check: `verdict`, `cheapest`, `betterBy`, `summary` and the receipt are about the product asked about, and a cheaper sister code never makes the claimed price wrong. `summary` is written to be quoted as it is. `maker.host` is the source of the claim. `url` is WEM's comparison for that model, not a shop. When `relation` is `differs`, the `differences` are what the maker says differs, and its price is not a saving. Absent is not evidence that no equivalent exists.
disclosureNoWEM’s affiliate disclosure for this answer, written to be quoted as it is. Its wording changes with the rows: it states whether every outbound link is affiliate-tracked, only some are, or none are. A version from an earlier answer may not fit this one: a row marked `affiliate: false` earns WEM nothing.
unavailableNoPresent and true only when WEM could not reach its catalogue at all. `found` is false for the same reason it is on an ordinary miss, so the two are indistinguishable without this flag. When it is set, WEM does not know whether it holds the product. The lookup could not be completed; it does not mean WEM has no offers or no price for it. `nextTool` is still present.
priceHistoryNoThe 90-day low, for telling a real discount from a repackaged one.
identityBasisNoHow this product’s offers are grouped — NOT how the shopper’s words found the product; that is `identity`. `barcode`: the product carries a GTIN, so every offer here is that same item. `curated-grouping`: it carries none, and the grouping is an inference. This field does not make a result a barcode match: when `identity.strength` is `inferred`, WEM matched the product by name.
lastConfirmedAtNoWhen WEM last actually read any price in this answer. Null means none of them can be dated, so the answer is not current.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed2 schema fields changed
    • changedOutput schema / description
      Previous value: -"The retailer offers WEM holds for one catalogue product, current prices first and each group cheapest first. When `found` is false WEM simply does not hold this product yet — that is a normal answer, not a failure, and it says nothing about whether the product exists or what it costs. `nextTool` then names `search_products` and the query. An empty catalogue result is not a statement that the product is unavailable or unpriced."New value: +"The retailer offers WEM holds for one catalogue product: available current prices cheapest first, then current offers last seen out of stock, then older prices. When `found` is false WEM simply does not hold this product yet — that is a normal answer, not a failure, and it says nothing about whether the product exists or what it costs. `nextTool` then names `search_products` and the query. An empty catalogue result is not a statement that the product is unavailable or unpriced."
    • changedOutput schema / properties / ranking / description
      Previous value: -"Why the offers are in this order. Offers are ordered by price, lowest first; affiliate commission is not an input."New value: +"Why the offers are in this order. Available current offers are ordered by price, lowest first; current offers last seen out of stock and older prices follow. Affiliate commission is not an input."
  2. Changed1 schema field changed
    • removedInput schema / anyOf
      Removed value: -[
      -  {
      -    "required": [
      -      "gtin"
      -    ]
      -  },
      -  {
      -    "required": [
      -      "slug"
      -    ]
      -  },
      -  {
      -    "required": [
      -      "wem_id"
      -    ]
      -  },
      -  {
      -    "required": [
      -      "title"
      -    ]
      -  }
      -]
  3. Changed3 schema fields changed
    • changedOutput schema / properties / links / description
      Previous value: -"The links from `offers`, cheapest first, pre-formatted. Give these to the user when you name a price from this result. Each opens the retailer’s page for the offer WEM priced; a retailer URL found elsewhere may be a different listing or price. The same links are in this array and, when a host renders one, in the card."New value: +"The links from `offers` in the same order, pre-formatted: current prices cheapest first, then any out of stock when last checked or not current, each saying so in its markdown. Give these to the user when you name a price from this result. Each opens the retailer’s page for the offer WEM priced; a retailer URL found elsewhere may be a different listing or price. The same links are in this array and, when a host renders one, in the card."
    • changedOutput schema / properties / lowPrice / description
      Previous value: -"Lowest CURRENT price in `offers` (read within 14 days). Null when no price is current or WEM holds none it may quote ; it is never taken from a stale or undated row, or from `unpriced`."New value: +"Lowest CURRENT price in `offers` (read within 14 days) from a shop not out of stock when last checked. Null when there is none, or WEM holds no price it may quote; it is never taken from a stale or undated row, a row with `inStock: false`, or `unpriced`."
    • changedOutput schema / properties / offers / description
      Previous value: -"Current prices first (`priceStatus`), each group ascending by price; a stale or undated row is listed, never compared. Barcode, slug and WEM ID rows are the product. A title match is inferred; `identity.strength` says which."New value: +"Current prices first (`priceStatus`), then current prices from a shop out of stock when last checked (`inStock: false`), then stale or undated rows, each group ascending by price. Only the first group is compared: an out-of-stock, stale or undated row is listed, never the cheapest. Barcode, slug and WEM ID rows are the product. A title match is inferred; `identity.strength` says which."
  4. Changed2 schema fields changed
    • changedOutput schema / properties / links / description
      Previous value: -"The links from `offers`, cheapest first, pre-formatted. Each opens the retailer’s page for the offer WEM priced; a retailer URL found elsewhere may be a different listing or price. The same links are in this array and, when a host renders one, in the card."New value: +"The links from `offers`, cheapest first, pre-formatted. Give these to the user when you name a price from this result. Each opens the retailer’s page for the offer WEM priced; a retailer URL found elsewhere may be a different listing or price. The same links are in this array and, when a host renders one, in the card."
    • changedOutput schema / properties / offers / items / properties / url / description
      Previous value: -"WEM tracked link to the retailer page for this offer. WEM never takes payment."New value: +"The page this price was read on. Give this url to the user when you name the price; a different url can be a different listing or price. The full URL; a shortened form is a different link. WEM never takes payment."
  5. Changed1 schema field changed
    • changedOutput schema / properties / coverage / properties / provider / description
      Previous value: -"aliexpress, or marketplace when mixed."New value: +"The marketplace (e.g. aliexpress), or marketplace when mixed."
  6. Changed3 schema fields changed
    • changedOutput schema / properties / coverage / properties / provider / description
      Previous value: -"ebay, aliexpress, or marketplace when mixed."New value: +"aliexpress, or marketplace when mixed."
    • changedOutput schema / properties / offers / items / properties / channel / description
      Previous value: -"retailer is a named shop (Boots, Currys). marketplace is eBay/AliExpress/Temu-style parallel listings. A marketplace cluster is not a set of competing authorised retailers."New value: +"retailer is a named shop (Boots, Currys). marketplace is AliExpress/Temu-style parallel listings. A marketplace cluster is not a set of competing authorised retailers."
    • changedOutput schema / properties / offers / items / properties / provider / description
      Previous value: -"Retailer slug, e.g. \"ebay\", \"currys\"."New value: +"Retailer slug, e.g. \"amazon\", \"currys\"."
  7. Changed4 schema fields changed
    • changedOutput schema / description
      Previous value: -"The retailer offers WEM holds for one catalogue product, current prices first and each group cheapest first. When `found` is false WEM simply does not hold this product yet — that is a normal answer, not a failure, and it says nothing about whether the product exists or what it costs. `nextTool` then names `search_products` and the query. An empty catalogue result is not a statement that the product is unavailable or unpriced; the live retailer search is a separate lookup and routinely finds supply this catalogue has not ingested."New value: +"The retailer offers WEM holds for one catalogue product, current prices first and each group cheapest first. When `found` is false WEM simply does not hold this product yet — that is a normal answer, not a failure, and it says nothing about whether the product exists or what it costs. `nextTool` then names `search_products` and the query. An empty catalogue result is not a statement that the product is unavailable or unpriced."
    • changedOutput schema / properties / nextTool / description
      Previous value: -"Present only when `found` is false. It names `search_products` and the query. A named model that misses the catalogue is the ordinary case; the live search is a separate lookup."New value: +"Present only when `found` is false. It names `search_products` and the query. A named model that misses the catalogue is the ordinary case."
    • changedOutput schema / properties / search / description
      Previous value: -"Present when this result names no offer. `url` is WEM's results page for the query and `markdown` is the same link, pre-formatted. WEM searches retailers live on that page, including shops it holds no affiliate programme with, so an empty or withheld result here is not evidence the product is unavailable or unpriced."New value: +"Present when this result names no offer. `url` is WEM's results page for the query and `markdown` is the same link, pre-formatted. This tool reads the catalogue only, so an empty result here is not evidence the product is unavailable or unpriced."
    • changedOutput schema / properties / unavailable / description
      Previous value: -"Present and true only when WEM could not reach its catalogue at all. `found` is false for the same reason it is on an ordinary miss, so the two are indistinguishable without this flag. When it is set, WEM does not know whether it holds the product. The lookup could not be completed; it does not mean WEM has no offers or no price for it. `nextTool` is still present, because the live retailer search does not depend on the catalogue."New value: +"Present and true only when WEM could not reach its catalogue at all. `found` is false for the same reason it is on an ordinary miss, so the two are indistinguishable without this flag. When it is set, WEM does not know whether it holds the product. The lookup could not be completed; it does not mean WEM has no offers or no price for it. `nextTool` is still present."
  8. Changed1 schema field changed
    • addedOutput schema / properties / offers / items / properties / shipping / description
      Added value: +"Delivery as the retailer stated it: `free`, or `cost` in the offer currency. Absent or null means not stated: say delivery is shown at the shop, never that it is free. Offers are ordered by item price; add a stated cost before calling one cheaper delivered."
  9. Changed43 schema fields changed
    • changedInput schema / properties / currency / description
      Previous value: -"ISO 4217 code for the shopper's market. Default GBP. Decides which market's offers WEM returns from its catalogue, not just how the answer reads — pass it explicitly, because WEM does not infer the market from IP. When WEM holds the product only in another currency it says so rather than presenting a foreign listing as the answer."New value: +"ISO 4217 code for the shopper's market. Default GBP. Decides which market's offers WEM returns from its catalogue, not just how the answer reads ; WEM does not infer the market from IP. When WEM holds the product only in another currency it says so rather than presenting a foreign listing as the answer."
    • changedInput schema / properties / gtin / description
      Previous value: -"Product barcode: EAN-13, UPC-A, EAN-8 or GTIN-14. Preferred key."New value: +"Product barcode: EAN-13, UPC-A, EAN-8 or GTIN-14. The strongest identifier."
    • changedInput schema / properties / title / description
      Previous value: -"Product or model name, or a bare MPN / merchant SKU (e.g. AF400UK). Weakest identifier — used only when no barcode, slug, or WEM ID is available. Same relevance gate as verify_offer; a miss means fall back to search_products, not a guess."New value: +"Product or model name, or an ASIN, MPN or merchant SKU (e.g. AF400UK). The weakest identifier: used only when no barcode, slug or WEM ID is given, and only if it passes a relevance check; otherwise `found` is false rather than a guess."
    • changedOutput schema / description
      Previous value: -"Every verified retailer offer for one catalogue product, cheapest first. When `found` is false WEM simply does not hold this product yet — that is a normal answer, not a failure, and it says nothing about whether the product exists or what it costs. Call `search_products` with `nextTool.query` before telling the user anything; reporting \"WEM returns nothing\" without doing so is wrong, because the live retailer search routinely finds supply this catalogue has not ingested."New value: +"The retailer offers WEM holds for one catalogue product, current prices first and each group cheapest first. When `found` is false WEM simply does not hold this product yet — that is a normal answer, not a failure, and it says nothing about whether the product exists or what it costs. `nextTool` then names `search_products` and the query. An empty catalogue result is not a statement that the product is unavailable or unpriced; the live retailer search is a separate lookup and routinely finds supply this catalogue has not ingested."
    • changedOutput schema / properties / coverage / description
      Previous value: -"Present when surviving rows include a marketplace cluster at similar prices. That is not a retail floor — do not name the cheapest marketplace listing as the deal. The listings are still in the payload; give the user those links. If `next` points at the Chrome extension, send the shopper there for shops WEM does not yet hold as partners."New value: +"Present when surviving rows include a marketplace cluster at similar prices. That is not a retail floor — the cheapest marketplace listing is not the deal. The listings stay in the payload, each with its url. `next`, when present, is the Chrome extension page for shops WEM does not yet hold as partners."
    • changedOutput schema / properties / degraded / description
      Previous value: -"Present when this call hit its time budget and skipped enrichment. The offers returned are COMPLETE — only the extras were dropped. Do not report a missing catalogue block or missing observed retailers as WEM holding nothing, and do not retry automatically."New value: +"Present when this call hit its time budget and skipped enrichment. The offers returned are COMPLETE — only the extras were dropped. A missing catalogue block or missing observed retailers does not mean WEM holds nothing, and no retry is needed."
    • changedOutput schema / properties / disclosure / description
      Previous value: -"Disclosure to relay once per answer, verbatim. Its wording changes with the rows: it states whether every outbound link is affiliate-tracked, only some are, or none are. Never substitute the version you saw last time — a row marked `affiliate: false` earns WEM nothing, and saying otherwise misdescribes it to the shopper."New value: +"WEM’s affiliate disclosure for this answer, written to be quoted as it is. Its wording changes with the rows: it states whether every outbound link is affiliate-tracked, only some are, or none are. A version from an earlier answer may not fit this one: a row marked `affiliate: false` earns WEM nothing."
    • changedOutput schema / properties / filtered / description
      Previous value: -"Offers of this product WEM held back, tallied by reason. \"Priced far below this product’s other offers\" is a marketplace listing under 40% of what the product sells for elsewhere — the shape of a counterfeit, a part or a mislisted variant. Those prices are not in `offers`, `lowPrice`, `links`, `summary` or `priceHistory.low`: never quote one as the cheapest, a deal or the 90-day low. Say how many were held back."New value: +"Offers of this product WEM held back, tallied by reason. \"Priced far below this product’s other offers\" is a marketplace listing under 40% of what the product sells for elsewhere — the shape of a counterfeit, a part or a mislisted variant. Those prices are not in `offers`, `lowPrice`, `links`, `summary` or `priceHistory.low`: none is the cheapest, a deal or the 90-day low. The tally says how many were held back."
    • changedOutput schema / properties / found / description
      Previous value: -"False when the product is not in WEM’s catalogue. The rest of the fields below are then absent — do not read that as the product being unavailable or unpriced. Check `unavailable` before describing the result, then follow `nextTool`."New value: +"False when the product is not in WEM’s catalogue. The rest of the fields below are then absent ; that does not mean the product is unavailable or unpriced. `unavailable` distinguishes an outage from a miss. `nextTool`, when present, names the next lookup."
    • changedOutput schema / properties / identity / description
      Previous value: -"How the shopper’s words reached this product. Decides how you describe the match: on `inferred`, say WEM matched it by name (not by barcode) and do not call the identity confirmed, even when `identityBasis` is `barcode`; on `exact`, the barcode itself resolved it."New value: +"How the shopper’s words reached this product. On `inferred`, WEM matched it by name (not by barcode) and the identity is not confirmed, even when `identityBasis` is `barcode`; on `exact`, the barcode itself resolved it."
    • changedOutput schema / properties / identityBasis / description
      Previous value: -"How this product’s offers are grouped — NOT how the shopper’s words found the product; that is `identity`. `barcode`: the product carries a GTIN, so every offer here is that same item. `curated-grouping`: it carries none, and the grouping is an inference. Never call a result a barcode match on the strength of this field: when `identity.strength` is `inferred`, say WEM matched the product by name."New value: +"How this product’s offers are grouped — NOT how the shopper’s words found the product; that is `identity`. `barcode`: the product carries a GTIN, so every offer here is that same item. `curated-grouping`: it carries none, and the grouping is an inference. This field does not make a result a barcode match: when `identity.strength` is `inferred`, WEM matched the product by name."
    • changedOutput schema / properties / lastConfirmedAt / description
      Previous value: -"When WEM last actually read any price in this answer. Null means none of them can be dated — say so rather than implying the answer is current."New value: +"When WEM last actually read any price in this answer. Null means none of them can be dated, so the answer is not current."
    • changedOutput schema / properties / links / description
      Previous value: -"The links from `offers`, cheapest first, pre-formatted to quote. Each opens the retailer’s page for the offer WEM priced; a retailer URL found elsewhere may be a different listing or price. State them in text even when the host renders a WEM card: the card may not reach the user."New value: +"The links from `offers`, cheapest first, pre-formatted. Each opens the retailer’s page for the offer WEM priced; a retailer URL found elsewhere may be a different listing or price. The same links are in this array and, when a host renders one, in the card."
    • changedOutput schema / properties / links / items / properties / price / description
      Previous value: -"Null for a listing in `unpriced`: its markdown says \"see price at Amazon\", and so must you."New value: +"Null for a listing in `unpriced`. The markdown says \"see price at Amazon\" in place of a figure."
    • changedOutput schema / properties / links / items / properties / url / description
      Previous value: -"WEM tracked redirect. Relay it exactly; never rewrite or shorten it."New value: +"WEM tracked redirect. The full URL; a shortened or rewritten form is a different link."
    • changedOutput schema / properties / lowPrice / description
      Previous value: -"Lowest CURRENT price in `offers` (read within 14 days). Null when no price is current or WEM holds none it may quote — never fill it from a stale or undated row, or from `unpriced`."New value: +"Lowest CURRENT price in `offers` (read within 14 days). Null when no price is current or WEM holds none it may quote ; it is never taken from a stale or undated row, or from `unpriced`."
    • changedOutput schema / properties / next / description
      Previous value: -"Where to send the shopper when WEM's set is thin. chrome_extension means compare the same product on any retailer page — WEM shows that price even without an affiliate programme."New value: +"Present when WEM's set of offers is thin. chrome_extension is a page that compares the same product on any retailer page, including shops without an affiliate programme."
    • changedOutput schema / properties / nextTool / description
      Previous value: -"Present only when `found` is false: the call to make next, with the text to pass. This is the recovery path, not a suggestion — a named model that misses the catalogue is the ordinary case, and the live search is where its offers are."New value: +"Present only when `found` is false. It names `search_products` and the query. A named model that misses the catalogue is the ordinary case; the live search is a separate lookup."
    • changedOutput schema / properties / offers / description
      Previous value: -"Current prices first (`priceStatus`), each group ascending by price; a stale or undated row is listed, never compared. Barcode, slug and WEM ID rows are the product. A title match is inferred — check identity.strength before stating it as exact."New value: +"Current prices first (`priceStatus`), each group ascending by price; a stale or undated row is listed, never compared. Barcode, slug and WEM ID rows are the product. A title match is inferred; `identity.strength` says which."
    • changedOutput schema / properties / offers / items / properties / affiliate / description
      Previous value: -"False when this link earns WEM nothing: the shop has no affiliate programme with WEM, or this link does not carry one. Still a real offer: list it with the others, and never call its link affiliate-tracked. Absent (or true) means the link is affiliate-tracked. `disclosure` already states whether all, some or none of the links are tracked; relay that rather than your own wording."New value: +"False when this link earns WEM nothing: the shop has no affiliate programme with WEM, or this link does not carry one. It is still a real offer, and its link is not affiliate-tracked. Absent (or true) means the link is affiliate-tracked. `disclosure` states whether all, some or none of the links are tracked."
    • changedOutput schema / properties / offers / items / properties / channel / description
      Previous value: -"retailer is a named shop (Boots, Currys). marketplace is eBay/AliExpress/Temu-style parallel listings. Do not present a marketplace cluster as competing authorised retailers."New value: +"retailer is a named shop (Boots, Currys). marketplace is eBay/AliExpress/Temu-style parallel listings. A marketplace cluster is not a set of competing authorised retailers."
    • changedOutput schema / properties / offers / items / properties / lastSeenAt / description
      Previous value: -"When WEM last READ this price from the retailer or a datafeed — the same reading `priceAgeDays` counts from, so the two always agree. Null means WEM cannot date the price: say it is undated, never that it is current."New value: +"When WEM last READ this price from the retailer or a datafeed — the same reading `priceAgeDays` counts from, so the two always agree. Null means WEM cannot date the price: it is undated, not current."
    • changedOutput schema / properties / offers / items / properties / price / description
      Previous value: -"Indicative price. The retailer sets the final price at checkout. Null, with `priceNote`, when WEM may not show this listing’s price: show the note and the link, never a figure of your own."New value: +"Indicative price. The retailer sets the final price at checkout. Null, with `priceNote`, when WEM may not show this listing’s price: the note stands in place of a figure."
    • changedOutput schema / properties / offers / items / properties / priceAgeDays / description
      Previous value: -"Calendar days (UK time) since WEM last READ this price: 0 read today, 1 yesterday. Null means WEM cannot say. Report null as undated; never present it as current."New value: +"Calendar days (UK time) since WEM last READ this price: 0 read today, 1 yesterday. Null means WEM cannot say. Null is undated, not current."
    • changedOutput schema / properties / offers / items / properties / priceNote / description
      Previous value: -"Present only when `price` is null: the words to show in its place, e.g. \"See price at Amazon\". An Amazon price is shown only when Amazon’s API supplied it just now."New value: +"Present only when `price` is null: the words that stand in its place, e.g. \"See price at Amazon\". An Amazon price is shown only when Amazon’s API supplied it just now."
    • changedOutput schema / properties / offers / items / properties / priceQualifier / description
      Previous value: -"Present when `price` is the OPENING price of a range, not the price of this item: a multi-variation listing where the seller advertises its cheapest variant and the shopper picks a size on the page. Quote it as \"from £X\", never as the price or the cheapest. Absent means the price is firm for the row as described."New value: +"Present when `price` is the OPENING price of a range, not the price of this item: a multi-variation listing where the seller advertises its cheapest variant and the shopper picks a size on the page. It reads as \"from £X\"; it is not the item's price or the cheapest. Absent means the price is firm for the row as described."
    • changedOutput schema / properties / offers / items / properties / priceRefresh / description
      Previous value: -"What re-reads this retailer’s prices: `live-api` a product-lookup API, `feed` a partner datafeed, `none` nothing on a schedule. `none` means the figure will not move on its own however long it sits — say so rather than quoting it flat, and date it with `priceAgeDays`."New value: +"What re-reads this retailer’s prices: `live-api` a product-lookup API, `feed` a partner datafeed, `none` nothing on a schedule. `none` means the figure will not move on its own however long it sits ; `priceAgeDays` dates it."
    • changedOutput schema / properties / offers / items / properties / priceStatus / description
      Previous value: -"`current`: WEM read this price within the last 14 days; only a current price may lead, be called the cheapest or lowest, be compared or enter a saving. `stale`: read longer ago. `undated`: WEM cannot say when. Show those two as \"last seen £X on 8 Sep\" or \"price not dated\", after the current ones, and never compare them. Absent on a row a provider answered just now."New value: +"`current`: WEM read this price within the last 14 days; only a current price may lead, be called the cheapest or lowest, be compared or enter a saving. `stale`: read longer ago. `undated`: WEM cannot say when. Those two read as \"last seen £X on 8 Sep\" or \"price not dated\", come after the current ones, and are not compared. Absent on a row a provider answered just now."
    • changedOutput schema / properties / offers / items / properties / unitPrice / description
      Previous value: -"Price per 100ml, 100g, litre, kg or item, from the size this row's title states. Present only when the rows differ in size, in one unit across all of them, and only for things bought by the amount (beauty, food, supplements), never an appliance's capacity. When comparing value across sizes, compare these, never the prices: a smaller size is not cheaper because its price is lower. Quote it beside the price (\"£29.95, £99.83 per 100ml\"). Absent does not mean the price is per unit."New value: +"Price per 100ml, 100g, litre, kg or item, from the size this row's title states. Present only when the rows differ in size, in one unit across all of them, and only for things bought by the amount (beauty, food, supplements), never an appliance's capacity. Across sizes, value is compared on these, not on the prices: a smaller size is not cheaper because its price is lower. It reads beside the price (\"£29.95, £99.83 per 100ml\"). Absent does not mean the price is per unit."
    • changedOutput schema / properties / offers / items / properties / url / description
      Previous value: -"WEM tracked link to the retailer. Send the user here — WEM never takes payment."New value: +"WEM tracked link to the retailer page for this offer. WEM never takes payment."
    • removedOutput schema / properties / offers / items / properties / verified
      Removed value: -{
      -  "type": "boolean"
      -}
    • changedOutput schema / properties / product / properties / image / description
      Previous value: -"Product photo, served from wem3.ai. Null when none is available — do not substitute one."New value: +"Product photo, served from wem3.ai. Null when none is available."
    • changedOutput schema / properties / product / properties / wem_id / description
      Previous value: -"WEM ID of this active product. Omit when unknown; never invent one."New value: +"WEM ID of this active product. Null when unknown."
    • changedOutput schema / properties / reason / description
      Previous value: -"Present only when `found` is false: which identifier missed, and why. When `unavailable` is true this describes the outage rather than a missing product — quote it as the reason the lookup failed, not as a fact about the product."New value: +"Present only when `found` is false: which identifier missed, and why. When `unavailable` is true this describes the outage rather than a missing product ; it is the reason the lookup failed, not a fact about the product."
    • changedOutput schema / properties / sameModel / description
      Previous value: -"Present only when the maker's own page says another model code is this product and a person at WEM has checked it. These are NOT offers for the product asked about: never merge them into `offers`, never call one this product's lowest price or the best deal. On verify_offer they are not part of the check: `verdict`, `cheapest`, `betterBy`, `summary` and the receipt are about the product asked about, and a cheaper sister code never makes the claimed price wrong. Mention it as a separate line: quote `summary`, name the maker's page (`maker.host`) as the source, and link `url`, WEM's comparison for that model, not a shop. When `relation` is `differs`, name the `differences` and never call its price a saving. Absent is not evidence that no equivalent exists."New value: +"Present only when the maker's own page says another model code is this product and a person at WEM has checked it. These are NOT offers for the product asked about: they are not part of `offers`, and none is this product's lowest price or the best deal. On verify_offer they are not part of the check: `verdict`, `cheapest`, `betterBy`, `summary` and the receipt are about the product asked about, and a cheaper sister code never makes the claimed price wrong. `summary` is written to be quoted as it is. `maker.host` is the source of the claim. `url` is WEM's comparison for that model, not a shop. When `relation` is `differs`, the `differences` are what the maker says differs, and its price is not a saving. Absent is not evidence that no equivalent exists."
    • changedOutput schema / properties / sameModel / items / properties / cheapest / description
      Previous value: -"The other model's cheapest named shop. Null when no named shop has an in-stock price for it. Give its age; never present it as current without one."New value: +"The other model's cheapest named shop. Null when no named shop has an in-stock price for it. `priceAgeDays` gives its age; without one it is not current."
    • changedOutput schema / properties / sameModel / items / properties / maker / description
      Previous value: -"The maker's own page that says so: the proof. Cite it; WEM is not the source of the claim."New value: +"The maker's own page that says so: the proof. WEM is not the source of the claim."
    • changedOutput schema / properties / sameModel / items / properties / url / description
      Previous value: -"WEM's comparison for that model. Link this, not a shop."New value: +"WEM's comparison for that model, not a shop."
    • changedOutput schema / properties / search / description
      Previous value: -"Present when this result names no offer. GIVE THE USER THIS LINK — it is the answer when WEM has nothing else to say, and `markdown` is ready to paste. WEM searches retailers live on that page, including shops it holds no affiliate programme with, so an empty or withheld result here is not evidence the product is unavailable or unpriced."New value: +"Present when this result names no offer. `url` is WEM's results page for the query and `markdown` is the same link, pre-formatted. WEM searches retailers live on that page, including shops it holds no affiliate programme with, so an empty or withheld result here is not evidence the product is unavailable or unpriced."
    • changedOutput schema / properties / unavailable / description
      Previous value: -"Present and true only when WEM could not reach its catalogue at all. `found` is false for the same reason it is on an ordinary miss, so the two are indistinguishable without this flag. When it is set, WEM does not know whether it holds the product: say the lookup could not be completed, never that WEM has no offers, no price, or does not stock it. Still call `nextTool` — the live retailer search does not depend on the catalogue."New value: +"Present and true only when WEM could not reach its catalogue at all. `found` is false for the same reason it is on an ordinary miss, so the two are indistinguishable without this flag. When it is set, WEM does not know whether it holds the product. The lookup could not be completed; it does not mean WEM has no offers or no price for it. `nextTool` is still present, because the live retailer search does not depend on the catalogue."
    • changedOutput schema / properties / unpriced / description
      Previous value: -"Listings whose price WEM may not show — today, Amazon rows Amazon’s API did not price just now. Give the link with `priceNote` (\"See price at Amazon\"). Never state, estimate or compare a price for one, never call it the cheapest, and never count it in a lowest price or a saving."New value: +"Listings whose price WEM may not show — today, Amazon rows Amazon’s API did not price just now. The row carries `priceNote` (e.g. \"See price at Amazon\") in place of a figure. No price is stated, estimated or compared for one; it is not the cheapest and is not counted in a lowest price or a saving."
    • changedOutput schema / properties / unpriced / items / properties / priceNote / description
      Previous value: -"What to show where a price would be, e.g. \"See price at Amazon\"."New value: +"What stands where a price would be, e.g. \"See price at Amazon\"."
    • changedOutput schema / properties / unpriced / items / properties / url / description
      Previous value: -"WEM tracked link. Relay it exactly."New value: +"WEM tracked link. The full URL; a shortened form is a different link."
  10. Changed2 schema fields changed
    • changedInput schema / properties / currency / description
      Previous value: -"ISO 4217 code for the shopper's market. Default GBP. Decides which retailers are searched, not just how the answer reads — pass it explicitly, because WEM does not infer the market from IP. When WEM holds the product only in another currency it says so rather than presenting a foreign listing as the answer."New value: +"ISO 4217 code for the shopper's market. Default GBP. Decides which market's offers WEM returns from its catalogue, not just how the answer reads — pass it explicitly, because WEM does not infer the market from IP. When WEM holds the product only in another currency it says so rather than presenting a foreign listing as the answer."
    • changedOutput schema / properties / links / description
      Previous value: -"The tracked links from `offers`, cheapest first, pre-formatted to quote. Give these to the user when you name an offer: they carry the attribution WEM is funded by, and a retailer URL you compose yourself does not. Present even when the host renders a WEM card — never assume the card reached the user."New value: +"The links from `offers`, cheapest first, pre-formatted to quote. Each opens the retailer’s page for the offer WEM priced; a retailer URL found elsewhere may be a different listing or price. State them in text even when the host renders a WEM card: the card may not reach the user."
  11. Changed13 schema fields changed
    • addedOutput schema / properties / highPrice / description
      Added value: +"Highest current price in `offers`; null with lowPrice."
    • changedOutput schema / properties / links / items / properties / currency / type
      Previous value: -"string"New value: +[
      +  "string",
      +  "null"
      +]
    • addedOutput schema / properties / links / items / properties / price / description
      Added value: +"Null for a listing in `unpriced`: its markdown says \"see price at Amazon\", and so must you."
    • changedOutput schema / properties / links / items / properties / price / type
      Previous value: -"number"New value: +[
      +  "number",
      +  "null"
      +]
    • addedOutput schema / properties / lowPrice / description
      Added value: +"Lowest CURRENT price in `offers` (read within 14 days). Null when no price is current or WEM holds none it may quote — never fill it from a stale or undated row, or from `unpriced`."
    • changedOutput schema / properties / offers / description
      Previous value: -"Ascending by price. Barcode, slug and WEM ID rows are the product. A title match is inferred — check identity.strength before stating it as exact."New value: +"Current prices first (`priceStatus`), each group ascending by price; a stale or undated row is listed, never compared. Barcode, slug and WEM ID rows are the product. A title match is inferred — check identity.strength before stating it as exact."
    • changedOutput schema / properties / offers / items / properties / lastSeenAt / description
      Previous value: -"When WEM last VISITED this offer row — not when it read the price. The refresh sweep touches this even when the retailer lookup fails, so it is not evidence the price is current. Use `priceAgeDays` to date a price; never this."New value: +"When WEM last READ this price from the retailer or a datafeed — the same reading `priceAgeDays` counts from, so the two always agree. Null means WEM cannot date the price: say it is undated, never that it is current."
    • changedOutput schema / properties / offers / items / properties / price / description
      Previous value: -"Indicative price. The retailer sets the final price at checkout."New value: +"Indicative price. The retailer sets the final price at checkout. Null, with `priceNote`, when WEM may not show this listing’s price: show the note and the link, never a figure of your own."
    • changedOutput schema / properties / offers / items / properties / price / type
      Previous value: -"number"New value: +[
      +  "number",
      +  "null"
      +]
    • changedOutput schema / properties / offers / items / properties / priceAgeDays / description
      Previous value: -"Whole days since WEM last actually READ this price from the retailer or a datafeed. Null means WEM cannot say — common and correct for Amazon, whose licence caps price retention at 24 hours. Report null as undated; never present it as current."New value: +"Calendar days (UK time) since WEM last READ this price: 0 read today, 1 yesterday. Null means WEM cannot say. Report null as undated; never present it as current."
    • addedOutput schema / properties / offers / items / properties / priceNote
      Added value: +{
      +  "description": "Present only when `price` is null: the words to show in its place, e.g. \"See price at Amazon\". An Amazon price is shown only when Amazon’s API supplied it just now.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / offers / items / properties / priceStatus
      Added value: +{
      +  "description": "`current`: WEM read this price within the last 14 days; only a current price may lead, be called the cheapest or lowest, be compared or enter a saving. `stale`: read longer ago. `undated`: WEM cannot say when. Show those two as \"last seen £X on 8 Sep\" or \"price not dated\", after the current ones, and never compare them. Absent on a row a provider answered just now.",
      +  "enum": [
      +    "current",
      +    "stale",
      +    "undated"
      +  ],
      +  "type": "string"
      +}
    • addedOutput schema / properties / unpriced
      Added value: +{
      +  "description": "Listings whose price WEM may not show — today, Amazon rows Amazon’s API did not price just now. Give the link with `priceNote` (\"See price at Amazon\"). Never state, estimate or compare a price for one, never call it the cheapest, and never count it in a lowest price or a saving.",
      +  "items": {
      +    "properties": {
      +      "channel": {
      +        "enum": [
      +          "retailer",
      +          "marketplace"
      +        ],
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "priceNote": {
      +        "description": "What to show where a price would be, e.g. \"See price at Amazon\".",
      +        "type": "string"
      +      },
      +      "provider": {
      +        "type": "string"
      +      },
      +      "title": {
      +        "type": "string"
      +      },
      +      "url": {
      +        "description": "WEM tracked link. Relay it exactly.",
      +        "type": "string"
      +      }
      +    },
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
  12. Changed2 schema fields changed
    • addedOutput schema / properties / filtered
      Added value: +{
      +  "additionalProperties": {
      +    "type": "number"
      +  },
      +  "description": "Offers of this product WEM held back, tallied by reason. \"Priced far below this product’s other offers\" is a marketplace listing under 40% of what the product sells for elsewhere — the shape of a counterfeit, a part or a mislisted variant. Those prices are not in `offers`, `lowPrice`, `links`, `summary` or `priceHistory.low`: never quote one as the cheapest, a deal or the 90-day low. Say how many were held back.",
      +  "type": [
      +    "object",
      +    "null"
      +  ]
      +}
    • changedOutput schema / properties / offers / items / properties / affiliate / description
      Previous value: -"False when this is a retailer page WEM observed without a programme. Still a real listing; the click is not commission-bearing. Absent means the usual partner path."New value: +"False when this link earns WEM nothing: the shop has no affiliate programme with WEM, or this link does not carry one. Still a real offer: list it with the others, and never call its link affiliate-tracked. Absent (or true) means the link is affiliate-tracked. `disclosure` already states whether all, some or none of the links are tracked; relay that rather than your own wording."
  13. Changed2 schema fields changed
    • changedOutput schema / properties / offers / items / properties / priceRefresh / description
      Previous value: -"What re-reads this retailer’s prices: `live-api` a product-lookup API, `feed` a partner datafeed, `crawl` WEM reading the retailer’s own public product page, `none` nothing at all. `none` means the figure is frozen at whatever seeded it and will not move however long it sits — say so rather than quoting it flat. A `crawl` price was observed on the retailer’s page rather than supplied by them, so date it with `priceAgeDays` and expect `affiliate: false` unless a programme also exists."New value: +"What re-reads this retailer’s prices: `live-api` a product-lookup API, `feed` a partner datafeed, `none` nothing on a schedule. `none` means the figure will not move on its own however long it sits — say so rather than quoting it flat, and date it with `priceAgeDays`."
    • changedOutput schema / properties / offers / items / properties / priceRefresh / enum
      Previous value: -[
      -  "live-api",
      -  "feed",
      -  "crawl",
      -  "none"
      -]New value: +[
      +  "live-api",
      +  "feed",
      +  "none"
      +]
  14. Changed1 schema field changed
    • changedOutput schema / properties / sameModel / description
      Previous value: -"Present only when the maker's own page says another model code is this product and a person at WEM has checked it. These are NOT offers for the product asked about: never merge them into `offers`, never call one this product's lowest price or the best deal, and never let one change a verify_offer verdict. Mention it as a separate line: quote `summary`, name the maker's page (`maker.host`) as the source, and link `url`, WEM's comparison for that model, not a shop. When `relation` is `differs`, name the `differences` and never call its price a saving. Absent means WEM holds no checked equivalent, not that none exists."New value: +"Present only when the maker's own page says another model code is this product and a person at WEM has checked it. These are NOT offers for the product asked about: never merge them into `offers`, never call one this product's lowest price or the best deal. On verify_offer they are not part of the check: `verdict`, `cheapest`, `betterBy`, `summary` and the receipt are about the product asked about, and a cheaper sister code never makes the claimed price wrong. Mention it as a separate line: quote `summary`, name the maker's page (`maker.host`) as the source, and link `url`, WEM's comparison for that model, not a shop. When `relation` is `differs`, name the `differences` and never call its price a saving. Absent is not evidence that no equivalent exists."
  15. Changed1 schema field changed
    • addedOutput schema / properties / sameModel
      Added value: +{
      +  "description": "Present only when the maker's own page says another model code is this product and a person at WEM has checked it. These are NOT offers for the product asked about: never merge them into `offers`, never call one this product's lowest price or the best deal, and never let one change a verify_offer verdict. Mention it as a separate line: quote `summary`, name the maker's page (`maker.host`) as the source, and link `url`, WEM's comparison for that model, not a shop. When `relation` is `differs`, name the `differences` and never call its price a saving. Absent means WEM holds no checked equivalent, not that none exists.",
      +  "items": {
      +    "properties": {
      +      "cheapest": {
      +        "description": "The other model's cheapest named shop. Null when no named shop has an in-stock price for it. Give its age; never present it as current without one.",
      +        "properties": {
      +          "currency": {
      +            "type": "string"
      +          },
      +          "price": {
      +            "type": "number"
      +          },
      +          "priceAgeDays": {
      +            "description": "Days since WEM read that price. Null means undated.",
      +            "type": [
      +              "number",
      +              "null"
      +            ]
      +          },
      +          "shop": {
      +            "type": "string"
      +          }
      +        },
      +        "type": [
      +          "object",
      +          "null"
      +        ]
      +      },
      +      "checkedOn": {
      +        "description": "When a person at WEM read the maker’s page (YYYY-MM-DD).",
      +        "type": "string"
      +      },
      +      "differences": {
      +        "description": "What the maker says differs. Empty when identical.",
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "maker": {
      +        "description": "The maker's own page that says so: the proof. Cite it; WEM is not the source of the claim.",
      +        "properties": {
      +          "host": {
      +            "type": "string"
      +          },
      +          "says": {
      +            "description": "The page's words, as checked.",
      +            "type": "string"
      +          },
      +          "url": {
      +            "type": "string"
      +          }
      +        },
      +        "type": "object"
      +      },
      +      "model": {
      +        "description": "The other model code, as the maker writes it.",
      +        "type": "string"
      +      },
      +      "relation": {
      +        "description": "`identical`: the maker says it is the same product. `differs`: the same product except `differences`; not like for like.",
      +        "enum": [
      +          "identical",
      +          "differs"
      +        ],
      +        "type": "string"
      +      },
      +      "summary": {
      +        "description": "WEM’s words for it, written to be quoted as they are.",
      +        "type": "string"
      +      },
      +      "url": {
      +        "description": "WEM's comparison for that model. Link this, not a shop.",
      +        "type": [
      +          "string",
      +          "null"
      +        ]
      +      }
      +    },
      +    "required": [
      +      "model",
      +      "relation",
      +      "differences",
      +      "maker",
      +      "checkedOn",
      +      "summary"
      +    ],
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
  16. Changed1 schema field changed
    • addedOutput schema / properties / offers / items / properties / unitPrice
      Added value: +{
      +  "description": "Price per 100ml, 100g, litre, kg or item, from the size this row's title states. Present only when the rows differ in size, in one unit across all of them, and only for things bought by the amount (beauty, food, supplements), never an appliance's capacity. When comparing value across sizes, compare these, never the prices: a smaller size is not cheaper because its price is lower. Quote it beside the price (\"£29.95, £99.83 per 100ml\"). Absent does not mean the price is per unit.",
      +  "properties": {
      +    "amount": {
      +      "type": "number"
      +    },
      +    "currency": {
      +      "description": "ISO 4217 code.",
      +      "type": "string"
      +    },
      +    "per": {
      +      "enum": [
      +        "100ml",
      +        "litre",
      +        "100g",
      +        "kg",
      +        "item"
      +      ],
      +      "type": "string"
      +    }
      +  },
      +  "type": "object"
      +}
  17. Changed3 schema fields changed
    • changedOutput schema / properties / identity / description
      Previous value: -"How the product was identified. Never present a strength of \"inferred\" as a barcode-exact identity."New value: +"How the shopper’s words reached this product. Decides how you describe the match: on `inferred`, say WEM matched it by name (not by barcode) and do not call the identity confirmed, even when `identityBasis` is `barcode`; on `exact`, the barcode itself resolved it."
    • changedOutput schema / properties / identity / properties / strength / description
      Previous value: -"exact = barcode; catalogued = a WEM key or listing id; inferred = matched on the product name and still a guess."New value: +"exact = barcode; catalogued = a WEM key or listing id; inferred = matched on the product name, MPN or SKU and still a guess."
    • addedOutput schema / properties / identityBasis
      Added value: +{
      +  "description": "How this product’s offers are grouped — NOT how the shopper’s words found the product; that is `identity`. `barcode`: the product carries a GTIN, so every offer here is that same item. `curated-grouping`: it carries none, and the grouping is an inference. Never call a result a barcode match on the strength of this field: when `identity.strength` is `inferred`, say WEM matched the product by name.",
      +  "enum": [
      +    "barcode",
      +    "curated-grouping"
      +  ],
      +  "type": "string"
      +}
  18. Changed1 schema field changed
    • addedOutput schema / properties / degraded
      Added value: +{
      +  "description": "Present when this call hit its time budget and skipped enrichment. The offers returned are COMPLETE — only the extras were dropped. Do not report a missing catalogue block or missing observed retailers as WEM holding nothing, and do not retry automatically.",
      +  "properties": {
      +    "note": {
      +      "type": "string"
      +    },
      +    "skipped": {
      +      "items": {
      +        "type": "string"
      +      },
      +      "type": "array"
      +    }
      +  },
      +  "type": "object"
      +}
  19. Changed1 schema field changed
    • changedInput schema / properties / title / description
      Previous value: -"Product or model name, e.g. Sony WH-1000XM5. Weakest identifier — used only when no barcode, slug, or WEM ID is available. Same relevance gate as verify_offer; a miss means fall back to search_products, not a guess."New value: +"Product or model name, or a bare MPN / merchant SKU (e.g. AF400UK). Weakest identifier — used only when no barcode, slug, or WEM ID is available. Same relevance gate as verify_offer; a miss means fall back to search_products, not a guess."
  20. Changed6 schema fields changed
    • changedOutput schema / properties / disclosure / description
      Previous value: -"Affiliate disclosure to relay once per answer. Outbound links are tracked at no extra cost to the buyer."New value: +"Disclosure to relay once per answer, verbatim. Its wording changes with the rows: it states whether every outbound link is affiliate-tracked, only some are, or none are. Never substitute the version you saw last time — a row marked `affiliate: false` earns WEM nothing, and saying otherwise misdescribes it to the shopper."
    • changedOutput schema / properties / found / description
      Previous value: -"False when the product is not in WEM’s catalogue. The rest of the fields below are then absent — do not read that as the product being unavailable or unpriced. Follow `nextTool`."New value: +"False when the product is not in WEM’s catalogue. The rest of the fields below are then absent — do not read that as the product being unavailable or unpriced. Check `unavailable` before describing the result, then follow `nextTool`."
    • changedOutput schema / properties / offers / items / properties / priceRefresh / description
      Previous value: -"What re-reads this retailer’s prices: `live-api` a product-lookup API, `feed` a partner datafeed, `none` nothing at all. `none` means the figure is frozen at whatever seeded it and will not move however long it sits — say so rather than quoting it flat."New value: +"What re-reads this retailer’s prices: `live-api` a product-lookup API, `feed` a partner datafeed, `crawl` WEM reading the retailer’s own public product page, `none` nothing at all. `none` means the figure is frozen at whatever seeded it and will not move however long it sits — say so rather than quoting it flat. A `crawl` price was observed on the retailer’s page rather than supplied by them, so date it with `priceAgeDays` and expect `affiliate: false` unless a programme also exists."
    • changedOutput schema / properties / offers / items / properties / priceRefresh / enum
      Previous value: -[
      -  "live-api",
      -  "feed",
      -  "none"
      -]New value: +[
      +  "live-api",
      +  "feed",
      +  "crawl",
      +  "none"
      +]
    • changedOutput schema / properties / reason / description
      Previous value: -"Present only when `found` is false: which identifier missed, and why."New value: +"Present only when `found` is false: which identifier missed, and why. When `unavailable` is true this describes the outage rather than a missing product — quote it as the reason the lookup failed, not as a fact about the product."
    • addedOutput schema / properties / unavailable
      Added value: +{
      +  "description": "Present and true only when WEM could not reach its catalogue at all. `found` is false for the same reason it is on an ordinary miss, so the two are indistinguishable without this flag. When it is set, WEM does not know whether it holds the product: say the lookup could not be completed, never that WEM has no offers, no price, or does not stock it. Still call `nextTool` — the live retailer search does not depend on the catalogue.",
      +  "type": "boolean"
      +}
  21. Changed1 schema field changed
    • addedOutput schema / properties / search
      Added value: +{
      +  "description": "Present when this result names no offer. GIVE THE USER THIS LINK — it is the answer when WEM has nothing else to say, and `markdown` is ready to paste. WEM searches retailers live on that page, including shops it holds no affiliate programme with, so an empty or withheld result here is not evidence the product is unavailable or unpriced.",
      +  "properties": {
      +    "markdown": {
      +      "description": "The same link, pre-formatted.",
      +      "type": "string"
      +    },
      +    "reason": {
      +      "type": "string"
      +    },
      +    "url": {
      +      "type": "string"
      +    }
      +  },
      +  "type": "object"
      +}
  22. Changed5 schema fields changed
    • changedOutput schema / description
      Previous value: -"Every verified retailer offer for one catalogue product, cheapest first."New value: +"Every verified retailer offer for one catalogue product, cheapest first. When `found` is false WEM simply does not hold this product yet — that is a normal answer, not a failure, and it says nothing about whether the product exists or what it costs. Call `search_products` with `nextTool.query` before telling the user anything; reporting \"WEM returns nothing\" without doing so is wrong, because the live retailer search routinely finds supply this catalogue has not ingested."
    • addedOutput schema / properties / found
      Added value: +{
      +  "description": "False when the product is not in WEM’s catalogue. The rest of the fields below are then absent — do not read that as the product being unavailable or unpriced. Follow `nextTool`.",
      +  "type": "boolean"
      +}
    • addedOutput schema / properties / nextTool
      Added value: +{
      +  "description": "Present only when `found` is false: the call to make next, with the text to pass. This is the recovery path, not a suggestion — a named model that misses the catalogue is the ordinary case, and the live search is where its offers are.",
      +  "properties": {
      +    "query": {
      +      "type": "string"
      +    },
      +    "tool": {
      +      "enum": [
      +        "search_products"
      +      ],
      +      "type": "string"
      +    }
      +  },
      +  "type": "object"
      +}
    • addedOutput schema / properties / reason
      Added value: +{
      +  "description": "Present only when `found` is false: which identifier missed, and why.",
      +  "type": "string"
      +}
    • changedOutput schema / required
      Previous value: -[
      -  "product",
      -  "offers",
      -  "priceHistory",
      -  "source",
      -  "disclosure"
      -]New value: +[
      +  "found"
      +]
  23. Changed1 schema field changed
    • addedOutput schema / properties / offers / items / properties / priceQualifier
      Added value: +{
      +  "description": "Present when `price` is the OPENING price of a range, not the price of this item: a multi-variation listing where the seller advertises its cheapest variant and the shopper picks a size on the page. Quote it as \"from £X\", never as the price or the cheapest. Absent means the price is firm for the row as described.",
      +  "enum": [
      +    "from"
      +  ],
      +  "type": "string"
      +}
  24. Changed1 schema field changed
    • addedInput schema / properties / currency
      Added value: +{
      +  "description": "ISO 4217 code for the shopper's market. Default GBP. Decides which retailers are searched, not just how the answer reads — pass it explicitly, because WEM does not infer the market from IP. When WEM holds the product only in another currency it says so rather than presenting a foreign listing as the answer.",
      +  "type": "string"
      +}
  25. Changed2 schema fields changed
    • addedOutput schema / properties / offers / items / properties / channel
      Added value: +{
      +  "description": "retailer is a named shop (Boots, Currys). marketplace is eBay/AliExpress/Temu-style parallel listings. Do not present a marketplace cluster as competing authorised retailers.",
      +  "enum": [
      +    "retailer",
      +    "marketplace"
      +  ],
      +  "type": "string"
      +}
    • addedOutput schema / properties / offers / items / properties / shipping / properties / estimate
      Added value: +{
      +  "description": "Delivery window when the feed stated one.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
  26. Changed3 schema fields changed
    • changedOutput schema / properties / coverage / description
      Previous value: -"Present when every surviving row is a marketplace listing clustered at similar prices. That is not a retail floor — do not name the cheapest as the deal, and do not present the listings as competing retailers. WEM does not hold an authorised retailer for this product."New value: +"Present when surviving rows include a marketplace cluster at similar prices. That is not a retail floor — do not name the cheapest marketplace listing as the deal. The listings are still in the payload; give the user those links. If `next` points at the Chrome extension, send the shopper there for shops WEM does not yet hold as partners."
    • addedOutput schema / properties / next
      Added value: +{
      +  "description": "Where to send the shopper when WEM's set is thin. chrome_extension means compare the same product on any retailer page — WEM shows that price even without an affiliate programme.",
      +  "properties": {
      +    "reason": {
      +      "type": "string"
      +    },
      +    "surface": {
      +      "enum": [
      +        "chrome_extension"
      +      ],
      +      "type": "string"
      +    },
      +    "url": {
      +      "type": "string"
      +    }
      +  },
      +  "type": "object"
      +}
    • addedOutput schema / properties / offers / items / properties / affiliate
      Added value: +{
      +  "description": "False when this is a retailer page WEM observed without a programme. Still a real listing; the click is not commission-bearing. Absent means the usual partner path.",
      +  "type": "boolean"
      +}
  27. Changed1 schema field changed
    • addedOutput schema / properties / coverage
      Added value: +{
      +  "description": "Present when every surviving row is a marketplace listing clustered at similar prices. That is not a retail floor — do not name the cheapest as the deal, and do not present the listings as competing retailers. WEM does not hold an authorised retailer for this product.",
      +  "properties": {
      +    "currency": {
      +      "type": "string"
      +    },
      +    "high": {
      +      "type": "number"
      +    },
      +    "kind": {
      +      "enum": [
      +        "marketplace_only"
      +      ],
      +      "type": "string"
      +    },
      +    "listings": {
      +      "type": "number"
      +    },
      +    "low": {
      +      "type": "number"
      +    },
      +    "provider": {
      +      "description": "ebay, aliexpress, or marketplace when mixed.",
      +      "type": "string"
      +    }
      +  },
      +  "type": "object"
      +}
  28. Changed3 schema fields changed
    • changedOutput schema / properties / lastConfirmedAt / description
      Previous value: -"When WEM last read these offers — the freshness of this answer. Same field verify_offer publishes."New value: +"When WEM last actually read any price in this answer. Null means none of them can be dated — say so rather than implying the answer is current."
    • changedOutput schema / properties / offers / items / properties / lastSeenAt / description
      Previous value: -"When WEM last wrote this offer row."New value: +"When WEM last VISITED this offer row — not when it read the price. The refresh sweep touches this even when the retailer lookup fails, so it is not evidence the price is current. Use `priceAgeDays` to date a price; never this."
    • changedOutput schema / properties / offers / items / properties / priceAgeDays / description
      Previous value: -"Whole days since WEM last wrote this price. Null means unknown age — report it as unknown rather than assuming it is current."New value: +"Whole days since WEM last actually READ this price from the retailer or a datafeed. Null means WEM cannot say — common and correct for Amazon, whose licence caps price retention at 24 hours. Report null as undated; never present it as current."
  29. Changed1 schema field changed
    • addedOutput schema / properties / links
      Added value: +{
      +  "description": "The tracked links from `offers`, cheapest first, pre-formatted to quote. Give these to the user when you name an offer: they carry the attribution WEM is funded by, and a retailer URL you compose yourself does not. Present even when the host renders a WEM card — never assume the card reached the user.",
      +  "items": {
      +    "properties": {
      +      "currency": {
      +        "type": "string"
      +      },
      +      "markdown": {
      +        "description": "The same link as `[Retailer — £0.00](url)`, for hosts rendering markdown.",
      +        "type": "string"
      +      },
      +      "price": {
      +        "type": "number"
      +      },
      +      "retailer": {
      +        "type": "string"
      +      },
      +      "url": {
      +        "description": "WEM tracked redirect. Relay it exactly; never rewrite or shorten it.",
      +        "type": "string"
      +      }
      +    },
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
  30. Changed4 schema fields changed
    • addedOutput schema / properties / lastConfirmedAt
      Added value: +{
      +  "description": "When WEM last read these offers — the freshness of this answer. Same field verify_offer publishes.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / offers / items / properties / lastSeenAt
      Added value: +{
      +  "description": "When WEM last wrote this offer row.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / offers / items / properties / priceAgeDays
      Added value: +{
      +  "description": "Whole days since WEM last wrote this price. Null means unknown age — report it as unknown rather than assuming it is current.",
      +  "type": [
      +    "number",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / offers / items / properties / priceRefresh
      Added value: +{
      +  "description": "What re-reads this retailer’s prices: `live-api` a product-lookup API, `feed` a partner datafeed, `none` nothing at all. `none` means the figure is frozen at whatever seeded it and will not move however long it sits — say so rather than quoting it flat.",
      +  "enum": [
      +    "live-api",
      +    "feed",
      +    "none"
      +  ],
      +  "type": "string"
      +}
  31. Changed4 schema fields changed
    • changedInput schema / anyOf
      Previous value: -[
      -  {
      -    "required": [
      -      "gtin"
      -    ]
      -  },
      -  {
      -    "required": [
      -      "slug"
      -    ]
      -  },
      -  {
      -    "required": [
      -      "wem_id"
      -    ]
      -  }
      -]New value: +[
      +  {
      +    "required": [
      +      "gtin"
      +    ]
      +  },
      +  {
      +    "required": [
      +      "slug"
      +    ]
      +  },
      +  {
      +    "required": [
      +      "wem_id"
      +    ]
      +  },
      +  {
      +    "required": [
      +      "title"
      +    ]
      +  }
      +]
    • addedInput schema / properties / title
      Added value: +{
      +  "description": "Product or model name, e.g. Sony WH-1000XM5. Weakest identifier — used only when no barcode, slug, or WEM ID is available. Same relevance gate as verify_offer; a miss means fall back to search_products, not a guess.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / identity
      Added value: +{
      +  "description": "How the product was identified. Never present a strength of \"inferred\" as a barcode-exact identity.",
      +  "properties": {
      +    "method": {
      +      "enum": [
      +        "gtin",
      +        "slug",
      +        "listing",
      +        "title"
      +      ],
      +      "type": "string"
      +    },
      +    "strength": {
      +      "description": "exact = barcode; catalogued = a WEM key or listing id; inferred = matched on the product name and still a guess.",
      +      "enum": [
      +        "exact",
      +        "catalogued",
      +        "inferred"
      +      ],
      +      "type": "string"
      +    }
      +  },
      +  "type": [
      +    "object",
      +    "null"
      +  ]
      +}
    • changedOutput schema / properties / offers / description
      Previous value: -"Ascending by price. Every row IS the product — resolved by barcode, not by title."New value: +"Ascending by price. Barcode, slug and WEM ID rows are the product. A title match is inferred — check identity.strength before stating it as exact."
  32. Changed2 schema fields changed
    • addedInput schema / anyOf
      Added value: +[
      +  {
      +    "required": [
      +      "gtin"
      +    ]
      +  },
      +  {
      +    "required": [
      +      "slug"
      +    ]
      +  },
      +  {
      +    "required": [
      +      "wem_id"
      +    ]
      +  }
      +]
    • removedInput schema / required
      Removed value: -[]
  33. Changed1 schema field changed
    • addedOutput schema / properties / ranking
      Added value: +{
      +  "description": "Why the offers are in this order. Offers are ordered by price, lowest first; affiliate commission is not an input.",
      +  "properties": {
      +    "basis": {
      +      "type": "string"
      +    },
      +    "parameters": {
      +      "description": "The published ranking parameters.",
      +      "type": "string"
      +    },
      +    "reason": {
      +      "type": "string"
      +    }
      +  },
      +  "type": "object"
      +}
  34. Changed2 schema fields changed
    • addedInput schema / properties / wem_id
      Added value: +{
      +  "description": "WEM ID (W + 10 Crockford characters + check). Active catalogue products only.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / product / properties / wem_id
      Added value: +{
      +  "description": "WEM ID of this active product. Omit when unknown; never invent one.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
  35. Changed1 schema field changed
    • changedInput schema / properties / slug / description
      Previous value: -"WEM canonical product slug, as in wem3.ai/pl/{slug}."New value: +"WEM catalogue slug, or a wem3.ai/pl/{slug} URL / host path. Hosts may pass either form."
  36. Changed1 schema field changed
    • addedOutput schema / properties / product / properties / image
      Added value: +{
      +  "description": "Product photo, served from wem3.ai. Null when none is available — do not substitute one.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
  37. Changed1 schema field changed
    • changedOutput schema / (root)
      Previous value: -nullNew value: +{
      +  "description": "Every verified retailer offer for one catalogue product, cheapest first.",
      +  "properties": {
      +    "currency": {
      +      "type": [
      +        "string",
      +        "null"
      +      ]
      +    },
      +    "disclosure": {
      +      "description": "Affiliate disclosure to relay once per answer. Outbound links are tracked at no extra cost to the buyer.",
      +      "type": "string"
      +    },
      +    "highPrice": {
      +      "type": [
      +        "number",
      +        "null"
      +      ]
      +    },
      +    "lowPrice": {
      +      "type": [
      +        "number",
      +        "null"
      +      ]
      +    },
      +    "offers": {
      +      "description": "Ascending by price. Every row IS the product — resolved by barcode, not by title.",
      +      "items": {
      +        "properties": {
      +          "brand": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "currency": {
      +            "description": "ISO 4217 code.",
      +            "type": "string"
      +          },
      +          "id": {
      +            "type": "string"
      +          },
      +          "image": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "inStock": {
      +            "type": [
      +              "boolean",
      +              "null"
      +            ]
      +          },
      +          "price": {
      +            "description": "Indicative price. The retailer sets the final price at checkout.",
      +            "type": "number"
      +          },
      +          "provider": {
      +            "description": "Retailer slug, e.g. \"ebay\", \"currys\".",
      +            "type": "string"
      +          },
      +          "rating": {
      +            "type": [
      +              "number",
      +              "null"
      +            ]
      +          },
      +          "reviewCount": {
      +            "type": [
      +              "number",
      +              "null"
      +            ]
      +          },
      +          "seller": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "shipping": {
      +            "properties": {
      +              "cost": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "free": {
      +                "type": "boolean"
      +              }
      +            },
      +            "type": [
      +              "object",
      +              "null"
      +            ]
      +          },
      +          "title": {
      +            "type": "string"
      +          },
      +          "url": {
      +            "description": "WEM tracked link to the retailer. Send the user here — WEM never takes payment.",
      +            "type": "string"
      +          },
      +          "verified": {
      +            "type": "boolean"
      +          }
      +        },
      +        "type": "object"
      +      },
      +      "type": "array"
      +    },
      +    "priceHistory": {
      +      "description": "The 90-day low, for telling a real discount from a repackaged one.",
      +      "properties": {
      +        "days": {
      +          "type": "number"
      +        },
      +        "low": {
      +          "type": [
      +            "number",
      +            "null"
      +          ]
      +        }
      +      },
      +      "type": "object"
      +    },
      +    "product": {
      +      "properties": {
      +        "brand": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "gtin": {
      +          "description": "The barcode identity was resolved on.",
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "slug": {
      +          "type": "string"
      +        },
      +        "title": {
      +          "type": "string"
      +        }
      +      },
      +      "required": [
      +        "slug",
      +        "title"
      +      ],
      +      "type": "object"
      +    },
      +    "source": {
      +      "type": "string"
      +    }
      +  },
      +  "required": [
      +    "product",
      +    "offers",
      +    "priceHistory",
      +    "source",
      +    "disclosure"
      +  ],
      +  "type": "object"
      +}
  38. First observed

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare a read-only, closed-world, non-destructive operation, and the description still adds substantial behaviour: result ordering (cheapest first, then stale/out-of-stock offers), the 90-day low being null in cross-currency cases, marketplace offers withheld and counted in `filtered`, and title matches flagged as inferred in `identity`. That is exactly the extra context the annotations cannot carry.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the invocation prerequisite and then packs only factual content into each sentence. Some sentences are long and clause-heavy (currency parenthetical, filtered-offer logic), which costs a little scannability but no sentence is filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, yet the description goes further and names the key response fields an agent must branch on (`found`, `identity`, `filtered`, `nextTool`). For a five-parameter, mutation-free lookup tool, an agent has everything needed to call it and interpret the result.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema alone justifies a baseline 3. The description nevertheless adds something the schema cannot: with zero required parameters, 'provide at least one of…' is the only place the at-least-one constraint is stated, and it explains the relative trustworthiness of barcode/slug/WEM ID versus an inferred title match.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: lists the retailer offers WEM's catalogue holds for one product, including ordering and the 90-day low. It implicitly separates itself from live-search siblings by saying 'there is no live retailer search', but it never distinguishes itself from close siblings like compare_products or find_lowest_price, which an agent could easily mistake for this tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a clear entry condition ('provide at least one of: gtin, slug, wem_id, or title') and an identifier-priority rule, plus a fallback path: when the product isn't held, `found` is false and `nextTool` names search_products. It lacks an explicit 'use this instead of X when...' statement against the other offer/pricing siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources