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.
Targeting decides where a campaign can act. It says nothing about which requests fire it (that is the trigger) or which products it affects (that is the rule).
The four dimensions
Section titled “The four dimensions”Each dimension can be listed directly or by tag:
| Dimension | Fields |
|---|---|
| Catalogs | catalog_ids, catalog_tags |
| Channels | channel_ids, channel_tags |
| Surfaces | surface_ids, surface_tags |
| Intents | intents — search, recommender, collection |
Values within one dimension are OR-ed; dimensions are AND-ed. So this targets either catalog, but only on search surfaces:
{ "catalog_ids": ["lbc_8w3k2p", "lbc_4hj9tv"], "intents": ["search"]}An empty dimension means all of it, not none. Leaving intents out targets every
intent; a campaign with no targeting at all covers the whole organization. That default is
deliberate — most campaigns want to be broad — but it also means an accidentally empty
dimension widens a campaign rather than disabling it.
Three rules:
- Narrowing by channel or surface needs a catalog scope. Surface IDs are only unique
within a catalog and a channel belongs to one, so
channel_ids,surface_idsor their tags withoutcatalog_idsorcatalog_tagsis rejected (catalog_scope_required).intentsalone is fine. intentsand surfaces are mutually exclusive. An intent already selects whole families of surfaces, so combining the two is rejected (intents_conflict). Pick one level of precision.- Every targeted catalog has to be able to fire. Targeting an intent no catalog in scope
serves is rejected (
catalog_without_targeted_intent), and so is a catalog that hosts none of the targeted surface IDs (catalog_without_targeted_surface): catalogs A and B withsurface_ids: ["lbs_recommender_homepage"]fail when B has no such surface.
The two-catalog example above therefore saves only if both catalogs host a search surface.
Tags make targeting reflow
Section titled “Tags make targeting reflow”A tag is resolved to its members when the campaign is saved, not when a request is served. Adding a catalog to a tag the campaign targets does not change what the campaign serves until the campaign is saved again; an update to the campaign, even one that changes nothing else, picks up the new members. A deleted surface receives no requests, so the campaign stops serving there on its own.
That freeze applies only to the dimensions you narrowed. A dimension you left open is stored as “any”, not as a list of what existed at the time, so it keeps up on its own: a campaign that names no surfaces serves a surface added to its catalog tomorrow, with nothing to re-save. It is the narrowed dimensions — the channels and surfaces a tag resolved to when you last saved — that stay as they were.
A campaign for “all German storefronts” therefore keeps meaning that as storefronts come and go, as long as it is saved after the tag changes. The stored targeting is not the whole answer to “what does this campaign cover”; two endpoints resolve the tags as they are right now.
Tag edits are checked against the campaigns that use the tag. Changing a tag’s members or
deleting it fails with 409, naming the campaign, when the change would leave a live
campaign with targeting the campaign API would refuse: adding a catalog without
lbs_search_main to a tag used by a campaign that targets that surface, for example.
Archived campaigns never block a tag edit.
Checking targeting before you save
Section titled “Checking targeting before you save”POST /platform/v1/campaigns/validate-targeting validates a targeting set without
persisting anything, and returns what it resolves to right now:
curl -X POST 'https://api.eu1.luigisbox.ai/platform/v1/campaigns/validate-targeting' \ -H 'Authorization: Bearer <token>' \ -H 'Content-Type: application/json' \ -d '{ "organization_id": "lbo_r2vn8c", "catalog_tags": ["lbt_k4d7rq"], "intents": ["search"] }'A 200 means the targeting would be accepted, and carries the enumerated scope — every
catalog, channel, surface and intent it can serve. A 422 lists every problem in
exception_details.problems, each with a stable code, the dimension it concerns and the
offending values. The
Authoring API lists
every code.
It checks targeting only: match and rules are not validated here.
Reading a saved campaign’s scope
Section titled “Reading a saved campaign’s scope”GET /platform/v1/campaigns/{campaign_id}/effective-scope answers the same question for a
campaign that already exists: every catalog, channel, surface and intent its targeting
resolves to right now.
It reads the tags as they stand now, while a narrowed dimension serves what the tag resolved to at the last save. After a tag edit the two disagree: the scope lists the catalog just added to the tag, which is not yet being served. Save the campaign and they agree again.
When a campaign is not firing, read it as “what this campaign is meant to cover”, not “what it is covering”. An empty list in a dimension usually means the campaign targets nothing there — a deleted surface, or a tag that no longer matches anything.
channel_ids is the exception: it lists the channels you created, so a catalog you have not
added one to reports an empty list while the campaign still serves on it. Read an empty
channel_ids as “not narrowed to a channel”, and check the other three dimensions for why a
campaign is not firing.
A debugging order that works
Section titled “A debugging order that works”When a campaign is not doing what you expect, check in this order — each step rules out everything below it:
- Status and schedule. Is it
active, and is now inside its window? Onlyactivecampaigns are served at all. - Saved since the last tag change. A tag edit reaches the campaign only when the campaign is saved again — and do this before step 3, which cannot tell you it is pending.
- Effective scope. Does it cover the catalog, channel and surface you are testing?
- Trigger. Does
matchaccept the request you are making? - Rule selector. Does
record_matchselect the products you expect? - Conflicts. Is another rule beating this one? See Resolving conflicts.
See also
Section titled “See also”- Triggers — which requests a campaign fires on
- Account structure — catalogs, channels, surfaces and tags
- Authoring API — the endpoints in full
Was this page helpful?
Thanks.