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). 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.
Why two counters?
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.
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.