Skip to content

Box catalog

GET /api/s2s/boxes returns the boxes available to your operator. Use it to build your own lobby, product grid, or “most popular” widget (Model B).

Request

GET https://{operator}.app.lootboxsolutions.com/api/s2s/boxes
GET https://{operator}.app.lootboxsolutions.com/api/s2s/boxes?page=2&perPage=50
GET https://{operator}.app.lootboxsolutions.com/api/s2s/boxes?ids=1,2,3
GET https://{operator}.app.lootboxsolutions.com/api/s2s/boxes?sort=-meta.popularity&perPage=12&currency=EUR&locale=pt-BR
GET https://{operator}.app.lootboxsolutions.com/api/s2s/boxes?filter[meta.theme]=fantasy&sort=-price
QueryNotes
(none)All adoptable boxes (active, with a current version), sorted by name — the first page.
pagePage number, 1-based. Defaults to 1. See Pagination.
perPagePage size. Defaults to 100, capped at 200.
idsComma-separated box ids — returns exactly those, regardless of state, so you can re-read previously adopted boxes and detect deactivations.
filter[<key>]Spatie query-builder style filter, e.g. filter[slug]=dragon-box or filter[meta.theme]=fantasy. Multiple keys combine with AND; comma-separate values for an OR within one key. Filterable: name, slug, isActive, isCollectible, meta.<key>.
sortOrdering — see Sorting. name (default), price, listed, or meta.<key>; prefix with - for descending.
limitAlias for perPage, kept for widgets that just cap the strip.
localeBCP-47-ish code (e.g. en, pt-BR). Localizes display fields (e.g. name) where a translation exists. Defaults to the operator default.
currencyISO-4217 code. Prices come back in this currency; must be one of your configured currencies. Defaults to the operator default.

Response

{
"boxes": [
{
"boxId": 42,
"name": "Dragon Box",
"slug": "dragon-box",
"isActive": true,
"isCollectible": false,
"isPublished": true,
"versionNumber": 7,
"priceMinor": 500,
"currency": "EUR",
"houseEdgeBps": 600,
"image": {
"alt": "Dragon Box",
"webp": "https://cdn…/boxes/dragon.webp",
"default": "https://cdn…/boxes/dragon.png"
},
"customAttributes": { "is_purchasable": true, "featured_rank": 3 },
"listedAt": "2026-03-14T08:30:00Z",
"updatedAt": "2026-06-01T09:00:00Z"
}
],
"pagination": {
"page": 1,
"perPage": 100,
"total": 240,
"totalPages": 3,
"hasMore": true
}
}
FieldNotes
boxIdInteger id; pass to /launches or /purchase.
name, slugDisplay fields. name honours the requested locale where a translation exists.
imageImage object with pre-converted format variants — webp and default URLs — plus alt. The default keeps the artwork’s original format, preserving transparency. Render a <picture> with the WebP source and the default fallback (see Images).
isActive, isPublishedLifecycle flags.
isCollectibletrue when the box is a collectibles box. It is an explicit marker, not something derived from the contents — a box can hold collectibles without being one. Use it to route the box into a dedicated collectibles surface: filter[isCollectible]=true for that grid, filter[isCollectible]=false for your main one. false for every box while collectibles are off for your operator (they are excluded from the catalog entirely).
versionNumberThe current version; pricing/odds belong to a version.
priceMinor, currencyOpen price, in the requested currency.
houseEdgeBpsHouse edge in basis points (e.g. 600 = 6%).
customAttributesYour custom box attributes — the typed properties you declared, with this box’s values (e.g. is_purchasable). Only declared attributes appear; {} when you’ve declared none.
listedAtISO 8601 timestamp — the day the box went on sale. Set when the box is first published and left alone by every later republish or price update, so it is the field to build a “Newest” sort or a “New” badge on. An operator can move it by hand from the box page (to re-list an existing box); doing so sends a box_updated webhook. null for a box that has never been published — only reachable through ids.
updatedAtLast change, for cache invalidation. Changes on every edit, including cosmetic ones — don’t use it to decide what is new.
paginationThe current page’s position — see Pagination.

Pagination

The catalog is paginated. Each response carries a pagination envelope alongside boxes:

FieldNotes
pageThe page you’re on (1-based).
perPageThe page size in effect (defaults to 100, capped at 200).
totalTotal boxes matching the request, across all pages.
totalPagesNumber of pages at this perPage.
hasMoretrue when another page follows — keep walking.

To mirror the whole catalog, walk pages until hasMore is false:

GET /api/s2s/boxes?page=1&perPage=200
GET /api/s2s/boxes?page=2&perPage=200
…until "hasMore": false

Pagination composes with filter[…], sort, ids, currency, and locale — the envelope counts only the boxes matching your filters. A page past the last one returns an empty boxes array (with hasMore: false), not an error.

Images

The image object ships each box artwork in several formats so you don’t transcode anything yourself. Use a modern format with a safe fallback:

<picture>
<source srcset="{box.image.webp}" type="image/webp" />
<img src="{box.image.default}" alt="{box.image.alt}" loading="lazy" />
</picture>

Sorting

Pass sort=<key> to order the list. Prefix the key with - for descending; no prefix is ascending.

sortOrders by
name / -nameBox display name. name (ascending) is the default.
price / -priceOpen price in the requested currency.
listed / -listedThe box’s listedAt date. -listed is “Newest first”. Boxes listed on the same day (everything an operator started with, for instance) fall back to the highest box id first, so the order is stable across pages; boxes with no listing date sort last.
meta.<key> / -meta.<key>A casino-controlled custom box property (meta.*) — e.g. meta.popularity, meta.featured_rank, meta.theme. Boxes without the property sort last. Use -meta.popularity for a curated “most popular” order.

Combine with perPage for a tidy home-page strip — the first page is all you need:

GET /api/s2s/boxes?sort=-meta.popularity&perPage=8&currency=EUR

Building your own lobby

A typical Model-B flow:

  1. Render your grid from GET /boxes (walking pages if your catalog is large; cache it; refresh on box_updated / box_version_published webhooks).
  2. On open, either:
    • mint a launch at that boxId and embed the iframe, or
    • read the box’s prize table to render your own box page, then call /purchase from your backend and render the result yourself.
  3. For a “Buy now” in a recommended widget, redirect the player to the box page and launch with autoOpen to buy + play on arrival — or use a pre-pay purchase so the box auto-opens when the player next lands on it.

Keep prices fresh: a box’s priceMinor belongs to its current version. Don’t hard-code prices — read them from the catalog (in the player’s currency) at render time.