earnrate 0.2.13 → 0.2.15

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.
Files changed (50) hide show
  1. package/README.md +10 -10
  2. package/data/CHANGELOG.md +28 -0
  3. package/data/VERSION +1 -1
  4. package/data/cards/amex-delta-skymiles-gold.yaml +7 -8
  5. package/data/cards/amex-delta-skymiles-platinum.yaml +7 -8
  6. package/data/cards/amex-delta-skymiles-reserve.yaml +9 -9
  7. package/data/cards/amex-marriott-bonvoy-business.yaml +7 -8
  8. package/data/cards/chase-ihg-one-rewards-business.yaml +1 -1
  9. package/data/cards/discover-it-cash-back.yaml +9 -6
  10. package/data/cards/venmo-credit-card.yaml +1 -1
  11. package/data/categories.yaml +10 -0
  12. package/data/merchants/seed.yaml +3 -1
  13. package/dist/cli/import.js +14 -11
  14. package/dist/cli/todo.js +3 -2
  15. package/dist/cli/wallet.js +50 -17
  16. package/dist/engine/cheat-sheet.js +15 -11
  17. package/dist/engine/checklist.js +23 -25
  18. package/dist/engine/credits.js +2 -2
  19. package/dist/engine/evaluate.js +1 -1
  20. package/dist/engine/explore.js +4 -4
  21. package/dist/engine/intro.js +1 -5
  22. package/dist/engine/memberships.js +2 -6
  23. package/dist/engine/offers.js +3 -5
  24. package/dist/engine/periods.js +5 -0
  25. package/dist/engine/profiles.js +2 -1
  26. package/dist/engine/recommend.js +46 -33
  27. package/dist/engine/record.js +2 -1
  28. package/dist/engine/rotation.js +2 -1
  29. package/dist/engine/rule-text.js +98 -33
  30. package/dist/engine/setup.js +7 -3
  31. package/dist/engine/simulate.js +4 -4
  32. package/dist/engine/text.js +29 -8
  33. package/dist/engine/wallet-change.js +5 -5
  34. package/dist/engine/wallet-summary.js +24 -21
  35. package/dist/format/card-text.js +2 -3
  36. package/dist/format/cards.js +7 -3
  37. package/dist/format/explore.js +2 -2
  38. package/dist/format/wallet-yaml.js +13 -17
  39. package/dist/format/wallet.js +14 -10
  40. package/dist/import/analyze.js +13 -1
  41. package/dist/mcp/context.js +2 -3
  42. package/dist/mcp/inputs.js +3 -3
  43. package/dist/mcp/schemas.js +2 -0
  44. package/dist/mcp/server.js +0 -2
  45. package/dist/mcp/tools/account.js +1 -1
  46. package/dist/mcp/tools/answers.js +10 -5
  47. package/dist/mcp/tools/wallet.js +10 -6
  48. package/dist/schema/card.js +6 -10
  49. package/dist/schema/merchant.js +3 -0
  50. package/package.json +2 -1
package/README.md CHANGED
@@ -58,11 +58,11 @@ Assumes Whole Foods Market codes as supermarkets (MCC 5411).
58
58
  Values points at: Amex Membership Rewards (MR) at 1¢, SoFi Member Rewards at 1¢, Chase Ultimate Rewards (UR) at 1¢.
59
59
  ```
60
60
 
61
- Options: `--online` or `--in-store`, `--valuation flat|points-heavy`, `--all`, `--json`, `--date`, `--country`. Run `earnrate --help` for details. To keep the wallet somewhere else, set `EARNRATE_WALLET` to its path.
61
+ Options: `--online` or `--in-store`, `--paying card|apple-pay|google-pay|paypal`, `--valuation flat|points-heavy`, `--cards`, `--member`, `--category`, `--all`, `--json`, `--date`, `--country`. Run `earnrate --help` for details. To keep the wallet somewhere else, set `EARNRATE_WALLET` to its path.
62
62
 
63
63
  `earnrate cheatsheet` prints the card to use for each everyday kind of purchase this quarter (groceries, dining, gas, Amazon, Costco, travel, streaming, drugstores, everything else), with your caps, picks and activations as they are now. `--cards` uses cards you name instead, and `--every-card` picks from every card to show what's worth getting. It's also in the My wallet tab on [earnrate.dev](https://earnrate.dev).
64
64
 
65
- `earnrate todo` lists what your saved cards need this quarter, soonest first: bonuses to activate (with Chase's deadline), categories to pick, caps running low and welcome bonuses to finish. `--ics earnrate.ics` also writes the dates as a calendar file with reminders. My wallet on earnrate.dev shows the same list as "To do now", with an "Add the dates to your calendar" button.
65
+ `earnrate todo` lists what your saved cards need this quarter, soonest first: bonuses to activate (with Chase's deadline), categories to pick, caps running low, welcome bonuses to finish, spend bonuses you're close to, statement credits to enroll in or use, and first-year rates ending. `--ics earnrate.ics` also writes the dates as a calendar file with reminders. My wallet on earnrate.dev shows the same list as "To do now", with an "Add the dates to your calendar" button.
66
66
 
67
67
  `earnrate missed` lists the stores earnrate didn't recognize when you asked here, most asked about first. They're kept only on your computer; [open an issue](https://github.com/cwasley/earnrate/issues) to suggest one for the data.
68
68
 
@@ -153,9 +153,9 @@ Every purchase on the card counts. Once earnrate knows how much you've spent on
153
153
 
154
154
  `earnrate simulate` adds a bonus's points to a card's yearly rewards when the year's spending on it reaches the total.
155
155
 
156
- ### Store credits
156
+ ### Statement credits
157
157
 
158
- Some cards give dollars back for spending at a named store, up to a limit each period: Amex Platinum's $75 a quarter at lululemon, Amex Gold's $7 a month at Dunkin', Blue Cash Preferred's $10 a month on Disney+, Hulu or ESPN subscriptions. The purchase still earns the card's rate; the credit comes on top. Asking about the store with no cards saved lists the cards with a credit there first, "With a store credit", in dollars ("$75 back each quarter, then 1x Amex Membership Rewards"), then the usual ranking by rate. A search for a kind of purchase ("clothing") doesn't.
158
+ Some cards give dollars back for spending at a named store, up to a limit each period: Amex Platinum's $75 a quarter at lululemon, Amex Gold's $7 a month at Dunkin', Blue Cash Preferred's $10 a month on Disney+, Hulu or ESPN subscriptions. The purchase still earns the card's rate; the credit comes on top. Asking about the store with no cards saved lists the cards with a credit there first, "With a statement credit", in dollars ("$75 back each quarter, then 1x Amex Membership Rewards"), then the usual ranking by rate. A search for a kind of purchase ("clothing") doesn't.
159
159
 
160
160
  With a saved wallet, a credit counts only once you mark it enrolled (these all need enrolling with the issuer), and only while some is left this period. Given an amount, its dollars count toward that card's return and can make it the pick; without one, the answer shows what's left. Recording a purchase from an AI assistant uses it up, and `earnrate todo` reminds you to enroll and to use what's left before the period ends:
161
161
 
@@ -240,10 +240,10 @@ Use the connector (or plugin) or the local extension, not both: each keeps its o
240
240
  - `best_card_for_purchase`: which card to use, with reasons
241
241
  - `best_cards_for` and `simulate_spending`: compare every card earnrate knows, for one purchase or a year of spending, with no wallet needed
242
242
  - `resolve_merchant`: how a merchant was identified
243
- - `list_wallet`: a to-do list for your cards right now, then your cards, caps left this period, chosen categories, store credits left, welcome-bonus and spend-bonus progress and the point values you've set
243
+ - `list_wallet`: a to-do list for your cards right now, then your cards, caps left this period, chosen categories, statement credits left, welcome-bonus and spend-bonus progress and the point values you've set
244
244
  - `cheat_sheet`: the card to use for each everyday kind of purchase this quarter
245
245
  - `compare_welcome_offers`: the best public sign-up offers on cards you don't have
246
- - `record_purchase`: books a purchase against its cap, any open welcome bonus, the card's yearly spend bonuses and an enrolled store credit in your wallet file, keeping your comments
246
+ - `record_purchase`: books a purchase against its cap, any open welcome bonus, the card's yearly spend bonuses and an enrolled statement credit in your wallet file, keeping your comments
247
247
  - `search_cards`, `add_card`, `update_card`, `remove_card`, `set_memberships` and `set_point_values`: set up the wallet in conversation, including what your points are worth (memberships and point values need no cards)
248
248
 
249
249
  Both servers also offer four prompts, ready-made questions you can pick in Claude instead of typing one. None needs your cards:
@@ -303,19 +303,19 @@ Every rule in `data/cards/` carries a short quote from the issuer's own page. In
303
303
  - rules whose quote is no longer on the page, which usually means the rule changed
304
304
  - earning lines that appeared or disappeared since the last baseline, such as a new perk
305
305
 
306
- A GitHub Action runs it every Monday and opens a "Card source changes" issue when something moved. After updating a card file, run `yarn check-sources --update` to record the new baseline. Some issuers block headless browsers on shared servers; those pages are listed as "couldn't check", and running the command locally covers them. The first run needs `yarn playwright install chromium`.
306
+ A GitHub Action runs it every Monday and opens a "Card source changes" issue when something moved or a page couldn't be read. After updating a card file, run `yarn check-sources --update --cards <ids>` to record the new baseline for those cards' pages. Some issuers block headless browsers on shared servers; those pages are listed as "couldn't check", and running the command locally covers them. The first run needs `yarn playwright install chromium`.
307
307
 
308
308
  Rotating categories (Freedom Flex) are added each quarter once the issuer posts them. Until then, answers say that quarter's categories are missing rather than guessing.
309
309
 
310
310
  ## Privacy Policy
311
311
 
312
- earnrate needs no account. The local version has no telemetry; the hosted server counts requests and errors by method, tool name or page only, never what was asked. The full policy is at [earnrate.dev/privacy](https://earnrate.dev/privacy); in short:
312
+ earnrate needs no account. The local version has no telemetry; the hosted server counts requests and errors by tool or page, with the kind of app they came from, and how many wallets are started and used each day, never what was asked. The full policy is at [earnrate.dev/privacy](https://earnrate.dev/privacy); in short:
313
313
 
314
- - **The Claude connector:** earnrate's server at mcp.earnrate.dev receives each question Claude asks it (a store or kind of purchase, and any amount, country, spending or cards you gave) and answers without keeping it; request logging is off. It counts requests and errors by method, tool name or page only. Connecting it starts an anonymous wallet (a recovery code, no email). It holds only what you save: card IDs and the details you gave, memberships and point values, never card numbers, and is encrypted with its own key that only your recovery code or your connected apps can unlock. Delete it any time; it's deleted automatically after 13 months unused, or a week if it's never used.
314
+ - **The Claude connector:** earnrate's server at mcp.earnrate.dev receives each question Claude asks it (a store or kind of purchase, and any amount, country, spending or cards you gave) and answers without keeping it; request logging is off. It counts requests and errors by tool or page, with the kind of app they came from, and how many wallets are started and used each day. Connecting it starts an anonymous wallet (a recovery code, no email). It holds only what you save: card IDs and the details you gave, memberships and point values, never card numbers, and is encrypted with its own key that only your recovery code or your connected apps can unlock. Delete it any time; it's deleted automatically after 13 months unused, or a week if it's never used.
315
315
  - **What it stores locally:** your wallet, a YAML file on your computer (`~/.earnrate/wallet.yaml` unless you set `EARNRATE_WALLET`). It lists cards by product ID, like `amex-gold`, plus your category picks, activations and how much you've spent toward caps. earnrate never asks for or stores card numbers, expiry dates, security codes or bank logins. When you ask about a store earnrate doesn't recognize, its name goes in `~/.earnrate/missed-merchants.json` (`earnrate missed` lists them; `EARNRATE_MISSED_MERCHANTS=off` keeps no list).
316
316
  - **What the local version sends:** nothing, by default. The optional merchant lookup (off unless you turn it on with your own Anthropic API key) sends only the name of a store earnrate doesn't recognize to Anthropic's API, and saves the answer in `~/.earnrate/merchant-cache.json`.
317
317
  - **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.
318
- - **earnrate.dev:** everything you enter on the site stays in your browser. The site counts visits with Cloudflare Web Analytics, without cookies.
318
+ - **earnrate.dev:** everything you enter on the site stays in your browser, except when you add the cheat sheet to Apple Wallet: then pass.earnrate.dev receives what the pass needs (card IDs, picks, activations, checkout names, memberships and the date) and keeps none of it. The site counts which of its parts get used, by name only, and counts visits with Cloudflare Web Analytics, without cookies.
319
319
  - **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 (a week, if it's never used); the record that an app is connected goes after 90 days unused.
320
320
  - **Terms:** [earnrate.dev/terms](https://earnrate.dev/terms).
321
321
  - **Contact:** [hello@earnrate.dev](mailto:hello@earnrate.dev).
package/data/CHANGELOG.md CHANGED
@@ -2,6 +2,34 @@
2
2
 
3
3
  Every change to card rules, merchants, categories or currencies under `data/`. Bump `data/VERSION` (`YYYY.MM.DD`, plus `.N` for a second release on the same day) and add a section here in the same commit. Each row cites the issuer page the new value came from.
4
4
 
5
+ ## 2026.11.06.1
6
+
7
+ Wording only; no rates changed. Ten categories take `preposition: on`, so answers and pages say "on flights" rather than "at flights": `dining.fast_food`, `grocery.online`, `gas.grocery_store`, `gas.superstore`, `transit.rideshare`, `travel.flights`, `travel.car_rental`, `travel.cruise`, `home_improvement` and `other`.
8
+
9
+ ## 2026.11.06
10
+
11
+ Wording only; no rates changed. `discover-it-cash-back`'s welcome-offer note says Cashback Match doubles every rate for the first year, instead of naming the data field it's stored in.
12
+
13
+ ## 2026.11.05.2
14
+
15
+ Evidence quotes only; no rates changed. `chase-ihg-one-rewards-business`'s Expensify credit quotes the page's "Enjoy up to $100 in statement credits towards Expensify purchases each calendar year." (a link now splits the old sentence), and `venmo-credit-card`'s foreign transaction fee quotes "No foreign transaction fee" on venmo.com (a footnote number now runs into the old one). Sources: [Chase](https://creditcards.chase.com/business-credit-cards/IHG/business), [Venmo](https://venmo.com/about/creditcard/).
16
+
17
+ ## 2026.11.05.1
18
+
19
+ Welcome offers replacing the four that ended 2026-11-04, read on Amex's pages 2026-11-05; none has an end date now. Discover it's foreign transaction fee, from a Discover page that states it.
20
+
21
+ | Card | Field | Was | Now | Sources |
22
+ | ----------------------------- | ------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
23
+ | amex-delta-skymiles-gold | `welcome_offer` | as high as 80,000 miles + $250 credit after $3,000 in 6 months, ending 2026-11-04 | as high as 80,000 miles after $2,000 in 6 months | [1](https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/delta-skymiles-gold-american-express-card/25330-10-0#offer-terms) · [2](https://www.americanexpress.com/us/credit-cards/card/delta-skymiles-gold-american-express-card/) |
24
+ | amex-delta-skymiles-platinum | `welcome_offer` | as high as 90,000 miles + $300 credit after $4,000 in 6 months, ending 2026-11-04 | as high as 90,000 miles after $3,000 in 6 months | [1](https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/delta-skymiles-platinum-american-express-card/25330-10-0#offer-terms) · [2](https://www.americanexpress.com/us/credit-cards/card/delta-skymiles-platinum-american-express-card/) |
25
+ | amex-delta-skymiles-reserve | `welcome_offer` | 50,000 miles + 2 Comfort Flight Certificates after $10,000 in 6 months, ending 2026-11-04 | as high as 125,000 miles after $5,000 in 6 months | [1](https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/delta-skymiles-reserve-american-express-card/25330-10-0#offer-terms) · [2](https://www.americanexpress.com/us/credit-cards/card/delta-skymiles-reserve-american-express-card/) |
26
+ | amex-marriott-bonvoy-business | `welcome_offer` | 100,000 points + Free Night Award after $8,000 in 6 months, ending 2026-11-04 | 75,000 points after $6,000 in 6 months | [1](https://www.americanexpress.com/en-us/credit-cards/apply/terms/business/prospect/marriott-bonvoy-business-american-express-card/45094-9-0#offer-terms) · [2](https://www.americanexpress.com/us/credit-cards/business/business-credit-cards/amex-marriott-bonvoy-business-credit-card/) |
27
+ | discover-it-cash-back | `foreign_transaction_fee` | unknown | 0 ("Discover® has no foreign transaction fee") | [1](https://www.discover.com/credit-cards/card-smarts/best-international-travel-credit-card/) |
28
+
29
+ ## 2026.11.05
30
+
31
+ Wording only; no rates changed. `walmart-plus`, `peloton-membership` and `uber-one` take `preposition: on`, so their credits read "on Walmart+ membership" rather than "at".
32
+
5
33
  ## 2026.11.04.15
6
34
 
7
35
  Merchant statement credits on every card whose issuer publishes them: 73 new credits on 31 cards, each quoted from the issuer's own pages (read 2026-11-04). With the three already entered, 76 credits on 32 cards. Credits a cardholder must pick a merchant for, ones once every several years, benefits that aren't spending, and the kinds `docs/feature-gaps.md` lists under "Merchant credits the schema can't express" aren't entered. No earn rates changed.
package/data/VERSION CHANGED
@@ -1 +1 @@
1
- 2026.11.04.15
1
+ 2026.11.06.1
@@ -5,7 +5,7 @@ color: '#c9a24f'
5
5
  issuer: amex
6
6
  network: amex
7
7
  currency: delta-skymiles
8
- last_verified: 2026-11-03
8
+ last_verified: 2026-11-05
9
9
  verified_by: claude
10
10
  source_urls:
11
11
  - https://www.americanexpress.com/us/credit-cards/card/delta-skymiles-gold-american-express-card/
@@ -17,12 +17,11 @@ first_year_fee: 0
17
17
  foreign_transaction_fee: 0
18
18
  welcome_offer:
19
19
  points: 80000
20
- spend: 3000
20
+ spend: 2000
21
21
  months: 6
22
- valid_to: 2026-11-04
23
22
  varies: true
24
23
  not_with: [amex-delta-skymiles-platinum, amex-delta-skymiles-reserve]
25
- note: 'The public offer is "as high as" 80,000 bonus miles plus a $250 statement credit, both after the same $3,000 in purchases; Amex shows the exact miles (which may be lower, or no offer) after you apply and before you accept. Amex: "You may not be eligible to receive a welcome offer if you have or have had this Card, the Delta SkyMiles® Platinum American Express Card, the Delta SkyMiles® Reserve American Express Card or previous versions of these Cards." Amex may also decline based on your card history.'
24
+ note: 'The public offer is "as high as" 80,000 bonus miles; Amex shows the exact miles (which may be lower, or no offer) after you apply and before you accept. Amex: "You may not be eligible for a welcome offer if you have or have had this Card or previous versions of this Card, or if you have or have had the Delta SkyMiles® Platinum American Express Card, the Delta SkyMiles® Reserve American Express Card or previous versions of these Cards." Amex may also decline based on your card history.'
26
25
  rules:
27
26
  - id: restaurants
28
27
  match: { categories: [dining] }
@@ -116,16 +115,16 @@ evidence:
116
115
  quote: 'will be $0 for the first year of Card Membership'
117
116
  - rule: welcome_offer
118
117
  url: https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/delta-skymiles-gold-american-express-card/25330-10-0#offer-terms
119
- quote: 'As High As 80,000 Bonus Miles + $250 Statement Credit'
118
+ quote: 'As High As 80,000 Bonus Miles'
120
119
  - rule: welcome_offer
121
120
  url: https://www.americanexpress.com/us/credit-cards/card/delta-skymiles-gold-american-express-card/
122
- quote: 'after you spend $3,000 in purchases on your new Card within the first 6 months'
121
+ quote: 'after you spend $2,000 in purchases on your new Card within the first 6 months'
123
122
  - rule: welcome_offer
124
123
  url: https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/delta-skymiles-gold-american-express-card/25330-10-0#offer-terms
125
- quote: 'Offer ends 11/04/2026 at 11:59pm MST.'
124
+ quote: 'You can earn these bonus miles after you make $2,000 or more'
126
125
  - rule: not_with
127
126
  url: https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/delta-skymiles-gold-american-express-card/25330-10-0#offer-terms
128
- quote: 'if you have or have had this Card, the Delta SkyMiles® Platinum American Express Card'
127
+ quote: 'if you have or have had the Delta SkyMiles® Platinum American Express Card, the Delta SkyMiles® Reserve American Express Card'
129
128
  - rule: foreign_transaction_fee
130
129
  url: https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/delta-skymiles-gold-american-express-card/25330-10-0#offer-terms
131
130
  quote: 'Foreign Transaction None Penalty Fees'
@@ -5,7 +5,7 @@ color: '#a9abae'
5
5
  issuer: amex
6
6
  network: amex
7
7
  currency: delta-skymiles
8
- last_verified: 2026-11-03
8
+ last_verified: 2026-11-05
9
9
  verified_by: claude
10
10
  source_urls:
11
11
  - https://www.americanexpress.com/us/credit-cards/card/delta-skymiles-platinum-american-express-card/
@@ -16,12 +16,11 @@ annual_fee: 350
16
16
  foreign_transaction_fee: 0
17
17
  welcome_offer:
18
18
  points: 90000
19
- spend: 4000
19
+ spend: 3000
20
20
  months: 6
21
- valid_to: 2026-11-04
22
21
  varies: true
23
22
  not_with: [amex-delta-skymiles-reserve]
24
- note: 'The public offer is "as high as" 90,000 bonus miles plus a $300 statement credit, both after the same $4,000 in purchases; Amex shows the exact miles (which may be lower, or no offer) after you apply and before you accept. Amex: "You may not be eligible to receive a welcome offer if you have or have had this Card, the Delta SkyMiles® Reserve American Express Card or previous versions of these Cards." Amex may also decline based on your card history.'
23
+ note: 'The public offer is "as high as" 90,000 bonus miles; Amex shows the exact miles (which may be lower, or no offer) after you apply and before you accept. Amex: "You may not be eligible for a welcome offer if you have or have had this Card or previous versions of this Card or if you have or have had the Delta SkyMiles® Reserve American Express Card or previous versions of these Cards." Amex may also decline based on your card history.'
25
24
  rules:
26
25
  - id: delta
27
26
  match: { merchants: [delta, delta-vacations, delta-stays] }
@@ -118,16 +117,16 @@ evidence:
118
117
  quote: 'Annual Fee $350 Transaction Fees'
119
118
  - rule: welcome_offer
120
119
  url: https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/delta-skymiles-platinum-american-express-card/25330-10-0#offer-terms
121
- quote: 'As High As 90,000 Bonus Miles + Earn $300 Statement Credit'
120
+ quote: 'As High As 90,000 Bonus Miles'
122
121
  - rule: welcome_offer
123
122
  url: https://www.americanexpress.com/us/credit-cards/card/delta-skymiles-platinum-american-express-card/
124
- quote: 'after you spend $4,000 in purchases on your new Card within the first 6 months'
123
+ quote: 'after you spend $3,000 in purchases on your new Card within the first 6 months'
125
124
  - rule: welcome_offer
126
125
  url: https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/delta-skymiles-platinum-american-express-card/25330-10-0#offer-terms
127
- quote: 'Offer ends 11/04/2026 at 23:59 MST.'
126
+ quote: 'You can earn these bonus miles after you make $3,000 or more'
128
127
  - rule: not_with
129
128
  url: https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/delta-skymiles-platinum-american-express-card/25330-10-0#offer-terms
130
- quote: 'if you have or have had this Card, the Delta SkyMiles® Reserve American Express Card'
129
+ quote: 'if you have or have had the Delta SkyMiles® Reserve American Express Card or previous versions of these Cards'
131
130
  - rule: foreign_transaction_fee
132
131
  url: https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/delta-skymiles-platinum-american-express-card/25330-10-0#offer-terms
133
132
  quote: 'Foreign Transaction None Penalty Fees'
@@ -5,7 +5,7 @@ color: '#2b2d33'
5
5
  issuer: amex
6
6
  network: amex
7
7
  currency: delta-skymiles
8
- last_verified: 2026-11-03
8
+ last_verified: 2026-11-05
9
9
  verified_by: claude
10
10
  source_urls:
11
11
  - https://www.americanexpress.com/us/credit-cards/card/delta-skymiles-reserve-american-express-card/
@@ -14,11 +14,11 @@ base_rate: 1
14
14
  annual_fee: 650
15
15
  foreign_transaction_fee: 0
16
16
  welcome_offer:
17
- points: 50000
18
- spend: 10000
17
+ points: 125000
18
+ spend: 5000
19
19
  months: 6
20
- valid_to: 2026-11-04
21
- note: 'Plus two Delta Comfort Flight Certificates after the same $10,000 in purchases: each is one round trip from the U.S. to the lower 48, Puerto Rico or the U.S. Virgin Islands, booked at least 21 days ahead, with up to $80 in taxes and fees, flown by January 15, 2028. Amex: "You may not be eligible to receive a welcome offer if you have or have had this Card or previous versions of this Card." Amex may also decline based on your card history.'
20
+ varies: true
21
+ note: 'The public offer is "as high as" 125,000 bonus miles; Amex shows the exact miles (which may be lower, or no offer) after you apply and before you accept. Amex: "You may not be eligible for a welcome offer if you have or have had this Card or previous versions of this Card." Amex may also decline based on your card history.'
22
22
  rules:
23
23
  - id: delta
24
24
  match: { merchants: [delta, delta-vacations, delta-stays] }
@@ -80,16 +80,16 @@ evidence:
80
80
  quote: 'The annual fee for the Delta SkyMiles® Reserve American Express Card is $650.'
81
81
  - rule: welcome_offer
82
82
  url: https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/delta-skymiles-reserve-american-express-card/25330-10-0#offer-terms
83
- quote: 'Earn 50,000 Bonus Miles + 2 Delta Comfort Flight Certificates'
83
+ quote: 'As High As 125,000 Bonus Miles'
84
84
  - rule: welcome_offer
85
85
  url: https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/delta-skymiles-reserve-american-express-card/25330-10-0#offer-terms
86
- quote: 'after you make $10,000 or more (the "Threshold Amount") in eligible purchases'
86
+ quote: 'You can earn these bonus miles after you make $5,000 or more'
87
87
  - rule: welcome_offer
88
88
  url: https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/delta-skymiles-reserve-american-express-card/25330-10-0#offer-terms
89
89
  quote: 'within your first 6 months of Card Membership'
90
90
  - rule: welcome_offer
91
- url: https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/delta-skymiles-reserve-american-express-card/25330-10-0#offer-terms
92
- quote: 'Offer ends 11/04/2026 at 23:59 MST.'
91
+ url: https://www.americanexpress.com/us/credit-cards/card/delta-skymiles-reserve-american-express-card/
92
+ quote: 'after you spend $5,000 in purchases on your new Card within the first 6 months'
93
93
  - rule: foreign_transaction_fee
94
94
  url: https://www.americanexpress.com/en-us/credit-cards/apply/terms/personal/delta-skymiles-reserve-american-express-card/25330-10-0#offer-terms
95
95
  quote: 'Foreign Transaction None Penalty Fees'
@@ -6,7 +6,7 @@ issuer: amex
6
6
  network: amex
7
7
  currency: marriott-bonvoy
8
8
  business: true
9
- last_verified: 2026-11-04
9
+ last_verified: 2026-11-05
10
10
  verified_by: claude
11
11
  source_urls:
12
12
  - https://www.americanexpress.com/us/credit-cards/business/business-credit-cards/amex-marriott-bonvoy-business-credit-card/
@@ -15,11 +15,10 @@ base_rate: 2
15
15
  annual_fee: 125
16
16
  foreign_transaction_fee: 0
17
17
  welcome_offer:
18
- points: 100000
19
- spend: 8000
18
+ points: 75000
19
+ spend: 6000
20
20
  months: 6
21
- valid_to: 2026-11-04
22
- note: 'Plus a Free Night Award (up to 50,000 points), which earnrate does not value. Amex: "You may not be eligible to receive a welcome offer if you have or have had this Card or the Starwood Preferred Guest® Business Credit Card from American Express or previous versions of these Cards," and may also decline based on your card history. Also not available to anyone who had a Marriott business card from Chase in the last 30 days, opened the Marriott Bonvoy Bountiful, Boundless or Bold card from Chase in the last 90 days, or got a new Card Member bonus on one of those Chase cards in the last 24 months.'
21
+ note: 'Amex: "You may not be eligible to receive a welcome offer if you have or have had this Card or the Starwood Preferred Guest® Business Credit Card from American Express or previous versions of these Cards," and may also decline based on your card history. Also not available to anyone who had a Marriott business card from Chase in the last 30 days, opened the Marriott Bonvoy Bountiful, Boundless or Bold card from Chase in the last 90 days, or got a new Card Member bonus on one of those Chase cards in the last 24 months.'
23
22
  rules:
24
23
  - id: marriott-hotels
25
24
  match: { merchants: [marriott] }
@@ -102,13 +101,13 @@ evidence:
102
101
  quote: 'Foreign Transaction None Penalty Fees'
103
102
  - rule: welcome_offer
104
103
  url: https://www.americanexpress.com/us/credit-cards/business/business-credit-cards/amex-marriott-bonvoy-business-credit-card/
105
- quote: 'Earn 100,000 Marriott Bonvoy® Bonus Points and 1 Free Night Award'
104
+ quote: 'Earn 75,000 Marriott Bonvoy® Bonus Points'
106
105
  - rule: welcome_offer
107
106
  url: https://www.americanexpress.com/en-us/credit-cards/apply/terms/business/prospect/marriott-bonvoy-business-american-express-card/45094-9-0#offer-terms
108
- quote: 'after you spend $8,000 or more (the “Threshold Amount”) in eligible purchases'
107
+ quote: 'You can earn 75,000 Marriott Bonvoy® bonus points after you complete $6,000 or more'
109
108
  - rule: welcome_offer
110
109
  url: https://www.americanexpress.com/en-us/credit-cards/apply/terms/business/prospect/marriott-bonvoy-business-american-express-card/45094-9-0#offer-terms
111
- quote: 'Offer ends 11/4/2026 at 11:59 PM MST.'
110
+ quote: 'within your first 6 months of Card Membership'
112
111
  todo:
113
112
  - The welcome offer ends 11/04/2026; re-check the card page for the next one.
114
113
  - The welcome offer's Free Night Award isn't valued; a welcome offer holds only points.
@@ -141,7 +141,7 @@ evidence:
141
141
  quote: 'up to a maximum accumulation of $100 in a calendar year'
142
142
  - rule: expensify
143
143
  url: https://creditcards.chase.com/business-credit-cards/IHG/business
144
- quote: 'purchases made directly with Expensify through expensify.com and the Expensify app'
144
+ quote: 'Enjoy up to $100 in statement credits towards Expensify purchases each calendar year.'
145
145
  todo:
146
146
  - "Starting January 1, 2027, $50,000 of purchases in a calendar year earns Diamond Elite status. It isn't listed as a milestone yet, because a milestone has no start date."
147
147
  - The $100 airline statement credit (after $250 on flights bought directly each calendar year) isn't modeled.
@@ -6,7 +6,7 @@ color: '#e8751a'
6
6
  issuer: discover
7
7
  network: discover
8
8
  currency: cash
9
- last_verified: 2026-11-03
9
+ last_verified: 2026-11-05
10
10
  verified_by: claude
11
11
  source_urls:
12
12
  - https://www.discover.com/credit-cards/cash-back/it-card/
@@ -18,16 +18,17 @@ source_urls:
18
18
  - https://www.discover.com/credit-cards/cash-back/cashback-bonus.html
19
19
  # The calendar page's "See our FAQs".
20
20
  - https://www.discover.com/credit-cards/card-smarts/using-cashback-bonus/
21
+ # Discover's travel-card article: the only Discover page that states the
22
+ # foreign transaction fee (the application terms' fee table has no row).
23
+ - https://www.discover.com/credit-cards/card-smarts/best-international-travel-credit-card/
21
24
  base_rate: 1
22
25
  annual_fee: 0
23
- # No page states it: the application terms' fee table lists no foreign
24
- # transaction fee row, but nothing says "None".
25
- foreign_transaction_fee: null
26
+ foreign_transaction_fee: 0
26
27
  welcome_offer:
27
28
  dollars: 200
28
29
  spend: 500
29
30
  months: 3
30
- note: One-time $200 statement credit, available by applying through the card page. Existing or previous cardmembers may not be eligible if they received a new cardmember bonus for this product in the past 48 months. New cardmembers also get Cashback Match, which is ranked as a first-year rate (intro_rates) rather than counted here.
31
+ note: One-time $200 statement credit, available by applying through the card page. Existing or previous cardmembers may not be eligible if they received a new cardmember bonus for this product in the past 48 months. New cardmembers also get Cashback Match, which doubles every rate for the first year; earnrate ranks it as a first-year rate, not as part of this offer.
31
32
  caps:
32
33
  # Discover's quarterly maximum on the rotating 5% categories.
33
34
  - id: five-percent-quarterly
@@ -96,7 +97,6 @@ todo:
96
97
  - 'Discover has published only 2026 on its 5% calendar (Oct-Dec 2026 is the last quarter shown). Add 2027 quarters when Discover posts them.'
97
98
  - Discover's 5% activation isn't retroactive (5% applies "from 10/1/26 (or the date on which you activate 5%, whichever is later)"), and you can activate until the quarter's last day. The schema's activate_by can't say that purchases before activation earn 1%; it's set to the quarter's last day and the rule notes say so.
98
99
  - The card page's and application terms' Cashback Match terms say "through your first 365 days"; the Cashback Bonus page says "your first 12 consecutive billing periods or 365 days, whichever is longer". Modeled as 365 days, the shorter. Cash back is matched only when processed within the period, so a purchase in its last days may post too late; earnrate goes by the purchase date.
99
- - Foreign transaction fee isn't stated on any Discover page or in the application terms (their fee table has no foreign transaction row). Left null; confirm with the Cardmember Agreement or Capital One.
100
100
  - Discover says 5% purchases "must be made with merchants in the U.S." The application terms put that sentence in the general earning paragraph, so it may also apply to the 1% base and Capital One Travel; only the rotating rules are marked us_only.
101
101
  - Amusement parks, tourist attractions, aquariums, zoos, record stores, video rental, pool halls and bowling alleys earn 5% as Q4 2026 entertainment but have no earnrate category. Golf courses, country clubs and collegiate sporting events are excluded but can't be told apart from the entertainment category.
102
102
  - Third-party payment accounts and digital wallet transactions may not earn 5% if they don't carry a qualifying MCC. Not modeled.
@@ -162,6 +162,9 @@ evidence:
162
162
  - rule: annual_fee
163
163
  url: https://disclosures.capitalone.com/disclosure.42300.en-US.html
164
164
  quote: 'Annual Fee None.'
165
+ - rule: foreign_transaction_fee
166
+ url: https://www.discover.com/credit-cards/card-smarts/best-international-travel-credit-card/
167
+ quote: 'Discover® has no foreign transaction fee'
165
168
  - rule: annual_fee
166
169
  url: https://www.discover.com/credit-cards/cash-back/it-card/
167
170
  quote: 'No, there is no annual fee for the Discover it Card.'
@@ -148,4 +148,4 @@ evidence:
148
148
  quote: 'Foreign Transaction Fee We will not charge you a foreign transaction fee.'
149
149
  - rule: foreign_transaction_fee
150
150
  url: https://venmo.com/about/creditcard/
151
- quote: 'No foreign transaction fee 2 Spend when you travel.'
151
+ quote: 'No foreign transaction fee'
@@ -21,6 +21,7 @@
21
21
  mccs: [5812, 5813, 5814]
22
22
  - id: dining.fast_food
23
23
  name: Fast food
24
+ preposition: on
24
25
  aliases:
25
26
  [fast food restaurants, coffee shop, coffee shops, coffee, cafe, cafes]
26
27
  mccs: [5814]
@@ -35,6 +36,7 @@
35
36
  mccs: [5411]
36
37
  - id: grocery.online
37
38
  name: Online grocery and delivery
39
+ preposition: on
38
40
  aliases: [grocery delivery]
39
41
 
40
42
  - id: superstore
@@ -57,8 +59,10 @@
57
59
  mccs: [5541, 5542]
58
60
  - id: gas.grocery_store
59
61
  name: Gas at grocery stores
62
+ preposition: on
60
63
  - id: gas.superstore
61
64
  name: Gas at superstores and warehouse clubs
65
+ preposition: on
62
66
  - id: ev_charging
63
67
  preposition: on
64
68
  name: EV charging
@@ -72,6 +76,7 @@
72
76
  mccs: [4111, 4112, 4131, 4784, 7523]
73
77
  - id: transit.rideshare
74
78
  name: Rideshare and taxis
79
+ preposition: on
75
80
  aliases: [rideshare, taxi, taxis]
76
81
  mccs: [4121]
77
82
 
@@ -81,6 +86,7 @@
81
86
  mccs: [4722]
82
87
  - id: travel.flights
83
88
  name: Flights
89
+ preposition: on
84
90
  aliases: [flights, airfare, airlines, flight, plane tickets]
85
91
  mccs: [4511]
86
92
  - id: travel.hotels
@@ -89,10 +95,12 @@
89
95
  mccs: [7011]
90
96
  - id: travel.car_rental
91
97
  name: Car rental
98
+ preposition: on
92
99
  aliases: [car rentals, rental cars]
93
100
  mccs: [7512]
94
101
  - id: travel.cruise
95
102
  name: Cruises
103
+ preposition: on
96
104
  aliases: [cruise]
97
105
  mccs: [4411]
98
106
 
@@ -149,6 +157,7 @@
149
157
  mccs: [5941]
150
158
  - id: home_improvement
151
159
  name: Home improvement
160
+ preposition: on
152
161
  aliases: [hardware stores, hardware store, home improvement store]
153
162
  mccs: [5200, 5211]
154
163
  - id: furniture
@@ -171,4 +180,5 @@
171
180
  - id: other
172
181
  slug: everyday-spending
173
182
  name: Everything else
183
+ preposition: on
174
184
  aliases: [everything, everything else, other]
@@ -748,7 +748,7 @@
748
748
 
749
749
  # Transit
750
750
  - { id: uber, name: Uber, category: transit.rideshare }
751
- - { id: uber-one, name: Uber One, category: other }
751
+ - { id: uber-one, name: Uber One, category: other, preposition: on }
752
752
  - { id: lyft, name: Lyft, category: transit.rideshare }
753
753
  - { id: amtrak, name: Amtrak, domains: [amtrak.com], category: transit }
754
754
  - {
@@ -3359,11 +3359,13 @@
3359
3359
  name: Walmart+ membership
3360
3360
  aliases: [walmart+, walmart+ membership fee]
3361
3361
  category: other
3362
+ preposition: on
3362
3363
  note: The Walmart+ membership fee, not shopping at Walmart.
3363
3364
  - id: peloton-membership
3364
3365
  name: Peloton membership
3365
3366
  aliases: [peloton all-access, peloton app, peloton app one, peloton strength+]
3366
3367
  category: other
3368
+ preposition: on
3367
3369
  note: A Peloton membership or app subscription, not equipment.
3368
3370
  - id: chatgpt
3369
3371
  name: ChatGPT
@@ -13,20 +13,21 @@ import { cardValuation } from "../engine/valuation.js";
13
13
  import { analyzeStatement, } from "../import/analyze.js";
14
14
  import { parseStatement } from "../import/statement.js";
15
15
  import { displayPath, readWallet, RUNTIME_ERROR, USAGE_ERROR, } from "./run.js";
16
- import { currencyName, dollarsAndCents, periodPhrase } from "../engine/text.js";
16
+ import { currencyName, dollarsAndCents, listed, periodPhrase, } from "../engine/text.js";
17
17
  const IMPORT_HELP = `Usage: ${CLI_NAME} import <statement.csv> --card <card-id> [--apply]
18
18
  ${CLI_NAME} import <statement.csv>:<card-id> [<statement.csv>:<card-id> ...]
19
19
 
20
20
  Reads card statements (Amex and Chase activity CSVs) and reports:
21
21
  - what the same purchases would have earned on earnrate's picks
22
22
  - where the issuer coded a merchant differently than earnrate assumes
23
- - the cap usage it would record for the current period
23
+ - the cap usage, welcome-bonus and spend-bonus spending, and statement
24
+ credits used that it would record for the current period
24
25
 
25
26
  Name several statements, each with its card, to see one report across them.
26
27
 
27
28
  Nothing is written unless you pass --apply, and then only cap totals,
28
- welcome-bonus spending and a fingerprint of each recorded purchase, so
29
- importing twice never counts twice.
29
+ welcome-bonus and spend-bonus spending, statement credits used and a
30
+ fingerprint of each recorded purchase, so importing twice never counts twice.
30
31
  The statements themselves are never stored.
31
32
 
32
33
  Options:
@@ -66,10 +67,7 @@ function valuationNote(catalog, wallet, cardIds, profile) {
66
67
  if (values.size === 0)
67
68
  return 'Cash back counted as cash.';
68
69
  const parts = [...values].map(([id, cents]) => `${String(cents)}¢ for ${currencyName(catalog, id)}`);
69
- const list = parts.length > 1
70
- ? `${parts.slice(0, -1).join(', ')} and ${parts.at(-1) ?? ''}`
71
- : (parts[0] ?? '');
72
- return `Points valued at ${list}${profile ? ` (the ${profile} profile)` : ', as in your wallet'}.`;
70
+ return `Points valued at ${listed(parts)}${profile ? ` (the ${profile} profile)` : ', as in your wallet'}.`;
73
71
  }
74
72
  function formatAnalysis(a, labelOf, valuation, categoryName) {
75
73
  const several = a.byCard.length > 1;
@@ -248,21 +246,26 @@ export async function runImport(argv, io) {
248
246
  ...(milestoneEntries.length > 0
249
247
  ? [`${String(milestoneEntries.length)} toward spend bonuses`]
250
248
  : []),
249
+ ...(analysis.credits.length > 0
250
+ ? [`${String(analysis.credits.length)} against statement credits`]
251
+ : []),
251
252
  ];
252
253
  if (!values.apply || toRecord.length === 0) {
253
254
  io.stdout(values.apply || toRecord.length === 0
254
255
  ? 'Nothing was written.\n'
255
- : `Nothing was written. Run again with --apply to record ${toRecord.join(' and ')}.\n`);
256
+ : `Nothing was written. Run again with --apply to record ${listed(toRecord)}.\n`);
256
257
  return 0;
257
258
  }
258
- await writeProgress(walletFile, analysis.bookings.map((e) => e.booking), analysis.bonuses, analysis.milestones);
259
+ await writeProgress(walletFile, analysis.bookings.map((e) => e.booking), analysis.bonuses, analysis.milestones, analysis.credits.map((e) => e.booking));
259
260
  for (const entry of analysis.bookings)
260
261
  recorded.add(fingerprint(entry));
262
+ for (const entry of analysis.credits)
263
+ recorded.add(fingerprint(entry, 'credit'));
261
264
  for (const entry of bonusEntries)
262
265
  recorded.add(fingerprint(entry, 'bonus'));
263
266
  for (const entry of milestoneEntries)
264
267
  recorded.add(fingerprint(entry, 'milestone'));
265
268
  await writeFileAtomic(ledgerFile, `${JSON.stringify({ version: 1, recorded: [...recorded] }, null, 2)}\n`);
266
- io.stdout(`Recorded ${toRecord.join(' and ')} in ${displayPath(walletFile)}.\n`);
269
+ io.stdout(`Recorded ${listed(toRecord)} in ${displayPath(walletFile)}.\n`);
267
270
  return 0;
268
271
  }
package/dist/cli/todo.js CHANGED
@@ -9,8 +9,9 @@ import { displayPath, readWallet, RUNTIME_ERROR, USAGE_ERROR, } from "./run.js";
9
9
  const TODO_HELP = `Usage: ${CLI_NAME} todo [--ics <file>] [--date <YYYY-MM-DD>]
10
10
 
11
11
  What your saved cards need this period, soonest first: bonuses to activate,
12
- categories to pick, spending caps running low, welcome bonuses to finish, and
13
- rotating categories the issuer hasn't posted yet.
12
+ categories to pick, spending caps running low, welcome bonuses to finish,
13
+ spend bonuses you're close to, statement credits to enroll in or use, first-year
14
+ rates ending, and rotating categories the issuer hasn't posted yet.
14
15
 
15
16
  Options:
16
17
  --ics <file> Also write the dated items as a calendar file to import