Saltar al contenido principal

Guía: Lectura desde GET · ← Aplicación

rule_effects — referencia completa

:::info Campo en la respuesta GET En GET /points y GET /points/{id}, este bloque viene en **rule_effects** (string JSON). Valida rule_group para bundle (no variantes legacy como rule_grouped en samples antiguos). :::

Define qué beneficio otorga la campaña (puntos fijos, ratio, porcentaje sobre líneas/total, etc.) y cómo se reparte entre el ticket y las líneas.

Pregunta que responde: «Si la campaña ganó, ¿cuántos puntos/saldo otorga y sobre qué base del carrito o del evento?»

Índice​


Matriz de efectos y distribuciones por when​

whenEfectos permitidosDistribucionesrule_group / limits
after_create_activity (carrito)percentage, ratio, direct_value, strikethrough, combo_price, formulaLas tres (total, individual_items, individual_items_pro_rata) con restricciones por tipo — ver tabla abajoSí en percentage, direct_value, combo_price
Slug de triggerSolo direct_valueSolo totalNo (rule_group: false)
after_create_custom_entitypercentage, ratio, direct_valueSolo totalNo
manualSolo direct_valueSolo totalNo

Restricciones de distribución por tipo de efecto (solo carrito; en trigger, custom event y manual aplica solo la fila de la matriz):

Efectototalindividual_itemsindividual_items_pro_rata
percentage✓✓✓
direct_value✓✓✓
ratio✓✓✓
strikethrough—✓ (única)—
combo_price✓—✓
formula—✓ (única)—

Para trigger, evento personalizado y manual: distribución siempre total, sin bundle (rule_group: false). Los límites de efecto (limits) y la mecánica bundle solo aplican en carrito.

Valor monetario de value (puntos y wallet)​

:::warning Lectura obligatoria para integraciones En campañas de puntos/wallet, el campo value de rule_effects se persiste siempre en valor monetario en base de datos, independientemente de accrual_mode (points o money). No interpretes value como «número de puntos» salvo que hayas convertido desde moneda. :::

Requisitos asociados a cada campaña:

RequisitoPara qué sirve
currency en la campañaMoneda en la que está expresado el value monetario.
score_exchange de esa moneda (configuración de Company)Cuántas unidades monetarias equivalen a 1 punto (tipo de cambio puntos ↔ dinero). Cada moneda suele tener un único score_exchange.

Conversión a puntos (cuando accrual_mode es points o cuando necesitas mostrar unidades al customer):

puntos_otorgados = value_monetario / score_exchange

Ejemplo: regla «1 punto cada 10 €» con punto que vale 0,01 €:

Concepto de negocioValor en GET / BD (rule_effects.value)
1 punto = 0,01 €0.01 (moneda de la campaña, p. ej. EUR)
Ratio «cada 10 € → 1 punto»En ratio, unit: 10 y value: 0.01 (no value: 1 en puntos)

Si la integración muestra puntos al customer, divide (o aplica la fórmula acordada con la API) usando el score_exchange vigente para currency. Con accrual_mode: money, el saldo en wallet suele alinearse 1:1 con el importe monetario acumulado; aun así, en configuración el value del efecto sigue guardándose en moneda.

En scores[] del ticket, el motor devuelve el otorgamiento ya calculado; al depurar discrepancias, parte siempre del value monetario del GET y del score_exchange, no de un conteo literal de puntos en rule_effects.


Aquí **rule_group** (agrupación bundle en el efecto) sí importa para integraciones — a diferencia de bundle / grouped en rule_conditions, que son internos y se ignoran.

Campos comunes en GET​

CampoRol
key / typeIdentifican el efecto; suelen coincidir (percentage, direct_value, …)
valueMagnitud principal: en puntos/wallet, importe monetario (direct_value, base del ratio) o porcentaje (percentage). Ver valor monetario. No aplica en strikethrough ni en formula
unitSolo **ratio**: unidad monetaria de referencia (p. ej. cada 10 €)
distributionCómo se reparte el efecto (ver tabla por tipo más abajo)
limitsVeces máximas que el efecto puede aplicar en la misma transacción
rule_grouptrue = agrupación / bundle (solo percentage, direct_value, combo_price). Requiere cantidades en condiciones + aplicación — ver Mecánica bundle
formulaSolo efecto **formula**: expresión fija de cálculo
strikethrough_dataSolo **strikethrough**: mapa clave producto → precio promocional. La clave puede ser **product_id** o **product_code** según use la compañía (debe coincidir con el identificador de línea en el ticket)

limits (límite del efecto, no de victoria)​

limits en rule_effects acota cuántas veces puede aplicarse el efecto en la misma transacción (mismo ticket). No es el tope de victoria de la campaña (once, presupuesto, etc.) ni un límite de unidades en rule_conditions.

ValorInterpretación
0Sin límite: el efecto puede aplicarse todas las veces que correspondan según condiciones y aplicación
> 0Como máximo esa cantidad de aplicaciones del efecto en el ticket

Qué cuenta como «una aplicación» depende de distribution y de rule_group:

ConfiguraciónQué cuenta como 1 aplicaciónQué limita limits: N
distribution: individual_items y rule_group null / falseUna unidad (línea interna de cantidad 1; ver expansión abajo)Máximo de unidades bonificadas en el ticket
rule_group: true (bundle / 2x1 / combo)Un grupo armado según quantity en rule_conditions + rule_applicationMáximo de grupos que reciben el beneficio (p. ej. cuántos 2x1)

Una unidad = una línea en el motor (aunque el ticket traiga quantity > 1)​

Para el cálculo de efectos, Loyal Guru trata cada unidad como una línea independiente. Si la integración envía una sola línea con quantity: 10, el motor la descompone internamente en 10 líneas de cantidad 1, ejecuta condiciones, rule_application, efectos y limits sobre ese modelo unitario y, al terminar, reagrupa el resultado en las mismas líneas que llegaron en el ticket (mismo número de líneas y cantidades que envió el retailer).

Implicaciones para integraciones:

  • Con distribution: individual_items, limits cuenta unidades (líneas internas de qty 1), no necesariamente filas del JSON de lines[]. limits: 10 con una línea agregada de qty 15 → como mucho 10 unidades con descuento en esa línea (las 5 restantes de esa línea, al precio pleno para ese efecto).
  • En POST /activities, effects[] puede mostrar el otorgamiento consolidado por línea original; la lógica interna siguió el modelo unitario.
  • Condiciones de victoria y aplicación también se evalúan sobre unidades expandidas cuando el motor lo requiere; la expansión es transparente para quien solo lee el GET de campaña, pero importa al interpretar limits y al depurar tickets con líneas multi-cantidad.

Ejemplo: % por producto con tope de unidades (sin bundle)​

Objetivo: 5 % sobre determinados productos, como máximo en 10 unidades del ticket.

CampoValorPor qué
typepercentageDescuento porcentual
value55 %
distributionindividual_itemsEl % se calcula por unidad (línea interna de qty 1)
rule_groupnull o falseSin agrupación: cada aplicación = una unidad
limits10Como mucho 10 unidades reciben el 5 % en esa compra

rule_application debe acotar los productos (p. ej. product_id o product_categories en las líneas). Si el ticket trae una línea con quantity: 15 del producto elegible, el motor la trata como 15 unidades; con limits: 10 solo 10 llevan el 5 % (en la respuesta suele verse reflejado en esa misma línea agregada). Si hay varias líneas en lines[], el tope de 10 se reparte entre unidades de todas ellas según prioridad/apilamiento.

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

Ejemplo: 2x1 con tope de grupos (con rule_group)​

Objetivo: mecánica 2x1 pero solo dos 2x1 por ticket (dos grupos bonificados).

CampoValorPor qué
rule_grouptrueCada aplicación = un grupo (par 2+1), no una línea suelta
limits2Como mucho 2 grupos con beneficio en la transacción

Condiciones de victoria (rule_conditions) definen el tamaño del grupo (p. ej. quantity ≥ 2); rule_application cuántas unidades del grupo llevan dto. (p. ej. ≥ 1). Detalle completo: Mecánica bundle (rule_group) — ahí el mismo ticket con 8 unidades y limits: 2 solo bonifica 2 de 4 grupos posibles.

No confundir con límites de victoria (once, num_per_day, presupuesto, etc.) en la configuración general de la campaña.

Distribuciones permitidas por tipo de efecto​

Efectototalindividual_itemsindividual_items_pro_rata
percentage✓✓✓
direct_value✓✓✓
ratio✓✓✓
strikethrough—✓ (única)—
combo_price✓—✓
formula—✓ (única)—

Restricciones de distribución por when​

whenDistribuciones permitidas
CarritoSegún tipo de efecto (tabla anterior)
Trigger, evento personalizado, manualSolo total
DistribuciónComportamiento breve
totalUn solo otorgamiento sobre el conjunto calificado / total de la transacción
individual_itemsEl efecto se evalúa por unidad (cada unidad como línea interna de qty 1; ver expansión unitaria)
individual_items_pro_rataEl beneficio se reparte proporcionalmente entre las líneas calificadas

Reglas de acumulación por efecto​

EfectoModo en carrito
strikethroughSiempre Highest Discount
combo_priceSiempre Highest Discount
percentage, ratio, direct_value, formulaStackable o Highest Discount (configurable)

Detalle: Acumulación.


percentage​

:::info Disponibilidad Solo evento carrito (after_create_activity). En trigger, evento personalizado y manual no está disponible — ver matriz por when. :::

Otorgamiento porcentual sobre líneas o total calificado (saldo en moneda según el % sobre la base; ver valor monetario).

{
"key": "percentage",
"type": "percentage",
"value": 20,
"limits": 0,
"formula": null,
"rule_group": null,
"distribution": "individual_items"
}
CampoLectura
value: 2020 % de descuento
distribution: individual_itemsEl % se aplica en cada línea que cumpla aplicación
limits: 0Sin tope de unidades bonificadas; con limits: 10, como mucho 10 unidades — ver ejemplo % con tope
rule_group: nullSin agrupación (p. ej. % simple en categoría). Con true, activa grupos según quantity en condiciones y aplicación

Integración: si el otorgamiento no aparece en las líneas esperadas del ticket, revisar **rule_application** además del % — en carrito, la aplicación suele ser obligatoria para % por línea (Detalles operativos §5).


direct_value​

:::info Disponibilidad Carrito: sí. Trigger, evento personalizado, manual: solo este tipo, con distribution: total. :::

Importe fijo de otorgamiento, expresado en moneda de la campaña en value (ver valor monetario).

{
"key": "direct_value",
"type": "direct_value",
"value": 5,
"limits": 0,
"formula": null,
"rule_group": null,
"distribution": "individual_items"
}
CampoLectura
value: 55 € (u otra moneda) de saldo monetario según currency; en modo puntos, convertir a unidades con score_exchange
distribution: individual_items5 € por línea calificada; con total, un único bloque de 5 € sobre el conjunto
rule_groupIgual que en percentage: true para bundles que reparten un importe fijo en grupo

Caso frecuente: €5 de dto. en cesta → direct_value, distribution: total, condición total_cart >= X.


ratio​

:::info Disponibilidad Carrito y evento personalizado. No en trigger ni manual. :::

Otorgamiento proporcional al gasto: por cada unit (importe en moneda de la campaña) gastado en base calificada, se otorga value en moneda (no en puntos enteros), repartido según distribution.

{
"key": "ratio",
"type": "ratio",
"unit": 10,
"value": 0.01,
"limits": 2,
"formula": null,
"distribution": "individual_items_pro_rata"
}
CampoLectura del ejemplo
unit: 10, value: 0.01Cada 10 € calificados → 0,01 € de saldo (= 1 punto si score_exchange es 0,01)
distribution: individual_items_pro_rataEse beneficio se prorratea entre las líneas que participan
limits: 2Como máximo 2 aplicaciones del ratio en la misma transacción (p. ej. hasta 20 € de base → 2 × 1 de beneficio si la regla lo permite)

value y unit en ratio están en moneda. Con accrual_mode: points, traduce el resultado a puntos con score_exchange antes de mostrarlo al customer.


strikethrough​

:::info Disponibilidad Solo carrito. Otorga saldo equivalente a la diferencia precio ticket − precio promocional (misma mecánica que descuento tachado, expresada como acumulación). :::

Sustituye el precio de la línea en el ticket por el precio promocional definido en strikethrough_data. El saldo otorgado equivale a la diferencia entre el precio enviado en el ticket y el precio promocional del efecto.

{
"key": "strikethrough",
"type": "strikethrough",
"limits": 0,
"formula": null,
"distribution": "individual_items",
"strikethrough_data": {
"233212": 1,
"343434": 9
}
}
CampoLectura
strikethrough_dataMapa { "clave_producto": precio_promocional }. La clave es el **product_id** o el **product_code** que use la compañía en catálogo/ticket — debe ser el mismo tipo de identificador que envía la integración en la línea
Sin valueLa magnitud del dto. sale del cálculo por línea (precio ticket − precio promo)
distributionSiempre individual_items (una evaluación por línea / producto)
limitsMisma semántica (0 = sin tope de aplicaciones del efecto)

Claves del mapa: en una misma campaña, las entradas suelen ser homogéneas (todas por ID numérico o todas por código). Si la compañía opera por SKU, espera claves tipo "12000041"; si opera por ID interno, claves tipo "233212".

Ejemplo numérico (por product_id): strikethrough_data["233212"]: 1, línea en ticket con ese producto y precio 3 → precio efectivo 1, descuento 2 (3 − 1).

Ejemplo (por product_code): strikethrough_data["CC-33CL-LATA"]: 0.99 — la línea del ticket debe traer el mismo product_code para que matchee.

Integración: si no aplica, comprobar tipo de clave (ID vs código) alineado con el ticket, que la clave exista en el mapa y el precio de línea enviado. Acumulación: siempre Highest Discount.


combo_price​

:::info Disponibilidad Solo carrito. Otorga saldo según precio fijo del bundle (mecánica compartida con descuentos combo). :::

Precio fijo del bundle para un grupo de productos que cumplen condiciones / aplicación.

{
"key": "combo_price",
"type": "combo_price",
"value": 10.9,
"limits": 0,
"formula": null,
"rule_group": true,
"distribution": "individual_items_pro_rata"
}
CampoLectura
value: 10.9Precio total del combo (10,9 €)
rule_group: trueEl motor agrupa líneas que cumplen la mecánica bundle antes de fijar el precio combo
distribution: individual_items_pro_rataEl ahorro del combo se reparte entre líneas del grupo. Alternativa: total (un solo bloque sobre el conjunto)
limits: 0Sin tope de veces que puede aplicar el combo en el ticket

Restricción: no usar individual_items en combo_price — solo total o individual_items_pro_rata. Acumulación: siempre Highest Discount.


formula (efecto promocional externo)​

:::info Disponibilidad Solo carrito. Saldo por línea según promoPrice (u atributo acordado) enviado por integración externa. :::

Otorgamiento calculado por línea a partir de un atributo enviado por un sistema externo (precio promo precalculado).

{
"key": "formula",
"type": "formula",
"limits": 0,
"formula": "lines.total - lines.attributes.promoPrice",
"distribution": "individual_items"
}
CampoLectura
formulaFija en configuración: lines.total - lines.attributes.promoPrice
Sin valueEl importe sale del cálculo por línea
distributionSiempre individual_items
rule_groupNo usa agrupación (formula no admite bundle)

Flujo de integración: el integrador debe enviar promoPrice (u homólogo acordado) en atributos de línea del ticket. Si falta en una línea, esa línea no recibe dto. por esta campaña. Si el resultado ≤ 0, no aplica descuento en esa línea.


Mecánica bundle (rule_group)​

En Owner App, Bundle / grupo (rule_group: true) indica al motor que forme grupos de líneas y aplique el efecto por grupo, no línea a línea de forma independiente.

Dónde aplica​

rule_effects.rule_groupTipos de efecto compatibles
truepercentage, direct_value, combo_price
false / nullCualquier efecto sin agrupación
No disponibleratio, strikethrough, formula

Los tres bloques que definen el bundle​

BloquePregunta que respondeQué validar en integración
**rule_conditions**¿Cuántas unidades hacen falta para formar un grupo?Misma cláusula de producto/categoría + subcondición quantity (ej. gte 2 → grupos de 2)
**rule_application**¿Sobre cuántas unidades del grupo cae el efecto?Misma familia de producto/categoría + subcondición quantity (ej. gte 1 → 1 unidad con beneficio por grupo)
**rule_effects**¿Qué beneficio y cuántas veces?rule_group: true, value / type, distribution, **limits**

Los flags bundle y grouped en el JSON de condiciones/aplicación son internos (ignorar valor). Lo operativo son las **quantity** anidadas y **rule_group** en el efecto.

Ejemplo completo: 2x1 del producto 2222 (100 % en 1 de cada 2)​

Victoria — armar grupos de 2 unidades (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"
}
]
}
}

Aplicación — el dto. va sobre 1 unidad por grupo (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"
}
]
}
}

Efecto — 100 % en esa unidad, con agrupación activa (rule_effects):

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

Interpretación del 2x1​

PasoLectura
Condiciones quantity: 2Cada grupo válido = 2 unidades del producto 2222 en el ticket
Aplicación quantity: 1Dentro de cada grupo, el 100 % aplica sobre 1 unidad (la “gratis” o bonificada del 2x1)
rule_group: trueEl motor agrupa antes de calcular; sin esto, el % se interpretaría línea a línea sin mecánica 2x1
distribution: individual_items_pro_rataReparto del otorgamiento de la unidad bonificada entre líneas del grupo (habitual en 2x1)
Ticket con 8 unidades 22228 ÷ 2 = 4 grupos → hasta 4 aplicaciones del efecto (4 unidades bonificadas si no hay tope)

limits del efecto con bundles​

Con rule_group: true, cada aplicación del efecto es un grupo (no una línea suelta). Por eso limits: 2 en un 2x1 significa «como mucho dos 2x1 en el ticket», no «dos unidades en total». Ver también ejemplo 2x1 con tope de grupos.

limitsEn el ejemplo 2x1
0Sin tope: todos los grupos posibles en el ticket reciben beneficio (8 uds. → 4 grupos → 4 bonificaciones)
1Solo 1 grupo recibe el efecto en la transacción (1 producto “gratis”, aunque haya más grupos posibles)
2Máximo 2 grupos (dos 2x1); con 8 uds. en ticket solo se bonifican 2 de los 4 grupos posibles

**limits** cuenta aplicaciones del efecto por transacción (grupos bonificados con rule_group, o líneas con individual_items sin grupo), no unidades totales en el carrito.

Resumen visual​

Ticket: 8 × producto 2222
│
rule_conditions (qty ≥ 2) → 4 grupos de 2
│
rule_application (qty ≥ 1) → 1 unidad con dto. por grupo
│
rule_effects (100%, rule_group) + limits
│
limits: 0 → 4 bonificaciones (4 grupos)
limits: 2 → 2 bonificaciones (solo 2 de 4 grupos)

Variantes habituales​

MecánicaCondiciones (grupo)Aplicación (beneficio)Efecto típico
2x1 mismo productoquantity gte 2 en productoquantity gte 1 en mismo productopercentage: 100 + rule_group: true
3x2 en categoríaquantity gte 3 en categoríaquantity gte 1 en categoríapercentage: 100 + rule_group: true
Bundle A + B + C productos por €10quantity gte 1 en A + quantity gte 1 en B + quantity gte 1 en Cfiltro de líneas del bundlecombo_price + rule_group: true
€ fijo por grupoquantity gte Nquantity gte 1direct_value + rule_group: true

Errores frecuentes — bundle​

SíntomaCausa habitual
2x1 no funcionarule_group: false o falta subcondición quantity en condiciones o aplicación
Bonifica de máslimits: 0 con muchos grupos en ticket; subir limits para capar grupos bonificados
Bonifica de menoslimits bajo (ej. 1) con muchas unidades; o condiciones exigen más unidades que las del ticket
Producto distinto en aplicaciónvalues / field en aplicación no coinciden con condiciones de victoria
Confundir flags bundle/groupedNo activan la mecánica solos; hace falta **rule_group: true** y las **quantity** correctas

Configuración vs respuesta de ticket​

ContextoCampoQué contiene
Campaña en GETrule_effects (a menudo string JSON)Configuración: type, value, distribution, limits, …
Ticket / compra evaluadascores[] (principal)Puntos/saldo ya calculados por campaña o línea
Ticket (descuentos)effects[]Descuentos monetarios en la misma actividad

Campos típicos en scores[] del ticket:

Campo en ticketRelación con config
rule_id / campaign_idID de la campaña de puntos
points / amountPuntos o saldo otorgados (según contrato API)
lineLínea del ticket si la distribución es por ítem
status / fechasPuede reflejar activación/caducidad según integración

Ejemplo (ratio en carrito):

{
"scores": [{
"rule_id": 201,
"line": 1,
"points": 150,
"currency": "POINTS"
}]
}

Integración: comparar rule_effects del GET con scores[] en POST /activities (o equivalente). Si scores[] está vacío, revisar when, elegibilidad (rule_conditions, audiencia), rule_application, flag apply_points en la compañía y metadatos de activación/caducidad — no cupón ni TPV.

Activación y caducidad (metadatos GET)​

Además de rule_effects, en GET /points suelen venir campos de ciclo de vida del saldo otorgado:

CampoUso
activation_period_type + activation_delayCuándo pasan de pendientes a disponibles
expiration_period_type + availability_durationCuándo caducan
currencyMoneda de puntos o wallet

Estos campos explican diferencias entre configuración en GET y saldo visible en app/wallet del customer.


Errores frecuentes — rule_effects​

ConfusiónCómo resolverlo
limits: 0 «desactiva límites»Correcto: sin tope de aplicaciones del efecto en la transacción
Una línea con quantity: 20 y limits: 10El motor expande a 20 unidades; solo 10 reciben el efecto; la respuesta puede seguir siendo una línea con el otorgamiento agregado — ver expansión unitaria
limits vs límites de victoriarule_effects.limits = repeticiones del efecto; once / num_* = cuántas veces puede ganar la campaña el cliente
Bundle / 2x1Trío: quantity en rule_conditions (tamaño grupo) + quantity en rule_application (uds. con dto.) + rule_group: true + limits
strikethrough «no descontó lo esperado»Clave del mapa (product_id vs product_code) debe coincidir con el identificador de la línea en el ticket; dto. = precio ticket − precio promo
combo_price con individual_itemsInválido en producto; usar total o individual_items_pro_rata
formula sin promoPrice en líneaIntegración incompleta; dto. 0 en esa línea
% o ratio sin aplicación en carritoRevisar rule_application — suele ser obligatoria para % por línea
Mismo % en config y distinto en ticketRevisar acumulación entre campañas y scores[]


Volver: Lectura desde GET · Checklist