Loyalty Program
Your brand can run a full points-and-tiers loyalty program: customers earn points on their spending, climb named tiers, and redeem points for rewards from your promo catalog. Everything runs automatically once configured — points accrue nightly, tier changes can trigger journeys, and balances are available in every email.
Where: Brand Settings → Loyalty (/brand?tab=loyalty).
What counts as "spend" — set this deliberately
This is the most important decision in the program, and it differs by industry:
- Commerce / retail: award on completed purchases. Handle refunds with manual adjustments so returned orders give their points back.
- SaaS / subscriptions: award on cleared subscription payments.
- Gaming & betting: award on wagers, never on deposits. Deposits are refundable — if deposits earned points, a player could deposit, collect points, and withdraw. Rates also differ by product: sportsbook stakes typically earn the full rate, while slots/casino turnover earns a fraction (the same balance cycles through slots many times over).
Earning is an explicit allowlist: nothing earns points unless you list it. That's deliberate — a catch-all rate would quietly start minting points on refundable money (deposits) the moment your data begins sending it. You list the exact event types that earn and the rate for each:
purchaseat 1 point per 1 [your currency]sportsbook_betat 1,casino_betat 0.1 — anddepositsimply isn't listed, so it can never earn.
Event types are picked from what your data actually sends (with event counts), plus a custom option for events you haven't started sending yet. If your brand's industry is recognized, the editor suggests a starting shape with the reasoning attached — apply it, then edit the event names to match your own data.
Simulate before you commit
"Simulate on my last 12 months" replays your proposed rules over your real revenue history — the same math the nightly engine runs — and shows:
- total points that would have been issued, and to how many customers,
- how points split across your spend types (this is where you catch a deposit accidentally earning),
- where customers would land across your tiers,
- worst-case reward liability: if you tell it what one point is worth in rewards, it prices every issued point as redeemed. Reality is cheaper (not all points get redeemed), but the worst case is the number to budget against.
Nothing is saved or awarded by simulating.
Tiers, and what happens when you change them
Tier standing comes from lifetime earned points — redeeming a reward never demotes anyone.
When you edit tiers on a running program, you choose the change policy:
- Grandfather existing members (default): raising a threshold never demotes someone who already earned the status. Recommended.
- Re-evaluate everyone: the new thresholds apply strictly on the next nightly run, in both directions.
Using the program in your marketing
- Tokens in any email, SMS or message:
{{p.points_balance}},{{p.loyalty_tier_name}},{{loyalty.terms_url}}. Liquid works too:{% if loyalty.points_balance > 1000 %}…{% endif %}. - Journeys: trigger on the custom event
loyalty_tier_changedto congratulate climbers (the event carriesfrom,toandpoints). - Customer 360: each person's points and tier appear in their value profile.
Integrating redemption into your product
Your customers spend points inside YOUR product — an account page, a rewards tab, a "redeem" button at checkout. Your backend talks to three endpoints, authenticated with an API key that has the Loyalty scope (Settings → API keys). Server-to-server only: never ship the key in browser code.
1. Show the balance (account page header):
curl -H "X-Api-Key: theaicmo_..." \
"https://theaicmo.com/api/loyalty/balance?identity=anna@example.com"
Returns points, lifetime points, tier, and the program shape (all tiers + thresholds + terms URL) so you can render "2,071 pts · Silver — 7,929 to Gold".
2. List the rewards shop:
curl -H "X-Api-Key: theaicmo_..." \
"https://theaicmo.com/api/loyalty/rewards"
Returns every active promo-catalog item that has a points price
(points_cost). Rewards without a points price stay out of the shop —
they're for journeys and manual grants.
3. Redeem (when the customer clicks the button):
curl -X POST -H "X-Api-Key: theaicmo_..." \
-H "Content-Type: application/json" \
-d '{"identity": "anna@example.com", "promo_id": "<reward id>",
"idempotency_key": "order-2041"}' \
https://theaicmo.com/api/loyalty/redeem
The catalog's points price is charged automatically (you can override
with points). The response is the receipt: what was spent, the grant
id, and the new balance. Safety built in:
- Idempotent: retrying with the same
idempotency_keyreturns the original receipt — never a second debit. - No overdrafts: insufficient balance answers
402with the current balance and the required amount. - The reward itself is delivered through the promo machinery like any
other grant, and a
promo_grantedevent lands in your data — so a journey can follow up ("your free spins are live").
Terms & conditions
Loyalty programs normally publish terms (how points are earned, expiry,
how changes are handled). Link yours in the editor and include it in
messages as {{loyalty.terms_url}}. Whether specific disclosures are
required for your market stays your call — encode what your license
mandates as your own guardrail rules.
Point expiry
If you set an expiry window, points age out nightly on FIFO terms:
redemptions are assumed to spend your oldest points first, and whatever
earned points fall outside the window (and weren't spent) are debited with
an expire entry in the ledger. Expiry reduces the spendable balance
only — lifetime points and tier standing are never touched.
Multiple brands
Points are a brand relationship. Each brand configures its own program,
and a customer shared between two of your brands holds a separate balance
with each — Brand B can never spend points earned at Brand A. With one
program on the account the API resolves the brand automatically; with
several, pass client_id to say which brand you mean (the API answers
brand_required rather than guessing). In multi-brand accounts, only
events stamped with a brand accrue to that brand's pot.
Honest limits (current version)
- Refunds/chargebacks don't auto-debit points yet — use manual adjustments.