Skip to main content

Campaigns API

The Loyal Guru Streaming API exposes endpoints to create, read, and update campaigns programmatically. Campaigns are separated into two endpoints based on their type.

Endpoints​

EndpointDescription
POST /discountsCreate a discount campaign
PATCH /discounts/:idUpdate an existing discount campaign
GET /discountsList all discount campaigns
POST /pointsCreate a points/wallet campaign
PATCH /points/:idUpdate an existing points/wallet campaign
GET /pointsList all points/wallet campaigns. Supports ?when=manual filter to retrieve exclusively Direct Assignment campaigns.

Creating a Discount Campaign (POST /discounts)​

Required fields (always)​

FieldTypeDescription
namestringUnique campaign name
whenstringMust be "after_create_activity"
loyalty_rule_category_idnumberID of an existing campaign category
currencystringISO currency code (must be active and have a score exchange defined)
select_customersstring"all_customers", "loyalty", or "targeted"
rule_conditionsstring (JSON)Victory conditions. Must include at least one country condition
rule_effectsstring (JSON)Effect definition (see below)
stackability_propertiesstring"stackability" or "highest_discount"

Fields with defaults (optional)​

FieldDefaultNotes
activefalseSet to true to activate on creation
recurrence_type"daily""daily", "weekly", "monthly", "monthly_last"
once"always"Frequency limit: "always", "num_day", "num_month", "num_year", "num_total"
coupon_requiredfalse (or true if targeted)Targeted campaigns always require a coupon
chance_to_accrue_period"always"Chances to accrue frequency

Effect structure (rule_effects)​

{
"key": "percentage",
"type": "percentage",
"value": 15,
"limits": 0,
"rule_group": false,
"distribution": "individual_items"
}
FieldDescription
typeOne of: percentage, ratio, direct_value, strikethrough, combo_price, formula
keyMust equal type
valueNumeric discount value. Not required for strikethrough (use strikethrough_data instead)
limitsInteger β‰₯ 0. Maximum times this effect can apply per transaction (0 = unlimited)
rule_groupBoolean. Enables bundle/group mode. Only for percentage, combo_price, direct_value
distribution"total", "individual_items", or "individual_items_pro_rata"

For ratio type, include unit:

{
"key": "ratio",
"type": "ratio",
"value": 1,
"unit": 1,
"limits": 0,
"rule_group": false,
"distribution": "individual_items"
}

For strikethrough type, include strikethrough_data instead of value. Map keys are product_id or product_code per company (must match the product identifier on ticket lines):

{
"key": "strikethrough",
"type": "strikethrough",
"strikethrough_data": {"123456": 19.99, "CC-33CL-LATA": 14.99},
"limits": 0,
"rule_group": false,
"distribution": "individual_items"
}

For formula type, use this exact value:

{
"key": "formula",
"type": "formula",
"value": "lines.total - lines.attributes.promoPrice",
"limits": 0,
"rule_group": false,
"distribution": "individual_items"
}

Stackability and priority​

stackability_propertiesRequires
"stackability"priority field (integer β‰₯ 1)
"highest_discount"No priority needed
{
"stackability_properties": "stackability",
"priority": 1
}

Creating a Points Campaign (POST /points)​

Points campaigns follow the same structure as discount campaigns with these differences:

when field​

Can be any valid moment slug for points/wallet (challenges are documented separately):

  • after_create_activity (cart)
  • after_create_custom_entity (custom event)
  • manual (direct assignment)
  • Any Company-configured trigger slug

:::info Audience in points Points campaigns require an identified customer to accrue balance. Use select_customers: "loyalty" or "targeted" only β€” all_customers does not apply (unlike mass discount campaigns). See Reading from GET β€” points integrations. :::

Additional fields for points campaigns​

FieldDefaultDescription
transferablefalseWhether earned points can be transferred to other customers
activation_period_type"relative""relative" or "fixed"
expiration_period_type"relative""relative" or "fixed"

Activation (relative):

{
"activation_period_type": "relative",
"activation_delay": 0,
"activation_delay_unit": "days"
}

Activation (fixed):

{
"activation_period_type": "fixed",
"activation_fixed_option_selected": "at_the_beginning_of_the_calendar_month"
}

Expiration (relative):

{
"expiration_period_type": "relative",
"availability_duration": 3,
"availability_duration_unit": "months",
"availability_duration_natural": false
}

Effect constraints by when​

Full matrix (distributions, rule_conditions, rule_application): points integrations β€” rule effects.

when valueAllowed effectsNotes
after_create_activityAll 6 types (percentage, ratio, direct_value, strikethrough, combo_price, formula)Full distribution options; rule_group / limits where applicable
after_create_custom_entitypercentage, ratio, direct_valueDistribution must be total; rule_group: false
manual (Direct Assignment)direct_value onlyDistribution must be total; rule_group: false; select_customers must be "loyalty"; rule_conditions must be empty {"conditions": {"and": []}}; stackability fixed to priority 1
Any other trigger slugdirect_value onlyDistribution must be total; rule_group: false; select_customers must be "loyalty"

Coupon parameters (coupon_params)​

coupon_params is required when coupon_required = true or select_customers = "targeted".

{
"coupon_params": {
"static_code": "SUMMER25"
}
}

For targeted campaigns, additional fields:

FieldDefaultDescription
auto_selectfalseApply automatically without customer presenting code
available_daysnullCoupon validity duration (days from send). Requires available_delay_days
available_delay_daysnullDelay before coupon activates (days from send). Requires available_days
printablefalseWhether the coupon is printable
print_days0How many days after send the coupon is printable
print_delay_days0Delay before coupon becomes printable
priority1Printing display priority

static_code must be unique across campaigns/coupons. Attempting to create another campaign or coupon with an existing static code returns a 400 error.

A static code is the shared code for one campaign/coupon. It cannot be reused as the static code of a different campaign, because the platform must resolve the code to one campaign unambiguously. Within that campaign, multiple Customer assignments or Vouchers can still exist behind the same static code; if redemption needs to choose between several matching Vouchers, the system uses the Voucher with the earliest expiration date.


Redeeming coupons during a purchase​

Pass the coupon or voucher code in coupons_desired:

{
"id": 5001,
"customer_id": 201,
"total": 120.00,
"currency": "EUR",
"coupons_desired": ["SUMMER25"],
"lines": [...]
}

coupons_desired accepts both static coupon codes and unique voucher codes. It replaces the deprecated vouchers_desired field.


Response β€” applied effects​

{
"effects": [
{
"rule_id": 2324,
"line": 2,
"rule_name": "Summer 25%",
"type": "discount",
"effectType": "percentage",
"distribution_type": "individual_items",
"select_customers": "loyalty",
"coupon_info": {
"voucher_id": null,
"voucher_code": "SUMMER25",
"coupon_id": 122211,
"coupon_name": "Summer Sale"
}
}
]
}

coupon_info is null when no coupon was involved.