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.
Each rule pairs a record_match — which products it applies to — with one effect.
{ "name": "Bury clearance stock", "record_match": { "operator": "and", "conditions": [ { "field": "clearance", "operator": "eq", "value": true } ] }, "effect": { "type": "bury", "strength_pct": 0.6, "mode": "hard" }}A rule’s name is required and is for you: it is what someone reads in six months when
they find results they cannot explain.
The four effects
Section titled “The four effects”| Effect | What it does | Fields |
|---|---|---|
pin | Place the product at a fixed position | position (1-indexed), mode, decay_window, respect_user_filters |
ban | Remove the product from results entirely | — |
boost | Raise the product’s ranking | strength_pct (0–1), mode, decay_window |
bury | Lower the product’s ranking | strength_pct (0–1), mode, decay_window |
They divide into two kinds:
pinandbanare absolute. A pin puts a product at a position; a ban removes it. Both are decisions about the output rather than about how strongly a product scores.boostandburyare relative. They change how strongly a product scores and let the results re-sort. Both are decisions about tendency.
position is 1-indexed: 1 is the top slot. The product is inserted there and everything
below shifts down — nothing is dropped.
A pin naming one product places that product. A pin whose selector names several
products — a list of IDs, or a criterion like brand eq "brand/northwear" — places
whichever of them ranks highest, and several such pins fill their positions with distinct
products. So “pin our best Northwear product to slot 1” is one rule, not a lookup you have
to do yourself.
A pin only ever moves a product up. A product that already ranks above the pin’s position is passed over: it needs no pin, and moving it down to the position would make the page worse. A pin naming several products then takes the next one down; a pin naming one places nothing, and the slot stays free for another rule to fill.
So a pin is not a way to push a product down — that is what bury is for.
In a campaign targeting one catalog, two pins that each name one product cannot share a position, or a product. A position holds one product and a product occupies one position, so only one of the pair could ever apply — the campaign is rejected rather than saved with the outcome left to chance.
Two cases fall outside that. Pins naming several products, or a criterion, are not covered: which product each takes depends on how the results rank, so they cannot be said to collide until the search runs. Neither is a campaign targeting several catalogs, where a position is filled once per catalog — a different hero product pinned to slot 1 in each is two pins that never meet, and nothing in the rule says which catalog it was written for.
A pinned product does not have to match the query. A campaign triggered by the query “wine” can pin a box of chocolates for Valentine’s Day: the product is fetched and placed even though the search would never return it. A pin naming one product has nothing to place when that product does not exist or a ban removes it. A pin selecting by criteria, or by a list of IDs, moves down its matches instead: a ban takes its top match out of the running and the next one takes the slot, so it places nothing only once no match is left.
Pins and the shopper’s filters
Section titled “Pins and the shopper’s filters”A pin places its product whether or not the filters in force would have kept it. That
covers the shopper’s own facet selections, anything inferred from their query, and the
scope of the collection being served: a pin on a chocolate places on the collection
collection:category/wine/member:product.
It does not cover the scope of the surface itself. A surface is configured to serve a particular part of your catalogue, and a pin cannot make it serve something else — that scope is your decision, not the campaign’s. Pin a product a surface excludes and the pin places nothing there.
When the pin names several products, which one it places depends on those filters:
- A product the filters keep, if there is one — a pin on
brand eq "brand/northwear"places a blue Northwear jacket for a shopper who filtered to blue. - Otherwise a product that only contradicts the attributes the pin’s own selector names. The same pin, for a shopper who filtered to blue and to another brand, still places a blue Northwear jacket: the pin overrides the brand it names and leaves the colour alone.
- Otherwise its best product, filters aside.
Relaxing is all or nothing at each step — a shopper filtered to size M who has no Northwear jacket in M is shown one in another size, not the nearest size.
Three things a widening pin never places through. A ban always applies, so a
banned product is never pinned. The object type
the request asks for always applies, so no pin ever answers a request for articles with a
product. And a surface collapsing on
primary or variant serves one kind of group member, so a pin fills its slot from that
kind — on a primary surface it places a primary, never a variant.
A pin naming one product by ID is the exception to the last one. It names the product
its author meant, so it takes its group’s slot even when that product is a variant and the
surface collapses on primary. The object type still holds: an ID pin on an article places
nothing on a request for products.
Set "respect_user_filters": true on the effect to turn all of this off: the pin is then
filtered like any other product, and places nothing when nothing it selects survives the
filters. Use it when a product must not be shown past a filter: an age-restricted or
regulated item the shopper has filtered away should stay away.
Pins and facets
Section titled “Pins and facets”Facets count everything the page matches (the query’s matches on a search, the whole
collection on a collection page) plus every product a pin placed, so a pinned product’s values
are counted like any other hit’s. Pin a chocolate onto the wine collection and the
category facet lists both wine and chocolate, and the total counts the chocolate too.
The shopper’s own filters are where a pin and the facets part. A pin ignores them, so it stays on the page after the shopper filters it away, but the facets follow the filters: the total and every facet on another field leave it out. Only the facet on the filtered field itself still includes it, as that facet always lists the values the shopper could switch to.
Boost and bury
Section titled “Boost and bury”strength_pct is a strength between 0 and 1, not a number of positions. How far a
product actually moves depends on how tightly packed the results around it are, so a boost
does not lift a product over results that match the query substantially better.
Start around 0.3 for a nudge and 0.7 for a strong push, then look at the result.
Boost and bury act on products the results already contain. A product that would not have been retrieved for this request is not boosted into it — a boost changes the order of an answer, it does not change what the answer is about. If you need a product to appear on a request it does not match, that is a pin.
Boost and bury never cancel out: when both would apply to one product, one wins outright and the other is discarded. At equal specificity the bury wins — see Resolving conflicts.
On a surface that recognizes product codes, a search for a code serves the products whose code matches it first, ordered by how strongly it matches. Boost, bury and pin act on that order like on any other: a buried matching product moves down, and a boost can lift another product above the matches. On a surface that collapses variants, a matching product can be represented by another member of its group.
Hard and soft mode
Section titled “Hard and soft mode”pin, boost and bury can fade. mode: "hard" (the default) applies the effect at full
strength for the campaign’s whole run. mode: "soft" fades it out — full strength at the
start, tapering to nothing. A ban is always hard: there is no half of a product removed.
{ "type": "boost", "strength_pct": 0.5, "mode": "soft", "decay_window": "P3D" }What fading means depends on the effect. A soft boost or bury weakens its score modifier until it stops moving the product at all. A soft pin loosens its hold on the position: the product starts at the slot the pin names and drifts back toward where it would have ranked on its own, instead of dropping back there in one step when the campaign ends.
{ "type": "pin", "position": 1, "mode": "soft", "decay_window": "P3D" }decay_window is an ISO 8601 duration: P3D is three days, PT12H twelve hours. It is
only valid with mode: "soft", and must be positive. Leave it out and the fade lasts the
whole campaign.
The fade counts from starts_at and needs both starts_at and ends_at: a soft effect in
a campaign without both bounds behaves like a hard one and never fades. Once the fade
reaches zero the rule stops applying for the rest of the campaign.
Soft mode is how you launch a new product hard and let it earn its own place, instead of ending a campaign with a cliff.
Sorting turns the ranking effects off
Section titled “Sorting turns the ranking effects off”A request that asks for an explicit sort is asking for its own order, so pin, boost
and bury do not apply to it — there is no ranking left for them to change. Sort by price
ascending and the cheapest product is first, whatever any campaign says.
The permission a pin carries goes with it. A pin normally places its product even when the shopper’s filters exclude it; under a sort there is no placement, so that permission is not granted either, and the product is filtered exactly like any other. A shopper who filters to blue and sorts by price sees no red one.
ban is the exception. It is enforced while the results are retrieved rather than while
they are ranked, so a banned product stays off a sorted page too.
Pin wins over boost on the same product
Section titled “Pin wins over boost on the same product”Pins are placed after scores are adjusted, so a pin overrides any movement a boost gave the same product. A product boosted to position 2 and then pinned to 1 ends up at 1 — and a product boosted upward while another product is pinned above it lands one slot lower than the boost alone would suggest.
Pin for a position, boost for a tendency. When both apply to one product, the pin decides.
Selecting products
Section titled “Selecting products”record_match uses the same condition syntax
as a trigger, over catalog fields rather than trigger. fields. It selects by identity
or by criteria, and the difference decides who wins a conflict:
By identity — the most specific kind of rule:
{ "field": "@id", "operator": "in", "value": ["product/sku-4410", "product/sku-4411"] }Each value is the object’s full @id, type included, exactly as the catalog returns it.
A bare sku-4410 names no object, so a rule on it could never act on anything, and the
save refuses it.
By criteria — matches whatever fits, now and later:
{ "field": "brand", "operator": "eq", "value": "brand/northwear" }Prefer criteria. A rule on clearance = true keeps meaning the right thing as stock turns
over; a list of IDs has to be maintained as stock changes.
“Product” here means any catalog object the surface serves. The same selectors and effects
apply to categories, brands or articles, and the ID carries the type: category/shoes works
like product/sku-1001.
For a pin, a list of several IDs counts as criteria, not identity: a pin claims one
position, so several IDs mean “whichever of these ranks highest”. Pin a single product when
it must win a conflict on specificity.
Operators follow the field’s type: gt, gte, lt, lte and range need a numeric
field; eq, neq, in, not_in and exists work on any field, as long as the literal
matches that field’s type. Break either rule — an operator the type does not support, or a
literal of the wrong type — and the save fails with 422. matches is not available in
record_match at all.
A selector is a little more permissive than a trigger, so read the
operator table as the trigger’s
set rather than this one’s: membership works on a boolean here, where a trigger admits only
eq, neq and exists. {"field": "clearance", "operator": "in", "value": [true]} is
accepted in a selector and rejected in a trigger.
A negative condition means has the attribute, and it differs — not the
missing-attribute rule that applies everywhere
else. Send {"field": "@category", "operator": "neq", "value": "category/shoes"} and it
comes back with an exists on @category beside it, so products with no category at all
are left out of the selector instead of banned along with the shoes. You can write that
pair yourself; if you do, it is kept as you wrote it.
A not around a single condition works the same way, from the outside: there it is the
positive operators that fire on a missing attribute, so not (@category is shoes) is
saved with an exists in front of it. A not around a whole group is left as written.
To select the products that lack the attribute as well, write both halves out; see Keeping the original reading.
An empty selector
Section titled “An empty selector”For boost and bury, a selector with no conditions is a scope-wide effect — blunt, but
coherent, and occasionally what you want. For pin and ban it is rejected with a 422,
because it names no product: a pin would not say what to place, and a ban would not say what
to remove.
The check is about the selector being empty, not about what it matches. A ban whose
conditions match every product, such as { "@id": { "exists": true } }, is accepted and
removes everything the campaign reaches.
Matching against the trigger
Section titled “Matching against the trigger”A rule’s value can reference the trigger instead of a literal — “products whose brand is
the same as the anchor’s brand”:
{ "field": "brand", "operator": "eq", "value": { "trigger_ref": "trigger.brand" }}That is one rule that works for every brand, instead of one rule per brand.
Two rules bind the reference. The operator has to match its cardinality: a reference
resolving to a single value takes eq or neq, one resolving to a list takes in or
not_in. And it has to name the same field the condition is on — brand can only be
compared with trigger.brand. Comparing one attribute against a different one is a
condition that can never be true, so the save is rejected rather than leaving you with a
rule that quietly matches nothing.
On a search request there is no anchor to read the attribute from, so a rule referencing
trigger.brand matches no product there.
trigger.request:anchor_id is the exception, and may be compared with any field. It is how
a rule picks out the anchor itself — @id equal to the anchor’s id, so a recommender can
rule its own anchor out of the results.
For a list-valued trigger attribute, pick which elements to use with path and select —
first, last, all, first_n, last_n, at_index, all_but_first, all_but_last.
first_n and last_n need an n; at_index needs an index. The default is the first
element of the outer list and all elements within it.
See also
Section titled “See also”- Resolving conflicts — who wins when rules collide
- Triggers — the condition syntax, and which requests fire
- Filters — the operator vocabulary and the missing-attribute rule
Was this page helpful?
Thanks.