Earning rules
Decide how members earn points.
Earning rules map events — a payment, a signup, a referral — to points awarded, with multipliers, caps, and eligibility windows. They are the configurable core of a loyalty program: when an event fires, Engagement evaluates every active rule and writes a points_transaction for each match.
Rule anatomyAsk
A rule attaches to a program, listens for one trigger, and computes points from an award expression. The optional fields shape when and how much.
Prop
Type
import { Vinr } from '@vinr/sdk';
const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });
// Earn 1 point per €1 spent on any completed payment.
const rule = await vinr.loyalty.earningRules.create({
program: 'prog_default',
trigger: 'payment.completed',
award: { type: 'per_amount', per: 100, points: 1 }, // per 100 minor units (= €1)
}); // "rule_..."The award.type controls the computation:
| Type | Meaning | Example |
|---|---|---|
per_amount | Points per N minor units of amount. | 1 point per €1 → { per: 100, points: 1 } |
fixed | A flat award regardless of amount. | Welcome bonus → { points: 500 } |
per_currency | Per-currency rate, for multi-currency programs. | { EUR: 1, USD: 1, GBP: 1.2 } |
Event triggersAsk
Rules react to events from across VINR. A single program can mix triggers — purchase points, a welcome bonus, and referral rewards are three rules, not three features.
| Trigger | Typical rule |
|---|---|
payment.completed | Award per €1 spent. |
loyalty.account.created | Welcome bonus. |
subscription.created | Bonus for committing to an annual plan. |
invoice.paid | Recurring-billing loyalty. |
referral.converted | Reward referrer and referee. |
For a payment to earn, it must be linked to a member. See Linking payments & loyalty for how customer and metadata resolve to a loyalty_account.
// A flat welcome bonus when a member enrolls.
await vinr.loyalty.earningRules.create({
program: 'prog_default',
trigger: 'loyalty.account.created',
award: { type: 'fixed', points: 500 },
});Multipliers & bonusesAsk
multiplier scales a rule's award. Multipliers compose: the effective rate is the rule multiplier times any active tier multiplier times any campaign multiplier in effect at the time of the event.
// Double points on the base purchase rule.
await vinr.loyalty.earningRules.update('rule_abc123', { multiplier: 2 });A Gold member (tier multiplier 1.5) earning under a 2x rule during a 3x weekend campaign earns 1 × 2 × 1.5 × 3 = 9 points per €1. Multipliers stack multiplicatively, not additively.
Caps & limitsAsk
Caps protect against runaway awards from large or repeated transactions. A cap limits points per member within a rolling window.
// Cap purchase earnings at 5,000 points per member per calendar month.
await vinr.loyalty.earningRules.update('rule_abc123', {
cap: { points: 5000 },
window: 'month',
});When a cap is hit, the resulting points_transaction is truncated to the remaining headroom and tagged capped: true in its metadata, so you can detect and communicate the limit. Set window: 'lifetime' for one-time bonuses you never want to award twice — for example, a welcome bonus fires once per member even if the trigger repeats.
Caps are evaluated at award time, not retroactively. Lowering a cap does not claw back points already granted; it only constrains future awards.
Testing rulesAsk
Use the sandbox to fire real events without moving money. Create a payment with a sandbox card, then inspect the member's points transactions.
Simulate a qualifying event
In sandbox, create a payment for a linked customer using test card 4242 4242 4242 4242. On completion it emits payment.completed.
Inspect the resulting transactions
List the member's points_transaction records and confirm the award, multiplier, and any cap behaved as configured.
const txns = await vinr.loyalty.pointsTransactions.list({
account: 'loy_member123',
limit: 5,
});
// Each ptx_ shows source rule, points, and metadata.capped if truncated.Verify the earned webhook
Confirm your endpoint received loyalty.points.earned. Verify the signature before trusting the payload.
const event = vinr.webhooks.verify(payload, signature); // x-vinr-signature
if (event.type === 'loyalty.points.earned') {
// event.data.points, event.data.account, event.data.rule
}Next stepsAsk
Linking payments & loyalty
Make payments resolve to a member so rules can fire.
Tiers and status
Tier multipliers that stack on earning rules.
Campaigns
Time-boxed bonus multipliers for earning.
Last updated on