Skip to content

Catalog

Catalog and campaign endpoints need the CATALOG scope. GET /brands accepts any key. Image URLs need no key.

GET /brands returns the brands the key may use, as a plain array: [{id, name, slug, primaryColour, logoUrl}]. Send a brand id as brandId on an order or a personal-link batch.

GET /catalog/categories returns categories and their subcategories, as a plain array.

[
{
"code": "FOOD_DRINK",
"name": "Food & Drink",
"subcategories": [{ "code": "FOOD_DRINK__WINE_CHAMPAGNE", "name": "Wine & Champagne" }]
}
]

A gift’s categories are subcategory codes. Both levels work as a filter.

GET /catalog/products?country=DE lists gifts for the delivery country, sorted by name.

Parameter Meaning
country Required. Delivery country, ISO 3166-1 alpha-2. An unknown code answers 400 invalid_request.
locale BCP 47 locale of the gift text. Gifts without that translation use their default text. An unsupported locale answers 400 invalid_request with the supported list.
q Text search.
category Category or subcategory code. Repeat to match any of several. An unknown code answers 400 invalid_request.
readyToSend true returns only gifts that ship as they are.
updatedSince Switches to catalog sync.
after, limit Paging. limit is 1–100, default 25.

GET /catalog/products/{productId}?country=DE returns one gift. A listed gift that cannot be supplied to the country returns 200 with available: false. A draft, archived, internal or unknown gift answers 404 not_found.

{
"id": "9b8a7c6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d",
"name": "Cashmere scarf",
"description": "Hand-finished in Scotland.",
"categories": ["FASHION__ACCESSORIES"],
"giftSet": false,
"readyToSend": true,
"customizationAvailable": true,
"variations": [{ "dimension": "Colour", "values": ["Black", "Tan"] }],
"images": [{ "url": "https://…", "width": 1000 }],
"listed": true,
"available": true,
"priced": true,
"estimate": { "amount": "190.00", "currency": "USD" },
"estimateEur": { "amount": "180.00", "currency": "EUR" },
"updatedAt": "2026-10-05T09:12:44Z"
}
Field Meaning
available The gift can be supplied to the country. Ordering an unavailable gift answers not_deliverable_to_country.
priced The gift has a price for the country’s region.
estimate Estimate in the tenant’s currency at today’s rate. null when unpriced.
estimateEur The same estimate in EUR. null when unpriced.
readyToSend The gift ships as it is.
customizationAvailable The gift can be personalised. See customization on orders.
giftSet The gift contains several items.
variations Dimensions to choose, with their values. An order names one value per dimension.
images Absolute URLs that work without a key.

An estimate is not a quote. No price is locked when you read it. You decide what to show: the estimate, the EUR estimate or no price.

Prices and availability change. After a sync, update your copy. Orders already placed are not affected. If a gift is no longer available, an order answers 422 not_deliverable_to_country. If campaign stock ran out, it answers 409 out_of_stock. In both cases no order is placed.

Keep a local copy instead of reading the catalog on every page view.

  1. First load: page through GET /catalog/products?country=DE and store every gift.
  2. Every few minutes: call GET /catalog/products?country=DE&updatedSince=<instant>.

With updatedSince, the response contains every gift whose listing, price or availability changed at or after that instant, oldest change first (by updatedAt, then id). Follow nextCursor until it is null.

  • country and locale still apply. They set available, priced, the estimates and the text. Unknown values answer 400 invalid_request. A gift that cannot be supplied returns available: false.
  • q, category and readyToSend are ignored. Filter your copy yourself. An unknown category still answers 400 invalid_request.
  • Removed gift (unpublished, archived or made internal): {"id": "…", "listed": false, "updatedAt": "…"}. Delete it from your copy.
  • Any other gift is returned in full. Replace your copy. Duplicates are harmless.
  • Next sync: pass the newest updatedAt you hold, minus five minutes. Keep updatedAt at full precision. Send Z, or encode + as %2B.

A campaign is a set of gifts under one brand, such as a VIP birthday selection. Campaigns are read-only.

  • GET /campaigns?brandId=…&status=ACTIVE lists active and completed campaigns within the key’s brands, sorted by name.
  • GET /campaigns/{campaignId} returns one campaign with its offerings. Optional locale sets the language of each offering name. An unsupported locale answers 400 invalid_request.
{
"id": "1e2d3c4b-5a69-4c1f-9a52-8d0e4f4b9a3e",
"name": "VIP birthdays",
"brandId": "0a6f2e1c-3b5d-4e7f-8a9b-0c1d2e3f4a5b",
"status": "ACTIVE",
"expiresAt": null,
"offerings": [
{
"productId": "9b8a7c6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d",
"name": "Cashmere scarf",
"images": [{ "url": "https://…", "width": 1000 }],
"supplyType": "STOCKED",
"status": "ACTIVE",
"skus": [
{ "variations": { "Colour": "Black" }, "stockRemaining": 12 },
{ "variations": { "Colour": "Tan" }, "stockRemaining": 0 }
]
}
]
}
  • name and images match the catalog gift, without a price. They are present for every offering, including gifts made for your brand only, which the catalog does not list.
  • supplyType is STOCKED (limited stock per variation) or ON_DEMAND (unlimited; stockRemaining is null).
  • An offering in NEEDS_REVIEW cannot be ordered until Luxorr reviews it.
  • A campaign that is not activated, or belongs to another brand or tenant, answers 404 not_found.

To order from a campaign, send campaignId; the order uses the campaign’s brand. To order from the catalog, send brandId.

Gift images and brand logos are absolute URLs that need no key. They point to the Luxorr CDN or to GET /catalog/images/{fileId}, which:

  • serves images of published public gifts, of published gifts in active or completed campaigns, and brand logos;
  • accepts scale=x1|x2|x3 for 500, 1000 or 1500 px;
  • can be cached for a year; a replaced image gets a new ID;
  • allows 600 requests per minute per client IP.