Loyalty Tiers
Tier Structure
Each tier has:
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier |
tier | integer | Level number (1 = base, higher = better) |
minAmount | decimal | Minimum 12-month spend to qualify |
rewardRuleId | string | Linked reward rule for point calculation |
redemptionCapability | decimal | Max % of booking payable via wallet |
metadata | JSONB | Additional tier data (name, icon, color, etc.) |
Example Tier Configuration
| Tier | Level | Min Spend | Reward Rule | Redemption |
|---|---|---|---|---|
| Silver | 1 | Rs 0 | 1% cashback | 20% |
| Gold | 2 | Rs 50,000 | 2% cashback | 30% |
| Platinum | 3 | Rs 200,000 | 3% cashback | 50% |
Opt-In Flow
Guest calls POST /api/loyalty/opt-in
→ LoyaltyService.optIn(accountId)
→ Assigns base tier (lowest tier number)
→ Sets loyaltyStartDate = today
→ Sets loyaltyEndDate = today + 1 year - 1 day
→ Sets tierNextUpdateDate = today + 2 years - 1 day
→ Creates wallet if not exists
Tier Evaluation Rules
Tier only upgrades, never downgrades (except for 2+ year re-evaluation).
On Checkout (daily, 4 AM)
After each checkout, the system re-evaluates the guest's tier based on 12-month spend. If spend exceeds the next tier's threshold, the tier is upgraded.
Annual Renewal (daily, 5 AM)
When tierNextUpdateDate passes:
- Resets
amountSpentLast12Monthsto zero - Extends dates by 1 year
- Preserves current tier
Long-Term Re-Evaluation (daily, 6 AM)
For members 2+ years in the program:
- Re-evaluates tier based on recent spend
- Allows both upgrade and downgrade
- Renews dates
Manual Re-Evaluation
After tier config changes (create/update), admins can trigger re-evaluation:
POST /api/v1/admin/loyalty-tiers/re-evaluate
This scans all members and upgrades any who qualify for a higher tier.
Tier Progression Response
The GET /api/loyalty endpoint returns:
{
"loyalty": { "id": "...", "accountId": "...", "loyaltyTierId": "...", "amountSpentLast12Months": 75000 },
"tier": { "id": "...", "tier": 2, "minAmount": 50000, "redemptionCapability": 30 },
"benefits": [{ "benefitType": "ROOM_UPGRADE", "isRedeemable": true }],
"rewardRule": { "amountSpent": 10000, "rewardPoints": 200, "expiryDays": 365 },
"nextTier": { "tier": 3, "minAmount": 200000 },
"amountToNextTier": 125000
}
Benefits
Benefits are linked to tiers via the loyalty_tier_benefit M2M table. Admin endpoints:
GET /api/v1/admin/loyalty-tiers/{id}/benefits # list benefits for tier
POST /api/v1/admin/loyalty-tiers/{id}/benefits # set benefits (replaces all)
Each benefit has:
| Field | Type | Description |
|---|---|---|
category | string | Grouping (e.g., UPGRADE, DISCOUNT, SERVICE) |
benefitType | string | Specific benefit (e.g., ROOM_UPGRADE, EARLY_CHECKIN) |
isRedeemable | boolean | Whether guest can actively redeem |
maxRedeemPerYear | integer | Annual redemption limit |
Scheduled Jobs
| Job | Cron | Purpose |
|---|---|---|
updateTiersOnCheckout | 0 0 4 * * * (4 AM) | Upgrade tiers post-checkout |
renewExpiredLoyalty | 0 0 5 * * * (5 AM) | Annual renewal |
changeTierForLongTermMembers | 0 0 6 * * * (6 AM) | 2+ year re-evaluation |
All cron expressions are configurable via properties (e.g., loyalty.scheduler.checkout-update).