earnrate 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/data/cards/amex-blue-business-plus.yaml +1 -0
- package/data/cards/amex-blue-cash-everyday.yaml +4 -3
- package/data/cards/amex-blue-cash-preferred.yaml +5 -7
- package/data/cards/amex-gold.yaml +1 -0
- package/data/cards/amex-platinum.yaml +1 -0
- package/data/cards/bank-of-america-atmos-rewards-ascent.yaml +6 -6
- package/data/cards/bank-of-america-atmos-rewards-summit.yaml +6 -6
- package/data/cards/chase-freedom-flex.yaml +5 -1
- package/data/cards/chase-southwest-plus.yaml +5 -5
- package/data/cards/chase-southwest-premier-business.yaml +8 -5
- package/data/cards/chase-southwest-premier.yaml +6 -6
- package/data/cards/chase-southwest-priority.yaml +6 -6
- package/data/cards/citi-aadvantage-business.yaml +6 -6
- package/data/cards/citi-aadvantage-platinum-select.yaml +10 -7
- package/data/cards/citi-costco-anywhere-visa-business.yaml +4 -4
- package/data/merchants/overrides.yaml +4 -1
- package/dist/data/file-store.js +3 -1
- package/dist/data/store.js +9 -1
- package/dist/data/wallet-edit.js +10 -1
- package/dist/engine/evaluate.js +22 -3
- package/dist/engine/offers.js +2 -1
- package/dist/engine/recommend.js +14 -1
- package/dist/engine/setup.js +3 -3
- package/dist/engine/wallet-change.js +40 -8
- package/dist/engine/wallet-summary.js +105 -3
- package/dist/format/offers.js +11 -5
- package/dist/format/wallet.js +48 -19
- package/dist/mcp/context.js +7 -3
- package/dist/mcp/inputs.js +12 -5
- package/dist/mcp/schemas.js +6 -0
- package/dist/mcp/server.js +4 -4
- package/dist/mcp/tools/account.js +42 -5
- package/dist/mcp/tools/answers.js +13 -4
- package/dist/schema/card.js +4 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -167,7 +167,7 @@ Spotted a wrong rate, or want a card added? [Open an issue](https://github.com/c
|
|
|
167
167
|
|
|
168
168
|
## Use it in an AI assistant (MCP)
|
|
169
169
|
|
|
170
|
-
**In Claude, nothing to install:** add a custom connector (Settings → Connectors) with the address `https://mcp.earnrate.dev`. It works in Claude on the web, desktop and phone.
|
|
170
|
+
**In Claude, nothing to install:** add a custom connector (Settings → Connectors) with the address `https://mcp.earnrate.dev`. It works in Claude on the web, desktop and phone. When you add it, an earnrate page opens once, where you start an anonymous wallet: no email, just a recovery code you keep. Then ask anything: with no cards saved it answers from every card. Name your cards for answers about yours, and save them to track caps and welcome bonuses across your Claude apps. From the chat you can export the wallet for the command line, switch to another one, or delete it. In Claude Code:
|
|
171
171
|
|
|
172
172
|
```sh
|
|
173
173
|
claude mcp add --transport http earnrate https://mcp.earnrate.dev
|
|
@@ -250,6 +250,7 @@ earnrate needs no account. The local version has no telemetry; the hosted server
|
|
|
250
250
|
- **AI assistants:** when you use earnrate through Claude or another assistant, that assistant's provider sees the conversation, including earnrate's answers, under its own privacy policy.
|
|
251
251
|
- **earnrate.dev:** everything you enter on the site stays in your browser. The site counts visits with Cloudflare Web Analytics, without cookies.
|
|
252
252
|
- **Retention:** locally, your data lives only in files you control, for as long as you keep them. Delete the wallet file, or clear the site's data in your browser, to remove it. A saved wallet on mcp.earnrate.dev lasts until you delete it or leave it unused for 13 months.
|
|
253
|
+
- **Terms:** [earnrate.dev/terms](https://earnrate.dev/terms).
|
|
253
254
|
- **Contact:** [hello@earnrate.dev](mailto:hello@earnrate.dev).
|
|
254
255
|
|
|
255
256
|
## Working on earnrate
|
|
@@ -14,6 +14,7 @@ welcome_offer:
|
|
|
14
14
|
points: 50000
|
|
15
15
|
spend: 5000
|
|
16
16
|
months: 3
|
|
17
|
+
varies: true
|
|
17
18
|
note: 'The public offer is "as high as" 50,000 points; Amex shows the exact amount after you apply. "Welcome offers vary and you may not be eligible for an offer." The card-specific eligibility rules are in the offer terms, which the headless browser can''t load.'
|
|
18
19
|
rules:
|
|
19
20
|
- id: amex-travel
|
|
@@ -3,8 +3,8 @@ name: American Express Blue Cash Everyday Card
|
|
|
3
3
|
issuer: amex
|
|
4
4
|
network: amex
|
|
5
5
|
currency: cash
|
|
6
|
-
last_verified: 2026-10-
|
|
7
|
-
verified_by:
|
|
6
|
+
last_verified: 2026-10-31
|
|
7
|
+
verified_by: claude
|
|
8
8
|
source_urls:
|
|
9
9
|
- https://www.americanexpress.com/us/credit-cards/card/blue-cash-everyday/
|
|
10
10
|
- https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/blue-cash-everyday-credit-card/25330-10-0
|
|
@@ -14,6 +14,7 @@ welcome_offer:
|
|
|
14
14
|
dollars: 200
|
|
15
15
|
spend: 2000
|
|
16
16
|
months: 6
|
|
17
|
+
varies: true
|
|
17
18
|
note: 'Paid as Reward Dollars, redeemable for a statement credit or at Amazon.com checkout. The card page says "Earn $200"; the offer terms say "as high as $200", with the exact amount shown before you accept. Not available if you have or have had this Card, the Cash Magnet Card, the Blue Cash Preferred Card (including the Morgan Stanley version) or previous versions of these Cards.'
|
|
18
19
|
rules:
|
|
19
20
|
- id: us-supermarkets
|
|
@@ -62,7 +63,7 @@ evidence:
|
|
|
62
63
|
quote: 'at supermarkets located in the U.S. (superstores, convenience stores, warehouse clubs,'
|
|
63
64
|
- rule: us-online-retail
|
|
64
65
|
url: https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/blue-cash-everyday-credit-card/25330-10-0
|
|
65
|
-
quote: '3% cash back on the first
|
|
66
|
+
quote: '3% cash back on the first $6,000 of U.S. online retail purchases'
|
|
66
67
|
- rule: us-online-retail
|
|
67
68
|
url: https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/blue-cash-everyday-credit-card/25330-10-0
|
|
68
69
|
quote: 'from a U.S. retail merchant that sells physical goods or merchandise directly to consumers'
|
|
@@ -3,8 +3,8 @@ name: American Express Blue Cash Preferred Card
|
|
|
3
3
|
issuer: amex
|
|
4
4
|
network: amex
|
|
5
5
|
currency: cash
|
|
6
|
-
last_verified: 2026-10-
|
|
7
|
-
verified_by:
|
|
6
|
+
last_verified: 2026-10-31
|
|
7
|
+
verified_by: claude
|
|
8
8
|
source_urls:
|
|
9
9
|
- https://www.americanexpress.com/us/credit-cards/card/blue-cash-preferred/
|
|
10
10
|
- https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/blue-cash-preferred-credit-card/25330-10-0#offer-terms
|
|
@@ -15,7 +15,8 @@ welcome_offer:
|
|
|
15
15
|
dollars: 300
|
|
16
16
|
spend: 3000
|
|
17
17
|
months: 6
|
|
18
|
-
|
|
18
|
+
varies: true
|
|
19
|
+
note: 'Paid as Reward Dollars, redeemable for a statement credit or at Amazon.com checkout. Up to $300: the offer terms say "as high as $300", with the exact amount shown after you apply and before you accept. The card page''s headline has shown both "Earn $300" and "As High As $300". Not available if you have or have had this Card, the Morgan Stanley Blue Cash Preferred or previous versions of these Cards.'
|
|
19
20
|
rules:
|
|
20
21
|
- id: us-supermarkets
|
|
21
22
|
match: { categories: [grocery.supermarket] }
|
|
@@ -67,10 +68,7 @@ evidence:
|
|
|
67
68
|
quote: 'will be $0 for the first year of Card Membership'
|
|
68
69
|
- rule: welcome_offer
|
|
69
70
|
url: https://www.americanexpress.com/us/credit-cards/card/blue-cash-preferred/
|
|
70
|
-
quote: '
|
|
71
|
-
- rule: welcome_offer
|
|
72
|
-
url: https://www.americanexpress.com/us/credit-cards/card/blue-cash-preferred/
|
|
73
|
-
quote: 'after you spend $3,000 in purchases on your new Card in the first 6 months of Card Membership'
|
|
71
|
+
quote: 'after you spend $3,000 in purchases on your new Card within the first 6 months of Card Membership'
|
|
74
72
|
- rule: welcome_offer
|
|
75
73
|
url: https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/blue-cash-preferred-credit-card/25330-10-0#offer-terms
|
|
76
74
|
quote: 'As High As $300 Cash Back Welcome Offer'
|
|
@@ -14,6 +14,7 @@ welcome_offer:
|
|
|
14
14
|
points: 100000
|
|
15
15
|
spend: 8000
|
|
16
16
|
months: 6
|
|
17
|
+
varies: true
|
|
17
18
|
note: 'The public offer is "as high as" 100,000 points; Amex shows the exact amount (which may be lower, or no offer) after you apply and before you accept. Not available if you have or have had this Card, the Premier Rewards Gold Card, the Platinum Card (including the Schwab and Morgan Stanley versions) or previous versions of these Cards; Amex may also decline based on your card history.'
|
|
18
19
|
rules:
|
|
19
20
|
- id: restaurants
|
|
@@ -14,6 +14,7 @@ welcome_offer:
|
|
|
14
14
|
points: 175000
|
|
15
15
|
spend: 12000
|
|
16
16
|
months: 6
|
|
17
|
+
varies: true
|
|
17
18
|
note: 'The public offer is "as high as" 175,000 points; Amex shows the exact amount (which may be lower, or no offer) after you apply and before you accept. Not available if you have or have had this Card, the Schwab or Morgan Stanley Platinum Card, or previous versions of these Cards; Amex may also decline based on your card history.'
|
|
18
19
|
# One cap covers flights booked directly and through Amex Travel.
|
|
19
20
|
caps:
|
|
@@ -3,15 +3,15 @@ name: Atmos Rewards Ascent Visa Signature Credit Card
|
|
|
3
3
|
issuer: bank-of-america
|
|
4
4
|
network: visa
|
|
5
5
|
currency: atmos-points
|
|
6
|
-
last_verified: 2026-10-
|
|
7
|
-
verified_by:
|
|
6
|
+
last_verified: 2026-10-31
|
|
7
|
+
verified_by: claude
|
|
8
8
|
source_urls:
|
|
9
9
|
- https://www.bankofamerica.com/credit-cards/products/alaska-airlines-credit-card/
|
|
10
10
|
base_rate: 1
|
|
11
11
|
annual_fee: 95
|
|
12
12
|
welcome_offer:
|
|
13
|
-
points:
|
|
14
|
-
spend:
|
|
13
|
+
points: 70000
|
|
14
|
+
spend: 2500
|
|
15
15
|
months: 3
|
|
16
16
|
note: Limited-time online offer, no end date stated; it may not be available elsewhere after leaving the page. The window is 90 days from account opening. The offer also includes a $99 Companion Fare (plus taxes and fees from $23).
|
|
17
17
|
rules:
|
|
@@ -62,10 +62,10 @@ evidence:
|
|
|
62
62
|
quote: '$95 annual fee'
|
|
63
63
|
- rule: welcome_offer
|
|
64
64
|
url: https://www.bankofamerica.com/credit-cards/products/alaska-airlines-credit-card/
|
|
65
|
-
quote: 'Get
|
|
65
|
+
quote: 'Get 70,000 bonus points and a $99 Companion Fare (plus taxes and fees from $23) with this offer.'
|
|
66
66
|
- rule: welcome_offer
|
|
67
67
|
url: https://www.bankofamerica.com/credit-cards/products/alaska-airlines-credit-card/
|
|
68
|
-
quote: 'spend $
|
|
68
|
+
quote: 'spend $2,500 or more on purchases within the first 90 days of opening your account'
|
|
69
69
|
todo:
|
|
70
70
|
- 'The 10% rewards bonus rules use a proposed membership id, bank-of-america-account. Bank of America does not say on this page which accounts are "eligible".'
|
|
71
71
|
- 20% back on Alaska and Hawaiian inflight purchases is a rebate, not points, and isn't modeled.
|
|
@@ -3,15 +3,15 @@ name: Atmos Rewards Summit Visa Infinite Credit Card
|
|
|
3
3
|
issuer: bank-of-america
|
|
4
4
|
network: visa
|
|
5
5
|
currency: atmos-points
|
|
6
|
-
last_verified: 2026-10-
|
|
7
|
-
verified_by:
|
|
6
|
+
last_verified: 2026-10-31
|
|
7
|
+
verified_by: claude
|
|
8
8
|
source_urls:
|
|
9
9
|
- https://www.bankofamerica.com/credit-cards/products/alaska-airlines-infinite-credit-card/
|
|
10
10
|
base_rate: 1
|
|
11
11
|
annual_fee: 395
|
|
12
12
|
welcome_offer:
|
|
13
|
-
points:
|
|
14
|
-
spend:
|
|
13
|
+
points: 80000
|
|
14
|
+
spend: 4000
|
|
15
15
|
months: 3
|
|
16
16
|
note: Limited-time online offer, no end date stated; it may not be available elsewhere after leaving the page. The window is 90 days from account opening. The offer also includes a 25,000-point Global Companion Award.
|
|
17
17
|
rules:
|
|
@@ -61,10 +61,10 @@ evidence:
|
|
|
61
61
|
quote: '$395 annual fee and no foreign transaction fees'
|
|
62
62
|
- rule: welcome_offer
|
|
63
63
|
url: https://www.bankofamerica.com/credit-cards/products/alaska-airlines-infinite-credit-card/
|
|
64
|
-
quote: 'Earn
|
|
64
|
+
quote: 'Earn 80,000 bonus points and a 25,000-point Global Companion Award after spending $4,000 or more on purchases'
|
|
65
65
|
- rule: welcome_offer
|
|
66
66
|
url: https://www.bankofamerica.com/credit-cards/products/alaska-airlines-infinite-credit-card/
|
|
67
|
-
quote: '$
|
|
67
|
+
quote: '$4,000 or more on purchases within the first 90 days of opening your account'
|
|
68
68
|
todo:
|
|
69
69
|
- '3x on "foreign transaction" purchases (anything charged in a foreign currency or processed abroad) is not modeled: rules have no foreign-only geography. It would make this card the pick for most purchases abroad.'
|
|
70
70
|
- 'The 10% rewards bonus rules use a proposed membership id, bank-of-america-account. Bank of America does not say on this page which accounts are "eligible".'
|
|
@@ -66,7 +66,7 @@ rules:
|
|
|
66
66
|
requires: { activation: true }
|
|
67
67
|
valid_from: 2026-10-01
|
|
68
68
|
valid_to: 2026-12-31
|
|
69
|
-
note:
|
|
69
|
+
note: Chase says activated categories earn 5% total (4% on top of the 1% base) and doesn't say whether that stacks with the 3% dining rate, so earnrate shows 5%, not 7%, until the $1,500 quarterly cap.
|
|
70
70
|
- id: q4-2026-american-red-cross
|
|
71
71
|
match: { merchants: [american-red-cross] }
|
|
72
72
|
rate: 5
|
|
@@ -120,6 +120,9 @@ evidence:
|
|
|
120
120
|
- rule: q4-2026-dining
|
|
121
121
|
url: https://creditcards.chase.com/cash-back-credit-cards/freedom/flex
|
|
122
122
|
quote: 'Dining American Red Cross'
|
|
123
|
+
- rule: q4-2026-dining
|
|
124
|
+
url: https://creditcards.chase.com/cash-back-credit-cards/freedom/flex
|
|
125
|
+
quote: '5% Cash Back rewards total for each $1 spent until $1,500 is spent'
|
|
123
126
|
- rule: q4-2026-american-red-cross
|
|
124
127
|
url: https://creditcards.chase.com/cash-back-credit-cards/freedom/flex
|
|
125
128
|
quote: 'American Red Cross'
|
|
@@ -144,4 +147,5 @@ evidence:
|
|
|
144
147
|
todo:
|
|
145
148
|
- "Chase's offer-details overlays for the Q4 2026 and Q1 2027 categories (exact grocery exclusions, what counts for American Red Cross) didn't render in the page dump; rules follow the calendar labels and the rewards-category FAQ."
|
|
146
149
|
- 'q4-2026-american-red-cross: what counts (presumably donations) is unconfirmed.'
|
|
150
|
+
- 'q4-2026-dining: whether the 4% additional stacks on the 3% dining rate (7% total) is unstated; showing 5% until Chase says.'
|
|
147
151
|
- 'Q2 2027 (Apr-Jun) shows "Coming Soon"; add it after Chase posts it (activation opens March 15, 2027).'
|
|
@@ -3,18 +3,18 @@ name: Southwest Rapid Rewards Plus Credit Card
|
|
|
3
3
|
issuer: chase
|
|
4
4
|
network: visa
|
|
5
5
|
currency: southwest-rapid-rewards
|
|
6
|
-
last_verified: 2026-10-
|
|
7
|
-
verified_by:
|
|
6
|
+
last_verified: 2026-10-31
|
|
7
|
+
verified_by: claude
|
|
8
8
|
source_urls:
|
|
9
9
|
- https://creditcards.chase.com/travel-credit-cards/southwest/plus
|
|
10
10
|
- https://www.chase.com/personal/credit-cards/rewards-category-faq
|
|
11
11
|
base_rate: 1
|
|
12
12
|
annual_fee: 99
|
|
13
13
|
welcome_offer:
|
|
14
|
-
points:
|
|
14
|
+
points: 80000
|
|
15
15
|
spend: 1000
|
|
16
16
|
months: 3
|
|
17
|
-
note: 'Not available to current cardmembers of any Southwest Rapid Rewards Credit Card, or previous cardmembers of any Southwest Rapid Rewards Credit Card who received a new Cardmember bonus within the last 24 months (this does not apply to business cards).'
|
|
17
|
+
note: 'Chase labels it a limited-time offer and states no end date. Not available to current cardmembers of any Southwest Rapid Rewards Credit Card, or previous cardmembers of any Southwest Rapid Rewards Credit Card who received a new Cardmember bonus within the last 24 months (this does not apply to business cards).'
|
|
18
18
|
caps:
|
|
19
19
|
- id: gas-and-grocery
|
|
20
20
|
amount: 5000
|
|
@@ -48,7 +48,7 @@ evidence:
|
|
|
48
48
|
quote: '$99 applied to first billing statement.'
|
|
49
49
|
- rule: welcome_offer
|
|
50
50
|
url: https://creditcards.chase.com/travel-credit-cards/southwest/plus
|
|
51
|
-
quote: 'Earn
|
|
51
|
+
quote: 'Earn 80,000 bonus points after you spend $1,000 on purchases in the first 3 months from account opening'
|
|
52
52
|
- rule: base_rate
|
|
53
53
|
url: https://creditcards.chase.com/travel-credit-cards/southwest/plus
|
|
54
54
|
quote: 'Earn 1 point for each dollar spent on all other purchases.'
|
|
@@ -3,8 +3,8 @@ name: Southwest Rapid Rewards Premier Business Credit Card
|
|
|
3
3
|
issuer: chase
|
|
4
4
|
network: visa
|
|
5
5
|
currency: southwest-rapid-rewards
|
|
6
|
-
last_verified: 2026-10-
|
|
7
|
-
verified_by:
|
|
6
|
+
last_verified: 2026-10-31
|
|
7
|
+
verified_by: claude
|
|
8
8
|
source_urls:
|
|
9
9
|
- https://creditcards.chase.com/southwest/premier-business-credit-card
|
|
10
10
|
- https://creditcards.chase.com/business-credit-cards/southwest/premier-business
|
|
@@ -12,10 +12,10 @@ base_rate: 1
|
|
|
12
12
|
business: true
|
|
13
13
|
annual_fee: 149
|
|
14
14
|
welcome_offer:
|
|
15
|
-
points:
|
|
15
|
+
points: 60000
|
|
16
16
|
spend: 3000
|
|
17
17
|
months: 3
|
|
18
|
-
note: '
|
|
18
|
+
note: 'Not available to current cardmembers of this business card, or previous cardmembers who received a new Cardmember bonus for this business card within the last 24 months.'
|
|
19
19
|
caps:
|
|
20
20
|
- id: gas-and-dining
|
|
21
21
|
amount: 8000
|
|
@@ -48,7 +48,10 @@ evidence:
|
|
|
48
48
|
quote: '$149 applied to first billing statement.'
|
|
49
49
|
- rule: welcome_offer
|
|
50
50
|
url: https://creditcards.chase.com/business-credit-cards/southwest/premier-business
|
|
51
|
-
quote: '
|
|
51
|
+
quote: 'Earn 60,000 points'
|
|
52
|
+
- rule: welcome_offer
|
|
53
|
+
url: https://creditcards.chase.com/business-credit-cards/southwest/premier-business
|
|
54
|
+
quote: 'after you spend $3,000 on purchases in the first 3 months from account opening'
|
|
52
55
|
- rule: base_rate
|
|
53
56
|
url: https://creditcards.chase.com/southwest/premier-business-credit-card
|
|
54
57
|
quote: 'You’ll earn 1 point for each $1 spent on all other purchases'
|
|
@@ -3,18 +3,18 @@ name: Southwest Rapid Rewards Premier Credit Card
|
|
|
3
3
|
issuer: chase
|
|
4
4
|
network: visa
|
|
5
5
|
currency: southwest-rapid-rewards
|
|
6
|
-
last_verified: 2026-10-
|
|
7
|
-
verified_by:
|
|
6
|
+
last_verified: 2026-10-31
|
|
7
|
+
verified_by: claude
|
|
8
8
|
source_urls:
|
|
9
9
|
- https://creditcards.chase.com/travel-credit-cards/southwest/premier
|
|
10
10
|
- https://www.chase.com/personal/credit-cards/rewards-category-faq
|
|
11
11
|
base_rate: 1
|
|
12
12
|
annual_fee: 149
|
|
13
13
|
welcome_offer:
|
|
14
|
-
points:
|
|
15
|
-
spend:
|
|
14
|
+
points: 85000
|
|
15
|
+
spend: 2000
|
|
16
16
|
months: 3
|
|
17
|
-
note: 'Not available to current cardmembers of any Southwest Rapid Rewards Credit Card, or previous cardmembers of any Southwest Rapid Rewards Credit Card who received a new Cardmember bonus within the last 24 months (this does not apply to business cards).'
|
|
17
|
+
note: 'Chase labels it a limited-time offer and states no end date. Not available to current cardmembers of any Southwest Rapid Rewards Credit Card, or previous cardmembers of any Southwest Rapid Rewards Credit Card who received a new Cardmember bonus within the last 24 months (this does not apply to business cards).'
|
|
18
18
|
caps:
|
|
19
19
|
- id: grocery-and-dining
|
|
20
20
|
amount: 8000
|
|
@@ -48,7 +48,7 @@ evidence:
|
|
|
48
48
|
quote: '$149 applied to first billing statement.'
|
|
49
49
|
- rule: welcome_offer
|
|
50
50
|
url: https://creditcards.chase.com/travel-credit-cards/southwest/premier
|
|
51
|
-
quote: 'Earn
|
|
51
|
+
quote: 'Earn 85,000 bonus points after you spend $2,000 on purchases in the first 3 months from account opening'
|
|
52
52
|
- rule: base_rate
|
|
53
53
|
url: https://creditcards.chase.com/travel-credit-cards/southwest/premier
|
|
54
54
|
quote: 'Earn 1 point for each dollar spent on all other purchases.'
|
|
@@ -3,17 +3,17 @@ name: Southwest Rapid Rewards Priority Credit Card
|
|
|
3
3
|
issuer: chase
|
|
4
4
|
network: visa
|
|
5
5
|
currency: southwest-rapid-rewards
|
|
6
|
-
last_verified: 2026-10-
|
|
7
|
-
verified_by:
|
|
6
|
+
last_verified: 2026-10-31
|
|
7
|
+
verified_by: claude
|
|
8
8
|
source_urls:
|
|
9
9
|
- https://creditcards.chase.com/travel-credit-cards/southwest/priority
|
|
10
10
|
base_rate: 1
|
|
11
11
|
annual_fee: 229
|
|
12
12
|
welcome_offer:
|
|
13
|
-
points:
|
|
14
|
-
spend:
|
|
13
|
+
points: 90000
|
|
14
|
+
spend: 3000
|
|
15
15
|
months: 3
|
|
16
|
-
note: 'Not available to current cardmembers of any Southwest Rapid Rewards Credit Card, or previous cardmembers of any Southwest Rapid Rewards Credit Card who received a new Cardmember bonus within the last 24 months (this does not apply to business cards).'
|
|
16
|
+
note: 'Chase labels it a limited-time offer and states no end date. Not available to current cardmembers of any Southwest Rapid Rewards Credit Card, or previous cardmembers of any Southwest Rapid Rewards Credit Card who received a new Cardmember bonus within the last 24 months (this does not apply to business cards).'
|
|
17
17
|
rules:
|
|
18
18
|
- id: southwest
|
|
19
19
|
match: { merchants: [southwest] }
|
|
@@ -37,7 +37,7 @@ evidence:
|
|
|
37
37
|
quote: '$229 applied to first billing statement.'
|
|
38
38
|
- rule: welcome_offer
|
|
39
39
|
url: https://creditcards.chase.com/travel-credit-cards/southwest/priority
|
|
40
|
-
quote: 'Earn
|
|
40
|
+
quote: 'Earn 90,000 bonus points after you spend $3,000 on purchases in the first 3 months from account opening'
|
|
41
41
|
- rule: base_rate
|
|
42
42
|
url: https://creditcards.chase.com/travel-credit-cards/southwest/priority
|
|
43
43
|
quote: 'Earn 1 point for each dollar spent on all other purchases.'
|
|
@@ -3,8 +3,8 @@ name: Citi / AAdvantage Business World Elite Mastercard
|
|
|
3
3
|
issuer: citi
|
|
4
4
|
network: mastercard
|
|
5
5
|
currency: aadvantage-miles
|
|
6
|
-
last_verified: 2026-10-
|
|
7
|
-
verified_by:
|
|
6
|
+
last_verified: 2026-10-31
|
|
7
|
+
verified_by: claude
|
|
8
8
|
source_urls:
|
|
9
9
|
- https://www.citi.com/credit-cards/citi-aadvantage-business-credit-card
|
|
10
10
|
# Miles post to the company's AAdvantage Business account, not a personal
|
|
@@ -14,9 +14,9 @@ business: true
|
|
|
14
14
|
annual_fee: 99
|
|
15
15
|
first_year_fee: 0
|
|
16
16
|
welcome_offer:
|
|
17
|
-
points:
|
|
18
|
-
spend:
|
|
19
|
-
months:
|
|
17
|
+
points: 75000
|
|
18
|
+
spend: 5000
|
|
19
|
+
months: 5
|
|
20
20
|
note: "Miles post to the business's AAdvantage Business account. Not available if you received a new account bonus for a Citi / AAdvantage Business account in the past 48 months, or converted another Citi card on which you earned a new account bonus in the last 48 months into one."
|
|
21
21
|
rules:
|
|
22
22
|
- id: american-airlines
|
|
@@ -72,7 +72,7 @@ evidence:
|
|
|
72
72
|
quote: 'The annual fee is $99. However, this fee is waived for the first 12 months.'
|
|
73
73
|
- rule: welcome_offer
|
|
74
74
|
url: https://www.citi.com/credit-cards/citi-aadvantage-business-credit-card
|
|
75
|
-
quote: '
|
|
75
|
+
quote: '75,000 AAdvantage® bonus miles For your business after $5,000 in purchases within the first 5 months of account opening'
|
|
76
76
|
- rule: welcome_offer
|
|
77
77
|
url: https://www.citi.com/credit-cards/citi-aadvantage-business-credit-card
|
|
78
78
|
quote: 'new account bonus for a Citi® / AAdvantage® Business account in the past 48 months'
|
|
@@ -3,18 +3,18 @@ name: Citi / AAdvantage Platinum Select World Elite Mastercard
|
|
|
3
3
|
issuer: citi
|
|
4
4
|
network: mastercard
|
|
5
5
|
currency: aadvantage-miles
|
|
6
|
-
last_verified: 2026-10-
|
|
7
|
-
verified_by:
|
|
6
|
+
last_verified: 2026-10-31
|
|
7
|
+
verified_by: claude
|
|
8
8
|
source_urls:
|
|
9
9
|
- https://www.citi.com/credit-cards/citi-aadvantage-platinum-elite-credit-card
|
|
10
10
|
base_rate: 1
|
|
11
11
|
annual_fee: 99
|
|
12
12
|
first_year_fee: 0
|
|
13
13
|
welcome_offer:
|
|
14
|
-
points:
|
|
15
|
-
spend:
|
|
16
|
-
months:
|
|
17
|
-
note: '
|
|
14
|
+
points: 50000
|
|
15
|
+
spend: 2500
|
|
16
|
+
months: 3
|
|
17
|
+
note: 'Not available if you received a new account bonus for a Citi / AAdvantage Platinum Select account in the past 48 months, or converted another Citi card on which you earned a new account bonus in the last 48 months into one.'
|
|
18
18
|
rules:
|
|
19
19
|
- id: american-airlines
|
|
20
20
|
match: { merchants: [american-airlines] }
|
|
@@ -62,7 +62,10 @@ evidence:
|
|
|
62
62
|
quote: 'Annual fee - $99, waived for the first 12 months.'
|
|
63
63
|
- rule: welcome_offer
|
|
64
64
|
url: https://www.citi.com/credit-cards/citi-aadvantage-platinum-elite-credit-card
|
|
65
|
-
quote: 'Earn
|
|
65
|
+
quote: 'Earn 50,000 AAdvantage® bonus miles'
|
|
66
|
+
- rule: welcome_offer
|
|
67
|
+
url: https://www.citi.com/credit-cards/citi-aadvantage-platinum-elite-credit-card
|
|
68
|
+
quote: 'after $2,500 in purchases in the first 3 months'
|
|
66
69
|
- rule: welcome_offer
|
|
67
70
|
url: https://www.citi.com/credit-cards/citi-aadvantage-platinum-elite-credit-card
|
|
68
71
|
quote: 'new account bonus for a Citi® / AAdvantage® Platinum Select® account in the past 48 months'
|
|
@@ -3,8 +3,8 @@ name: Costco Anywhere Visa Business Card by Citi
|
|
|
3
3
|
issuer: citi
|
|
4
4
|
network: visa
|
|
5
5
|
currency: cash
|
|
6
|
-
last_verified: 2026-10-
|
|
7
|
-
verified_by:
|
|
6
|
+
last_verified: 2026-10-31
|
|
7
|
+
verified_by: claude
|
|
8
8
|
source_urls:
|
|
9
9
|
- https://www.citi.com/credit-cards/costco-anywhere-visa-business-card
|
|
10
10
|
# Paid once a year as a Costco reward certificate after the February statement,
|
|
@@ -30,7 +30,7 @@ rules:
|
|
|
30
30
|
exclude: { categories: [gas.superstore, gas.grocery_store] }
|
|
31
31
|
rate: 4
|
|
32
32
|
cap: gas-and-ev-combined
|
|
33
|
-
note: Other eligible gas and EV charging worldwide. Gas or EV charging at superstores, supermarkets
|
|
33
|
+
note: Other eligible gas and EV charging worldwide. Gas or EV charging at some superstores, supermarkets and warehouse clubs other than Costco, and fuel for non-automobile use, earn 1%. Citi keeps a list of excluded merchants at citi.com/Costco_Exclusions.
|
|
34
34
|
- id: restaurants
|
|
35
35
|
match: { categories: [dining] }
|
|
36
36
|
rate: 3
|
|
@@ -63,7 +63,7 @@ evidence:
|
|
|
63
63
|
quote: '4% cash back on other eligible gas and electric vehicle (EV) charging purchases worldwide'
|
|
64
64
|
- rule: gas-and-ev-charging
|
|
65
65
|
url: https://www.citi.com/credit-cards/costco-anywhere-visa-business-card
|
|
66
|
-
quote: 'for gas or EV charging purchased at some superstores, supermarkets
|
|
66
|
+
quote: 'for gas or EV charging purchased at some superstores, supermarkets and warehouse clubs other than Costco'
|
|
67
67
|
- rule: restaurants
|
|
68
68
|
url: https://www.citi.com/credit-cards/costco-anywhere-visa-business-card
|
|
69
69
|
quote: '3% cash back on restaurant purchases (including cafes, bars, lounges, and fast food restaurants)'
|
|
@@ -113,7 +113,10 @@
|
|
|
113
113
|
category: warehouse_club
|
|
114
114
|
in_store:
|
|
115
115
|
accepts: [visa]
|
|
116
|
-
|
|
116
|
+
online:
|
|
117
|
+
accepts: [visa, mastercard]
|
|
118
|
+
# customerservice.costco.com/app/answers/answer_view/a_id/719 (2026-10-31)
|
|
119
|
+
note: Warehouse registers take Visa only for credit; Costco.com takes Visa and Mastercard.
|
|
117
120
|
- id: costco-gas
|
|
118
121
|
name: Costco Gas
|
|
119
122
|
aliases: [costco gas station]
|
package/dist/data/file-store.js
CHANGED
|
@@ -69,7 +69,9 @@ export class FileWalletStore {
|
|
|
69
69
|
return this.change(this.existing((w) => updateInWallet(this.catalog, w, input, today)), (text, change) => updateCardText(text ?? '', input.id, change.fields));
|
|
70
70
|
}
|
|
71
71
|
removeCard(cardId) {
|
|
72
|
-
return this.change(this.existing((w) =>
|
|
72
|
+
return this.change(this.existing((w) => w.cards.length === 1 && w.cards[0]?.id === cardId
|
|
73
|
+
? failed(`${cardId} is the last card in ${this.where}. To start over, delete that file; to swap cards, add the new one first.`)
|
|
74
|
+
: removeFromWallet(w, cardId)), (text) => removeCardText(text ?? '', cardId));
|
|
73
75
|
}
|
|
74
76
|
setMemberships(memberships) {
|
|
75
77
|
return this.change(this.existing((w) => withMemberships(this.catalog, w, memberships)), (text, change) => setMembershipsText(text ?? '', change.wallet.memberships));
|
package/dist/data/store.js
CHANGED
|
@@ -30,7 +30,11 @@ export class JsonWalletStore {
|
|
|
30
30
|
const change = apply(before);
|
|
31
31
|
if (!change.ok)
|
|
32
32
|
return failed(change.error);
|
|
33
|
-
|
|
33
|
+
// An empty wallet is stored as none: the schema needs a card.
|
|
34
|
+
if (change.wallet.cards.length === 0)
|
|
35
|
+
await this.backend.remove();
|
|
36
|
+
else
|
|
37
|
+
await this.backend.write(JSON.stringify(change.wallet));
|
|
34
38
|
return {
|
|
35
39
|
ok: true,
|
|
36
40
|
wallet: change.wallet,
|
|
@@ -62,6 +66,10 @@ export class JsonWalletStore {
|
|
|
62
66
|
erase() {
|
|
63
67
|
return this.serialized(() => this.backend.remove());
|
|
64
68
|
}
|
|
69
|
+
// Nothing to sign out of: the backend is the caller's own storage.
|
|
70
|
+
disconnect() {
|
|
71
|
+
return Promise.resolve();
|
|
72
|
+
}
|
|
65
73
|
record(purchase, merchant) {
|
|
66
74
|
return this.serialized(async () => {
|
|
67
75
|
const wallet = await this.load();
|
package/dist/data/wallet-edit.js
CHANGED
|
@@ -33,12 +33,20 @@ function fieldLines(field, f, indent) {
|
|
|
33
33
|
case 'checkout_label':
|
|
34
34
|
case 'anniversary': {
|
|
35
35
|
const value = f[field];
|
|
36
|
-
return value
|
|
36
|
+
return value == null ? '' : `${indent}${field}: ${scalar(value)}\n`;
|
|
37
37
|
}
|
|
38
38
|
case 'activated':
|
|
39
39
|
return f.activated === undefined
|
|
40
40
|
? ''
|
|
41
41
|
: `${indent}activated: ${flowList(f.activated)}\n`;
|
|
42
|
+
case 'cap_usage': {
|
|
43
|
+
if (f.cap_usage === undefined)
|
|
44
|
+
return '';
|
|
45
|
+
const caps = Object.entries(f.cap_usage)
|
|
46
|
+
.map(([key, u]) => `${indent} ${key}: { period: ${scalar(u.period)}, spent: ${String(u.spent)} }\n`)
|
|
47
|
+
.join('');
|
|
48
|
+
return caps === '' ? '' : `${indent}cap_usage:\n${caps}`;
|
|
49
|
+
}
|
|
42
50
|
case 'bonus': {
|
|
43
51
|
if (!f.bonus)
|
|
44
52
|
return '';
|
|
@@ -66,6 +74,7 @@ const FIELDS = [
|
|
|
66
74
|
'anniversary',
|
|
67
75
|
'chosen',
|
|
68
76
|
'activated',
|
|
77
|
+
'cap_usage',
|
|
69
78
|
'bonus',
|
|
70
79
|
];
|
|
71
80
|
function cardsSeq(text) {
|
package/dist/engine/evaluate.js
CHANGED
|
@@ -56,7 +56,26 @@ export function pickedCategories(card, held, date) {
|
|
|
56
56
|
}
|
|
57
57
|
return { picked, staleChoice };
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
// Activating a rotating quarter (Freedom Flex) turns on all its categories:
|
|
60
|
+
// rules on the same rotating cap with the same dates share one activation.
|
|
61
|
+
export function activationGroup(card, ruleId) {
|
|
62
|
+
const rule = card.rules.find((r) => r.id === ruleId);
|
|
63
|
+
const cap = typeof rule?.cap === 'string'
|
|
64
|
+
? card.caps.find((c) => c.id === rule.cap)
|
|
65
|
+
: undefined;
|
|
66
|
+
if (!rule || !cap?.rotating)
|
|
67
|
+
return [ruleId];
|
|
68
|
+
return card.rules
|
|
69
|
+
.filter((r) => r.requires?.activation === true &&
|
|
70
|
+
r.cap === rule.cap &&
|
|
71
|
+
r.valid_from === rule.valid_from &&
|
|
72
|
+
r.valid_to === rule.valid_to)
|
|
73
|
+
.map((r) => r.id);
|
|
74
|
+
}
|
|
75
|
+
export function isActivated(card, held, ruleId) {
|
|
76
|
+
return activationGroup(card, ruleId).some((id) => held.activated.includes(id));
|
|
77
|
+
}
|
|
78
|
+
function applicability(card, rule, ctx, held, picked) {
|
|
60
79
|
const { match, exclude } = rule;
|
|
61
80
|
const inCategory = (cats) => cats.some((c) => categoryMatches(c, ctx.category));
|
|
62
81
|
if (match.categories?.length && !inCategory(match.categories))
|
|
@@ -98,7 +117,7 @@ function applicability(rule, ctx, held, picked) {
|
|
|
98
117
|
const membership = rule.requires?.membership;
|
|
99
118
|
if (membership !== undefined && !ctx.memberships.has(membership))
|
|
100
119
|
return 'no';
|
|
101
|
-
if (rule.requires?.activation && !held
|
|
120
|
+
if (rule.requires?.activation && !isActivated(card, held, rule.id))
|
|
102
121
|
return 'needs-activation';
|
|
103
122
|
return 'applies';
|
|
104
123
|
}
|
|
@@ -158,7 +177,7 @@ export function evaluateCard(card, held, ctx) {
|
|
|
158
177
|
let nominal = base;
|
|
159
178
|
const blocked = [];
|
|
160
179
|
for (const rule of card.rules) {
|
|
161
|
-
const status = applicability(rule, ctx, held, picked);
|
|
180
|
+
const status = applicability(card, rule, ctx, held, picked);
|
|
162
181
|
if (status === 'no')
|
|
163
182
|
continue;
|
|
164
183
|
if (status === 'needs-activation') {
|
package/dist/engine/offers.js
CHANGED
|
@@ -34,6 +34,7 @@ export function compareOffers(catalog, wallet, filters, today) {
|
|
|
34
34
|
spend: offer.spend,
|
|
35
35
|
months: offer.months,
|
|
36
36
|
validTo: offer.valid_to ?? null,
|
|
37
|
+
varies: offer.varies === true,
|
|
37
38
|
note: offer.note ?? null,
|
|
38
39
|
annualFee: card.annual_fee ?? null,
|
|
39
40
|
firstYearFee,
|
|
@@ -47,7 +48,7 @@ export function compareOffers(catalog, wallet, filters, today) {
|
|
|
47
48
|
offers.sort((a, b) => b.firstYearValue - a.firstYearValue || a.cardId.localeCompare(b.cardId));
|
|
48
49
|
const notes = [
|
|
49
50
|
`Compares the ${String(catalog.cards.length)} cards earnrate knows, not every card on the market.`,
|
|
50
|
-
"Each offer is the public one on the issuer's page, checked weekly.
|
|
51
|
+
"Each offer is the public one on the issuer's page on the date shown, checked weekly. Yours may differ: issuers test and target offers, so use the one shown to you when you apply. Issuers also limit who can get a bonus; each card's note says how.",
|
|
51
52
|
"First-year value is the bonus at your point values minus the first year's annual fee. It leaves out what the card earns on your spending and any credits or perks.",
|
|
52
53
|
];
|
|
53
54
|
return { offers, withoutOffer, notes };
|
package/dist/engine/recommend.js
CHANGED
|
@@ -23,6 +23,11 @@ const PERIOD_WORDS = {
|
|
|
23
23
|
const displayName = (e) => e.held.checkout_label ?? e.card.name;
|
|
24
24
|
// "Worth X or more" must hold at X itself, so round up; "less than X" rounds down.
|
|
25
25
|
const roundUp = (n) => String(Math.ceil(n * 100 - EPSILON) / 100);
|
|
26
|
+
// At exactly the break-even value the two cards tie, so only an exact
|
|
27
|
+
// threshold reads "more than".
|
|
28
|
+
const worthAbove = (n) => Math.abs(n * 100 - Math.round(n * 100)) < EPSILON
|
|
29
|
+
? `more than ${num(n)}¢`
|
|
30
|
+
: `${roundUp(n)}¢ or more`;
|
|
26
31
|
const roundDown = (n) => String(Math.floor(n * 100 + EPSILON) / 100);
|
|
27
32
|
function compareEntries(a, b) {
|
|
28
33
|
const byReturn = b.effectiveReturnPct - a.effectiveReturnPct;
|
|
@@ -218,7 +223,7 @@ function sensitivity(catalog, entries) {
|
|
|
218
223
|
if (o.range && oRate > 0) {
|
|
219
224
|
const threshold = winner.effectiveReturnPct / oRate;
|
|
220
225
|
if (threshold > o.centsPerUnit && threshold <= o.range[1]) {
|
|
221
|
-
consider((threshold - o.centsPerUnit) / o.centsPerUnit, `${displayName(other)} beats ${displayName(winner)} here if ${currencyLabel(catalog, o.currency)} points are worth ${
|
|
226
|
+
consider((threshold - o.centsPerUnit) / o.centsPerUnit, `${displayName(other)} beats ${displayName(winner)} here if ${currencyLabel(catalog, o.currency)} points are worth ${worthAbove(threshold)} (you value them at ${num(o.centsPerUnit)}¢).`);
|
|
222
227
|
}
|
|
223
228
|
}
|
|
224
229
|
}
|
|
@@ -283,6 +288,14 @@ export function recommend(catalog, wallet, purchase, merchant = resolveMerchant(
|
|
|
283
288
|
}
|
|
284
289
|
if (!winner) {
|
|
285
290
|
warnings.push(`None of your cards work at ${merchant.resolvedName}.`);
|
|
291
|
+
const online = merchant.channel === 'in_store' && merchant.merchantId !== null
|
|
292
|
+
? resolveMerchantById(catalog, merchant.merchantId, 'online')
|
|
293
|
+
: null;
|
|
294
|
+
const alt = online
|
|
295
|
+
? rankEntries(catalog, wallet, { ...purchase, channel: 'online' }, online, online.category).entries[0]
|
|
296
|
+
: undefined;
|
|
297
|
+
if (alt)
|
|
298
|
+
warnings.push(`Ordering online instead? ${displayName(alt)} works there (${num(alt.effectiveReturnPct)}%).`);
|
|
286
299
|
}
|
|
287
300
|
else if (merchant.accepts === null &&
|
|
288
301
|
(winner.card.network === 'amex' || winner.card.network === 'discover')) {
|