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β
| Endpoint | Description |
|---|---|
POST /discounts | Create a discount campaign |
PATCH /discounts/:id | Update an existing discount campaign |
GET /discounts | List all discount campaigns |
POST /points | Create a points/wallet campaign |
PATCH /points/:id | Update an existing points/wallet campaign |
GET /points | List all points/wallet campaigns. Supports ?when=manual filter to retrieve exclusively Direct Assignment campaigns. |
Creating a Discount Campaign (POST /discounts)β
Required fields (always)β
| Field | Type | Description |
|---|---|---|
name | string | Unique campaign name |
when | string | Must be "after_create_activity" |
loyalty_rule_category_id | number | ID of an existing campaign category |
currency | string | ISO currency code (must be active and have a score exchange defined) |
select_customers | string | "all_customers", "loyalty", or "targeted" |
rule_conditions | string (JSON) | Victory conditions. Must include at least one country condition |
rule_effects | string (JSON) | Effect definition (see below) |
stackability_properties | string | "stackability" or "highest_discount" |
Fields with defaults (optional)β
| Field | Default | Notes |
|---|---|---|
active | false | Set 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_required | false (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"
}
| Field | Description |
|---|---|
type | One of: percentage, ratio, direct_value, strikethrough, combo_price, formula |
key | Must equal type |
value | Numeric discount value. Not required for strikethrough (use strikethrough_data instead) |
limits | Integer β₯ 0. Maximum times this effect can apply per transaction (0 = unlimited) |
rule_group | Boolean. 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_properties | Requires |
|---|---|
"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β
| Field | Default | Description |
|---|---|---|
transferable | false | Whether 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 value | Allowed effects | Notes |
|---|---|---|
after_create_activity | All 6 types (percentage, ratio, direct_value, strikethrough, combo_price, formula) | Full distribution options; rule_group / limits where applicable |
after_create_custom_entity | percentage, ratio, direct_value | Distribution must be total; rule_group: false |
manual (Direct Assignment) | direct_value only | Distribution 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 slug | direct_value only | Distribution 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:
| Field | Default | Description |
|---|---|---|
auto_select | false | Apply automatically without customer presenting code |
available_days | null | Coupon validity duration (days from send). Requires available_delay_days |
available_delay_days | null | Delay before coupon activates (days from send). Requires available_days |
printable | false | Whether the coupon is printable |
print_days | 0 | How many days after send the coupon is printable |
print_delay_days | 0 | Delay before coupon becomes printable |
priority | 1 | Printing display priority |
static_codemust 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_desiredaccepts both static coupon codes and unique voucher codes. It replaces the deprecatedvouchers_desiredfield.
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.