Skip to main content

Loyalty program setup

This guide walks an operator through standing up a loyalty program end to end: designing earn rules and tiers, previewing the config safely, reading member balances, routing redemptions through the incentives engine, and putting the card on holders’ phones. It covers the workflow; the Loyalty API reference has the per-endpoint contract.

1. Mental model

Loyalty is a points-and-tiers program computed on top of the events you are already sending to the CDP — there is no separate loyalty ledger to sync. The moving parts:
  • Earn rules — named event patterns that grant points: a flat grant per event (perEvent), or a per-unit multiplier on an event property like an order total (perUnit).
  • Tiers — an ordered ladder (Bronze → Platinum) keyed on a contact’s lifetime earned points. Redemptions never lower lifetime points, so climbing a tier is one-way.
  • Balance — the spendable remainder, recomputed on demand from the full event history minus redemptions and operator adjustments.
  • Redemption — spending points routes through the Incentives fulfillment engine, the same primitive behind referrals and surveys.
Because balance derives from the CDP event stream, backfills, imports, and webhook-driven events all feed the program the moment they land — no re-sync job.

2. Prerequisites

  • CDP events flowing. Points accrue from events in your workspace. If you have not wired event ingestion, start with the CDP API reference; the event names you send (account.created, order-completed, review.submitted, …) are the names earn rules match on.
  • An API key with contacts:read for reads, contacts:write for writes. The dashboard Loyalty page is role-gated to owner and admin.
  • A notion of your rewards economics. Tiers and earn rates are free, but redemption pays out real value (a code, a gift card, a custom perk) through your own fulfillment — budget earn rates against the rewards you plan to honor.

Supported earn-rule shapes

Tiers must always include one tier at threshold 0 so every member has a current tier; the schema enforces this.

3. Configure earn rules

A program is a name, a list of earn rules, a tier ladder, and an optional point-expiry window:
If you never configure a program, Orbit still computes memberships against a sensible built-in default: a welcome bonus on signup, points per dollar on orders, a review bonus, and a four-tier Bronze → Platinum ladder with a 365-day expiry. Every rule needs a stable id so you can update or remove it later without affecting the rest, and an label for the dashboard. perUnit rules name the numeric event property (field) the multiplier applies to — usually an order total.

4. Tiers and thresholds

Tiers are an ordered ladder evaluated against lifetime earned points — redemption and expiry never demote a member. Design notes:
  • Always anchor one tier at threshold 0 so the ladder has a base. Without it the program is rejected.
  • Threshold is lifetime-earned, not current balance. A member who earned 5,000 points and burned 4,800 is still tier Gold.
  • Benefits copy is operator-authored — a display string on the member’s profile and on their wallet card.
The dashboard sorts tiers by threshold and shows each tier’s benefits stack on the program detail card.

5. Preview before it ships

POST /api/v1/loyalty/preview runs the accrual and tier engine against sample events you supply — either against your current program or an override in the same body — with no database write. Use it to answer “what would this event have earned under the new rules?” before adopting a config:
The response returns per-event point attribution and the resulting tier. A preview call runs against a validated program, so a missing base tier at threshold 0 fails there with a normal 422, not at production time.

6. Redeem points through incentives

A redemption debits points and routes the reward through the Incentives engine — the same fulfillment primitive behind referral payouts and survey thank-yous:
Two safety properties hold:
  • Concurrency-safe debit. Concurrent redemptions against the same contact serialise, so a double-click never overspends a shared balance. An attempt that exceeds the available balance returns 409 INSUFFICIENT_POINTS.
  • Operator adjustment path. POST /api/v1/loyalty/members/{contactId}/adjust credits or debits points directly — useful for customer-service gestures. A debit adjustment uses the same overspend-safe path as redemption; a credit grants points without a matching event.
For audit, every incentive issuance routes into the ledger you can filter — see Incentives.

7. Carry the card in Apple and Google Wallet

The member-facing card lives in Wallet Passes: issue a loyalty_card pass at enrollment, deliver the save link over SMS or email, and update the pass’s points and tier fields after every earn/spend event so the phone app is the holder’s source of truth. When the member redeems, that redemption flows through the same incentives engine described above, and the reward the member receives is the resulting code, gift card, or custom perk. The relationship is spelled out in Incentives § Relationship to Wallet Passes. The full pass lifecycle — issuing idempotently, updating by generation counter, voiding cleanly — is in Issue and deliver a wallet pass end to end.

8. Work the dashboard

The Loyalty page under Marketing → Loyalty mirrors the API:
  • Program card shows the active program’s name, earn rules, tiers with their benefits, and the points expiry window. A tenant on the built-in default sees a Customize program button; once a custom program exists, the same button reads Edit program. Resetting to the default from the danger zone replaces your earn rules and tiers but leaves member balances untouched.
  • Members panel lists contacts with any loyalty activity, each with its computed balance and tier. The panel fetches its own query, so a program-card outage never hides the member list.
  • Program / Redeem / Adjust dialogs are the write surfaces: the program dialog edits earn rules and tiers; Redeem points and Adjust points act on the selected member.
Tier and balance for any member can also be read programmatically via GET /api/v1/loyalty/members/{contactId}, which returns available points, lifetimeEarned, expiringSoon, and nextExpiryAt for the dashboard or your own back-office UI.

9. Analytics and fraud guardrails

Balances recompute on demand from the event stream — every GET /members/* call evaluates the full history minus redemptions, so a balance always reflects the current program and current history. This keeps the model honest: change earn rules tomorrow and the math applies to the same events; there is no data migration or re-sync job to run. Two guardrails are built in:
  • Serialised debits. Redemptions and debit adjustments on one contact run through a per-contact lock, so concurrent spend on a shared (family or business) balance cannot overspend it. A reward draw bigger than the available balance gets a structured 409 INSUFFICIENT_POINTS instead of a partial debit.
  • Event-sourced incentives. Every payout leaves an audit-grade record in the incentives ledger, filterable by source, reward_type, and contact_id. An unusual redemption velocity by one contact shows up there, not as a silent drain.
For rate-limit and cooldown behavior on the endpoints, see Rate-limit and cooldown taxonomy.