Campaigns

Campaigns define how and when members are rewarded — with points, a tangible reward (coupon, gift card, free item, discount voucher), or both. Every earn transaction is evaluated against all active campaigns for the tenant, and multiple campaigns can match and pay out simultaneously.

Earn rule types

The engine supports nine earn rule types across two evaluation paths: POS rules (evaluated on POST /transactions/evaluate) and lifecycle rules (evaluated on POST /members/:id/events).

SPEND_PERCENTAGE

Award points as a percentage of the basket total. A 1% rate on a R150 purchase awards 150 points. Set rate (percentage) and optionally minBasketCents.

FIXED_POINTS

Award a flat number of points per qualifying transaction. Set points and optionally minBasketCents.

VISIT_FIXED

Award fixed points per store visit, regardless of basket value.

PRODUCT_PURCHASE

Award points when specific SKUs or product categories appear in the basket.

RANDOM

Award a random points amount within a range, with an optional win probability.

STAMP_CARD

Collect N stamps; issue a reward or points on completion. Stamps come from transactions — or from a custom event when triggerEventKey is set (one stamp per matching event).

SURPRISE_DELIGHT

Every Nth qualifying transaction triggers a reward — deterministic cadence, not probability.

SIGNUP_BONUS

Lifecycle rule: fires once per member on their first SIGNUP event. Can award points or a tangible reward.

ON_DEMAND

Lifecycle rule: fires on any member event (BIRTHDAY, ANNIVERSARY, CUSTOM, …). Can award points or a tangible reward.

REFERRAL_BONUS

Lifecycle rule: fires on a REFERRAL event — points to the referred member plus an optional referrer bonus.

EVENT_TRIGGER

Fires when a custom event matches eventKey, with optional payload conditions. Awards points or a tangible reward.

Signup bonus & on-demand rewards

SIGNUP_BONUS and ON_DEMAND are lifecycle rules — they fire on member events rather than POS transactions. Both support a rewardType field that switches the outcome from points to a tangible reward.

Available rewardType values: POINTS (default), COUPON_VOUCHER, GIFT_CARD, DISCOUNT_VOUCHER, FREE_ITEM.

JSON
// SIGNUP_BONUS — welcome voucher instead of points
{
  "type":        "SIGNUP_BONUS",
  "rewardType":  "COUPON_VOUCHER",
  "provider":    "internal",
  "description": "Welcome voucher — R20 off your next visit"
}

// ON_DEMAND — birthday discount voucher
{
  "type":         "ON_DEMAND",
  "rewardType":   "DISCOUNT_VOUCHER",
  "discountType": "PERCENTAGE",
  "value":        15,
  "description":  "Happy Birthday — 15% off your next purchase",
  "maxPerMember": 1
}

Tangible rewards are issued asynchronously after the event is processed. The fulfilment worker generates the reward code and updates the reward state from PENDING_FULFILMENT to ISSUED. Poll GET /members/:memberId/rewards to retrieve issued codes.

Surprise & Delight

SURPRISE_DELIGHT fires on a deterministic cadence — every Nth qualifying POS transaction triggers a reward. Unlike RANDOM (probability-based), the Nth-transaction mechanic is predictable and ideal for "every 5th visit wins a prize" programmes.

JSON
{
  "type": "SURPRISE_DELIGHT",
  "n":    5,
  "rewardRules": [
    {
      "n":      5,
      "reward": {
        "type":        "FREE_ITEM",
        "description": "Free slice of cake on your 5th visit"
      }
    }
  ]
}

reward accepts any reward spec: COUPON_VOUCHER, FREE_ITEM, GIFT_CARD, or DISCOUNT_VOUCHER. Multiple rules can be defined at different N thresholds within the same campaign.

Campaign states

Every campaign moves through a simple state machine:

Draft

Campaign is being configured. Not evaluated at checkout.

Active

Campaign is live. Evaluated for every transaction.

Paused

Temporarily stopped. Not evaluated but retains its configuration.

Archived

Permanently ended. Historical data is preserved.

API-managed campaigns

Campaigns created via the API are marked as API-managed in the merchant portal. These campaigns can only be updated or deleted via the API — the portal UI disables editing to prevent conflicts.

Use X-API-Managed: true when creating campaigns programmatically if you want to enforce this constraint.

Budget constraints

Set budgetCents on a campaign to cap total points awarded. Once the budget is exhausted, the campaign stops matching new transactions automatically — no manual intervention required.

Campaigns — Loyalty Engine Docs