
Loyalty Program Optimization
- 65 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Design and tune tiered loyalty programs (earn rates, VIP tiers, redemption thresholds) and connect them to email to lift repeat purchase rate and CLV.
About
Covers the strategic design of loyalty programs, including tier thresholds, non-purchase earning actions, and Klaviyo email integration for balance and expiry emails. A developer or marketer uses it to launch a new program or fix low redemption and engagement in an existing one.
- Platform comparison (Smile.io, Yotpo, LoyaltyLion) with recommended earn/redeem structure
- Points-balance, tier-upgrade, and expiry email flows plus program performance metrics
Loyalty Program Optimization by the numbers
- 65 all-time installs (skills.sh)
- Ranked #525 of 853 Sales & Marketing skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill loyalty-program-optimizationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 65 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Design and tune tiered loyalty programs (earn rates, VIP tiers, redemption thresholds) and connect them to email to lift repeat purchase rate and CLV.
Files
Loyalty Program Optimization
Overview
A well-designed loyalty program increases repeat purchase rate by 20–40% and CLV by giving customers a compelling reason to consolidate their spending with your brand. Dedicated loyalty apps handle the entire points engine, tier management, redemption mechanics, and customer-facing portal without custom code. The strategic decisions — which tiers to create, what benefits to offer, and how to avoid training customers to wait for redemptions — are where the real work is.
When to Use This Skill
- When launching a new loyalty program from scratch
- When an existing points program has low redemption rates or member engagement
- When wanting to add tiered VIP benefits to an existing points program
- When diagnosing whether your loyalty program is driving incremental revenue or just rewarding purchases that would have happened anyway
Core Instructions
Step 1: Choose the right loyalty platform
| Platform | Best For | Shopify | WooCommerce | BigCommerce | Price |
|---|---|---|---|---|---|
| Smile.io | Simplicity, quick setup | App Store | Plugin | App Marketplace | Free tier; $49/mo for tiers |
| Yotpo Loyalty | Brands already using Yotpo Reviews/SMS | App Store | Limited | App Marketplace | $199+/mo |
| LoyaltyLion | Advanced program design, custom rules | App Store | Plugin | App Marketplace | $399+/mo |
| Stamped Loyalty | Brands using Stamped Reviews | App Store | — | — | $119+/mo |
| YITH WooCommerce Points and Rewards | WooCommerce only | — | Plugin | — | $149/yr |
Recommend Smile.io for most stores — it has the best balance of features and simplicity, works on all major platforms, and has a free tier to start.
Step 2: Design your program structure
Before installing anything, define:
Points earn rate:
- 1 point per $1 spent (= 1% cashback equivalent at 100 points = $1)
- Adjust based on your margins — consumer goods (30%+ margin): 1 point per $1 is fine; lower margin products: 0.5 points per $1
Tier thresholds (use annual spend):
| Tier | Annual Spend | Benefits |
|---|---|---|
| Member | $0+ | 1x points earn |
| Silver | $500+ | 1.5x points, free standard shipping |
| Gold | $1,500+ | 2x points, free expedited shipping, early access to sales |
| Platinum | $5,000+ | 3x points, members-only products, priority support |
Redemption rate: 100 points = $1 discount (set a minimum of 500 points to redeem)
Non-purchase earn actions (makes program stickier at no revenue cost):
- Account sign-up: 100 points
- Leave a review: 50 points
- Refer a friend: 200 points (if they purchase)
- Birthday month bonus: 2x points on all purchases
Step 3: Set up your loyalty program
---
Shopify with Smile.io
1. Install Smile.io from the Shopify App Store 2. Go to Smile.io → Points → Ways to Earn and configure:
- "Place an order": points per dollar (set your earn rate)
- "Create an account": 100 points
- "Happy birthday": 2x points in birthday month
- "Write a review": 50 points (connects to Judge.me or Stamped Reviews)
- "Refer a friend": 200 points per successful referral
3. Go to Smile.io → Points → Ways to Redeem and configure:
- "Discount on order": 100 points = $1 off
- Set minimum order value for redemption ($30 recommended)
4. Go to Smile.io → VIP → Create Program and set up your tiers:
- Enter the tier names, point thresholds, and benefits (free shipping, multipliers, etc.)
5. Go to Smile.io → Rewards Panel to customize the widget that appears on your storefront 6. Connect Smile.io to Klaviyo under Smile.io → Integrations — this enables sending points balance in Klaviyo emails
---
WooCommerce with YITH WooCommerce Points and Rewards
1. Install YITH WooCommerce Points and Rewards from the WordPress plugin directory ($149/yr) 2. Go to YITH → Points and Rewards → Points → Earn Points and configure:
- Points per currency unit: 1 point per $1
- Registration bonus: 100 points
- Review bonus: 50 points
3. Go to YITH → Points and Rewards → Redemption and set:
- Points per currency: 100 points = $1 off
- Maximum discount allowed: 20% of cart value (prevents 100% discount gaming)
4. For tiers: install YITH WooCommerce Membership to create VIP tiers based on spend level
Alternative: Use Gratisfaction plugin (more feature-rich, $99/yr) which includes gamification, social sharing rewards, and birthday bonuses.
---
BigCommerce with Smile.io
1. Install Smile.io from the BigCommerce App Marketplace 2. Configuration is identical to the Shopify setup described above 3. Smile.io integrates with BigCommerce's native coupon system for redemption
---
Custom / Headless
Use a loyalty API platform like Voucherify or Loyaltycrm.com rather than building from scratch. These provide:
- Points ledger and transaction API
- Tier management
- Redemption code generation
- Webhooks for earn/redeem events
If you must build custom:
// Core points earn function — call this from your order webhook
async function earnPoints(params: {
customerId: string;
orderId: string;
orderValue: number; // subtotal, not including tax/shipping
reason: 'purchase' | 'review' | 'referral' | 'signup';
}) {
const customer = await db.customers.findById(params.customerId);
const tier = customer.loyaltyTier; // 'member', 'silver', 'gold', 'platinum'
const multiplier = { member: 1.0, silver: 1.5, gold: 2.0, platinum: 3.0 }[tier];
const basePoints = Math.floor(params.orderValue * 1); // 1 point per $1
const pointsToAward = Math.floor(basePoints * multiplier);
await db.loyaltyTransactions.create({
customerId: params.customerId,
orderId: params.orderId,
type: 'earn',
points: pointsToAward,
reason: params.reason,
});
await db.customers.increment(params.customerId, { loyaltyPoints: pointsToAward });
}Step 4: Connect loyalty to email marketing
The highest-ROI loyalty email is the points balance email — it drives repurchases by reminding customers they have value waiting.
In Klaviyo (connected to Smile.io): 1. Add points balance to every post-purchase email: You earned {{ event.SmilePointsEarned }} points. Balance: {{ profile.SmilePointsBalance }} points 2. Create a flow triggered by: Smile.io Points Balance Threshold → Balance reaches 500 points 3. Email: "You have enough points to redeem for $5 off your next order" 4. Create a flow triggered by: Approaching Tier Upgrade → 100 points away from Silver 5. Email: "You're almost Silver! Spend $X more to unlock free shipping"
Points expiry email: 1. Create a flow triggered by 30 days before point expiry 2. Email: "Your 320 points ($3.20) expire in 30 days — use them before they're gone" 3. This is one of the highest-converting loyalty emails
Step 5: Measure program performance
| Metric | Healthy Target | Where to Find |
|---|---|---|
| Member enrollment rate | > 30% of customers enrolled | Smile.io → Dashboard |
| Points redemption rate | > 20% of earned points redeemed | Smile.io → Analytics |
| Revenue from loyalty members vs. non-members | Members should be 2-3x higher | Compare order value in Shopify/Analytics |
| Repeat purchase rate for enrolled vs. non-enrolled | Members should be 20%+ higher | Shopify Customer Cohorts or Smile.io analytics |
If redemption rate is below 15%, the redemption threshold is too high or customers are unaware of their balance. Lower the minimum redemption threshold or add a points balance widget to the account page.
Best Practices
- Announce points balance in every post-purchase email — "You earned 45 points — you now have 320 points ($3.20 to redeem)" drives engagement; most loyalty apps include this via email integration
- Use annual spend for tier qualification, not current balance — rewards consistent spend, not balance gaming
- Add non-purchase earning actions — reviews, referrals, and social shares make the program stickier without pure revenue cost
- Create "double points" events — point multiplier promotions drive purchase without the perceived cheapness of a discount code
- Set a minimum redemption threshold ($5 minimum / 500 points) — prevents micro-redemptions that create operational overhead
- Email members 30 days before points expire — expiry warning emails are among the highest-converting loyalty emails
Common Pitfalls
| Problem | Solution |
|---|---|
| Low redemption rate (< 20%) | Lower the minimum redemption threshold; add a points balance widget to the account page; include balance in order confirmation emails |
| Tier status not downgrading | Configure annual re-qualification in your loyalty app settings; communicate downgrade 30 days in advance |
| Members gaming the system with micro-purchases | Set minimum order value for earning points ($15+) in app settings |
| Points not reversing on refunds | Configure refund rules in your loyalty app — Smile.io has this under Settings → Points → Refund Policy |
| Loyalty program costs more than it generates | Track incremental revenue: compare AOV and purchase frequency of loyalty members vs. non-members using a matched control group |
Related Skills
- @customer-retention-engine
- @lifecycle-marketing-automation
- @referral-viral-loops
- @email-marketing-automation
- @review-generation-engine
{
"context": "Tests whether the agent implements the points earn engine with correct earn rates, non-purchase earn amounts, birthday multiplier logic, transaction logging, tier evaluation trigger, and minimum order threshold.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Base earn rate",
"max_score": 10,
"description": "Uses a base earn rate of 1 point per $1 of order value for purchase transactions (e.g. Math.floor(orderValue * 1))"
},
{
"name": "Review points amount",
"max_score": 8,
"description": "Awards exactly 50 points for 'review' reason (not a calculated value based on order amount)"
},
{
"name": "Referral points amount",
"max_score": 8,
"description": "Awards exactly 200 points for 'referral' reason"
},
{
"name": "Signup points amount",
"max_score": 8,
"description": "Awards exactly 100 points for 'signup' reason"
},
{
"name": "Birthday multiplier doubling",
"max_score": 10,
"description": "During birthday month, multiplies the tier multiplier by 2 (e.g. tier.multiplier * 2), not a fixed bonus"
},
{
"name": "Transaction balanceBefore",
"max_score": 8,
"description": "Logs the customer's points balance BEFORE the transaction in the loyaltyTransactions record"
},
{
"name": "Transaction balanceAfter",
"max_score": 8,
"description": "Logs the customer's points balance AFTER the transaction in the loyaltyTransactions record"
},
{
"name": "Transaction reason field",
"max_score": 6,
"description": "Logs the earn reason (e.g. 'purchase', 'review', 'referral', 'signup') in the transaction record"
},
{
"name": "Tier evaluation triggered",
"max_score": 10,
"description": "Calls evaluateTierStatus (or equivalent) after awarding points so tier upgrades are immediately assessed"
},
{
"name": "Minimum order threshold",
"max_score": 12,
"description": "Enforces a minimum order value for earning points on purchases (e.g. $15 minimum), and does NOT award points for orders below this threshold"
},
{
"name": "loyaltyPointsEarned update",
"max_score": 12,
"description": "Updates both the customer's current balance (loyaltyPoints) AND their lifetime earned counter (loyaltyPointsEarned) when points are awarded"
}
]
}
Loyalty Points Engine Implementation
Problem/Feature Description
A growing direct-to-consumer apparel brand, ThreadCo, is launching their first customer loyalty program. Their engineering team has built out the customer and order database layer, but needs the core points engine that determines how many points customers earn for different types of interactions with the brand.
The brand wants to reward customers not just for purchases, but for community engagement activities as well — writing product reviews, referring friends, and signing up for the program. They also want a special bonus during a customer's birthday month to drive engagement. Every interaction should be fully auditable, with a complete record of how a customer's balance changed over time. After points are awarded, the system should assess whether the customer qualifies for a new tier.
The team also wants to protect against points abuse from very small orders — customers should only earn points on orders above a meaningful purchase threshold.
Output Specification
Implement the earnPoints function in TypeScript as loyalty-engine.ts. The function should:
- Accept parameters for customerId, orderId, orderValue, and the reason for earning points
- Calculate points based on the type of earning event
- Apply any applicable multipliers for the customer's tier and birthday month
- Record the transaction in the database with a full before/after balance snapshot
- Update the customer's running points total
- Trigger a tier status check after awarding points
- Return the number of points awarded
Also write a loyalty-engine.test.ts file with at least 4 test cases covering different earn scenarios (you may stub database calls).
You should stub the database layer and helper functions (db, getCustomerTier, isCustomerBirthdayMonth, evaluateTierStatus) — you do not need to implement them fully.
{
"context": "Tests whether the agent implements points expiry with advance warning and scheduled jobs, annual tier downgrade with advance communication, correct analytics metrics, post-purchase email content guidance, double-points promotions preference, and the 20% redemption rate diagnostic threshold.",
"type": "weighted_checklist",
"checklist": [
{
"name": "12-month expiry cutoff",
"max_score": 8,
"description": "expireStalePoints uses a cutoff of 365 days (12 months) to identify expirable points, not a shorter or longer period"
},
{
"name": "Monthly cadence comment",
"max_score": 5,
"description": "expireStalePoints includes a comment or documentation indicating it should be run monthly (not daily or weekly)"
},
{
"name": "30-day expiry warning",
"max_score": 10,
"description": "Sends expiry warning notification 30 days before points actually expire (e.g. calls sendPointsExpiryWarning with addDays(new Date(), 30))"
},
{
"name": "Expiry job scheduling",
"max_score": 8,
"description": "Creates a loyaltyPointExpiryJobs record (or equivalent deferred job) with expiresAt 30 days from now — does NOT immediately delete the points"
},
{
"name": "Annual tier review",
"max_score": 8,
"description": "Implements a runAnnualTierReview (or equivalent) function that re-evaluates tier status for customers"
},
{
"name": "Downgrade advance notice",
"max_score": 10,
"description": "Communicates tier downgrades 30 days in advance (e.g. sends notification/email before applying downgrade, or schedules downgrade for 30 days later)"
},
{
"name": "Active members metric",
"max_score": 8,
"description": "getLoyaltyMetrics counts active members as customers where loyaltyEnrolledAt is not null AND loyaltyPoints > 0"
},
{
"name": "30-day redemption rate metric",
"max_score": 8,
"description": "getLoyaltyMetrics retrieves redemption rate scoped to the last 30 days (not all-time)"
},
{
"name": "Post-purchase email balance disclosure",
"max_score": 8,
"description": "loyalty-recommendations.md recommends including both points earned on the order AND the running total balance (with dollar equivalent) in post-purchase emails"
},
{
"name": "Double points preference",
"max_score": 10,
"description": "loyalty-recommendations.md recommends point multiplier events (double points / bonus points) over discount codes for the product launch campaign"
},
{
"name": "20% redemption threshold",
"max_score": 9,
"description": "loyalty-recommendations.md identifies a redemption rate below 20% as the trigger to consider lowering the redemption threshold or simplifying the process"
},
{
"name": "Incremental revenue metric",
"max_score": 8,
"description": "getLoyaltyMetrics includes an incrementalRevenue calculation (e.g. calls calculateIncrementalLoyaltyRevenue or equivalent)"
}
]
}
Loyalty Program Lifecycle Management and Analytics
Problem/Feature Description
NaturalBloom, a health and wellness DTC brand, has been running a loyalty program for 18 months. The marketing team is concerned that the program feels stale — members are accumulating points but not redeeming them, and the team has no visibility into whether the program is actually driving incremental revenue. The engineering team has been asked to implement the operational infrastructure that keeps the program healthy over time.
There are three main gaps to close: (1) the brand has never expired old points, and the liability on the balance sheet is growing; (2) there's no regular process to demote members who no longer meet their tier threshold after a period of inactivity; (3) the marketing team has no dashboard metrics to work from. Additionally, the CRM team wants to redesign post-purchase emails to make the loyalty value more tangible for members, and the promotions team is planning a product launch campaign and wants to know the best promotional mechanic to drive purchases.
Output Specification
Produce the following in a file called loyalty-lifecycle.ts:
1. An expireStalePoints function implementing the monthly points expiry process, including advance notification logic. 2. A runAnnualTierReview function that re-evaluates all customers' tier status and handles downgrades. 3. A getLoyaltyMetrics function returning key program health indicators.
Also produce a loyalty-recommendations.md file (max 400 words) covering:
- The recommended format/content for post-purchase loyalty emails
- The recommended promotional mechanic for the upcoming product launch (choose between point multiplier events vs. discount codes, and justify your choice)
- A decision framework for when the brand should consider adjusting the redemption threshold (include a specific metric threshold that should trigger a review)
{
"context": "Tests whether the agent correctly implements the 4-tier structure with proper thresholds and multipliers, redemption rate and constraints, rolling annual points for tier qualification, upgrade bonuses, and refund point reversals.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Silver threshold",
"max_score": 5,
"description": "LOYALTY_TIERS defines Silver tier with minPoints of exactly 500"
},
{
"name": "Gold threshold",
"max_score": 5,
"description": "LOYALTY_TIERS defines Gold tier with minPoints of exactly 1500"
},
{
"name": "Platinum threshold",
"max_score": 5,
"description": "LOYALTY_TIERS defines Platinum tier with minPoints of exactly 5000"
},
{
"name": "Tier multipliers",
"max_score": 8,
"description": "Multipliers are exactly: Member=1.0, Silver=1.5, Gold=2.0, Platinum=3.0"
},
{
"name": "Redemption rate",
"max_score": 8,
"description": "Uses a redemption rate of 100 points per $1 (e.g. pointsToRedeem / 100 to calculate discount value)"
},
{
"name": "Minimum redemption",
"max_score": 8,
"description": "Throws an error or rejects redemption requests below 500 points"
},
{
"name": "Balance validation",
"max_score": 8,
"description": "Throws an error (e.g. 'Insufficient points balance') when pointsToRedeem exceeds customer's current loyaltyPoints"
},
{
"name": "Single-use discount code",
"max_score": 8,
"description": "Creates the discount code with singleUse: true (or equivalent flag preventing reuse)"
},
{
"name": "Discount code expiry",
"max_score": 8,
"description": "Sets the discount code to expire 30 days from creation (not some other duration)"
},
{
"name": "Rolling 12-month earn for tier",
"max_score": 12,
"description": "Tier qualification uses points EARNED over the last 365 days (not current balance), e.g. sumEarnedInPeriod with since: subDays(new Date(), 365)"
},
{
"name": "Upgrade bonus points",
"max_score": 8,
"description": "When a tier upgrade occurs, awards bonus points (e.g. calls earnPoints with reason 'signup' for the bonus)"
},
{
"name": "Refund point reversal",
"max_score": 11,
"description": "Implements a reverseOrderPoints (or equivalent) function that debits/removes points earned from a refunded order"
},
{
"name": "Tier upgrade email",
"max_score": 6,
"description": "Calls sendTierUpgradeEmail (or equivalent) when a customer upgrades tiers"
}
]
}
Loyalty Tier System and Points Redemption
Problem/Feature Description
StyleVault, a mid-market fashion retailer, is upgrading their basic points program into a full tiered loyalty system with checkout redemption. Their customers have been accumulating points for over a year, and now the brand wants to introduce meaningful status tiers with differentiated perks, so that high-spending customers feel recognized and rewarded.
At checkout, customers should be able to apply their points balance as a discount. The redemption system needs to be airtight — preventing customers from redeeming more than they have, and ensuring the resulting discount code can only be used once and doesn't stay valid indefinitely.
The team also needs the tier qualification logic to correctly evaluate customers when their points balance changes. They want to reward customers who achieve a new tier with a surprise bonus, and send them an email celebrating their new status. Separately, the returns team has flagged that when orders are refunded, the points that were earned from those orders need to be clawed back.
Output Specification
Implement the following in a file called loyalty-system.ts:
1. A LOYALTY_TIERS constant defining the full tier structure with names, qualification thresholds, earn multipliers, and available benefit types. 2. A redeemPoints function that validates the redemption request, generates a discount code, logs the transaction, and updates the customer balance. 3. An evaluateTierStatus function that determines whether a customer should change tiers and handles the upgrade flow. 4. A reverseOrderPoints function that debits points when an order is refunded.
You may stub db, createUniqueDiscount, sendTierUpgradeEmail, earnPoints, subDays, and addDays.
Also produce a loyalty-system-design.md file (max 300 words) explaining the key design decisions in your implementation — specifically how tier qualification works and how redemption codes are protected.
{
"name": "finsi/loyalty-program-optimization",
"version": "0.1.0",
"summary": "Design and optimize tiered loyalty programs with points, rewards, exclusive perks, and member-only benefits that increase repeat purchase rates and CLV",
"skills": {
"loyalty-program-optimization": {
"path": "SKILL.md"
}
}
}