Guide: Reading from GET Β· β Application
rule_effects β complete reference
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
limits(effect limit, not win limit)- Allowed distributions by effect type
- Stacking (stackability) by effect
percentagedirect_valueratiostrikethroughcombo_priceformula(external promotional effect)- Bundle mechanics (
rule_group) - Configuration vs cart response
- Common mistakes β
rule_effects
Common fields in GETβ
| Field | Role |
|---|---|
key / type | Identify the effect; usually match (percentage, direct_value, β¦) |
value | Main magnitude (% or amount). Does not apply to strikethrough or formula |
unit | Only **ratio**: monetary reference unit (e.g. every β¬10) |
distribution | How the effect is split (see per-type table below) |
limits | Maximum times the effect can apply in the same transaction |
rule_group | true = grouping / bundle (only percentage, direct_value, combo_price). Requires quantities in conditions + application β see Bundle mechanics |
formula | Only **formula** effect: fixed calculation expression |
strikethrough_data | Only **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.
| Value | Interpretation |
|---|---|
0 | No limit: the effect may apply as many times as conditions and application allow |
> 0 | At most that many effect applications on the cart |
What counts as one application depends on distribution and rule_group:
| Configuration | What counts as 1 application | What limits: N caps |
|---|---|---|
distribution: individual_items and rule_group null / false | One 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_application | Maximum 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,limitscounts units (internal qty-1 lines), not necessarily rows in thelines[]JSON.limits: 10with 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
limitsand 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.
| Field | Value | Why |
|---|---|---|
type | percentage | Percentage discount |
value | 5 | 5% |
distribution | individual_items | The % is calculated per unit (internal qty-1 line) |
rule_group | null or false | No grouping: each application = one unit |
limits | 10 | At 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).
| Field | Value | Why |
|---|---|---|
rule_group | true | Each application = one group (2+1 pair), not a standalone line |
limits | 2 | At 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β
| Effect | total | individual_items | individual_items_pro_rata |
|---|---|---|---|
percentage | β | β | β |
direct_value | β | β | β |
ratio | β | β | β |
strikethrough | β | β (only) | β |
combo_price | β | β | β |
formula | β | β (only) | β |
| Distribution | Brief behavior |
|---|---|
total | A single discount on the qualified set / transaction total |
individual_items | The effect is evaluated per unit (each unit as an internal qty-1 line; see unit expansion) |
individual_items_pro_rata | The benefit is split proportionally across qualified lines |
Stacking (stackability) by effectβ
| Effect | Cart mode |
|---|---|
strikethrough | Always Highest Discount |
combo_price | Always Highest Discount |
percentage, ratio, direct_value, formula | Stackable 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"
}
| Field | Reading |
|---|---|
value: 20 | 20% discount |
distribution: individual_items | The % applies on each line that meets application |
limits: 0 | No cap on bonified units; with limits: 10, at most 10 units β see % cap example |
rule_group: null | No 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"
}
| Field | Reading |
|---|---|
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_group | Same 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"
}
| Field | Reading of the example |
|---|---|
unit: 10, value: 1 | Every β¬10 qualified β 1 of discount (or other benefit per campaign mode) |
distribution: individual_items_pro_rata | That benefit is prorated across participating lines |
limits: 2 | At 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
ratiotype may grant balance (points or wallet) instead of a monetary discount β checkcurrency/ 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
}
}
| Field | Reading |
|---|---|
strikethrough_data | Map { "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 value | Discount amount comes from per-line calculation (cart price β promo price) |
distribution | Always individual_items (one evaluation per line / product) |
limits | Same 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"
}
| Field | Reading |
|---|---|
value: 10.9 | Total combo price (β¬10.9) |
rule_group: true | Engine groups lines that meet bundle mechanics before setting combo price |
distribution: individual_items_pro_rata | Combo savings are split across group lines. Alternative: total (single block on the set) |
limits: 0 | No 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"
}
| Field | Reading |
|---|---|
formula | Fixed in configuration: lines.total - lines.attributes.promoPrice |
No value | Amount comes from per-line calculation |
distribution | Always individual_items |
rule_group | Does 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_group | Compatible effect types |
|---|---|
true | percentage, direct_value, combo_price |
false / null | Any effect without grouping |
| Not available | ratio, strikethrough, formula |
The three blocks that define the bundleβ
| Block | Question it answers | What 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β
| Step | Reading |
|---|---|
Conditions quantity: 2 | Each valid group = 2 units of product 2222 on the cart |
Application quantity: 1 | Within each group, 100% applies to 1 unit (the βfreeβ or bonified unit in 2x1) |
rule_group: true | Engine groups before calculating; without this, % would be read line by line without 2x1 mechanics |
distribution: individual_items_pro_rata | Split of the bonified unitβs discount across group lines (typical in 2x1) |
Ticket with 8 units of 2222 | 8 Γ· 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.
limits | In the 2x1 example |
|---|---|
0 | No cap: all possible groups on the cart get benefit (8 units β 4 groups β 4 bonifications) |
1 | Only 1 group gets the effect in the transaction (1 βfreeβ product even if more groups are possible) |
2 | At 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 withrule_group, or lines withindividual_itemswithout 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β
| Mechanics | Conditions (group) | Application (benefit) | Typical effect |
|---|---|---|---|
| 2x1 same product | quantity gte 2 on product | quantity gte 1 on same product | percentage: 100 + rule_group: true |
| 3x2 on category | quantity gte 3 on category | quantity gte 1 on category | percentage: 100 + rule_group: true |
| Bundle A + B + C products for β¬10 | quantity gte 1 on A + quantity gte 1 on B + quantity gte 1 on C | bundle line filter | combo_price + rule_group: true |
| Fixed β¬ per group | quantity gte N | quantity gte 1 | direct_value + rule_group: true |
Common mistakes β bundleβ
| Symptom | Usual cause |
|---|---|
| 2x1 does not work | rule_group: false or missing quantity subcondition in conditions or application |
| Over-bonifies | limits: 0 with many groups on the cart; raise limits to cap bonified groups |
| Under-bonifies | Low limits (e.g. 1) with many units; or conditions require more units than on the cart |
| Different product in application | values / field in application do not match win conditions |
Confusing bundle/grouped flags | They do not enable mechanics alone; need **rule_group: true** and correct **quantity** values |
Configuration vs cart responseβ
| Context | Field | Contents |
|---|---|---|
| Campaign in GET | rule_effects (often JSON string) | Configuration: type, value, distribution, limits, β¦ |
| Evaluated cart / purchase | effects[] | Runtime result per line or block |
Typical fields in cart **effects[]**:
| Cart field | Relation to config |
|---|---|
effectType | Maps to rule_effects.type (percentage, direct_value, β¦) |
distribution_type | Maps to rule_effects.distribution |
discount_value | Calculated amount already applied on that line or block |
rule_id | ID 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β
| Confusion | How to resolve it |
|---|---|
limits: 0 βdisables limitsβ | Correct: no cap on effect applications in the transaction |
One line with quantity: 20 and limits: 10 | Engine expands to 20 units; only 10 get the effect; response may still be one line with aggregated discount β see unit expansion |
limits vs win limits | rule_effects.limits = effect repetitions; once / num_* = how many times the customer can win the campaign |
| Bundle / 2x1 | Trio: 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_items | Invalid in product; use total or individual_items_pro_rata |
formula without promoPrice on line | Incomplete integration; discount 0 on that line |
| % or ratio without application on cart | Check rule_application β usually required for line-level % |
| Same % in config, different on the cart | Check stackability (Highest Discount won another campaign) and effects[] |
Back: Reading from GET Β· Checklist