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.
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).
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.
// 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 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.
{
"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.
Every campaign moves through a simple state machine:
Campaign is being configured. Not evaluated at checkout.
Campaign is live. Evaluated for every transaction.
Temporarily stopped. Not evaluated but retains its configuration.
Permanently ended. Historical data is preserved.
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.
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.