I’ve used Mint.com to categorize and measure my spending for years. It is an amazing tool, but the user interface for tagging transactions has always had a frustrating limitation. After adding a tag to a transaction, you don’t see that tag anywhere on the page. I wrote a user script to display them:
How do I install it?
Follow the (very short) installation instructions on GitHub.
Why is this necessary?
On Mint, a transaction must belong to one category (for example, Vacation or Restaurants). At the same time, a transaction may belong to any number of tags (for example, I can add the Taiwan trip tag to an expense in the Vacation category, or add Work lunch and Client X tags to an expense in the Restaurants category). The way I use Mint, categories and tags are both important to gaining a complete understanding of where my money goes. Unfortunately, in the transactions list (above), only categories are displayed.
The only time you ever see what tags you have applied to a transaction is when you are editing it:
I’m baffled that the tags feature has such a glaring omission in functionality… so I wrote a user script to fix it.
How does it work?
Find XHR responses containing transaction data
I notice that the Mint app makes a request to
getJsonData.xevent on every page load. To investigate, I try loading different transactions into the page by clicking “Next” at the bottom of the transactions list, or by performing a transaction search. Each time, another request is made to that URL. Filtering the requests to show only those for this URL, and inspecting the content of the responses, confirms that the list of transactions is included in this data. Digging deeper into the structure of the data, I can find how the “labels” for each transaction are represented:
Overwrite the browser’s XHR code to intercept the data
getJsonData.xevent endpoint. Then my code can store its own copy of the data before the Mint app even sees it.
I overwrite the
XMLHttpRequest.prototype.open method to run my code first, and then call the browser’s original version of the method. This allows me to inspect every request:
React to changes in the content of the transactions table
Next, I need to use the data stored by the code above to add the tags to the transactions table. I can’t simply add them as soon as the data arrives, because the Mint app needs a chance to process that data and populate the table with it. A hacky way to do this would be to use
setInterval to check the transactions table every few seconds to see if it’s changed, and add/remove tags as necessary. But there’s a better way: the MutationObserver API. This is a new-ish browser API, almost tailor-made for the use case of third-party code watching for changes to the DOM.
React to the user editing a transaction
It turns out that I can’t respond only to changes to the transactions table (triggered by navigating between pages of transactions or a search). I also need to update the table after the user edits a transaction. For this, the code must intercept XHR requests, so that it can spy on POSTs to the edit transaction endpoint (just as it spied on XHR responses for the transaction list above).
It doesn’t take much code
The final code is a bit more nuanced than just the above. For example, Mint makes a separate request for a mapping from tag ID to tag name, which also must be cached. When Mint updates the transactions table, it actually triggers multiple DOM mutation events for each row (one each for date, merchant, price, etc.), so the code must be smart enough to update each row only once. Also, observing the DOM must be paused while the user script itself mutates the DOM by adding tags.