Skip to main content

Guide: Reading from GET Β· ← Application

rule_effects β€” complete reference

info

Field in the GET response

In GET /discounts and GET /discounts/{id}, this block is returned in **rule_effects** (JSON string). Validate rule_group for bundles (not legacy variants such as rule_grouped in older samples).

Defines what benefit the campaign grants (monetary discount, product price substitution, combo price, etc.) and how it is split between the cart and lines.

Question it answers: β€œIf the campaign won, how much does it discount or what does it show, and on what cart basis?”

Here **rule_group** (bundle grouping in the effect) does matter for integrations β€” unlike bundle / grouped in rule_conditions, which are internal and ignored.

On this page​


Common fields in GET​

FieldRole
key / typeIdentify the effect; usually match (percentage, direct_value, …)
valueMain magnitude (% or amount). Does not apply to strikethrough or formula
unitOnly **ratio**: monetary reference unit (e.g. every €10)
distributionHow the effect is split (see per-type table below)
limitsMaximum times the effect can apply in the same transaction
rule_grouptrue = grouping / bundle (only percentage, direct_value, combo_price). Requires quantities in conditions + application β€” see Bundle mechanics
formulaOnly **formula** effect: fixed calculation expression
strikethrough_dataOnly **strikethrough**: map product key β†’ promotional price. Key may be **product_id** or **product_code** depending on the Company (must match the line identifier on the cart)

limits (effect limit, not win limit)​

limits in rule_effects caps how many times the effect can apply in the same transaction (same cart). It is not the campaign win cap (once, budget, etc.) nor a unit cap in rule_conditions.

ValueInterpretation
0No limit: the effect may apply as many times as conditions and application allow
> 0At most that many effect applications on the cart

What counts as one application depends on distribution and rule_group:

ConfigurationWhat counts as 1 applicationWhat limits: N caps
distribution: individual_items and rule_group null / falseOne unit (internal line with qty 1; see expansion below)Maximum units that get the benefit on the cart
rule_group: true (bundle / 2x1 / combo)One group built per quantity in rule_conditions + rule_applicationMaximum groups that receive the benefit (e.g. how many 2x1 deals)

One unit = one line in the engine (even when the cart has quantity > 1)​

For effect calculation, Loyal Guru treats each unit as a separate line. If the integration sends a single line with quantity: 10, the engine splits it internally into 10 lines of quantity 1, runs conditions, rule_application, effects, and limits on that unit model, then merges the result back into the same lines received on the cart (same number of lines and quantities the retailer sent).

Implications for integrations:

  • With distribution: individual_items, limits counts units (internal qty-1 lines), not necessarily rows in the lines[] JSON. limits: 10 with one aggregated line of qty 15 β†’ at most 10 units get the discount on that line (the remaining 5 stay at full price for that effect).
  • On POST /carts, effects[] may show the discount consolidated per original line; internal logic still used the unit model.
  • Win conditions and application are also evaluated on expanded units when the engine requires it. Expansion is transparent when you only read the campaign GET, but it matters when interpreting limits and when debugging multi-quantity lines.

Example: line-level % with a unit cap (no bundle)​

Goal: 5% on selected products, on at most 10 units on the cart.

FieldValueWhy
typepercentagePercentage discount
value55%
distributionindividual_itemsThe % is calculated per unit (internal qty-1 line)
rule_groupnull or falseNo grouping: each application = one unit
limits10At most 10 units get the 5% on that purchase

rule_application must scope products (e.g. product_id or product_categories on lines). If the cart has one line with quantity: 15 of the eligible product, the engine treats it as 15 units; with limits: 10 only 10 get the 5% (usually reflected on that same aggregated line in the response). With several rows in lines[], the cap of 10 is shared across units from all of them per priority/stacking.

{
"type": "percentage",
"value": 5,
"distribution": "individual_items",
"rule_group": null,
"limits": 10
}

Example: 2x1 with a group cap (rule_group)​

Goal: 2x1 mechanics but only two 2x1 deals per cart (two bonified groups).

FieldValueWhy
rule_grouptrueEach application = one group (2+1 pair), not a standalone line
limits2At most 2 groups with benefit in the transaction

Win conditions (rule_conditions) define group size (e.g. quantity β‰₯ 2); rule_application defines how many units in the group get the benefit (e.g. β‰₯ 1). Full walkthrough: Bundle mechanics (rule_group) β€” same cart with 8 units and limits: 2 only bonifies 2 of 4 possible groups.

Do not confuse with win limits (once, num_per_day, budget, etc.) in the campaign’s general configuration.

Allowed distributions by effect type​

Effecttotalindividual_itemsindividual_items_pro_rata
percentageβœ“βœ“βœ“
direct_valueβœ“βœ“βœ“
ratioβœ“βœ“βœ“
strikethroughβ€”βœ“ (only)β€”
combo_priceβœ“β€”βœ“
formulaβ€”βœ“ (only)β€”
DistributionBrief behavior
totalA single discount on the qualified set / transaction total
individual_itemsThe effect is evaluated per unit (each unit as an internal qty-1 line; see unit expansion)
individual_items_pro_rataThe benefit is split proportionally across qualified lines

Stacking (stackability) by effect​

EffectCart mode
strikethroughAlways Highest Discount
combo_priceAlways Highest Discount
percentage, ratio, direct_value, formulaStackable or Highest Discount (configurable)

Details: Stackability.


percentage​

Percentage discount on qualified lines or total.

{
"key": "percentage",
"type": "percentage",
"value": 20,
"limits": 0,
"formula": null,
"rule_group": null,
"distribution": "individual_items"
}
FieldReading
value: 2020% discount
distribution: individual_itemsThe % applies on each line that meets application
limits: 0No cap on bonified units; with limits: 10, at most 10 units β€” see % cap example
rule_group: nullNo grouping (e.g. simple % on a category). With true, activates groups per quantity in conditions and application

Integration: if the discount does not appear on the expected cart lines, check **rule_application** in addition to the % β€” for cart campaigns, application is usually required for line-level % (Create a Discount Campaign β€” Application Conditions).


direct_value​

Fixed discount amount (campaign currency).

{
"key": "direct_value",
"type": "direct_value",
"value": 5,
"limits": 0,
"formula": null,
"rule_group": null,
"distribution": "individual_items"
}
FieldReading
value: 5€5 (or other currency) per campaign currency
distribution: individual_items€5 per qualified line; with total, a single €5 block on the set
rule_groupSame as percentage: true for bundles that split a fixed amount per group

Common case: €5 off the cart β†’ direct_value, distribution: total, condition total_cart >= X.


ratio​

Benefit proportional to spend in monetary units: for each unit € spent (on qualified base), value of benefit is granted, split per distribution.

{
"key": "ratio",
"type": "ratio",
"unit": 10,
"value": 1,
"limits": 2,
"formula": null,
"distribution": "individual_items_pro_rata"
}
FieldReading of the example
unit: 10, value: 1Every €10 qualified β†’ 1 of discount (or other benefit per campaign mode)
distribution: individual_items_pro_rataThat benefit is prorated across participating lines
limits: 2At most 2 applications of the ratio in the same transaction (e.g. up to €20 base β†’ 2 Γ— 1 benefit if the rule allows)

In points campaigns, the same ratio type may grant balance (points or wallet) instead of a monetary discount β€” check currency / campaign context. In discount campaigns with this JSON, the operational read is spend-scaled discount with proration.


strikethrough​

Replaces the line price on the cart with the promotional price in strikethrough_data. The applied discount is the difference between the price sent on the cart and the effect’s promotional price.

{
"key": "strikethrough",
"type": "strikethrough",
"limits": 0,
"formula": null,
"distribution": "individual_items",
"strikethrough_data": {
"233212": 1,
"343434": 9
}
}
FieldReading
strikethrough_dataMap { "product_key": promotional_price }. Key is **product_id** or **product_code** as the Company uses in catalog/cart β€” must be the same identifier type the integration sends on the line
No valueDiscount amount comes from per-line calculation (cart price βˆ’ promo price)
distributionAlways individual_items (one evaluation per line / product)
limitsSame semantics (0 = no cap on effect applications)

Map keys: within one campaign, entries are usually homogeneous (all numeric ID or all code). If the Company uses SKU, expect keys like "12000041"; if internal ID, keys like "233212".

Numeric example (by product_id): strikethrough_data["233212"]: 1, line on the cart with that product and price 3 β†’ effective price 1, discount 2 (3 βˆ’ 1).

Example (by product_code): strikethrough_data["CC-33CL-LATA"]: 0.99 β€” the cart line must carry the same product_code to match.

Integration: if it does not apply, verify key type (ID vs code) aligned with the cart, that the key exists in the map, and the line price sent. Stacking: always Highest Discount.


combo_price​

Fixed bundle price for a product group that meets conditions / application.

{
"key": "combo_price",
"type": "combo_price",
"value": 10.9,
"limits": 0,
"formula": null,
"rule_group": true,
"distribution": "individual_items_pro_rata"
}
FieldReading
value: 10.9Total combo price (€10.9)
rule_group: trueEngine groups lines that meet bundle mechanics before setting combo price
distribution: individual_items_pro_rataCombo savings are split across group lines. Alternative: total (single block on the set)
limits: 0No cap on how many times the combo can apply on the cart

Restriction: do not use individual_items with combo_price β€” only total or individual_items_pro_rata. Stacking: always Highest Discount.


formula (external promotional effect)​

Per-line discount calculated from an attribute sent by an external system (precomputed promo price).

{
"key": "formula",
"type": "formula",
"limits": 0,
"formula": "lines.total - lines.attributes.promoPrice",
"distribution": "individual_items"
}
FieldReading
formulaFixed in configuration: lines.total - lines.attributes.promoPrice
No valueAmount comes from per-line calculation
distributionAlways individual_items
rule_groupDoes not use grouping (formula does not support bundle)

Integration flow: the integrator must send promoPrice (or agreed equivalent) in cart line attributes. If missing on a line, that line gets no discount from this campaign. If the result ≀ 0, no discount on that line.


Bundle mechanics (rule_group)​

In Owner App, Bundle / group (rule_group: true) tells the engine to form groups of lines and apply the effect per group, not independently line by line.

Where it applies​

rule_effects.rule_groupCompatible effect types
truepercentage, direct_value, combo_price
false / nullAny effect without grouping
Not availableratio, strikethrough, formula

The three blocks that define the bundle​

BlockQuestion it answersWhat to validate in integration
**rule_conditions**How many units are needed to form a group?Same product/category clause + quantity subcondition (e.g. gte 2 β†’ groups of 2)
**rule_application**On how many units in the group does the effect apply?Same product/category family + quantity subcondition (e.g. gte 1 β†’ 1 unit with benefit per group)
**rule_effects**What benefit and how many times?rule_group: true, value / type, distribution, **limits**

The bundle and grouped flags in conditions/application JSON are internal (ignore value). What matters operationally are nested **quantity** and **rule_group** on the effect.

Full example: 2x1 on product 2222 (100% on 1 of every 2)​

Win β€” form groups of 2 units (rule_conditions):

{
"conditions": {
"and": [
{
"key": "cart_product_id",
"field": "product_code",
"bundle": 1,
"values": ["2222"],
"grouped": true,
"operator": "in",
"conditions": [
{ "field": "quantity", "value": 2, "operator": "gte" }
],
"array_field": "lines"
}
]
}
}

Application β€” discount on 1 unit per group (rule_application):

{
"conditions": {
"and": [
{
"key": "cart_product_id",
"field": "product_code",
"bundle": 1,
"values": ["2222"],
"grouped": true,
"operator": "in",
"conditions": [
{ "field": "quantity", "value": 1, "operator": "gte" }
],
"array_field": "lines"
}
]
}
}

Effect β€” 100% on that unit, grouping on (rule_effects):

{
"key": "percentage",
"type": "percentage",
"value": 100,
"limits": 1,
"formula": null,
"rule_group": true,
"distribution": "individual_items_pro_rata"
}

2x1 interpretation​

StepReading
Conditions quantity: 2Each valid group = 2 units of product 2222 on the cart
Application quantity: 1Within each group, 100% applies to 1 unit (the β€œfree” or bonified unit in 2x1)
rule_group: trueEngine groups before calculating; without this, % would be read line by line without 2x1 mechanics
distribution: individual_items_pro_rataSplit of the bonified unit’s discount across group lines (typical in 2x1)
Ticket with 8 units of 22228 Γ· 2 = 4 groups β†’ up to 4 effect applications (4 bonified units if no cap)

Effect limits with bundles​

With rule_group: true, each effect application is one group (not a standalone line). So limits: 2 on a 2x1 means β€œat most two 2x1 deals on the cart”, not β€œtwo units total”. See also 2x1 group cap example.

limitsIn the 2x1 example
0No cap: all possible groups on the cart get benefit (8 units β†’ 4 groups β†’ 4 bonifications)
1Only 1 group gets the effect in the transaction (1 β€œfree” product even if more groups are possible)
2At most 2 groups (two 2x1 deals); with 8 units on the cart only 2 of 4 possible groups are bonified

**limits** counts effect applications per transaction (bonified groups with rule_group, or lines with individual_items without grouping), not total units in the cart.

Visual summary​

Ticket: 8 Γ— product 2222
β”‚
rule_conditions (qty β‰₯ 2) β†’ 4 groups of 2
β”‚
rule_application (qty β‰₯ 1) β†’ 1 unit with discount per group
β”‚
rule_effects (100%, rule_group) + limits
β”‚
limits: 0 β†’ 4 bonifications (4 groups)
limits: 2 β†’ 2 bonifications (only 2 of 4 groups)

Common variants​

MechanicsConditions (group)Application (benefit)Typical effect
2x1 same productquantity gte 2 on productquantity gte 1 on same productpercentage: 100 + rule_group: true
3x2 on categoryquantity gte 3 on categoryquantity gte 1 on categorypercentage: 100 + rule_group: true
Bundle A + B + C products for €10quantity gte 1 on A + quantity gte 1 on B + quantity gte 1 on Cbundle line filtercombo_price + rule_group: true
Fixed € per groupquantity gte Nquantity gte 1direct_value + rule_group: true

Common mistakes β€” bundle​

SymptomUsual cause
2x1 does not workrule_group: false or missing quantity subcondition in conditions or application
Over-bonifieslimits: 0 with many groups on the cart; raise limits to cap bonified groups
Under-bonifiesLow limits (e.g. 1) with many units; or conditions require more units than on the cart
Different product in applicationvalues / field in application do not match win conditions
Confusing bundle/grouped flagsThey do not enable mechanics alone; need **rule_group: true** and correct **quantity** values

Configuration vs cart response​

ContextFieldContents
Campaign in GETrule_effects (often JSON string)Configuration: type, value, distribution, limits, …
Evaluated cart / purchaseeffects[]Runtime result per line or block

Typical fields in cart **effects[]**:

Cart fieldRelation to config
effectTypeMaps to rule_effects.type (percentage, direct_value, …)
distribution_typeMaps to rule_effects.distribution
discount_valueCalculated amount already applied on that line or block
rule_idID of the campaign that produced the effect

Example (line % discount):

{
"effects": [{
"rule_id": 101,
"line": 1,
"type": "discount",
"effectType": "percentage",
"distribution_type": "individual_items",
"discount_value": 1.50
}]
}

Integration: if the cart does not reflect the GET discount, compare rule_effects with response effects[]; if effects[] is empty, check eligibility (rule_conditions, audience, coupon) or rule_application, not only effect type.


Common mistakes β€” rule_effects​

ConfusionHow to resolve it
limits: 0 β€œdisables limits”Correct: no cap on effect applications in the transaction
One line with quantity: 20 and limits: 10Engine expands to 20 units; only 10 get the effect; response may still be one line with aggregated discount β€” see unit expansion
limits vs win limitsrule_effects.limits = effect repetitions; once / num_* = how many times the customer can win the campaign
Bundle / 2x1Trio: quantity in rule_conditions (group size) + quantity in rule_application (units with discount) + rule_group: true + limits
strikethrough β€œdid not discount as expected”Map key (product_id vs product_code) must match line identifier on the cart; discount = cart price βˆ’ promo price
combo_price with individual_itemsInvalid in product; use total or individual_items_pro_rata
formula without promoPrice on lineIncomplete integration; discount 0 on that line
% or ratio without application on cartCheck rule_application β€” usually required for line-level %
Same % in config, different on the cartCheck stackability (Highest Discount won another campaign) and effects[]


Back: Reading from GET Β· Checklist