What you’ll build
| Use case | How it works |
|---|---|
| Category multipliers | 5% on dining, 3% on groceries, 1% on everything else |
| Spend threshold bonus | Rate jumps from 1% to 3% after $2,500/month in total spend |
| Retroactive bonus | When crossing the threshold, a one-time bonus covers the delta on prior spend |
| Monthly reset | Counters reset on the 1st of each month via automation |
Assumptions
This guide assumes you already have:- A program created
- An asset linked to that program (
CASHBACK_USD, scale 2,UNLIMITED,SIMPLE)
The rules
1. Track monthly spend
Every rule in this program needs to know how much the participant has spent this month. We track two counters: total spend (for the threshold check) and non-category spend (for the retroactive bonus calculation).monthly_spend tracks everything, for the $2,500 threshold:
2. Track non-category spend
monthly_base_spend tracks only non-category purchases. This is what the retroactive bonus pays out on.
Counter values in conditions are snapshots: they reflect the state before the current event’s actions execute. These rules increment the counters in the database, but all later rules still see the pre-increment values. This is important for threshold detection.
3. Retroactive bonus at $2,500
When a participant crosses the $2,500 monthly threshold, they should retroactively earn an extra 2% on their non-category spend so far. This makes up the difference between the 1% they already earned and the 3% high-spender rate.snapshot < 2500: hasn’t crossed yet(snapshot + event.amount) >= 2500: this event crosses it
monthly_base_spend (not monthly_spend) so the retroactive bonus only applies to purchases that earned the 1% base rate. Dining and grocery purchases already earned their full 5%/3%. Paying an extra 2% on those would overshoot.
4. Dining: 5%
stop_after_match: true ensures a dining purchase earns 5% and only 5%. It won’t also match the grocery or base rules.
5. Groceries: 3%
stop_after_match prevents the base and high-spender rules from stacking on top.
6. High-spender: 3% on everything else
After crossing the $2,500 monthly threshold, all remaining non-category purchases earn 3% instead of 1%.(snapshot + event.amount) >= 2500 so the threshold-crossing purchase itself earns at the higher rate.
7. Base: 1% on everything else
The catch-all for purchases below the threshold that don’t match a category.stop_after_match.
8. Monthly counter reset
Automation
Create one automation to fire the monthly reset:monthly_reset event to every participant with a non-zero spend counter. The filter avoids unnecessary events for inactive participants.
Rule evaluation flow
Here’s how a single purchase flows through the rules:stop_after_match. Rules 50-60 always evaluate regardless of category.
Example event
Your backend sends this when a card transaction settles:type, amount, and mcc are used by rule conditions. Include whatever else you need for your own analytics.
Edge cases and watchouts
Rounding
Withscale: 2, amounts are stored to the cent. Use round(expr, 2) in every CREDIT to avoid precision issues:
The threshold-crossing purchase
The event that pushesmonthly_spend past $2,500 earns at the 3% rate (high-spender), not 1%. This is because high_spender_cashback checks (snapshot + event.amount) >= 2500, which is true for the crossing purchase. The retroactive bonus also fires on the same event, covering all prior spend.
Category purchases don’t care about the threshold
A dining purchase always earns 5% whether the participant has spent $500 or $5,000 this month. Thestop_after_match on dining_cashback (order 100) fires before the threshold rules (300, 1000) are even evaluated. The threshold only affects non-category purchases.
The second counter
monthly_base_spend exists so the retroactive bonus only pays out on purchases that earned 1%. Without it, a participant who spent $2,000 on dining before crossing the threshold would get an extra 2% on those dining purchases, bumping them from 5% to 7%, which isn’t intended. The retroactive bonus should only upgrade 1% purchases to 3%.
Counter resets and tier status
On the 1st of each month, the counter drops to zero. There’s no “tier” to maintain: the high-spender rate is purely counter-driven. A participant who spent $10,000 last month starts fresh at 1% the next month.Refund handling
This example doesn’t include refund rules. In production, you’d add rules to:- Debit cashback earned on the refunded transaction
- Reduce the
monthly_spendcounter so the threshold status stays accurate
original_cashback and original_amount from the original transaction record. allow_negative lets the debit succeed even if the participant has already spent the cashback; without it, a DEBIT against an insufficient available balance fails and the refund event is marked FAILED.
Excluded transaction types
The base rule doesn’t exclude any MCC codes. In practice, you’d want to block non-qualifying transactions like cash advances or wire transfers:Large MCC lists
The CELin operator works well for short lists (5-20 entries). If you need to match against hundreds of merchants or categories, move the classification into your backend and pass a flag like event.is_dining: true in the event payload instead.