Pattern: loyalty tier engine
A complete loyalty layer: points earned on purchase events, three tiers (Bronze, Silver, Gold) with a points multiplier and benefits per tier, rendered client-side. Rules and tiers are configured once, then read through the SDK.
Key takeaways
Quick read- A rule reacts to a purchase event and runs an award_points action. The action grants a FIXED amount; compute proportional points in your backend.
- Tier qualification reads lifetime points and recomputes on threshold crossings, emitting tier.changed.v1.
- A tier carries a point_multiplier (applied automatically) plus a free-form benefits object you interpret. There is no separate perks engine or auto-apply.
- Use useTier on the dashboard, useRewards on the wallet, usePoints in the header.
- Liability is a real number; track outstanding points and projected redemption monthly.
Anatomy
What you are building
API
A rule (POST /api/v1/events/rules, or the dashboard Rules builder) reacts to purchase events with an award_points action. POST /api/v1/gamify/admin/tiers defines each tier, its points criteria, its point_multiplier, and a benefits object.
SDK
usePoints, useTier, useBadges render the wallet. useRewards exposes claimable rewards.
User sees
Points balance in the header, tier badge with progress bar to the next tier, claimable perks section.
Step 1: earn rules
Turn purchases into points
# A rule reacts to an event and runs typed actions. The action that
# grants points is "award_points". Author rules in the dashboard
# (Settings -> Rules, which validates the typed conditions for you), or
# programmatically:
curl -X POST https://api.bricqs.co/api/v1/events/rules \
-H "X-API-Key: bq_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Skincare purchase bonus",
"trigger_event": "purchase_completed",
"actions": { "actions": [
{ "type": "award_points",
"config": { "amount": 50, "currency": "points", "reason": "skincare_bonus" } }
] },
"priority": 0
}'award_points grants a FIXED amount, not a ratio of the purchase. Conditions use a typed DSL best authored in the dashboard rule builder.
The rules engine does not multiply points by an event attribute. For “X points per unit spent,” compute the amount in your backend and award it directly (send the computed points on your purchase_completed event, or grant via the points API). Keep proportional math on your side; use rules for fixed bonuses and triggers.
Step 2: tiers
Define tiers with points criteria
# One call per tier. Each tier has a level, a criteria_type, an optional
# point_multiplier, and a free-form benefits object. Admin-scoped key or
# dashboard session.
curl -X POST https://api.bricqs.co/api/v1/gamify/admin/tiers \
-H "X-API-Key: bq_live_..." \
-H "Content-Type: application/json" \
-d '{
"code": "gold",
"name": "Gold",
"level": 3,
"criteria_type": "points",
"point_multiplier": 2.0,
"benefits": { "free_shipping": true, "early_access": true }
}'criteria_type is points | milestones | badges | composite. criteria_config (omitted here) carries the typed threshold for that type — author it in the dashboard tier builder. There is no requalification field; handle annual resets yourself.
Step 3: perks
Perks = multiplier + benefits (no perks engine)
There is no /admin/perks endpoint and no auto-apply perk engine. A tier's
perks are exactly two real things:
1. point_multiplier -> Bricqs applies this automatically to point awards
for participants in the tier (e.g. 2x for Gold).
2. benefits (object) -> free-form metadata you define on the tier and read
back via useTier(). Bricqs stores it; YOUR app acts
on it. Example: { "free_shipping": true } -> your
checkout applies free shipping when the participant's
tier benefits say so.
Anything time-based (a birthday voucher, a launch early-access window) is your
scheduler emitting an event or granting a reward, not a Bricqs perk trigger.Step 4: render
Three hooks for the wallet
"use client";
import { usePoints, useTier, useRewards } from "@bricqs/sdk-react";
export function LoyaltyWallet() {
// engagementId inherited from <BricqsProvider>
const { balance } = usePoints();
const { currentTier, nextTierName, pointsToNext } = useTier();
const { rewards } = useRewards();
return (
<section className="grid gap-6">
<div>
<p className="text-sm text-slate-500">Points</p>
<p className="text-3xl font-bold">{balance.toLocaleString()}</p>
</div>
{currentTier && (
<div>
<p className="font-bold">{currentTier.tierName}</p>
{nextTierName && pointsToNext != null && (
<small className="text-slate-500">
{pointsToNext.toLocaleString()} pts to {nextTierName}
</small>
)}
</div>
)}
{rewards.length > 0 && (
<div>
<h3 className="font-bold">Rewards ready to use</h3>
<ul className="grid gap-2">
{rewards.map((r) => (
<li key={r.id}>{r.name}</li>
))}
</ul>
</div>
)}
</section>
);
}Step 5: audits
Monthly health check
Metrics to track monthly (compute from your event/webhook stream and
participant state reads — there is no built-in reports endpoint):
Active member share target 40 to 65%
Reward percentage target 1 to 5% of revenue
Outstanding liability ratio target 1 to 4% of trailing 90-day revenue
Tier distribution watch for over-promotion (Gold > 10% is a flag)
Redemption velocity target 55 to 75%
Pipe reward.claimed.v1 and points events into your warehouse; review with
finance every month.Developer FAQ
Common questions when integrating gamification with Bricqs.
Ready to ship?
Wire it up with the Bricqs SDK or API
Headless SDK for React UIs, REST API for any backend. Same engine behind both.
