Loyalty accounts (members)
Represent enrolled customers and their balances.
A loyalty account is a member: the link between a customer and a program, carrying their points balance, tier, and full transaction history. Every earn and redemption flows through an account, so getting enrollment and customer linking right is the foundation of any engagement integration.
The loyalty account objectAsk
An account (loy_...) belongs to exactly one program and, when known, references one customer. Its balances are derived from the underlying points_transaction ledger — you never set them directly.
Prop
Type
balance is spendable now; lifetime_points only ever increases and is what tiers evaluate against. A redemption lowers balance but never lifetime_points.
Enrolling membersAsk
Create an account to enroll someone explicitly — typically at signup or first checkout. Link the customer at the same time so their next payment earns immediately.
import { Vinr } from '@vinr/sdk';
const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });
const member = await vinr.loyalty.accounts.create({
program: 'prog_default',
customer: 'cust_abc123',
metadata: { signup_channel: 'web' },
});
console.log(member.id, member.balance); // "loy_7Qk..." 0If a loyalty.account.created welcome bonus rule is configured, the new member's balance reflects it on the next read.
Enroll on first qualifying event
You don't have to enroll up front. Enable auto-enrollment on the program and VINR creates an account the first time a linked customer triggers a qualifying event (such as payment.completed). The member exists from the moment they earn their first point — no separate call needed.
Auto-enrollment requires the payment to carry a customer. Anonymous guest payments cannot be auto-enrolled because there is nobody to link. See Linking payments & loyalty.
Linking to a customerAsk
The customer field is the bridge between Payments and Engagement. When a payment names that customer, its earning rules resolve to this account.
await vinr.loyalty.accounts.create({
program: 'prog_default',
customer: 'cust_abc123',
});// Attach a customer to a standalone member created earlier.
await vinr.loyalty.accounts.update('loy_7Qk...', {
customer: 'cust_abc123',
});// Avoid duplicate enrollments — resolve before creating.
const { data } = await vinr.loyalty.accounts.list({
program: 'prog_default',
customer: 'cust_abc123',
limit: 1,
});
const member = data[0] ?? await vinr.loyalty.accounts.create({
program: 'prog_default',
customer: 'cust_abc123',
});A customer may hold at most one active account per program. Attempting a second returns 409 account_already_exists with the existing loy_ id — use that to recover idempotently.
Balances and historyAsk
The account exposes a derived balance, but the source of truth is the points_transaction ledger. List transactions to show members exactly how their balance moved.
const account = await vinr.loyalty.accounts.retrieve('loy_7Qk...');
console.log(account.balance, account.lifetime_points);
const ledger = await vinr.loyalty.transactions.list({
account: 'loy_7Qk...',
limit: 20,
});
for (const ptx of ledger.data) {
// ptx_... — type: 'earn' | 'redeem' | 'expire' | 'adjust' | 'reverse'
console.log(ptx.id, ptx.type, ptx.amount, ptx.reason);
}Each transaction records its origin (the triggering payment, redemption, or manual adjustment), so the ledger doubles as an audit trail. To grant or correct points outside a rule — goodwill, support resolutions, migrations — post an adjustment rather than editing a balance:
curl -X POST https://api.vinr.com/v1/loyalty/transactions \
-H "X-Api-Key: $VINR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "account": "loy_7Qk...", "type": "adjust", "amount": 500, "reason": "goodwill_credit" }'Closing and merging accountsAsk
Close an account to retire a member while preserving their history for reporting. Closing is reversible by reopening; it does not delete the ledger.
await vinr.loyalty.accounts.close('loy_7Qk...'); // status -> "closed"When a customer ends up with two accounts in the same program — for example after merging duplicate customer records — merge them so balances and lifetime points combine into one survivor.
const survivor = await vinr.loyalty.accounts.merge({
source: 'loy_dupOld...', // emptied, status -> "merged"
target: 'loy_7Qk...', // receives source balance + lifetime points
});Merging is irreversible. The source account's balance and lifetime_points move to the target, its transactions are re-parented, and a loyalty.account.merged event fires. Reconcile any in-flight redemptions before merging.
Next stepsAsk
Linking payments & loyalty
Identify the member on every payment so purchases earn.
Tiers and status
How lifetime points promote members between tiers.
Earning rules
Configure how events award points to accounts.
Last updated on