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.
Campaigns live on the platform API and need a token for the
https://api.<region>.luigisbox.ai/platform audience.
POST /platform/v1/campaignsGET /platform/v1/campaignsGET /platform/v1/campaigns/{campaign_id}PATCH /platform/v1/campaigns/{campaign_id}DELETE /platform/v1/campaigns/{campaign_id}
POST /platform/v1/campaigns/validate-targetingPOST /platform/v1/campaigns/trigger-schemaGET /platform/v1/campaigns/{campaign_id}/effective-scopeThe first five are the resource; the last three are authoring aids — they resolve targeting and tell you what is authorable, without persisting anything. They are covered under Targeting and Triggers.
Creating a campaign
Section titled “Creating a campaign”| Field | Required | Notes |
|---|---|---|
organization_id | Yes | Immutable |
name | Yes | 1–256 characters |
match | Yes | The trigger tree |
rules | Yes | 1–50 rules |
status | No | draft, active, paused. Defaults to draft. |
starts_at / ends_at | No | UTC instants |
timezone | No | Informational; records the zone the schedule was authored in |
catalog_ids, channel_ids, surface_ids, intents | No | Targeting; empty means all |
catalog_tags, channel_tags, surface_tags | No | Targeting by tag |
Each rule:
| Field | Required | Notes |
|---|---|---|
name | Yes | 1–256 characters |
record_match | Yes | Product selector. Must select something for a pin. |
effect | Yes | One of pin, ban, boost, bury |
The response is the created campaign with its lbm_… ID, each rule’s lbr_… ID, and audit
fields. See Overview for a complete request.
Effect shapes
Section titled “Effect shapes”{ "type": "pin", "position": 1 }{ "type": "ban" }{ "type": "boost", "strength_pct": 0.4, "mode": "hard" }{ "type": "bury", "strength_pct": 0.6, "mode": "soft", "decay_window": "P7D" }position is a positive integer; strength_pct is between 0 and 1 inclusive;
decay_window is an ISO 8601 duration, valid only with mode: "soft", and must be
positive. mode applies to boost and bury and defaults to hard.
Validation you will meet
Section titled “Validation you will meet”| Situation | Result |
|---|---|
A name already used by another live campaign in the organization | 409 |
channel_ids, surface_ids or their tags without catalog_ids / catalog_tags | 422, code catalog_scope_required |
intents together with surface_ids / surface_tags | 422, code intents_conflict |
| A catalog or channel from another organization, directly or through a tag | 422, code catalog_not_in_organization / channel_not_in_organization |
| A tag that does not exist in the organization | 422, code tag_not_found |
A tag of one dimension used in another, such as a channel tag in catalog_tags | 422, code tag_dimension_mismatch |
| A surface ID that exists in none of the targeted catalogs | 422, code surface_not_in_catalog_scope |
| A targeted catalog hosting none of the targeted surface IDs | 422, code catalog_without_targeted_surface |
| A channel whose catalog is not among the targeted catalogs | 422, code channel_outside_catalog_scope |
| An intent no targeted catalog serves | 422, code catalog_without_targeted_intent |
A pin whose record_match has no conditions | 422 |
decay_window on mode: "hard", or ≤ 0 | 422 |
| A condition tree deeper than 10 levels or over 100 conditions | 422 |
| An operator the field does not accept in some targeted catalog | 422 |
A trigger.request:* field that is not one of the four | 422 |
starts_at not strictly before ends_at | 422 |
timezone with neither starts_at nor ends_at | 422 |
Targeting problems come back in exception_details.problems, each with a stable code, its
dimension and the offending values — so an authoring UI can point at the field that is
wrong rather than showing one message.
Updating
Section titled “Updating”PATCH is partial: a field the body omits is left unchanged.
curl -X PATCH 'https://api.eu1.luigisbox.ai/platform/v1/campaigns/lbm_p7q2zx' \ -H 'Authorization: Bearer <token>' \ -H 'Content-Type: application/json' \ -d '{"status": "paused"}'One exception: a rules list replaces the whole rule set. To change one rule, send them
all. Read the campaign, edit the rules array, send it back. A partial rules list is not a
partial update — it is a smaller rule set.
Echo each rule’s id back. A rule sent with the id from a previous response is
updated in place and keeps that ID; a rule sent without one is created fresh, and a rule
missing from the list is dropped. The ID is what reporting aggregates a rule’s performance
on, so recreating a rule you meant to edit splits its history in two.
organization_id is immutable. An archived campaign rejects every update with 409,
including a status change, and so does a name another live campaign already uses.
Archiving
Section titled “Archiving”DELETE /platform/v1/campaigns/{campaign_id} sets the status to archived rather than
erasing the record, so what ran stays auditable. It responds 204.
There is no un-archive. Use paused for anything you might want back. Archiving releases
the campaign’s name, so a new campaign may reuse it.
Listing
Section titled “Listing”curl -G 'https://api.eu1.luigisbox.ai/platform/v1/campaigns' \ --data-urlencode 'organization_id=lbo_r2vn8c' \ --data-urlencode 'status=active' \ --data-urlencode 'intent=search' \ --data-urlencode 'query=spring' \ --data-urlencode 'sort=starts_at:desc' \ -H 'Authorization: Bearer <token>'| Filter | Matches |
|---|---|
organization_id | Owning organization |
status | Lifecycle state |
query | Case-insensitive substring of the name |
effect_type | Campaigns containing a rule with this effect. Repeatable. |
intent | Intents the campaign can actually serve right now. Repeatable. |
Filters combine AND across facets and OR within a repeatable one.
intent matches against the intents of the surfaces currently in the campaign’s scope, not
the stored intents array. A catalog-wide campaign matches every
intent its catalogs host, and a campaign whose targeted surfaces were deleted matches none —
the same answer effective scope
gives.
sort takes <field>:<direction>, with the field one of name, status, rules_count,
starts_at or updated_at, and the direction asc or desc.
The response is the standard paginated envelope
plus facet_counts.status, a breakdown by status that ignores the status filter — so a UI
can show “12 active, 3 paused” alongside a filtered list.
Limits
Section titled “Limits”| Rules per campaign | 1–50 |
| Campaign and rule name | 1–256 characters |
| Condition tree depth | 10 |
| Conditions per tree | 100 |
See also
Section titled “See also”- Business rules overview — the concepts these fields express
- Targeting · Triggers · Rules and effects
- API Reference — the generated contract for every endpoint above
Was this page helpful?
Thanks.