Pre-launch API. The concepts described here are settled, but the API shape is not: request and response fields, parameters and defaults can still change. Build against it, and talk to your Luigi's Box contact before you put an integration into production.
Every call you make names at least one of these entities. Most accounts are set up once with Luigi’s Box and rarely change shape afterwards, but reading them — and adding a market or a placement — is something you can do yourself.
The domain model explains what these entities are. This page is about working with them over the API.
Organization lbo_└── Catalog lbc_ ├── Channel lbn_ └── Surface lbs_<intent>_<slug>Tag lbt_ (groups catalogs, channels or surfaces)Everything here is under /platform/v1/, needs a token for the
https://api.<region>.luigisbox.ai/platform audience, and a grant to read or manage kernel
entities — reading, writing and deleting are granted separately.
What can I see?
Section titled “What can I see?”One call returns the whole tree your credentials can reach. It is paginated by organization and defaults to 20, so ask for a page large enough to hold it:
curl -G 'https://api.eu1.luigisbox.ai/platform/v1/me/entitlements' \ -d 'size=1000' \ -H 'Authorization: Bearer <token>'Everything your credentials can reach, nested:
{ "items": [ { "id": "lbo_r2vn8c", "name": "Example Retail", "catalogs": [ { "id": "lbc_8w3k2p", "name": "Example — EN", "language": "en_GB", "channels": [{ "id": "lbn_4hj9tv", "name": "example.com" }], "surfaces": [ { "id": "lbs_search_main", "surface_kind": "search", "slug": "main" } ] } ] } ], "page": 1, "size": 1000, "total": 1}The response is scoped to your own credentials — it lists exactly what you are entitled to and nothing else — so it answers which catalog to index into and which channel to search on.
Paginated by organization with page and size, in the same envelope every list on the
API uses: one items entry is one organization, with its whole subtree nested inside, and
total counts organizations. size defaults to 20 and accepts up to 1000 here —
higher than the usual limit of 100, so a token entitled to many organizations can read the
whole tree in one call.
Organizations
Section titled “Organizations”Your company. The billing entity and the root of everything else.
GET /platform/v1/organizationsPOST /platform/v1/organizationsGET /platform/v1/organizations/{organization_id}PATCH /platform/v1/organizations/{organization_id}DELETE /platform/v1/organizations/{organization_id}An organization has a name and nothing else to configure. Most integrations only ever
read this.
Catalogs
Section titled “Catalogs”A catalog is one set of objects in one language, with its own settings. Separate catalogs are how you handle separate languages or regions.
curl -X POST 'https://api.eu1.luigisbox.ai/platform/v1/catalogs' \ -H 'Authorization: Bearer <token>' \ -H 'Content-Type: application/json' \ -d '{ "organization_id": "lbo_r2vn8c", "name": "Example — German", "language": "de_DE" }'| Field | Required | Notes |
|---|---|---|
organization_id | Yes | |
name | Yes | |
language | Yes | xx_XX — a lowercase language, an underscore, an uppercase region |
settings | No | Catalog-level settings |
seed_surface_templates | No | Templates to seed the catalog’s first surfaces from |
The language decides how text is analyzed: de_DE gets German stemming and compound
handling, so a search for “Schuhe” finds Wanderschuhe.
List with ?organization_id=… — that parameter is also the authorization scope, see
Status codes. Reading, updating and
deleting follow the usual shape.
Channels
Section titled “Channels”A channel is a serving destination and a view over one catalog. Two mechanisms make one catalog serve several markets without duplicating content:
| Mechanism | Field | What it does |
|---|---|---|
| Visibility filter | visibility_filter | Which objects this channel may serve at all |
| Attribute aliases | attribute_aliases | Which of an object’s destination-specific values this channel serves, and under which name |
Catalogs and channels covers both in full, including which of an aliased field’s two names a response is keyed by. What follows is the API surface.
curl -X POST 'https://api.eu1.luigisbox.ai/platform/v1/channels' \ -H 'Authorization: Bearer <token>' \ -H 'Content-Type: application/json' \ -d '{ "catalog_id": "lbc_8w3k2p", "name": "example.de", "attribute_aliases": { "price": "price_de", "availability": "availability_de" } }'Each product carries several destination-specific values — price_de, price_at — and the
channel decides which one answers to the canonical name price. Your storefront asks for
price and gets the right one, with no per-request context and no duplicated products.
Both fields are validated against the catalog when you write them, so a name the catalog
does not carry — or an operator its type does not allow — is a 422. The check needs a catalog with content, so neither
field can be set before the catalog’s first ingest: create the channel without them and set
them once it has been fed. When the check cannot run at all, the write fails with a
retryable 503 instead of saving unvalidated. See
Attribute aliases
for the full list of what is rejected and why.
visibility_filter is the one clearable field on a channel: send null to remove it.
Everything else, omit to leave unchanged. See
Partial updates.
List with ?catalog_id=… for one catalog’s channels, or ?organization_id=… for every
channel in an organization. Sending both narrows to the intersection.
Surfaces
Section titled “Surfaces”A surface is a configured discovery unit, scoped to a catalog. Every channel bound to that catalog can call any of its surfaces — there is no per-channel duplication.
curl -X POST 'https://api.eu1.luigisbox.ai/platform/v1/catalogs/lbc_8w3k2p/surfaces' \ -H 'Authorization: Bearer <token>' \ -H 'Content-Type: application/json' \ -d '{ "surface_kind": "search", "slug": "main" }'| Field | Required | Notes |
|---|---|---|
surface_kind | Yes | The intent: search, recommender or collection |
slug | Yes | Lowercase letters, digits and underscores, starting with a letter, 1–48 characters |
The two combine into the surface’s ID — lbs_search_main — and slugs are unique per
catalog and intent. See
Surface identifiers.
A surface’s response reports its default_version. Ranking configuration lives on versions
rather than on the surface itself, which is what lets an A/B test run two versions of one
surface without duplicating it. Version authoring is done with Luigi’s Box; the surface
endpoints here create, read, list and delete the surface itself.
GET /platform/v1/catalogs/{catalog_id}/surfacesPOST /platform/v1/catalogs/{catalog_id}/surfacesGET /platform/v1/catalogs/{catalog_id}/surfaces/{surface_id}DELETE /platform/v1/catalogs/{catalog_id}/surfaces/{surface_id}A tag is a named group, and it exists so that things which target many entities — chiefly campaigns — can target a group instead of a list.
One tag groups exactly one dimension:
dimension | Members are |
|---|---|
catalog | Catalog IDs |
channel | Channel IDs |
surface | {catalog_id, surface_id} pairs |
curl -X POST 'https://api.eu1.luigisbox.ai/platform/v1/tags' \ -H 'Authorization: Bearer <token>' \ -H 'Content-Type: application/json' \ -d '{ "organization_id": "lbo_r2vn8c", "dimension": "catalog", "name": "EU markets", "members": ["lbc_8w3k2p", "lbc_4hj9tv"] }'Surface tags name the catalog alongside the surface, because a surface ID is only unique within its catalog:
{ "organization_id": "lbo_r2vn8c", "dimension": "surface", "name": "All search surfaces", "members": [ { "catalog_id": "lbc_8w3k2p", "surface_id": "lbs_search_main" }, { "catalog_id": "lbc_4hj9tv", "surface_id": "lbs_search_main" } ]}At least one member is required. dimension is immutable — changing it would orphan the
members — and a members list on a PATCH replaces the whole set.
Tags reflow
Section titled “Tags reflow”A campaign targeting EU markets resolves that tag to its members each time the campaign is saved. Add a catalog to the tag, save each campaign targeting it, and every one of them covers the new market with no targeting edited.
Editing a tag therefore changes what every campaign using it serves. Before changing
membership, check which campaigns point at it —
GET /platform/v1/campaigns?intent=… and
each campaign’s
effective scope answer that.
List with ?organization_id=…, optionally &dimension=….
Setting up a new market
Section titled “Setting up a new market”The usual sequence, when an organization already exists:
- Create the catalog for the language —
POST /platform/v1/catalogs. - Register its feeds and mappings — Feed management API.
- Create the channel for the storefront, with its visibility filter and attribute projection.
- Create the surfaces the storefront will call.
- Ask Luigi’s Box for credentials — a publishable key bound to the new channel, or an OAuth client for your backend.
- Verify with
/me/entitlementsand one search request.
Steps 2 and 4 determine most of the ranking configuration. Do both with your Luigi’s Box contact.
See also
Section titled “See also”- Domain model — what these entities mean
- Identifiers — their formats
- Business rules — the main consumer of tags
- Feed management API — filling a new catalog
- Authentication overview — credentials for a new channel
Was this page helpful?
Thanks.