Skip to content

    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.

    Triggers

    View source

    A campaign’s match decides whether it applies to a given request. It is a condition tree, and every leaf field carries a trigger. prefix.

    Search requests whose query contains “shoes”:

    {
    "operator": "and",
    "conditions": [
    { "field": "trigger.request:query", "operator": "matches", "value": "shoes" }
    ]
    }

    Recommendations anchored on one product:

    {
    "operator": "and",
    "conditions": [
    { "field": "trigger.request:anchor_id", "operator": "eq", "value": "product/sku-1001" }
    ]
    }

    Collection pages scoped to one category:

    {
    "operator": "and",
    "conditions": [
    { "field": "trigger.request:intent", "operator": "eq", "value": "collection" },
    { "field": "trigger.@category", "operator": "eq", "value": "category/shirts" }
    ]
    }

    The prefix is what distinguishes this tree from a rule’s record_match: trigger. fields describe the request, unprefixed fields describe a product.

    Four fields, under the reserved request: namespace:

    FieldValue
    trigger.request:queryThe search query. matches fires when the value appears in the query as whole words, in order: gift card fires on “gift card for men”, not on “card gift”, and sho does not fire on “shoes”. Case and accents are ignored. The comparison is on analyzed words, not raw text, so the catalog’s language rules apply first — shoe can fire on “shoes”, and a word the language treats as filler is dropped from both sides before the order is checked.
    trigger.request:intentsearch, recommender or collection
    trigger.request:typeThe requested object type, e.g. product. Present only when the request is scoped to a single type.
    trigger.request:anchor_idA recommender anchor’s ID in type/value form, e.g. product/sku-1001

    eq, neq, in and not_in on trigger.request:query compare the whole query, case-insensitively. Any other trigger.request:* name is rejected with 422, and so is a trigger_ref value: a trigger compares against literals only. A match with no conditions fires on every request in the targeting scope.

    Fields from the object the request is about

    Section titled “Fields from the object the request is about”

    Everything else is a catalog attribute of the object the request is about — the recommender’s anchor, or the collection’s scope:

    { "field": "trigger.brand", "operator": "eq", "value": "brand/northwear" }

    That fires the campaign on any recommendation anchored on a Northwear product, or any collection page scoped to that brand. It says nothing about which products the rules then affect — that is record_match.

    A recommendation with several anchors fires the campaign when any one of the first ten anchors satisfies the conditions. A collection exposes its scope when the scope is an AND of equality or membership conditions (@category IN ("category/shirts"), brand = "brand/northwear"), one attribute per condition, written exactly as in the request’s collection_filters. A scope using OR, NOT or a range exposes nothing, so no positive condition on a catalog attribute can fire on it — what a negative one does is the next paragraph. The shopper’s own filters never trigger a campaign.

    A search request has no such object. A positive condition on a catalog attribute (eq, in, a comparison) therefore never fires on search. A negative one (neq, not_in, or a not group) fires on every search request, because the attribute is missing there and a negative test on a missing attribute is true. The same holds for a recommendation with no anchor and for a collection that exposes no attributes. If the attribute condition should decide which products are affected rather than when the campaign fires, put it on the rule instead.

    POST /platform/v1/campaigns/trigger-schema takes a targeting set and returns the trigger fields authorable across it, with the exact operators each one accepts:

    Terminal window
    curl -X POST 'https://api.eu1.luigisbox.ai/platform/v1/campaigns/trigger-schema' \
    -H 'Authorization: Bearer <token>' \
    -H 'Content-Type: application/json' \
    -d '{"organization_id": "lbo_r2vn8c", "catalog_ids": ["lbc_8w3k2p"]}'

    Each entry carries a name (unprefixed — add trigger. to use it), a source of catalog or request, the operators it accepts, and a value_domain saying whether the values list is binding:

    value_domainMeaning
    closedA fixed choice; anything else is rejected on save
    suggestedWhat the scope carries today, but other values are accepted
    openFree input; values is empty

    values is populated for trigger.request:intent (closed) and trigger.request:type (suggested); the other two request fields and every catalog attribute are open with an empty list. For a catalog attribute, read the values from catalog metadata instead.

    The response is paginated, and query narrows it to a case-insensitive substring of the field name — a large catalog can carry a lot of attributes.

    Two reasons to call this endpoint rather than assume the field names:

    • The operators it returns are the ones that will be accepted on save. If you are building an authoring UI, offer exactly these and a save can never be rejected on operator grounds. They also tell you which value input to render: range and comparison operators mean the field is numeric, membership-only means it is a keyword.
    • It answers for the whole targeted scope, conservatively. A field that is numeric in one targeted catalog and text in another is reported with the keyword-safe operator set, because that is the set that will survive validation across every catalog in scope.

    The same syntax serves match and record_match. A condition tree is operator plus conditions, nestable:

    {
    "operator": "and",
    "conditions": [
    { "field": "brand", "operator": "eq", "value": "brand/northwear" },
    {
    "operator": "or",
    "conditions": [
    { "field": "color", "operator": "eq", "value": "blue" },
    { "field": "color", "operator": "eq", "value": "navy" }
    ]
    }
    ]
    }

    Group operators are and, or, not. Leaf operators depend on the field’s type:

    FieldOperators
    Text, and any field the catalog has no type foreq, neq, in, not_in, exists
    Numericthe above plus gt, gte, lt, lte, range
    Booleaneq, neq, exists
    trigger.request:intent, trigger.request:type, trigger.request:anchor_idas text
    trigger.request:queryas text, plus matches

    matches works only on trigger.request:query: not on any other trigger field, and not in a rule’s record_match. The trigger-schema endpoint reports the exact set for each field.

    A tree may nest up to 10 levels deep and hold up to 100 conditions in total. Past either, the save is rejected; split a tree that large into several rules.

    The evaluation rules are the same as discovery filters, including the missing-attribute rule. The tree is written differently, and campaigns accept two operators discovery does not — matches and range. See Other surfaces that filter.