{"openapi":"3.0.3","info":{"title":"super-mcp API","version":"0.1.0","description":"Canonical Israeli supermarket product, price, and promotion data.\n\nMCP: /mcp is SuperMCP for online supermarket delivery (optimize_delivery). Online prices are NOT shelf prices — each storefront's own regulated feed rows are used. This deployment is open: no API key is required. /v1/admin/* still needs a master key."},"servers":[{"url":"/"}],"security":[],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"API key via Authorization: Bearer <key> (sha256-hashed and matched against api_key.key_hash). Query-string ?api_key= is rejected."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}},"Product":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"gtin":{"type":"string","nullable":true},"name":{"type":"string"},"brand":{"type":"string","nullable":true},"categoryL1":{"type":"string","nullable":true},"categoryL2":{"type":"string","nullable":true},"sizeQty":{"type":"number","nullable":true},"sizeUnit":{"type":"string","nullable":true},"pieceCount":{"type":"number","nullable":true}}},"PricedProduct":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"gtin":{"type":"string","nullable":true},"name":{"type":"string"},"brand":{"type":"string","nullable":true},"categoryL1":{"type":"string","nullable":true},"categoryL2":{"type":"string","nullable":true},"sizeQty":{"type":"number","nullable":true},"sizeUnit":{"type":"string","nullable":true},"pieceCount":{"type":"number","nullable":true},"fromPrice":{"type":"number","nullable":true,"description":"Lowest price at any storefront delivering to the requested location — a floor across shops, not a national price, so quote it as 'from ₪X'. The ordinary price, never a club or coupon rate. Null when no delivering storefront prices this product."},"pricedAtStorefronts":{"type":"integer","description":"How many delivering storefronts price it at all. 1 means fromPrice is a single shop's price rather than a market rate."},"pricedAtChains":{"type":"integer","description":"How many distinct chains, the better guide to how easy the product is to buy."},"normalizedUnitPrice":{"type":"number","nullable":true,"description":"fromPrice per 100g / 100ml / piece, and THE field to compare on: a 100g pack can undercut a 125g one on pack price while costing more per gram. Results are sorted by it. Null when the package size could not be parsed."},"normalizedUnitBasis":{"type":"string","nullable":true,"enum":["per_100g","per_100ml","per_piece"]}}},"ProductListing":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"chainId":{"type":"string"},"chainName":{"type":"string"},"itemCode":{"type":"string"},"name":{"type":"string"},"brand":{"type":"string","nullable":true},"qty":{"type":"number","nullable":true},"unit":{"type":"string","nullable":true},"canonicalQty":{"type":"number","nullable":true},"canonicalUnit":{"type":"string","nullable":true},"measureUnparseable":{"type":"boolean"},"orderable":{"type":"boolean","description":"True when some delivery or pickup storefront prices this listing today. False means the chain files a catalogue entry but nothing can be bought from it."}}},"PriceRow":{"type":"object","properties":{"storeId":{"type":"string","format":"uuid"},"storeName":{"type":"string"},"chainId":{"type":"string"},"chainName":{"type":"string"},"city":{"type":"string","nullable":true},"address":{"type":"string","nullable":true},"lat":{"type":"number","nullable":true},"lng":{"type":"number","nullable":true},"distanceKm":{"type":"number","nullable":true},"listingId":{"type":"string","format":"uuid"},"itemCode":{"type":"string"},"listPrice":{"type":"number"},"unitPrice":{"type":"number","nullable":true,"description":"₪ per 100g, 100ml, or per unit, computed from the SHELF price. Ignores promotions; use effectiveUnitPrice to compare what is actually paid."},"effectiveUnitPrice":{"type":"number","nullable":true,"description":"unitPrice with any applicable active promo applied. This is what `sort=unit_price` orders by."},"unitBasis":{"type":"string","enum":["per_100g","per_100ml","per_unit","unknown"]},"currency":{"type":"string"},"effectivePrice":{"type":"number","description":"listPrice with any applicable active promo applied."},"promoApplied":{"type":"boolean"},"promoDescription":{"type":"string","nullable":true},"clubOnly":{"type":"boolean","description":"The quoted price needs the chain loyalty card."},"couponOnly":{"type":"boolean","description":"The quoted price needs a clipped coupon."},"link":{"type":"string","nullable":true,"description":"Clickable storefront URL to open this product on the chain's site (search-by-barcode, or by name for chains that don't index barcodes). Null when the chain has no online store."},"freshness":{"type":"object","properties":{"sourceTs":{"type":"string","format":"date-time","description":"When the chain published this price."},"ingestedAt":{"type":"string","format":"date-time","description":"When this service last ingested it."}}}}},"Chain":{"type":"object","properties":{"id":{"type":"string"},"sourceId":{"type":"string"},"market":{"type":"string"},"nameHe":{"type":"string"},"nameEn":{"type":"string","nullable":true},"currency":{"type":"string"}}},"Store":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"chainId":{"type":"string"},"chainName":{"type":"string"},"storeCode":{"type":"string"},"name":{"type":"string"},"address":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"zip":{"type":"string","nullable":true},"lat":{"type":"number","nullable":true},"lng":{"type":"number","nullable":true},"geoSource":{"type":"string","nullable":true,"description":"Provenance of lat/lng: address, feed, city_centroid, or null."},"storeKind":{"type":"string","nullable":true,"enum":["branch","online","pickup","warehouse"],"description":"Fulfilment kind. Only 'branch' is somewhere a shopper can walk in; online/pickup/warehouse rows carry prices but are excluded from basket store recommendations."},"distanceKm":{"type":"number","nullable":true}}},"StoreLocationMetadata":{"type":"object","properties":{"scope":{"type":"string","enum":["unscoped","city","near","city_near"]},"precision":{"type":"string","enum":["none","city","radius"]},"fallbackApplied":{"type":"boolean"},"warning":{"type":"string","nullable":true},"distanceReliable":{"type":"boolean","description":"False only when distance cannot order the candidates at all — every matching store collapses onto one shared coordinate. A city-level origin no longer clears this: distances stay usable but coarse (see distanceApproximate)."},"distanceApproximate":{"type":"boolean","description":"True when distances are usable but coarse — a city-level origin, or stores placed by city centroid rather than branch address. Present them as approximate."},"requested":{"type":"object","properties":{"city":{"type":"string","nullable":true},"near":{"type":"object","nullable":true,"properties":{"lat":{"type":"number"},"lng":{"type":"number"}}},"radiusKm":{"type":"number","nullable":true}}},"origin":{"type":"object","nullable":true,"description":"Provenance of the resolved user origin point (never includes raw location text).","properties":{"precision":{"type":"string","enum":["address","street","neighborhood","city","coordinates"]},"provider":{"type":"string","enum":["nominatim","city_centroid","coordinates"]},"cached":{"type":"boolean"},"fallbackApplied":{"type":"boolean"},"displayName":{"type":"string","nullable":true},"attribution":{"type":"string","nullable":true}}}}},"Promotion":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"chainId":{"type":"string"},"chainName":{"type":"string"},"storeId":{"type":"string","format":"uuid","nullable":true},"storeCode":{"type":"string","nullable":true},"promoCode":{"type":"string"},"description":{"type":"string"},"mechanicType":{"type":"string","enum":["simple_discount","n_for_price","second_unit_pct","club_price","spend_threshold","other"]},"mechanicParams":{"type":"object"},"clubOnly":{"type":"boolean"},"startTs":{"type":"string","format":"date-time"},"endTs":{"type":"string","format":"date-time"},"sourceTs":{"type":"string","format":"date-time"},"ingestedAt":{"type":"string","format":"date-time"},"itemCodes":{"type":"array","items":{"type":"string"}}}},"BasketLine":{"type":"object","properties":{"itemIndex":{"type":"integer"},"productId":{"type":"string","format":"uuid"},"name":{"type":"string"},"qty":{"type":"number"},"qtyMode":{"type":"string","enum":["packs","weighted_kg_or_l","units"]},"qtyAssumed":{"type":"boolean","description":"Present and true when qty is a FLOOR, not a conversion: the listing is sold by the pack and its pack size is unknown, so a request in grams or litres came back as one pack whatever was asked for. lineTotal is therefore not a price for the requested amount. Such a line is excluded from cross-store comparison (it is charged a market reference instead), but this store's own total still shows the one-pack figure. Treat it as a lower bound, not a quote."},"listingId":{"type":"string","format":"uuid"},"itemCode":{"type":"string"},"unitPrice":{"type":"number"},"lineTotal":{"type":"number"},"sizeQty":{"type":"number","nullable":true},"sizeUnit":{"type":"string","nullable":true},"normalizedUnitPrice":{"type":"number","nullable":true,"description":"Shelf price per 100 g / 100 ml / piece for the priced SKU. Use this to compare two stores whose equivalents differ in pack size."},"normalizedUnitBasis":{"type":"string","nullable":true,"enum":["per_100g","per_100ml","per_piece"]},"promoApplied":{"type":"boolean"},"promoDescription":{"type":"string","nullable":true},"clubOnly":{"type":"boolean","description":"True when lineTotal relies on a loyalty-club promo, so the shopper needs the chain's card to pay it."},"couponOnly":{"type":"boolean","description":"True when lineTotal relies on a promo that needs a clipped coupon. Real but conditional: without the coupon the shopper pays the shelf price."},"substituted":{"type":"boolean"},"substitutionReason":{"type":"string","nullable":true},"originalProductId":{"type":"string","format":"uuid","nullable":true},"link":{"type":"string","nullable":true},"cheaperAlternative":{"type":"object","description":"A cheaper way to satisfy this same line at this same storefront, present only when one exists. lineTotal is directly comparable with this line's, because both are the cost of meeting the request: a half-size pack appears as two packs rather than as a cheaper row. Every alternative is cheaper per 100 g / 100 ml / piece as well as per line, so it cannot be a smaller or lesser product wearing a lower price, and the saving may be quoted as it stands. Computed only for the storefronts the recommendations name and the legs of splitOrder, which are the same storefronts that keep their lines at response_detail=summary. It is an option to offer, never a swap to make: pin it by re-sending the line with its productId.","properties":{"productId":{"type":"string","format":"uuid"},"name":{"type":"string"},"sizeQty":{"type":"number","nullable":true},"sizeUnit":{"type":"string","nullable":true},"qty":{"type":"number","description":"Packs of this product needed to satisfy the line."},"lineTotal":{"type":"number","description":"What the line costs instead, promotions included."},"saving":{"type":"number","description":"This line's lineTotal minus the above. Always positive."},"normalizedUnitPrice":{"type":"number","nullable":true},"normalizedUnitBasis":{"type":"string","nullable":true,"enum":["per_100g","per_100ml","per_piece"]},"clubOnly":{"type":"boolean"},"couponOnly":{"type":"boolean"}}},"freshness":{"type":"object","properties":{"sourceTs":{"type":"string","format":"date-time","description":"When the chain published this price."},"ingestedAt":{"type":"string","format":"date-time","description":"When this service last ingested it."}}}}},"DeliveryPlan":{"type":"object","properties":{"serviceSlug":{"type":"string"},"brand":{"type":"string"},"serviceType":{"type":"string","enum":["delivery","pickup","marketplace"]},"marketplace":{"type":"string","nullable":true},"storefrontUrl":{"type":"string","nullable":true},"chainId":{"type":"string"},"chainName":{"type":"string"},"storeId":{"type":"string","format":"uuid"},"currency":{"type":"string"},"itemsSubtotal":{"type":"number","description":"Goods at this storefront, over the lines it prices."},"itemsComparableSubtotal":{"type":"number","description":"Same-basket item figure: adds a market reference price for lines it does not stock."},"totalScope":{"type":"string","enum":["complete_basket","priced_lines_only"]},"deliveryFee":{"type":"number","nullable":true,"description":"null means NOT KNOWN. Never read it as free."},"assumedDeliveryFee":{"type":"number","nullable":true,"description":"Ranking-only stand-in used when deliveryFee is null, set to the market's verified ₪35.90. Never quote it to a shopper as the price."},"deliveryFeeIsFloor":{"type":"boolean","description":"True when deliveryFee is a published LOWER BOUND, not the charge (a marketplace sets the real figure at checkout from the distance). Quote it as 'from ₪X'; deliveredTotal is a minimum too."},"serviceFee":{"type":"number","description":"Marketplace operations fee (דמי תפעול); 0 for chains."},"deliveredTotal":{"type":"number","nullable":true,"description":"THE HEADLINE FIGURE: itemsSubtotal + deliveryFee + serviceFee. null when the fee is unknown."},"deliveredComparableTotal":{"type":"number","description":"What storefronts are ranked on: same basket everywhere, plus fees. Always present."},"deliveryTerms":{"type":"object","description":"Where this storefront's fee came from. Item prices are regulated feed data; delivery terms are read by a human off a retailer's terms page, so they carry their own provenance and decay.","properties":{"confidence":{"type":"string","enum":["verified","reported","estimated","unknown"],"description":"verified: read from the retailer's own binding terms on verifiedAt. reported: a cited secondary source. estimated: a category default. unknown: no fee is established, or the figure is past its 90-day TTL — do not quote it."},"verifiedAt":{"type":"string","format":"date","nullable":true},"sourceUrl":{"type":"string","nullable":true},"stale":{"type":"boolean","description":"True when the figure is older than the terms TTL."}}},"meetsMinimum":{"type":"boolean","description":"False means the order CANNOT be placed as it stands. Never present it as available."},"minimumOrder":{"type":"number","nullable":true},"amountToMinimum":{"type":"number","nullable":true,"description":"Shekels of goods still needed."},"minimumKnown":{"type":"boolean"},"requiresMembership":{"type":"string","nullable":true,"description":"Set when this rate needs a card or subscription, e.g. 'credit_card', 'wolt_plus'."},"coverage":{"type":"object","properties":{"serves":{"type":"boolean"},"matchedScope":{"type":"string","nullable":true,"enum":["national","city","radius","polygon",null]},"confidence":{"type":"string","nullable":true,"enum":["verified","reported","estimated",null]},"reason":{"type":"string","nullable":true,"enum":["outside_service_area","address_too_vague","coverage_unknown",null],"description":"Why it does not serve the address. coverage_unknown means WE have no service area recorded, which is not the same as the retailer refusing."}}},"freeDeliveryThreshold":{"type":"number","nullable":true},"nextFeeBreak":{"type":"object","nullable":true,"description":"A cheaper fee tier reachable by spending more. Not only free delivery: Shufersal's pickup fee drops ₪15 → ₪10 above ₪750 without reaching zero, and that is the same advice.","properties":{"atSubtotal":{"type":"number"},"fee":{"type":"number"},"gap":{"type":"number","description":"Shekels of extra goods needed."},"saving":{"type":"number","description":"Shekels saved on the fee."},"worthTopUp":{"type":"boolean","description":"True when the gap costs less than the saving, i.e. spending more leaves them better off."}}},"pricedLines":{"type":"integer"},"resolvableLines":{"type":"integer"},"requestedLines":{"type":"integer"},"coverageRatio":{"type":"number"},"imputedTotal":{"type":"number"},"imputedLines":{"type":"integer"},"clubOnlyLines":{"type":"integer"},"couponOnlyLines":{"type":"integer"},"priceFeedAsOf":{"type":"string","nullable":true,"format":"date-time","description":"When this storefront's retailer last published price data. A store-level fact: chains stamp per-line timestamps two incompatible ways, so only the newest one says when the retailer last spoke."},"priceFeedStale":{"type":"boolean","description":"True when this storefront has published no new prices for more than 7 days. It is still ranked and still worth comparing; treat its figures as what the shop published on priceFeedAsOf rather than as today's price. A storefront silent for more than 14 days is treated as abandoned instead: it is excluded from plans and listed in unavailableStores with reason price_feed_stale. The exception is when no storefront inside that window can take the order, since answering 'nobody can sell to you' would be worse than answering late; then the abandoned ones are returned too and notes says so."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/BasketLine"}},"missingItems":{"type":"array","items":{"type":"object"}},"linesTruncated":{"type":"boolean"},"venues":{"type":"array","items":{"type":"string"},"description":"Every marketplace venue this plan stands for, its own serviceSlug first. Present only at response_detail=summary, when branches of one marketplace chain priced this basket to the same money and were collapsed into one row. Any slug here is valid for get_delivery_terms."},"handoffUrl":{"type":"string","description":"This basket at this storefront as one page: every line with its name, quantity, price and product link, in Hebrew. Give it to the shopper instead of the per-line link fields, which otherwise leave them opening a tab per item. Present on the storefronts the recommendations name. The basket is held encrypted under a key that exists only in the link's fragment, so nothing but the link opens it, and the page shows the prices as of priceFeedAsOf rather than re-pricing. The link stops working after 30 days."}}},"DeliveryPlanSummary":{"type":"object","properties":{"serviceSlug":{"type":"string"},"brand":{"type":"string"},"serviceType":{"type":"string","enum":["delivery","pickup","marketplace"]},"marketplace":{"type":"string","nullable":true},"storefrontUrl":{"type":"string","nullable":true},"chainId":{"type":"string"},"chainName":{"type":"string"},"storeId":{"type":"string","format":"uuid"},"currency":{"type":"string"},"itemsSubtotal":{"type":"number","description":"Goods at this storefront, over the lines it prices."},"itemsComparableSubtotal":{"type":"number","description":"Same-basket item figure: adds a market reference price for lines it does not stock."},"totalScope":{"type":"string","enum":["complete_basket","priced_lines_only"]},"deliveryFee":{"type":"number","nullable":true,"description":"null means NOT KNOWN. Never read it as free."},"assumedDeliveryFee":{"type":"number","nullable":true,"description":"Ranking-only stand-in used when deliveryFee is null, set to the market's verified ₪35.90. Never quote it to a shopper as the price."},"deliveryFeeIsFloor":{"type":"boolean","description":"True when deliveryFee is a published LOWER BOUND, not the charge (a marketplace sets the real figure at checkout from the distance). Quote it as 'from ₪X'; deliveredTotal is a minimum too."},"serviceFee":{"type":"number","description":"Marketplace operations fee (דמי תפעול); 0 for chains."},"deliveredTotal":{"type":"number","nullable":true,"description":"THE HEADLINE FIGURE: itemsSubtotal + deliveryFee + serviceFee. null when the fee is unknown."},"deliveredComparableTotal":{"type":"number","description":"What storefronts are ranked on: same basket everywhere, plus fees. Always present."},"deliveryTerms":{"type":"object","description":"Where this storefront's fee came from. Item prices are regulated feed data; delivery terms are read by a human off a retailer's terms page, so they carry their own provenance and decay.","properties":{"confidence":{"type":"string","enum":["verified","reported","estimated","unknown"],"description":"verified: read from the retailer's own binding terms on verifiedAt. reported: a cited secondary source. estimated: a category default. unknown: no fee is established, or the figure is past its 90-day TTL — do not quote it."},"verifiedAt":{"type":"string","format":"date","nullable":true},"sourceUrl":{"type":"string","nullable":true},"stale":{"type":"boolean","description":"True when the figure is older than the terms TTL."}}},"meetsMinimum":{"type":"boolean","description":"False means the order CANNOT be placed as it stands. Never present it as available."},"minimumOrder":{"type":"number","nullable":true},"amountToMinimum":{"type":"number","nullable":true,"description":"Shekels of goods still needed."},"minimumKnown":{"type":"boolean"},"requiresMembership":{"type":"string","nullable":true,"description":"Set when this rate needs a card or subscription, e.g. 'credit_card', 'wolt_plus'."},"coverage":{"type":"object","properties":{"serves":{"type":"boolean"},"matchedScope":{"type":"string","nullable":true,"enum":["national","city","radius","polygon",null]},"confidence":{"type":"string","nullable":true,"enum":["verified","reported","estimated",null]},"reason":{"type":"string","nullable":true,"enum":["outside_service_area","address_too_vague","coverage_unknown",null],"description":"Why it does not serve the address. coverage_unknown means WE have no service area recorded, which is not the same as the retailer refusing."}}},"freeDeliveryThreshold":{"type":"number","nullable":true},"nextFeeBreak":{"type":"object","nullable":true,"description":"A cheaper fee tier reachable by spending more. Not only free delivery: Shufersal's pickup fee drops ₪15 → ₪10 above ₪750 without reaching zero, and that is the same advice.","properties":{"atSubtotal":{"type":"number"},"fee":{"type":"number"},"gap":{"type":"number","description":"Shekels of extra goods needed."},"saving":{"type":"number","description":"Shekels saved on the fee."},"worthTopUp":{"type":"boolean","description":"True when the gap costs less than the saving, i.e. spending more leaves them better off."}}},"pricedLines":{"type":"integer"},"resolvableLines":{"type":"integer"},"requestedLines":{"type":"integer"},"coverageRatio":{"type":"number"},"imputedTotal":{"type":"number"},"imputedLines":{"type":"integer"},"clubOnlyLines":{"type":"integer"},"couponOnlyLines":{"type":"integer"},"priceFeedAsOf":{"type":"string","nullable":true,"format":"date-time","description":"When this storefront's retailer last published price data. A store-level fact: chains stamp per-line timestamps two incompatible ways, so only the newest one says when the retailer last spoke."},"priceFeedStale":{"type":"boolean","description":"True when this storefront has published no new prices for more than 7 days. It is still ranked and still worth comparing; treat its figures as what the shop published on priceFeedAsOf rather than as today's price. A storefront silent for more than 14 days is treated as abandoned instead: it is excluded from plans and listed in unavailableStores with reason price_feed_stale. The exception is when no storefront inside that window can take the order, since answering 'nobody can sell to you' would be worse than answering late; then the abandoned ones are returned too and notes says so."}}},"DeliveryTermsProvenance":{"type":"object","description":"Where this storefront's fee came from. Item prices are regulated feed data; delivery terms are read by a human off a retailer's terms page, so they carry their own provenance and decay.","properties":{"confidence":{"type":"string","enum":["verified","reported","estimated","unknown"],"description":"verified: read from the retailer's own binding terms on verifiedAt. reported: a cited secondary source. estimated: a category default. unknown: no fee is established, or the figure is past its 90-day TTL — do not quote it."},"verifiedAt":{"type":"string","format":"date","nullable":true},"sourceUrl":{"type":"string","nullable":true},"stale":{"type":"boolean","description":"True when the figure is older than the terms TTL."}}},"DeliveryCoverageReport":{"type":"object","properties":{"serves":{"type":"boolean"},"matchedScope":{"type":"string","nullable":true,"enum":["national","city","radius","polygon",null]},"confidence":{"type":"string","nullable":true,"enum":["verified","reported","estimated",null]},"reason":{"type":"string","nullable":true,"enum":["outside_service_area","address_too_vague","coverage_unknown",null],"description":"Why it does not serve the address. coverage_unknown means WE have no service area recorded, which is not the same as the retailer refusing."}}},"SplitOrderPlan":{"type":"object","nullable":true,"properties":{"reason":{"type":"string","enum":["cheaper","more_of_the_list"],"description":"cheaper: one order could buy this list and two buy it for materially less, after BOTH delivery fees. more_of_the_list: no single storefront stocks everything and the second order fills the gap, which costs MORE — `saving` is negative and says by how much."},"storeCount":{"type":"integer"},"legs":{"type":"array","description":"Dearest leg first: the one a shopper will treat as the main order.","items":{"type":"object","properties":{"serviceSlug":{"type":"string"},"brand":{"type":"string"},"chainName":{"type":"string"},"itemIndexes":{"type":"array","items":{"type":"integer"},"description":"Which of the requested items this leg buys."},"itemsSubtotal":{"type":"number"},"deliveryFee":{"type":"number","description":"Always a published fee, never the ranking assumption a single plan may fall back on. A split recommends paying a SECOND fee, so it may not rest on a figure we would refuse to quote for one order."},"serviceFee":{"type":"number"},"legTotal":{"type":"number"},"minimumOrder":{"type":"number","nullable":true},"handoffUrl":{"type":"string","description":"This leg's items as one page. Two orders need two pages, so there is one per leg and none for the split as a whole."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/BasketLine"},"description":"Present at response_detail=standard and above. At summary the leg reports its itemIndexes and the storefront's own plan keeps its full lines, so nothing is lost and nothing is sent twice."}}}},"deliveredTotal":{"type":"number","description":"Every leg's goods plus every leg's fees. Observed prices only, nothing imputed."},"pricedLines":{"type":"integer"},"requestedLines":{"type":"integer"},"clubOnlyLines":{"type":"integer"},"couponOnlyLines":{"type":"integer"},"comparedWith":{"type":"object","description":"The single order this was measured against, in real money.","properties":{"serviceSlug":{"type":"string"},"brand":{"type":"string"},"deliveredTotal":{"type":"number"},"pricedLines":{"type":"integer"}}},"saving":{"type":"number","description":"comparedWith.deliveredTotal minus this plan's. Negative when reason is more_of_the_list, where the second order buys more of the list at a higher price."}}},"InsightsProduct":{"type":"object","properties":{"gtin":{"type":"string"},"name":{"type":"string"},"brand":{"type":"string","nullable":true},"slug":{"type":"string"},"size":{"type":"object","nullable":true,"properties":{"qty":{"type":"number"},"unit":{"type":"string","enum":["g","ml","unit"]}}},"category":{"type":"object","nullable":true,"description":"Class keys, never display text. Labels are the client's job.","properties":{"l1":{"type":"string"},"l2":{"type":"string"},"slug":{"type":"string"}}},"prices":{"type":"array","items":{"type":"object","properties":{"chainId":{"type":"string"},"chainSlug":{"type":"string"},"chainName":{"type":"string"},"price":{"type":"number"},"unitPrice":{"type":"number","nullable":true,"description":"Per 100g or 100ml, computed from price and pack size. The feed's own unit_price column is not used: several chains file a zero or a placeholder there."},"listingName":{"type":"string","description":"The chain's own name for what was priced."},"url":{"type":"string","nullable":true,"description":"Storefront search link, when one exists."},"freshness":{"type":"object","description":"When the price list behind a figure was published, and whether that is now stale.","properties":{"asOf":{"type":"string","format":"date","description":"Israel local date."},"stale":{"type":"boolean","description":"Older than the 7-day staleness limit."}}}}}},"spread":{"type":"object","description":"Range across the chains that sell the item. One price per chain, never per store.","properties":{"min":{"type":"number"},"max":{"type":"number"},"median":{"type":"number"},"ratio":{"type":"number","description":"max / min."},"saving":{"type":"number","description":"Shekels between cheapest and dearest."},"chainCount":{"type":"integer"}}},"promotions":{"type":"array","items":{"type":"object","properties":{"chainId":{"type":"string"},"chainSlug":{"type":"string"},"chainName":{"type":"string"},"description":{"type":"string"},"endsAt":{"type":"string","format":"date"},"clubOnly":{"type":"boolean","description":"Members only. Never present it as a shelf price."}}}},"alternatives":{"type":"array","description":"Same class, comparable pack size, cheaper per 100g or 100ml.","items":{"type":"object","properties":{"gtin":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"cheapest":{"type":"number"},"unitPrice":{"type":"number","nullable":true},"chainCount":{"type":"integer"}}}},"history":{"type":"object","description":"What the market did since 2026-08-16, the day continuous price recording began. Each storefront's price is carried forward between recorded changes, so days has no gaps and every step in it appears in moves.","properties":{"watchingSince":{"type":"string","format":"date","nullable":true},"days":{"type":"array","description":"One entry per calendar day, oldest first.","items":{"type":"object","properties":{"date":{"type":"string","format":"date"},"cheapest":{"type":"number"},"dearest":{"type":"number"}}}},"moves":{"type":"array","description":"Recorded changes in a chain's cheapest price, newest first. Empty means the price has not moved since recording began.","items":{"type":"object","properties":{"date":{"type":"string","format":"date"},"chainId":{"type":"string"},"chainSlug":{"type":"string"},"chainName":{"type":"string"},"from":{"type":"number"},"to":{"type":"number"}}}}}}}},"InsightsProductIndexEntry":{"type":"object","properties":{"gtin":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string","description":"URL segment: Hebrew words then the barcode."},"brand":{"type":"string","nullable":true},"categorySlug":{"type":"string","nullable":true},"chainCount":{"type":"integer"},"min":{"type":"number"},"max":{"type":"number"},"ratio":{"type":"number"}}},"InsightsChainSummary":{"type":"object","properties":{"chainId":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"},"nameEn":{"type":"string"},"branchCount":{"type":"integer","description":"Physical branches in the catalogue. NOT branches we hold prices for; there are none."},"cityCount":{"type":"integer"},"pricedProducts":{"type":"integer"},"basketIndex":{"type":"integer","nullable":true,"description":"Standard-basket total as a percentage of the market median. 100 is average. Null unless the chain stocks every line of the basket, and the median is taken over those chains only, so both sides of the fraction are real prices for the same list."},"freshness":{"type":"object","description":"When the price list behind a figure was published, and whether that is now stale.","properties":{"asOf":{"type":"string","format":"date","description":"Israel local date."},"stale":{"type":"boolean","description":"Older than the 7-day staleness limit."}},"nullable":true}}},"InsightsBasket":{"type":"object","properties":{"lineCount":{"type":"integer"},"unitCount":{"type":"integer","description":"Line count weighted by quantity."},"lines":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"qty":{"type":"integer"},"cheapest":{"type":"number","nullable":true},"dearest":{"type":"number","nullable":true}}}},"chains":{"type":"array","items":{"type":"object","properties":{"chainId":{"type":"string"},"chainSlug":{"type":"string"},"chainName":{"type":"string"},"total":{"type":"number","description":"Sum of the lines this chain prices. Not comparable on its own."},"covered":{"type":"integer"},"comparableTotal":{"type":"number","description":"total plus the market median for each line this chain does not sell. The only figure two chains may be ranked on: a chain stocking less would otherwise finish first."},"freshness":{"type":"object","description":"When the price list behind a figure was published, and whether that is now stale.","properties":{"asOf":{"type":"string","format":"date","description":"Israel local date."},"stale":{"type":"boolean","description":"Older than the 7-day staleness limit."}}},"lines":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"qty":{"type":"integer"},"unitPrice":{"type":"number","nullable":true},"name":{"type":"string","nullable":true,"description":"What the chain actually sells at that price."}}}}}}},"spread":{"type":"object","description":"Range across the chains that sell the item. One price per chain, never per store.","properties":{"min":{"type":"number"},"max":{"type":"number"},"median":{"type":"number"},"ratio":{"type":"number","description":"max / min."},"saving":{"type":"number","description":"Shekels between cheapest and dearest."},"chainCount":{"type":"integer"}},"nullable":true}}},"InsightsSpread":{"type":"object","description":"Range across the chains that sell the item. One price per chain, never per store.","properties":{"min":{"type":"number"},"max":{"type":"number"},"median":{"type":"number"},"ratio":{"type":"number","description":"max / min."},"saving":{"type":"number","description":"Shekels between cheapest and dearest."},"chainCount":{"type":"integer"}}},"InsightsFreshness":{"type":"object","description":"When the price list behind a figure was published, and whether that is now stale.","properties":{"asOf":{"type":"string","format":"date","description":"Israel local date."},"stale":{"type":"boolean","description":"Older than the 7-day staleness limit."}}}}},"paths":{"/health":{"get":{"summary":"Health check","security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"time":{"type":"string"}}}}}}}}},"/ready":{"get":{"summary":"Readiness check","description":"Dependency-aware readiness probe (store coordinate coverage and current local prices). Distinct from /health, which remains a dependency-free liveness check.","security":[],"responses":{"200":{"description":"Ready — stores and current prices are available","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ready","degraded"]},"checkedAt":{"type":"string","format":"date-time"},"storeCoordinates":{"type":"object","properties":{"total":{"type":"integer"},"valid":{"type":"integer"},"coverage":{"type":"number","description":"valid / total (0 when total is 0)."}}},"localPrices":{"type":"object","properties":{"currentRows":{"type":"integer","nullable":true},"storesWithCurrentPrices":{"type":"integer","nullable":true},"newestSourceTs":{"type":"string","format":"date-time","nullable":true},"freshnessHours":{"type":"integer"}}}}}}}},"503":{"description":"Degraded — missing stores or current price rows","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ready","degraded"]},"checkedAt":{"type":"string","format":"date-time"},"storeCoordinates":{"type":"object","properties":{"total":{"type":"integer"},"valid":{"type":"integer"},"coverage":{"type":"number","description":"valid / total (0 when total is 0)."}}},"localPrices":{"type":"object","properties":{"currentRows":{"type":"integer","nullable":true},"storesWithCurrentPrices":{"type":"integer","nullable":true},"newestSourceTs":{"type":"string","format":"date-time","nullable":true},"freshnessHours":{"type":"integer"}}}}}}}}}}},"/openapi.json":{"get":{"summary":"OpenAPI document","security":[],"responses":{"200":{"description":"OK"}}}},"/.well-known/agent-card.json":{"get":{"summary":"A2A agent card","description":"Agent2Agent discovery document describing this server's identity, skills and MCP endpoint. The same card is served at /.well-known/agent.json (the pre-0.3 name) and at /v1/agent-card.json and /v1/agent.json, where callers were already asking. The skills are the MCP tools; the interface it advertises is MCP Streamable HTTP, not one of A2A's own protocol bindings.","security":[],"responses":{"200":{"description":"OK"}}}},"/mcp":{"post":{"summary":"MCP Streamable HTTP endpoint — online supermarket delivery (JSON-RPC 2.0)","description":"SuperMCP remote server for groceries delivered to an address, exposing optimize_delivery, list_delivery_options, get_delivery_terms, search_products, get_product, suggest_similar_products, get_promotions. Ranks storefronts on what the ORDER costs (items + delivery fee + service fee), not what the goods cost, because a ₪35.90 delivery fee outweighs most price differences between chains. Online prices are not shelf prices: each storefront's own regulated feed rows are used. Delivery fees carry a confidence and a verifiedAt — a fee marked unknown must not be quoted. This is the only MCP surface. No credential is required, and sending one is what produces a 401 here.","responses":{"200":{"description":"JSON-RPC response or SSE stream"}}}},"/v1/products":{"get":{"summary":"Search products (Hebrew/English), priced when a location is given","description":"Free-text catalogue search that also answers 'how much is X' for a single item. With city, near or location every hit carries fromPrice and normalizedUnitPrice and the list comes back cheapest per unit first; without one those fields are absent and the order is by relevance. For a whole shopping list use POST /v1/delivery/optimize instead: these prices sit at different storefronts and each carries its own delivery fee, so they must not be summed.","parameters":[{"name":"q","in":"query","schema":{"type":"string"}},{"name":"category","in":"query","schema":{"type":"string"}},{"name":"brand","in":"query","schema":{"type":"string"}},{"name":"gtin","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","default":20,"maximum":100}},{"name":"city","in":"query","schema":{"type":"string"},"description":"Prefer products priced in this city"},{"name":"near","in":"query","schema":{"type":"string"},"description":"lat,lng — prefer products priced nearby. Do not combine with location."},{"name":"location","in":"query","schema":{"type":"string","minLength":3,"maxLength":300},"description":"Free-text neighborhood/address; resolved to coordinates. Do not combine with near."},{"name":"radius_km","in":"query","schema":{"type":"number","default":10}},{"name":"store_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"in_stock_only","in":"query","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"gtin":{"type":"string","nullable":true},"name":{"type":"string"},"brand":{"type":"string","nullable":true},"categoryL1":{"type":"string","nullable":true},"categoryL2":{"type":"string","nullable":true},"sizeQty":{"type":"number","nullable":true},"sizeUnit":{"type":"string","nullable":true},"pieceCount":{"type":"number","nullable":true},"fromPrice":{"type":"number","nullable":true,"description":"Lowest price at any storefront delivering to the requested location — a floor across shops, not a national price, so quote it as 'from ₪X'. The ordinary price, never a club or coupon rate. Null when no delivering storefront prices this product."},"pricedAtStorefronts":{"type":"integer","description":"How many delivering storefronts price it at all. 1 means fromPrice is a single shop's price rather than a market rate."},"pricedAtChains":{"type":"integer","description":"How many distinct chains, the better guide to how easy the product is to buy."},"normalizedUnitPrice":{"type":"number","nullable":true,"description":"fromPrice per 100g / 100ml / piece, and THE field to compare on: a 100g pack can undercut a 125g one on pack price while costing more per gram. Results are sorted by it. Null when the package size could not be parsed."},"normalizedUnitBasis":{"type":"string","nullable":true,"enum":["per_100g","per_100ml","per_piece"]}}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/products/similar":{"get":{"summary":"Suggest similar products a shopper might have meant","description":"After a delivery basket priced the wrong SKU, search catalogue neighbours. Not cheaper nearby shelf substitutes — those are GET /v1/products/{id}/substitutes.","parameters":[{"name":"q","in":"query","schema":{"type":"string"},"description":"Shopper words in Hebrew (original line or correction)."},{"name":"product_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Rejected product to exclude."},{"name":"limit","in":"query","schema":{"type":"integer","default":8,"maximum":50}},{"name":"city","in":"query","schema":{"type":"string"}},{"name":"near","in":"query","schema":{"type":"string"},"description":"lat,lng. Do not combine with location."},{"name":"location","in":"query","schema":{"type":"string","minLength":3,"maxLength":300},"description":"Free-text neighborhood/address. Do not combine with near."},{"name":"radius_km","in":"query","schema":{"type":"number","default":10}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"query":{"type":"string"},"rejected":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"gtin":{"type":"string","nullable":true},"name":{"type":"string"},"brand":{"type":"string","nullable":true},"categoryL1":{"type":"string","nullable":true},"categoryL2":{"type":"string","nullable":true},"sizeQty":{"type":"number","nullable":true},"sizeUnit":{"type":"string","nullable":true},"pieceCount":{"type":"number","nullable":true},"fromPrice":{"type":"number","nullable":true,"description":"Lowest price at any storefront delivering to the requested location — a floor across shops, not a national price, so quote it as 'from ₪X'. The ordinary price, never a club or coupon rate. Null when no delivering storefront prices this product."},"pricedAtStorefronts":{"type":"integer","description":"How many delivering storefronts price it at all. 1 means fromPrice is a single shop's price rather than a market rate."},"pricedAtChains":{"type":"integer","description":"How many distinct chains, the better guide to how easy the product is to buy."},"normalizedUnitPrice":{"type":"number","nullable":true,"description":"fromPrice per 100g / 100ml / piece, and THE field to compare on: a 100g pack can undercut a 125g one on pack price while costing more per gram. Results are sorted by it. Null when the package size could not be parsed."},"normalizedUnitBasis":{"type":"string","nullable":true,"enum":["per_100g","per_100ml","per_piece"]}},"nullable":true},"products":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"gtin":{"type":"string","nullable":true},"name":{"type":"string"},"brand":{"type":"string","nullable":true},"categoryL1":{"type":"string","nullable":true},"categoryL2":{"type":"string","nullable":true},"sizeQty":{"type":"number","nullable":true},"sizeUnit":{"type":"string","nullable":true},"pieceCount":{"type":"number","nullable":true},"fromPrice":{"type":"number","nullable":true,"description":"Lowest price at any storefront delivering to the requested location — a floor across shops, not a national price, so quote it as 'from ₪X'. The ordinary price, never a club or coupon rate. Null when no delivering storefront prices this product."},"pricedAtStorefronts":{"type":"integer","description":"How many delivering storefronts price it at all. 1 means fromPrice is a single shop's price rather than a market rate."},"pricedAtChains":{"type":"integer","description":"How many distinct chains, the better guide to how easy the product is to buy."},"normalizedUnitPrice":{"type":"number","nullable":true,"description":"fromPrice per 100g / 100ml / piece, and THE field to compare on: a 100g pack can undercut a 125g one on pack price while costing more per gram. Results are sorted by it. Null when the package size could not be parsed."},"normalizedUnitBasis":{"type":"string","nullable":true,"enum":["per_100g","per_100ml","per_piece"]}}}}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/products/{id}":{"get":{"summary":"Get canonical product + per-chain listings","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"allOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"gtin":{"type":"string","nullable":true},"name":{"type":"string"},"brand":{"type":"string","nullable":true},"categoryL1":{"type":"string","nullable":true},"categoryL2":{"type":"string","nullable":true},"sizeQty":{"type":"number","nullable":true},"sizeUnit":{"type":"string","nullable":true},"pieceCount":{"type":"number","nullable":true}}},{"type":"object","properties":{"listings":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"chainId":{"type":"string"},"chainName":{"type":"string"},"itemCode":{"type":"string"},"name":{"type":"string"},"brand":{"type":"string","nullable":true},"qty":{"type":"number","nullable":true},"unit":{"type":"string","nullable":true},"canonicalQty":{"type":"number","nullable":true},"canonicalUnit":{"type":"string","nullable":true},"measureUnparseable":{"type":"boolean"},"orderable":{"type":"boolean","description":"True when some delivery or pickup storefront prices this listing today. False means the chain files a catalogue entry but nothing can be bought from it."}}}}}}]}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/products/{id}/prices":{"get":{"summary":"Compare prices nearby (default 10km), promos applied","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"city","in":"query","schema":{"type":"string"}},{"name":"near","in":"query","schema":{"type":"string"},"description":"'lat,lng'. Do not combine with location."},{"name":"location","in":"query","schema":{"type":"string","minLength":3,"maxLength":300},"description":"Free-text neighborhood/address. Do not combine with near."},{"name":"radius_km","in":"query","schema":{"type":"number","default":10}},{"name":"sort","in":"query","schema":{"type":"string","enum":["price","unit_price"],"default":"price"},"description":"price = pack total; unit_price = cheaper per 100g/100ml/unit"},{"name":"include_club","in":"query","schema":{"type":"boolean","default":true}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"storeId":{"type":"string","format":"uuid"},"storeName":{"type":"string"},"chainId":{"type":"string"},"chainName":{"type":"string"},"city":{"type":"string","nullable":true},"address":{"type":"string","nullable":true},"lat":{"type":"number","nullable":true},"lng":{"type":"number","nullable":true},"distanceKm":{"type":"number","nullable":true},"listingId":{"type":"string","format":"uuid"},"itemCode":{"type":"string"},"listPrice":{"type":"number"},"unitPrice":{"type":"number","nullable":true,"description":"₪ per 100g, 100ml, or per unit, computed from the SHELF price. Ignores promotions; use effectiveUnitPrice to compare what is actually paid."},"effectiveUnitPrice":{"type":"number","nullable":true,"description":"unitPrice with any applicable active promo applied. This is what `sort=unit_price` orders by."},"unitBasis":{"type":"string","enum":["per_100g","per_100ml","per_unit","unknown"]},"currency":{"type":"string"},"effectivePrice":{"type":"number","description":"listPrice with any applicable active promo applied."},"promoApplied":{"type":"boolean"},"promoDescription":{"type":"string","nullable":true},"clubOnly":{"type":"boolean","description":"The quoted price needs the chain loyalty card."},"couponOnly":{"type":"boolean","description":"The quoted price needs a clipped coupon."},"link":{"type":"string","nullable":true,"description":"Clickable storefront URL to open this product on the chain's site (search-by-barcode, or by name for chains that don't index barcodes). Null when the chain has no online store."},"freshness":{"type":"object","properties":{"sourceTs":{"type":"string","format":"date-time","description":"When the chain published this price."},"ingestedAt":{"type":"string","format":"date-time","description":"When this service last ingested it."}}}}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/products/{id}/substitutes":{"get":{"summary":"Suggest cheaper similar products (by unit price)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"city","in":"query","schema":{"type":"string"}},{"name":"near","in":"query","schema":{"type":"string"},"description":"'lat,lng'. Do not combine with location."},{"name":"location","in":"query","schema":{"type":"string","minLength":3,"maxLength":300},"description":"Free-text neighborhood/address. Do not combine with near."},{"name":"radius_km","in":"query","schema":{"type":"number","default":10}},{"name":"limit","in":"query","schema":{"type":"integer","default":10,"maximum":50}},{"name":"cheaper_only","in":"query","schema":{"type":"boolean","default":true}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"product":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"gtin":{"type":"string","nullable":true},"name":{"type":"string"},"brand":{"type":"string","nullable":true},"categoryL1":{"type":"string","nullable":true},"categoryL2":{"type":"string","nullable":true},"sizeQty":{"type":"number","nullable":true},"sizeUnit":{"type":"string","nullable":true},"pieceCount":{"type":"number","nullable":true}}},"baseline":{"type":"object","properties":{"bestUnitPrice":{"type":"number","nullable":true},"unitBasis":{"type":"string","enum":["per_100g","per_100ml","per_unit","unknown"]},"currency":{"type":"string"},"storeId":{"type":"string","format":"uuid","nullable":true},"storeName":{"type":"string","nullable":true}}},"substitutes":{"type":"array","items":{"type":"object","properties":{"product":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"gtin":{"type":"string","nullable":true},"name":{"type":"string"},"brand":{"type":"string","nullable":true},"categoryL1":{"type":"string","nullable":true},"categoryL2":{"type":"string","nullable":true},"sizeQty":{"type":"number","nullable":true},"sizeUnit":{"type":"string","nullable":true},"pieceCount":{"type":"number","nullable":true}}},"bestUnitPrice":{"type":"number"},"unitBasis":{"type":"string","enum":["per_100g","per_100ml","per_unit","unknown"]},"currency":{"type":"string"},"storeId":{"type":"string","format":"uuid"},"storeName":{"type":"string"},"chainId":{"type":"string"},"chainName":{"type":"string"},"distanceKm":{"type":"number","nullable":true},"unitPriceSaving":{"type":"number","nullable":true},"matchReason":{"type":"string","enum":["same_category","similar_name","same_category_and_name"]}}}}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/products/{id}/history":{"get":{"summary":"Price history for a product","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"store_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"from","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"storeId":{"type":"string","format":"uuid"},"storeName":{"type":"string"},"chainId":{"type":"string"},"price":{"type":"number"},"unitPrice":{"type":"number","nullable":true},"currency":{"type":"string"},"sourceTs":{"type":"string","format":"date-time"}}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/chains":{"get":{"summary":"List chains","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sourceId":{"type":"string"},"market":{"type":"string"},"nameHe":{"type":"string"},"nameEn":{"type":"string","nullable":true},"currency":{"type":"string"}}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/stores":{"get":{"summary":"List stores","parameters":[{"name":"chain","in":"query","schema":{"type":"string"}},{"name":"city","in":"query","schema":{"type":"string"}},{"name":"near","in":"query","schema":{"type":"string"},"description":"'lat,lng'. Do not combine with location."},{"name":"location","in":"query","schema":{"type":"string","minLength":3,"maxLength":300},"description":"Free-text neighborhood/address, e.g. 'נווה עמל, הרצליה'. Do not combine with near."},{"name":"radius_km","in":"query","schema":{"type":"number","default":10}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"stores":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"chainId":{"type":"string"},"chainName":{"type":"string"},"storeCode":{"type":"string"},"name":{"type":"string"},"address":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"zip":{"type":"string","nullable":true},"lat":{"type":"number","nullable":true},"lng":{"type":"number","nullable":true},"geoSource":{"type":"string","nullable":true,"description":"Provenance of lat/lng: address, feed, city_centroid, or null."},"storeKind":{"type":"string","nullable":true,"enum":["branch","online","pickup","warehouse"],"description":"Fulfilment kind. Only 'branch' is somewhere a shopper can walk in; online/pickup/warehouse rows carry prices but are excluded from basket store recommendations."},"distanceKm":{"type":"number","nullable":true}}}},"location":{"type":"object","properties":{"scope":{"type":"string","enum":["unscoped","city","near","city_near"]},"precision":{"type":"string","enum":["none","city","radius"]},"fallbackApplied":{"type":"boolean"},"warning":{"type":"string","nullable":true},"distanceReliable":{"type":"boolean","description":"False only when distance cannot order the candidates at all — every matching store collapses onto one shared coordinate. A city-level origin no longer clears this: distances stay usable but coarse (see distanceApproximate)."},"distanceApproximate":{"type":"boolean","description":"True when distances are usable but coarse — a city-level origin, or stores placed by city centroid rather than branch address. Present them as approximate."},"requested":{"type":"object","properties":{"city":{"type":"string","nullable":true},"near":{"type":"object","nullable":true,"properties":{"lat":{"type":"number"},"lng":{"type":"number"}}},"radiusKm":{"type":"number","nullable":true}}},"origin":{"type":"object","nullable":true,"description":"Provenance of the resolved user origin point (never includes raw location text).","properties":{"precision":{"type":"string","enum":["address","street","neighborhood","city","coordinates"]},"provider":{"type":"string","enum":["nominatim","city_centroid","coordinates"]},"cached":{"type":"boolean"},"fallbackApplied":{"type":"boolean"},"displayName":{"type":"string","nullable":true},"attribution":{"type":"string","nullable":true}}}}}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/promotions":{"get":{"summary":"List promotions","parameters":[{"name":"store_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"product_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"active","in":"query","schema":{"type":"boolean","default":true},"description":"Only currently-running promotions. Pass false for expired or not-yet-started ones."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"chainId":{"type":"string"},"chainName":{"type":"string"},"storeId":{"type":"string","format":"uuid","nullable":true},"storeCode":{"type":"string","nullable":true},"promoCode":{"type":"string"},"description":{"type":"string"},"mechanicType":{"type":"string","enum":["simple_discount","n_for_price","second_unit_pct","club_price","spend_threshold","other"]},"mechanicParams":{"type":"object"},"clubOnly":{"type":"boolean"},"startTs":{"type":"string","format":"date-time"},"endTs":{"type":"string","format":"date-time"},"sourceTs":{"type":"string","format":"date-time"},"ingestedAt":{"type":"string","format":"date-time"},"itemCodes":{"type":"array","items":{"type":"string"}}}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/delivery/optimize":{"post":{"operationId":"optimizeDelivery","summary":"Price a shopping list for delivery across online storefronts","description":"Prices a whole basket at every Israeli online supermarket that delivers to an address and ranks them on what the ORDER costs, not what the goods cost. Online prices are not shelf prices: a chain's website runs its own price book (measured: Rami Levy's online store shares 22% of its prices with its own branches; Carrefour's runs ~8% below its shelves), so every figure comes from the storefront's own regulated feed rows. Storefronts that do not serve the address, sit below the minimum order, or have published no fresh prices for over a week, are returned in unavailableStores with a reason rather than dropped.","tags":["delivery"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"description":"Either an initial request (items + a destination) or a resume ({continuation, answers} only).","properties":{"items":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"object","additionalProperties":false,"properties":{"product_id":{"type":"string","format":"uuid"},"gtin":{"type":"string","minLength":1},"query":{"type":"string","minLength":1},"pack_qty":{"type":"number","exclusiveMinimum":0,"description":"Number of PACKS to buy, whatever each pack holds. Send it only for a count the shopper made in packs ('two six-packs'). For a count of individual items send amount with unit=יח instead: a six-pack is one pack and six bottles, so pack_qty=24 there buys 144 bottles. Mutually exclusive with amount."},"amount":{"type":"number","exclusiveMinimum":0,"description":"Physical amount (requires unit), e.g. 1.5 with unit=kg. Also the right field for a count of individual items: 24 with unit=יח means 24 bottles however they are packaged. Mutually exclusive with pack_qty."},"unit":{"type":"string","minLength":1,"description":"Required with amount: kg, g, L, ml, unit, יח, etc. Do not pair mass/volume units with pack_qty."}},"description":"Exactly one of product_id, gtin, or query, and at most one of pack_qty or amount+unit (a line with neither is priced as one pack and reported in assumptions). pack_qty sent with a count unit (unit/units/יח) is read as amount+unit, since that pairing states a number of items rather than of packs."}},"address":{"type":"string","minLength":3,"maxLength":300,"description":"Delivery address in Israel, free text, e.g. 'מנדלסון 1, תל אביב'. Preferred: a storefront whose service area is a polygon or a depot radius can only be tested against a located point."},"city":{"type":"string","description":"Hebrew or English city (or CBS locality code). Enough for a chain that publishes a settlement list; not enough for a polygon or radius service area."},"near":{"type":"string","description":"'lat,lng', e.g. '32.078,34.774'. Do not combine with address."},"preference":{"type":"string","enum":["cheapest","balanced"],"description":"cheapest takes the lowest delivered figure outright; balanced (default) prefers a storefront whose terms we verified when the money is close."},"slot_type":{"type":"string","enum":["standard","pickup"],"description":"standard (default) is delivery to the door; pickup is click-and-collect, cheaper where offered but the shopper travels. These are the only two slots any storefront publishes."},"memberships":{"type":"array","items":{"type":"string"},"description":"Cards or subscriptions the shopper holds that unlock a cheaper rate."},"max_split_stores":{"type":"integer","minimum":2,"maximum":3,"default":2,"description":"How many storefronts the returned splitOrder may spread the list over. Default 2. A third adds a third delivery fee, so it only wins when it reaches items the other two do not stock."},"include_club":{"type":"boolean","default":true},"include_coupon":{"type":"boolean","default":true},"resolution_mode":{"type":"string","enum":["fast","strict","preview"]},"response_detail":{"type":"string","enum":["summary","standard","debug"],"default":"summary","description":"How much per-line detail to return. summary gives every storefront's totals, fees and coverage but the lines[] breakdown only for the storefronts the recommendations name; standard returns lines for every storefront; debug adds resolution internals."},"intent":{"type":"string","maxLength":120,"description":"The shopper's own framing of this shop, in their own words, e.g. 'ברביקיו ל-12 אנשים'. Not a summary of the item list. Shown on the handoff page beside what was priced, so a shopper opening the link sees why they asked. Optional; omit rather than inventing one. Longer text is truncated rather than rejected."},"continuation":{"type":"string"},"answers":{"type":"array","items":{"type":"object","additionalProperties":false,"properties":{"item_index":{"type":"integer","minimum":0},"product_id":{"type":"string","format":"uuid"}},"required":["item_index","product_id"]}}}}}}},"responses":{"200":{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":"string","enum":["complete","needs_confirmation"]},"currency":{"type":"string"},"address":{"type":"object"},"preference":{"type":"string"},"slotType":{"type":"string"},"cheapestDelivered":{"$ref":"#/components/schemas/DeliveryPlanSummary"},"bestVerifiedTerms":{"$ref":"#/components/schemas/DeliveryPlanSummary"},"bestSingleOrder":{"$ref":"#/components/schemas/DeliveryPlanSummary"},"splitOrder":{"$ref":"#/components/schemas/SplitOrderPlan"},"plans":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryPlan"}},"unavailableStores":{"type":"array","items":{"type":"object"},"description":"Storefronts that could not serve this basket, each with a reason. At response_detail=summary only the actionable ones are listed (below_minimum_order, price_feed_stale); the rest were ruled out on the address alone and are counted in unavailableStoresOmitted."},"unavailableStoresOmitted":{"type":"integer","description":"How many storefronts were left out of unavailableStores at summary detail. Absent at standard and debug, which return the whole list."},"items":{"type":"array","items":{"type":"object"}},"assumptions":{"type":"array","items":{"type":"object"},"description":"Every product choice made for the shopper, each with a kind. kind=generic_default means the line named no particular product and came back as the everyday version of it, which is the intended answer and needs no warning; kind=substitution means the line named a product or brand and the selection is not it; kind=unresolved means nothing was selected. Surface the substitutions."},"storefrontsCompared":{"type":"integer"},"notes":{"type":"array","items":{"type":"string"}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/delivery/options":{"get":{"operationId":"listDeliveryOptions","summary":"Which online storefronts deliver to an address","description":"The online counterpart to /v1/stores. Answers 'who will come to me, on what terms, and how sure are we' rather than 'what is near me'. Set include_unavailable=true to see storefronts that do NOT serve the address, each with a reason.","tags":["delivery"],"parameters":[{"name":"address","in":"query","schema":{"type":"string","minLength":3,"maxLength":300,"description":"Delivery address in Israel, free text, e.g. 'מנדלסון 1, תל אביב'. Preferred: a storefront whose service area is a polygon or a depot radius can only be tested against a located point."},"description":"Delivery address in Israel, free text, e.g. 'מנדלסון 1, תל אביב'. Preferred: a storefront whose service area is a polygon or a depot radius can only be tested against a located point."},{"name":"city","in":"query","schema":{"type":"string","description":"Hebrew or English city (or CBS locality code). Enough for a chain that publishes a settlement list; not enough for a polygon or radius service area."},"description":"Hebrew or English city (or CBS locality code). Enough for a chain that publishes a settlement list; not enough for a polygon or radius service area."},{"name":"near","in":"query","schema":{"type":"string","description":"'lat,lng', e.g. '32.078,34.774'. Do not combine with address."},"description":"'lat,lng', e.g. '32.078,34.774'. Do not combine with address."},{"name":"chain","in":"query","schema":{"type":"string"},"description":"Filter to one chain, by its name as it appears in chainName (שופרסל) or by its legal barcode id. A value matching neither returns no options, which is not the same answer as nobody delivering here."},{"name":"include_unavailable","in":"query","schema":{"type":"boolean"}}],"responses":{"200":{"type":"object","properties":{"data":{"type":"object","properties":{"options":{"type":"array","description":"Each option carries catalogSize (priced items we hold for that storefront) and catalogVisibility. partial_index means WE cannot see the whole shop, because those prices are read off a site that cannot be paged; it is not a claim that the retailer is small. full_catalogue means the count is the retailer's complete published price file for that storefront.","items":{"type":"object"}},"destinationKnown":{"type":"boolean"},"sharedNotes":{"type":"object","additionalProperties":{"type":"string"},"description":"Notes carried by more than one storefront, sent once. Those options report notesRef instead of notes; look the text up here by that key. Absent when every note is unique to its storefront."}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/delivery/services/{slug}":{"get":{"operationId":"getDeliveryTerms","summary":"One storefront's full published delivery terms","description":"Every fee band over basket size, the minimum order, the service area, and where each figure came from. Use to explain a plan's deliveryFee, or to answer 'what do I need to spend for free delivery?'.","tags":["delivery"],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"description":"Storefront slug, e.g. 'shufersal-online' — from a plan's serviceSlug."}],"responses":{"200":{"type":"object","properties":{"data":{"type":"object"}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/handoffs/{id}":{"get":{"operationId":"getBasketHandoff","summary":"The sealed basket behind a handoff link","description":"Public and unauthenticated: the caller is a shopper's browser following a link out of a chat window, with no API key and no reason to get one. The response is the basket encrypted with AES-256-GCM, and this service cannot decrypt it: the key lives only in the link's fragment, which never reaches a server. Reading a row without that fragment gains nothing.","tags":["delivery"],"security":[],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{16}$"},"description":"16 base64url characters, derived from the link's secret rather than chosen. Anything else is rejected on shape."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"iv":{"type":"string","description":"base64 of the 12-byte AES-GCM nonce."},"ciphertext":{"type":"string","description":"base64 of the ciphertext with its 16-byte auth tag appended."}}}}}}}},"400":{"description":"Malformed id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Unknown or expired, deliberately indistinguishable: saying which would confirm that an id was real, and links stop working after 30 days.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/insights/products":{"get":{"summary":"Every product with a cross-chain comparison","description":"The comparable corpus: products four or more chains sell at a current price. Order by 'spread' for the widest price gaps, or by 'name' for a stable walk (the ordering a paginated crawl needs).","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","default":60,"maximum":500}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}},{"name":"min_chains","in":"query","schema":{"type":"integer","default":4,"minimum":4,"maximum":11}},{"name":"category","in":"query","schema":{"type":"string"},"description":"Category slug, e.g. 'hummus-tahini-salads'."},{"name":"order","in":"query","schema":{"type":"string","enum":["spread","name"],"default":"spread"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"products":{"type":"array","items":{"type":"object","properties":{"gtin":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string","description":"URL segment: Hebrew words then the barcode."},"brand":{"type":"string","nullable":true},"categorySlug":{"type":"string","nullable":true},"chainCount":{"type":"integer"},"min":{"type":"number"},"max":{"type":"number"},"ratio":{"type":"number"}}}},"total":{"type":"integer","description":"Matching the same filters, for pagination."},"minChains":{"type":"integer"}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/insights/products/{slug}":{"get":{"summary":"One product priced at every chain that sells it","description":"Accepts a bare GTIN or the full 'words-gtin' page segment; only the trailing barcode is read.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"gtin":{"type":"string"},"name":{"type":"string"},"brand":{"type":"string","nullable":true},"slug":{"type":"string"},"size":{"type":"object","nullable":true,"properties":{"qty":{"type":"number"},"unit":{"type":"string","enum":["g","ml","unit"]}}},"category":{"type":"object","nullable":true,"description":"Class keys, never display text. Labels are the client's job.","properties":{"l1":{"type":"string"},"l2":{"type":"string"},"slug":{"type":"string"}}},"prices":{"type":"array","items":{"type":"object","properties":{"chainId":{"type":"string"},"chainSlug":{"type":"string"},"chainName":{"type":"string"},"price":{"type":"number"},"unitPrice":{"type":"number","nullable":true,"description":"Per 100g or 100ml, computed from price and pack size. The feed's own unit_price column is not used: several chains file a zero or a placeholder there."},"listingName":{"type":"string","description":"The chain's own name for what was priced."},"url":{"type":"string","nullable":true,"description":"Storefront search link, when one exists."},"freshness":{"type":"object","description":"When the price list behind a figure was published, and whether that is now stale.","properties":{"asOf":{"type":"string","format":"date","description":"Israel local date."},"stale":{"type":"boolean","description":"Older than the 7-day staleness limit."}}}}}},"spread":{"type":"object","description":"Range across the chains that sell the item. One price per chain, never per store.","properties":{"min":{"type":"number"},"max":{"type":"number"},"median":{"type":"number"},"ratio":{"type":"number","description":"max / min."},"saving":{"type":"number","description":"Shekels between cheapest and dearest."},"chainCount":{"type":"integer"}}},"promotions":{"type":"array","items":{"type":"object","properties":{"chainId":{"type":"string"},"chainSlug":{"type":"string"},"chainName":{"type":"string"},"description":{"type":"string"},"endsAt":{"type":"string","format":"date"},"clubOnly":{"type":"boolean","description":"Members only. Never present it as a shelf price."}}}},"alternatives":{"type":"array","description":"Same class, comparable pack size, cheaper per 100g or 100ml.","items":{"type":"object","properties":{"gtin":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"cheapest":{"type":"number"},"unitPrice":{"type":"number","nullable":true},"chainCount":{"type":"integer"}}}},"history":{"type":"object","description":"What the market did since 2026-08-16, the day continuous price recording began. Each storefront's price is carried forward between recorded changes, so days has no gaps and every step in it appears in moves.","properties":{"watchingSince":{"type":"string","format":"date","nullable":true},"days":{"type":"array","description":"One entry per calendar day, oldest first.","items":{"type":"object","properties":{"date":{"type":"string","format":"date"},"cheapest":{"type":"number"},"dearest":{"type":"number"}}}},"moves":{"type":"array","description":"Recorded changes in a chain's cheapest price, newest first. Empty means the price has not moved since recording began.","items":{"type":"object","properties":{"date":{"type":"string","format":"date"},"chainId":{"type":"string"},"chainSlug":{"type":"string"},"chainName":{"type":"string"},"from":{"type":"number"},"to":{"type":"number"}}}}}}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/insights/search":{"get":{"summary":"Type-ahead over the comparable corpus","description":"Name match, ranked by where the term falls. Only returns products that have a page.","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":60}},{"name":"limit","in":"query","schema":{"type":"integer","default":10,"maximum":30}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"products":{"type":"array","items":{"type":"object","properties":{"gtin":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string","description":"URL segment: Hebrew words then the barcode."},"brand":{"type":"string","nullable":true},"categorySlug":{"type":"string","nullable":true},"chainCount":{"type":"integer"},"min":{"type":"number"},"max":{"type":"number"},"ratio":{"type":"number"}}}}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/insights/products/prices":{"get":{"summary":"Every chain's price for a cart of picked products","description":"Prices a shopper's own cart in one request, keyed `p:<gtin>`. Each line carries an `equivalence`: `class` means every chain is quoted its own cheapest product in the same class and pack band, which is what makes the totals comparable; `exact` means only the chains publishing that barcode are priced, because for that product the taxonomy cannot name a substitute worth standing behind. Unknown barcodes are dropped rather than rejected.","parameters":[{"name":"gtins","in":"query","required":true,"description":"Comma-separated barcodes, deduped and capped at 60.","schema":{"type":"string","maxLength":1000}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"gtin":{"type":"string"},"name":{"type":"string"},"brand":{"type":"string","nullable":true},"size":{"type":"object","nullable":true,"properties":{"qty":{"type":"number"},"unit":{"type":"string"}}},"equivalence":{"type":"string","enum":["class","exact"]},"chains":{"type":"array","items":{"type":"object"}}}}}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/insights/categories":{"get":{"summary":"Categories with enough comparable products to rank","description":"Class keys, not display names. Fresh produce is absent by necessity: loose fruit and vegetables carry chain-local item codes rather than barcodes, so nothing cross-chain can be said about them.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"categories":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"l1":{"type":"string"},"l2":{"type":"string"},"productCount":{"type":"integer"},"topRatio":{"type":"number"}}}}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/insights/categories/{slug}":{"get":{"summary":"One category, widest price gap first","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"slug":{"type":"string"},"l1":{"type":"string"},"l2":{"type":"string"},"productCount":{"type":"integer"},"topRatio":{"type":"number"},"products":{"type":"array","items":{"type":"object","properties":{"gtin":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string","description":"URL segment: Hebrew words then the barcode."},"brand":{"type":"string","nullable":true},"categorySlug":{"type":"string","nullable":true},"chainCount":{"type":"integer"},"min":{"type":"number"},"max":{"type":"number"},"ratio":{"type":"number"}}}},"commoditySlugs":{"type":"array","items":{"type":"string"}}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/insights/chains":{"get":{"summary":"Every chain, ranked on the same standard basket","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"chains":{"type":"array","items":{"type":"object","properties":{"chainId":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"},"nameEn":{"type":"string"},"branchCount":{"type":"integer","description":"Physical branches in the catalogue. NOT branches we hold prices for; there are none."},"cityCount":{"type":"integer"},"pricedProducts":{"type":"integer"},"basketIndex":{"type":"integer","nullable":true,"description":"Standard-basket total as a percentage of the market median. 100 is average. Null unless the chain stocks every line of the basket, and the median is taken over those chains only, so both sides of the fraction are real prices for the same list."},"freshness":{"type":"object","description":"When the price list behind a figure was published, and whether that is now stale.","properties":{"asOf":{"type":"string","format":"date","description":"Israel local date."},"stale":{"type":"boolean","description":"Older than the 7-day staleness limit."}},"nullable":true}}}}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/insights/chains/{slug}":{"get":{"summary":"One chain: basket position, strongest categories, branches by city","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"allOf":[{"type":"object","properties":{"chainId":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"},"nameEn":{"type":"string"},"branchCount":{"type":"integer","description":"Physical branches in the catalogue. NOT branches we hold prices for; there are none."},"cityCount":{"type":"integer"},"pricedProducts":{"type":"integer"},"basketIndex":{"type":"integer","nullable":true,"description":"Standard-basket total as a percentage of the market median. 100 is average. Null unless the chain stocks every line of the basket, and the median is taken over those chains only, so both sides of the fraction are real prices for the same list."},"freshness":{"type":"object","description":"When the price list behind a figure was published, and whether that is now stale.","properties":{"asOf":{"type":"string","format":"date","description":"Israel local date."},"stale":{"type":"boolean","description":"Older than the 7-day staleness limit."}},"nullable":true}}},{"type":"object","properties":{"cities":{"type":"array","items":{"type":"object","properties":{"city":{"type":"string"},"branchCount":{"type":"integer"}}}},"basket":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"price":{"type":"number","nullable":true},"marketMedian":{"type":"number","nullable":true},"name":{"type":"string","nullable":true}}}},"strongCategories":{"type":"array","description":"Where this chain sits furthest below the market, compared product by product.","items":{"type":"object","properties":{"slug":{"type":"string"},"l2":{"type":"string"},"deltaPercent":{"type":"number","description":"Negative is cheaper than the market."}}}}}}]}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/insights/commodities":{"get":{"summary":"Every everyday commodity, priced at every chain","description":"A commodity is a shelf, not a barcode: a product class plus a pack-size band, priced at each chain as the cheapest current product inside it. `pick` names what that price actually bought.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/insights/commodities/{slug}":{"get":{"summary":"One commodity priced at every chain","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/insights/basket":{"get":{"summary":"Price a basket of commodities at every chain","description":"Without `items` this prices the published standard basket, which is what the site's index quotes. Rank on comparableTotal, never on total.","parameters":[{"name":"items","in":"query","schema":{"type":"string","maxLength":2000},"description":"Comma-separated `slug:qty`, e.g. 'milk:4,bread:3'. Repeats are merged."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"lineCount":{"type":"integer"},"unitCount":{"type":"integer","description":"Line count weighted by quantity."},"lines":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"qty":{"type":"integer"},"cheapest":{"type":"number","nullable":true},"dearest":{"type":"number","nullable":true}}}},"chains":{"type":"array","items":{"type":"object","properties":{"chainId":{"type":"string"},"chainSlug":{"type":"string"},"chainName":{"type":"string"},"total":{"type":"number","description":"Sum of the lines this chain prices. Not comparable on its own."},"covered":{"type":"integer"},"comparableTotal":{"type":"number","description":"total plus the market median for each line this chain does not sell. The only figure two chains may be ranked on: a chain stocking less would otherwise finish first."},"freshness":{"type":"object","description":"When the price list behind a figure was published, and whether that is now stale.","properties":{"asOf":{"type":"string","format":"date","description":"Israel local date."},"stale":{"type":"boolean","description":"Older than the 7-day staleness limit."}}},"lines":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"qty":{"type":"integer"},"unitPrice":{"type":"number","nullable":true},"name":{"type":"string","nullable":true,"description":"What the chain actually sells at that price."}}}}}}},"spread":{"type":"object","description":"Range across the chains that sell the item. One price per chain, never per store.","properties":{"min":{"type":"number"},"max":{"type":"number"},"median":{"type":"number"},"ratio":{"type":"number","description":"max / min."},"saving":{"type":"number","description":"Shekels between cheapest and dearest."},"chainCount":{"type":"integer"}},"nullable":true}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/insights/basket/history":{"get":{"summary":"What the standard basket has cost at each chain, day by day","description":"Recorded totals, not a reconstruction: each point is the figure this API published that day, and the last point of every series is the total `/v1/insights/basket` returns right now. Only chains that stock the whole basket appear, because a total over fewer lines is smaller for carrying less and is not comparable with the rest. Days with no record are absent rather than interpolated, so a step in `points` is always a movement between two published totals. History begins 2026-08-16; `watchingSince` is null before anything was recorded.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"watchingSince":{"type":"string","nullable":true,"description":"First day covered, YYYY-MM-DD"},"days":{"type":"array","items":{"type":"string"},"description":"Every date any chain has a point on, oldest first"},"chains":{"type":"array","items":{"type":"object","properties":{"chainId":{"type":"string"},"chainSlug":{"type":"string"},"chainName":{"type":"string"},"points":{"type":"array","description":"Oldest first","items":{"type":"object","properties":{"date":{"type":"string"},"total":{"type":"number"}}}},"first":{"type":"number","description":"The basket at this chain on the earliest day held"},"latest":{"type":"number","description":"Today's total, matching /v1/insights/basket"},"change":{"type":"number","description":"latest - first; negative is cheaper"},"changePercent":{"type":"number"}}}},"moves":{"type":"array","description":"Newest first, capped. One entry per step in any chain's points.","items":{"type":"object","properties":{"date":{"type":"string"},"chainId":{"type":"string"},"chainSlug":{"type":"string"},"chainName":{"type":"string"},"from":{"type":"number"},"to":{"type":"number"}}}}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/insights/deals":{"get":{"summary":"Live promotions, priced per unit and graded against the market","description":"`promoPrice` is what one unit costs under the promotion, after `minQty` is divided out. The baseline is the median price of the same barcode at the OTHER chains, one price per chain, so a chain that raised a price in order to cut it does not appear here. `grade` is `real` at or below 80% of that median, `not_a_deal` at or above it, `ordinary` between.","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","default":40,"maximum":120}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deals":{"type":"array","items":{"type":"object","properties":{"gtin":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"category":{"type":"object","nullable":true,"properties":{"l1":{"type":"string"},"l2":{"type":"string"},"slug":{"type":"string"}}},"chainId":{"type":"string"},"chainSlug":{"type":"string"},"chainName":{"type":"string"},"promoPrice":{"type":"number","description":"Per unit, after minQty"},"shelfPrice":{"type":"number","description":"Same listing, same chain, no promotion"},"marketPrice":{"type":"number","description":"Median at the other chains"},"bestElsewhere":{"type":"number","description":"Cheapest a shopper could pay at another chain today: shelf price or that chain's own live promotion, whichever is lower"},"bestElsewhereChain":{"type":"object","nullable":true,"properties":{"chainId":{"type":"string"},"chainSlug":{"type":"string"},"chainName":{"type":"string"}}},"bestElsewhereIsPromo":{"type":"boolean","description":"Whether bestElsewhere is a promotion there rather than an ordinary price"},"bestElsewhereMinQty":{"type":"integer","description":"Units required together to get bestElsewhere; 1 when unconditional"},"bestElsewhereClubOnly":{"type":"boolean","description":"bestElsewhere needs that chain's loyalty card"},"minQty":{"type":"integer","description":"Units required together; 1 means no condition"},"discountPercent":{"type":"integer","description":"promoPrice against marketPrice; negative when dearer"},"grade":{"type":"string","enum":["real","ordinary","not_a_deal"]},"description":{"type":"string"},"clubOnly":{"type":"boolean","description":"Needs a loyalty card"},"endsAt":{"type":"string","format":"date"},"chainCount":{"type":"integer"},"freshness":{"type":"object","description":"When the price list behind a figure was published, and whether that is now stale.","properties":{"asOf":{"type":"string","format":"date","description":"Israel local date."},"stale":{"type":"boolean","description":"Older than the 7-day staleness limit."}}}}}}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/v1/insights/meta":{"get":{"summary":"Corpus size and the published standard basket","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"minChainsForPage":{"type":"integer"},"comparableProducts":{"type":"integer"},"standardBasket":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"qty":{"type":"integer"}}}}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}}}}