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.
The Luigi’s Box AI API is one host, and the conventions on this page hold across every endpoint on it.
Base URL
Section titled “Base URL”https://api.<region>.luigisbox.ai/<service>/v1/<resource><region>is the zone your account runs in, for exampleeu1. Examples here useeu1.<service>is the product prefix:discovery,index,catalog,ledger,events,platform. One host, one path prefix per product.v1is the URL namespace, not a stability promise. It only changes for a breaking revision, and a new version runs alongside the old one. While the API is pre-launch, the shape insidev1can still change — see the notice at the top of every page.
https://api.eu1.luigisbox.ai/discovery/v1/searchhttps://api.eu1.luigisbox.ai/index/v1/lbc_8w3k2p/index/https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/sources/feedshttps://api.eu1.luigisbox.ai/platform/v1/channelsTokens are minted on a separate host, https://auth.<region>.luigisbox.ai. See
Authentication.
Timestamps
Section titled “Timestamps”Every timestamp field ends in _at and is UTC ISO-8601:
{ "created_at": "2026-03-14T09:21:07Z" }Headers
Section titled “Headers”On the request
Section titled “On the request”| Header | When | Purpose |
|---|---|---|
Authorization: Bearer <token> | Always | Your access token |
Content-Type: application/json | On requests with a body | |
X-Lbx-Visitor-Id | Required on discovery and event requests | Identifies the browser |
X-Request-Id | Optional | Your own correlation ID; echoed back |
On the response
Section titled “On the response”| Header | When | Purpose |
|---|---|---|
X-Request-Id | Always | Yours if you sent one, otherwise generated. Quote it in support requests. |
RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset | Rate-limited endpoints | Your current budget |
Retry-After | On 429 | How long to wait |
Identifying the shopper
Section titled “Identifying the shopper”Discovery and analytics need to know which shopper a request belongs to, without knowing
who they are. X-Lbx-Visitor-Id carries that.
| Scope | One browser, long-lived |
| Who generates it | Your integration |
| Required | Yes, on discovery requests and on analytics events |
| Survives a token refresh | Yes |
It is an opaque string you own; a UUID is the usual choice. It is not used to authorize anything, so it does not need to be secret. It does need to be consistent, because it is what joins a shopper’s discovery requests to the analytics events that follow.
If your backend generates this value instead of passing through what your frontend produced, every shopper collapses into one: personalization stops working and reports stop being meaningful.
A signed-in shopper additionally passes user_id — a body field on discovery
requests, a field on events. It is separate and optional; see
Personalization.
Pagination
Section titled “Pagination”Endpoints that list stored records page with page and size, and return a consistent
envelope:
curl -G 'https://api.eu1.luigisbox.ai/platform/v1/channels' \ --data-urlencode 'catalog_id=lbc_8w3k2p' \ --data-urlencode 'page=2' \ --data-urlencode 'size=50' \ -H 'Authorization: Bearer <token>'The envelope is the same on every such endpoint:
{ "items": [{ "id": "lbn_4hj9tv", "name": "example.com" }], "page": 2, "size": 50, "total": 61}Discovery results are different: they page with an opaque cursor, because a ranked result
set is not a stable list of rows. See
Sorting and pagination.
Sorting
Section titled “Sorting”On collection endpoints, one parameter, field:direction, with the direction always
spelled out:
?sort=created_at:desc?sort=price:ascThere is no multi-field sort and no -field shorthand. A field is sortable only if the
response shows it.
Discovery sorts differently: its requests are JSON, so sort is an object rather than a
string. See Sorting and pagination.
Every collection has a documented, deterministic default order on a value that does not
change — usually id or created_at — so paging through it twice gives you the same
records in the same order.
Partial updates
Section titled “Partial updates”A PATCH body means “change exactly what I named”:
- A field you omit is left unchanged.
- A field you send as
nullis cleared, when the field can be cleared. - A field that cannot be cleared rejects
nullwith422.
This changes a feed’s URL and nothing else:
curl -X PATCH 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/sources/feeds/41' \ -H 'Authorization: Bearer <token>' \ -H 'Content-Type: application/json' \ -d '{"url": "https://example.com/feeds/products.xml.gz", "expected_version": 7}'The same holds for partial updates to catalog objects, where omitting a field is how you leave a stored attribute alone.
Writes return the resource
Section titled “Writes return the resource”A successful POST, PUT or PATCH responds with the complete resource — server-assigned
IDs, resolved defaults and computed fields included. No follow-up GET is needed to read
back what was written.
Secrets are the exception: they are accepted and never echoed back. A resource that holds one reports whether it is set rather than what it is.
Deletes respond 204 with no body. Requests that are accepted for asynchronous processing
respond 202 — see Content API.
The OpenAPI contract
Section titled “The OpenAPI contract”Every endpoint on the surface is described by one OpenAPI 3.1 document, and the API Reference is generated from it. The same document is downloadable:
curl -O https://docs.luigisbox.ai/openapi.yamlIt is the authoritative description of the API — paths, parameters, request and response schemas, enums, defaults, and the error shape. Where this documentation and the contract disagree, the contract is right.
Generating a client
Section titled “Generating a client”Point a generator at the spec to get a typed client in your language:
# TypeScript typesnpx openapi-typescript https://docs.luigisbox.ai/openapi.yaml -o src/luigisbox.d.ts
# A full client, any of ~50 languagesnpx @openapitools/openapi-generator-cli generate \ -i https://docs.luigisbox.ai/openapi.yaml \ -g python \ -o ./luigisbox-clientAny OpenAPI 3.1 generator works — openapi-generator, openapi-typescript,
oapi-codegen, kiota, NSwag. Luigi’s Box does not publish SDKs, so a generated client
is the supported way to get one, and it is regenerable rather than something you maintain
by hand.
Two properties of the contract affect the generated code:
- Operation IDs are curated, verb first —
list_catalogs,index_objects,get_object_timeline— so generated method names read like the API rather than like the URL. - One schema name means one thing surface-wide. A
Catalogis the sameCatalogwhichever service returns it, so a generated model is shared rather than duplicated per endpoint.
Keeping up with changes
Section titled “Keeping up with changes”Regenerate when you pull a new spec. While the API is pre-launch that regeneration can change request and response shapes, not only add to them, so review the diff rather than assuming your existing calls still compile.
Once the API launches the contract only grows within v1 — new endpoints, new optional
fields, new enum values — and anything that would break a caller lands in a new version path
running alongside the old one.
If your generator is strict about unknown enum values, allow for them: a value added to an enum is not a breaking change on our side, and a client that hard-fails on one will break on a change we consider additive.
Other uses for the spec
Section titled “Other uses for the spec”- Mock server.
npx @stoplight/prism-cli mock openapi.yamlgives you a local API that answers with schema-valid responses — useful before your credentials arrive. - Request validation in tests. Assert your payloads against the schemas before sending them.
- Import into a client. Postman, Insomnia, Bruno and Paw all read the spec directly.
See also
Section titled “See also”- Errors and rate limits
- Authentication overview
- Identifiers
- API Reference — every endpoint, parameter and schema
Was this page helpful?
Thanks.