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.
const url = 'https://api.eu1.luigisbox.ai/platform/v1/catalogs/example/surfaces/example';const options = { method: 'PATCH', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"input_type":"product","output_types":["product","category","brand","query","article"]}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request PATCH \ --url https://api.eu1.luigisbox.ai/platform/v1/catalogs/example/surfaces/example \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "input_type": "product", "output_types": [ "product", "category", "brand", "query", "article" ] }'Update the object types a surface is queried with and answers with. intent and slug are immutable — the surface id is derived from them. The new values reach the Discovery pipeline on the next cache write, not instantly.
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters ”Request Body required
Section titled “Request Body required ”Partial update. Only the fields the body names are applied.
intent and slug are immutable — the surface id is derived from them, so
changing either would rename the resource. input_type is settable only on a
recommender and null-only on the other kinds; the router judges it against the stored
kind, since a partial update never restates it.
object
Catalog object types this surface may answer with. Non-empty; duplicates collapse and input order is preserved. Acts as an allowlist: a request asking for a type absent from this list is rejected rather than answered with fewer results. It is a property of the surface, not of its kind — a recommender declares whatever it recommends, so a category recommender carries category.
Responses
Section titled “ Responses ”Successful Response
object
The kind of discovery this Surface performs — it fixes which Discovery API endpoint serves the Surface and what input a request must supply (search a query, recommender anchor objects, collection a scope). It is the contract, not the algorithm: a search Surface may retrieve lexically, semantically or both. Immutable — it is the middle slot of the surface id lbs_<intent>_<slug>, so changing it means a new Surface.
Catalog object types this surface may answer with. Non-empty; duplicates collapse and input order is preserved. Acts as an allowlist: a request asking for a type absent from this list is rejected rather than answered with fewer results. It is a property of the surface, not of its kind — a recommender declares whatever it recommends, so a category recommender carries category.
The surface-dimension tags this surface belongs to, ordered by name, each carrying both its id and its name — the id is what the tag_id filter takes, the name is what a person reads, and neither needs a further request. Read-only here: membership is changed through POST /platform/v1/tags, or PATCH /platform/v1/tags/{tag_id} with add_members / remove_members, which carry {catalog_id, surface_id} pairs. A surface belongs to at most 20 tags.
A tag as another entity’s response reports it: the id to act on, and the name to show.
Both, because each answers a different question and neither substitutes for the other. The id is what every write and filter takes, so acting on the tag needs no lookup; the name is what a person reads, so displaying it needs none either. A bare id would force a second request for something the server already had in hand.
Not the full :class:Tag: an entity’s tag list is a reference, not a copy — repeating the
members and audit columns of a tag on every surface that belongs to it would grow without
bound and go stale between the two.
A tag a caller sends stays a bare id (CampaignCreate.surface_tags and its siblings). A
request field cannot take this shape without making the write response impossible to send
back unchanged, so the rule is: send an id, receive an id and a name.
object
The tag’s id — what tag_id filters and the tag writes take.
Tag name as namespace:value, lowercased on write (intent:SEARCH is stored as intent:search). The intent, language and recommender namespaces are platform-managed: writing one requires platform:reserved-tags:write and deleting one requires platform:reserved-tags:delete.
Example
{ "intent": "search"}Missing or invalid credentials
Canonical error body — every non-2xx response uses this shape.
object
Machine-readable error context; empty object when there is nothing to add.
object
Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. filter_unknown_field or cursor_mismatch. Safe to branch on; reason is not. Absent when the status code says all there is to say.
Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value.
One field-level reason a request was rejected.
object
Path to the offending value from the request root, e.g. ["body", "filters", 0, "operator"].
What is wrong with the value at loc.
Stable machine code for the problem, e.g. missing or string_too_short.
Human-readable, actionable error message.
Example generated
{ "exception_details": { "error_code": "example", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example"}Authenticated but not permitted
Canonical error body — every non-2xx response uses this shape.
object
Machine-readable error context; empty object when there is nothing to add.
object
Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. filter_unknown_field or cursor_mismatch. Safe to branch on; reason is not. Absent when the status code says all there is to say.
Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value.
One field-level reason a request was rejected.
object
Path to the offending value from the request root, e.g. ["body", "filters", 0, "operator"].
What is wrong with the value at loc.
Stable machine code for the problem, e.g. missing or string_too_short.
Human-readable, actionable error message.
Example generated
{ "exception_details": { "error_code": "example", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example"}Resource not found
Canonical error body — every non-2xx response uses this shape.
object
Machine-readable error context; empty object when there is nothing to add.
object
Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. filter_unknown_field or cursor_mismatch. Safe to branch on; reason is not. Absent when the status code says all there is to say.
Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value.
One field-level reason a request was rejected.
object
Path to the offending value from the request root, e.g. ["body", "filters", 0, "operator"].
What is wrong with the value at loc.
Stable machine code for the problem, e.g. missing or string_too_short.
Human-readable, actionable error message.
Example generated
{ "exception_details": { "error_code": "example", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example"}Request validation failed
Canonical error body — every non-2xx response uses this shape.
object
Machine-readable error context; empty object when there is nothing to add.
object
Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. filter_unknown_field or cursor_mismatch. Safe to branch on; reason is not. Absent when the status code says all there is to say.
Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value.
One field-level reason a request was rejected.
object
Path to the offending value from the request root, e.g. ["body", "filters", 0, "operator"].
What is wrong with the value at loc.
Stable machine code for the problem, e.g. missing or string_too_short.
Human-readable, actionable error message.
Example generated
{ "exception_details": { "error_code": "example", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example"}Was this page helpful?
Thanks.