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/tags/example';const options = { method: 'PATCH', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"add_members":[{"catalog_id":"example","surface_id":"example"}],"name":"example","remove_members":[{"catalog_id":"example","surface_id":"example"}]}'};
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/tags/example \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "add_members": [ { "catalog_id": "example", "surface_id": "example" } ], "name": "example", "remove_members": [ { "catalog_id": "example", "surface_id": "example" } ] }'Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters ”Request Body required
Section titled “Request Body required ”Partial update. name replaces; membership is edited as a delta.
dimension is immutable (changing it would orphan members across tables),
so it is not accepted — extra="forbid" rejects it (and any stray field) with 422.
Membership is named as what changes rather than as the resulting set, so two callers
editing different members cannot erase each other: each element is applied
idempotently, and the write needs neither a prior read nor a concurrency token. The
resulting set is (stored - remove_members) | add_members; the two lists must be
disjoint, so no ordering between them has to be defined. Because dimension is
absent, the repository checks the result against the tag’s stored dimension.
object
A surface member of a surface-dimension tag: a surface id paired with its catalog.
object
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.
A surface member of a surface-dimension tag: a surface id paired with its catalog.
object
Example generated
{ "add_members": [ { "catalog_id": "example", "surface_id": "example" } ], "name": "example", "remove_members": [ { "catalog_id": "example", "surface_id": "example" } ]}Responses
Section titled “ Responses ”Successful Response
Tag response: identity + the members for its dimension.
object
Which kind of entity a Tag groups: catalogs, channels, or surfaces. A tag’s members are all of one dimension.
A surface member of a surface-dimension tag: a surface id paired with its catalog.
object
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
{ "dimension": "catalog"}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"}The caller lacks the permission for this tag, or the tag name uses a reserved namespace and the caller lacks the matching reserved-tags permission.
The canonical error body of a tag write refused for using a reserved namespace.
Typed rather than a bare ErrorResponse because the two lists are what the caller
needs in order to act: which name was refused, and which namespaces to avoid.
object
The exception_details of a tag write refused for using a reserved namespace.
object
Every reserved namespace, so a caller can rename without a second refusal.
The names in the request that use a reserved namespace.
Human-readable, actionable error message.
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
{ "exception_details": { "namespaces": [ "intent", "language", "recommender" ], "reserved_names": [ "intent:search" ] }}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"}State conflict: a referencing campaign’s targeting would become invalid, or wider than it was authored to be, or the name is already taken.
The canonical error body of a write blocked by another campaign’s targeting.
object
The exception_details of a write blocked by another campaign’s targeting.
Names the campaign that would end up invalid, since the rejected write is not it.
object
The campaign whose targeting the write would invalidate.
Every targeting violation found in one pass; never truncated. Named for what it carries, so it sits beside filter_problems on the same 422 without a client having to work out which of the two it was given. A 422 carries one list or the other, never both: the first check to refuse ends the request.
One targeting violation: a stable machine code plus the offending values.
object
Stable machine code of the violated rule.
The targeting axis the violation is about.
Human-readable explanation of the violation.
The offending values on that axis.
Human-readable, actionable error message.
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
{ "exception_details": { "targeting_problems": [ { "code": "catalog_scope_required", "dimension": "catalog" } ] }}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.