PlutocracyMercury developer docs

Zones

Recommendation blocks the merchant configures in Mercury, rendered by one call: by zone id or by page template, with the cart's contents for cart zones.

A zone is a product block the merchant sets up in the Mercury admin: "You may also like" on the product page, a cart upsell, a row of picks on the home page. The zone decides which products and how to show them; your storefront asks for it and renders what comes back.

A zone has an area (cart, product, collection, cms, wishlist or shortcode), an appearance (grid, carousel, list, tiles or single) and a mode: automatic (products from a collection, with rule groups) or manual (products the merchant picked).

Rendering a zone

By id, when you know which zone the merchant placed:

curl "https://mercury.plutocracy.io/zone/query/6f2c...?currency=AUD&bypassToken=$MERCURY_BYPASS_TOKEN"

By template, to let the merchant's configuration choose:

curl "https://mercury.plutocracy.io/zone/query?templateName=product&bypassToken=$MERCURY_BYPASS_TOKEN"

templateName is one of cart, collection, product, cms (or index) and wishlist. If the id misses, the template is used; anything else falls back to the store's first zone.

The response is the zone (its title, appearance and settings) with products, rendered the same way as search results: prices in major units, prices narrowed to ?currency=. Collection zones return up to 20 products in the collection's own order, leaving out unavailable products unless the zone allows them; manual zones return the picked products. query (the filter format) narrows either kind.

Cart zones

A cart zone's rule groups depend on what is in the cart ("if the cart has a camera, suggest lenses"). Send the cart lines in a POST body with the same URL:

const cart = await (await fetch('/cart.js')).json();
const res = await fetch('https://mercury.plutocracy.io/zone/query?templateName=cart', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ lines: cart.items.map((item) => ({ handle: item.handle })) }),
});
const zone = await res.json(); // 201 Created: the same payload as a GET

Only lines[].handle is read. The POST answers 201 rather than 200; treat any 2xx as success.

When there is nothing to show

Zones are built to never break a page:

  • If the zone can't be resolved for any reason, you get 200 with a fallback: { "title": "", "products": [...] }, twelve in-stock products. Render it or hide the block when title is empty, as suits the page.
  • When the matching rule group is set to show nothing, the body is empty (a 2xx with no JSON). Check for an empty body before parsing.
const text = await res.text();
const zone = text ? JSON.parse(text) : null;
if (!zone || zone.products.length === 0) hideBlock();