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/campaigns/validate-targeting';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"catalog_ids":["example"],"catalog_tags":["example"],"channel_ids":["example"],"channel_tags":["example"],"intents":["search"],"organization_id":"example","surface_ids":["example"],"surface_tags":["example"]}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.eu1.luigisbox.ai/platform/v1/campaigns/validate-targeting \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "catalog_ids": [ "example" ], "catalog_tags": [ "example" ], "channel_ids": [ "example" ], "channel_tags": [ "example" ], "intents": [ "search" ], "organization_id": "example", "surface_ids": [ "example" ], "surface_tags": [ "example" ] }'Validate a campaign’s targeting without persisting anything. 200 means the targeting would be accepted and returns the scope it resolves to right now — every catalog, channel, surface and Intent it can serve, enumerated; 422 lists every problem in exception_details.problems, each with a stable code, its dimension and the offending values. match and rules are not checked.
Authorizations
Section titled “Authorizations ”Request Body required
Section titled “Request Body required ”A campaign’s targeting on its own — no name, no match, no rules, no persistence.
Ids are canonicalized, deduped and format-checked exactly as they are on a real
write, and every targeting violation is reported uniformly as a problems entry.
object
Surface Intents this campaign targets — the kind of discovery it applies to. Empty means every Intent, not none. Narrowing by Intent is mutually exclusive with narrowing by surface_ids / surface_tags: an Intent targets whole families of surfaces, so combining the two is rejected with intents_conflict. A targeted Intent no scoped catalog hosts a surface for is rejected with catalog_without_targeted_intent — it could never trigger.
Responses
Section titled “ Responses ”Successful Response
The scope an accepted targeting set resolves to; validity is carried by the 200.
object
What a campaign’s targeting resolves to right now, fully enumerated.
Explicit picks are unioned with the picked tags’ current members and resolved against the catalogs, channels and surfaces that exist at read time. That makes this what the campaign is meant to cover rather than what it is covering: a dimension narrowed by a tag serves the members that tag held when the campaign was last saved, so after a tag edit this reports the new members while serving still follows the old ones until the campaign is saved again.
A dimension with no reference at all widens to its universe — all catalogs of the
organization, all channels and surfaces of those catalogs. An empty list therefore means
the campaign targets nothing in that dimension, with one exception spelled out for
channel_ids below.
intents is derived from surfaces rather than echoing the stored array: a
campaign whose targeted surface was deleted reports no surfaces and no Intents.
A channel referenced explicitly, or through a channel tag, is always listed in
channel_ids. A campaign that narrows by no channel is unrestricted by channel at
request time, whatever channel_ids reports.
object
The channels the campaign narrows to, excluding each catalog’s implicit default channel. Empty therefore means it narrows to no channel you created, not that it reaches none: a campaign that does not narrow by channel is dispatched to every channel of its catalogs.
The distinct Intents of surfaces, sorted — derived at read time, never the stored targeting echoed back. A campaign whose targeted surface has since been deleted reports no surfaces and no Intents.
A concrete surface reference: a surface id paired with its catalog.
object
Example
{ "effective_scope": { "intents": [ "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
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": { "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
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": { "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example"}Request validation failed. A targeting rejection lists every problem at once in exception_details.problems.
The canonical error body of a rejected targeting set, with problems typed.
reason and request_id are the canonical fields; exception_details carries
the typed problems list.
object
The exception_details of a rejected targeting set: every problem found at once.
object
Every targeting violation found in one pass; never truncated.
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
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": { "problems": [ { "code": "catalog_scope_required", "dimension": "catalog" } ] }}Was this page helpful?
Thanks.