Loyalty API
Loyalty is a points-and-tiers rewards program computed on top of the events you’re already sending to the Customer Data Platform — there’s no separate ledger table to sync. Points accrue from a contact’s behavioral CDP events per rules you define (e.g.100 pts for account.created, 1 pt per $1 of order-completed), points burn through redemptions, and a contact’s balance + tier are recomputed on demand from that event history.
Base path: /api/v1/loyalty
Authentication: API key (X-API-Key) or session JWT. Reads require contacts:read; writes require contacts:write.
Program config
A program has earn rules and tiers:perEventrule — a flat point grant every time a named event fires (e.g.50points onreview.submitted).perUnitrule —points = value_in_event_property × pointsPerUnit, rounded per the rule’sroundingmode (e.g.1point per$1of an order’stotal).- Tiers — an ordered ladder (
Bronze→Platinum), each with a lifetime-pointsthresholdand operator-authoredbenefitscopy. The base tier’s threshold is0so every contact always has a current tier. pointsExpiryDays— optional; when set, points lapse this many days after they’re earned. Omit for points that never expire.
Node
POST /program (create) and GET /program return this same envelope — create responds 201, read includes program_source: "default" until you author a program. DELETE /program resets to the default and responds 204 with an empty body (the points ledger is untouched — only the config reverts).
Preview before you commit
POST /preview runs the accrual/tier engine against sample events you supply — either your current program or an override — with no database write. Use it to tune earn rules and tier thresholds before adopting them.
Node
program to run against your saved (or default) program; send program to evaluate a candidate override. Either way the response projects the balance and tier those events would produce for one contact:
Members, balance, and redemption
GET /members lists every contact with loyalty activity, each with a computed balance and tier. Pages use limit (1–100, default 25) and offset as documented in Pagination — the members list is one of the offset-based endpoint families.
Node
GET /members/{contactId} returns one contact’s full state — available points, lifetimeEarned (drives tier; redemptions never lower it), expiringSoon, and nextExpiryAt.
Node
POST /members/{contactId}/redeem atomically debits points — concurrent redemptions against the same contact can’t overspend a shared balance. It returns 409 INSUFFICIENT_POINTS when the requested amount exceeds the available balance.
Node
201 with the post-debit state:
POST /members/{contactId}/adjust is the operator override: {"direction": "credit", "points": 200, "reason": "Customer service gesture"} grants points directly; {"direction": "debit", ...} removes them through the same overspend-safe path as redemption.
Credit:
Node
"direction": "debit" with the same points / reason / reference fields. A debit responds 201 with direction: "debit", the deducted points, and the post-debit state; it returns the same 409 INSUFFICIENT_POINTS envelope as redeem when the balance can’t cover the deduction.
Segments and journeys
Every member’s computed state is exposed as traits —loyalty_points_balance, loyalty_lifetime_points, loyalty_tier, loyalty_tier_id — usable directly as entry conditions in Segments and flow triggers, e.g. loyalty_points_balance >= 500.
See also
- Loyalty program guide — step-by-step tutorial for building a program end to end
- CDP API — the event stream loyalty accrues from
- Segments API — build an audience from loyalty traits
- Pagination — page through the members list
- REST API recipes — task-by-task curl and Node cookbook