Tap-to-Track: Eight Seconds From Purchase to Spreadsheet
Every expense app I tried failed at the same moment: the one where you are standing at a counter with a receipt in your hand. Tap-to-Track is what I built instead — a Back Tap, three taps, and the entry is in a Google Sheet I own.
Expense tracking does not fail because people lack discipline. It fails at the counter. You pay for lunch, you are walking back to your desk, and logging it means unlock, find the app, wait for it to load, tap through four screens. So you tell yourself you will do it tonight, and you do not. A month later the data is gone and the habit is dead.
The commercial apps do remove that friction, but they remove it by taking something: your bank credentials, your attention through ads, or a subscription. Your spending history becomes their asset. Tap-to-Track removes the friction without taking anything. The data lives in a spreadsheet you own, the code runs in your own Google account, and the entry takes one gesture and three taps.
What the eight seconds look like
Back Tap is the iOS gesture almost nobody uses — Settings, Accessibility, Touch, right at the bottom — and it fires a Shortcut when you tap the back of the phone twice. That is the entry point. There are three others for when the gesture is inconvenient: a home screen icon, Siri, and Control Centre. The screen never has to leave the lock.
So what? The gesture is the design, not a garnish on it. Anything that requires finding an icon has already lost to the queue behind you.
The architecture, in one line
There is no fourth box. The web app is deployed from the spreadsheet itself, so it needs no credentials to reach it — it is already inside your account. Nothing is published, nothing is hosted, nothing is shared.
The amount field does arithmetic
Real spending is rarely one clean number. A dinner gets split three ways; an invoice needs GST added; a discount comes off a quantity. So the amount field takes expressions, with correct operator precedence and optional =.
| You type | Logs | Use case |
|---|---|---|
120 | 120 | normal |
=500/3 | 166.67 | split three ways |
=(250+130)/2 | 190 | two items, split in half |
=1200*18% | 216 | GST |
=1500*2-300 | 2700 | quantity, minus a discount |
1,200 · ₹450 | 1200 · 450 | commas and symbols tolerated |
-50 | −50 | refund |
Two lunches on the same day
The sheet holds one cell per category per day, so a second entry has to combine with the first rather than overwrite it. It does, and it keeps full precision while doing so — a plain number stays a plain number, and a cell only becomes a formula if you typed arithmetic.
| Cell before | You enter | Becomes | Displays |
|---|---|---|---|
| (empty) | 120 | 120 | ₹120 |
| (empty) | =500/3 | =500/3 | ₹166.67 |
75 | 120 | 195 | ₹195 |
75 | =500/3 | =75+(500/3) | ₹241.67 |
=576-50 | 120 | =576-50+(120) | ₹646 |
Remarks append with a semicolon, so the day's cell carries all three descriptions. There is one level of undo, and it restores the exact prior state — including whether the cell was a number or a formula.
So what? Subtotals stay exact because the sheet keeps the arithmetic, not the rounded result. ₹166.67 three times is ₹500, not ₹500.01.
The layout of the sheet is not hardcoded
The obvious way to write to a spreadsheet is to hardcode the column: groceries go in column D. That works until somebody inserts a column, and then a month of groceries quietly lands in Transport. The script never stores a column position. On every single request it:
Because it navigates by name, you can insert columns, reorder categories, add charts, or add whole new categories and it adapts with no code change. If the sheet stops looking like the sheet it expects, it refuses to write rather than guessing.
So what? Refusing is a feature. A tracker that silently files things in the wrong place is worse than one that stops and says so.
The spreadsheet is the actual product
The template in the repository is a complete tracker with zero data in it — every formula, chart and conditional format works, every value is blank. Twelve tabs on the Indian financial year, ten categories with sub-categories, each paired with its Remarks column. Change two cells on the Summary sheet and all twelve tabs re-date themselves; weekends turn red on their own.
Above that sits a summary dashboard — year-to-date income, expense, savings rate, best month, top category, month-over-month change, and a mid-month savings projection that weights recurring against discretionary spend — and a per-month dashboard with a category breakdown, percentage of income, and a donut chart. None of it is locked. It is a spreadsheet; extend it.
Adapting it outside India
The tab names are month names, so a different financial year is a rename plus a date-formula tweak. Categories are discovered from the headers, so a new one works immediately — just add it to the Shortcut's list too. Currency is a cell number format; the script stores plain numbers. The one thing to preserve is the three anchors it navigates by: every category column immediately followed by a column headed Remarks, and row 2 containing Date and Daily Expenditure.
The security thinking, since it is a public endpoint
The web app is deployed with access set to "Anyone", which sounds alarming and is worth being precise about: that setting governs who can call the URL, not who can read your sheet. A shared token guards it, and requests without the correct token are rejected before any sheet access happens. The URL is the only secret in the system. Treat it like a password.
No eval(). An arithmetic field is exactly where you reach for eval, and on a public endpoint that would run arbitrary code with your Drive permissions. Instead there is a hand-written recursive-descent parser behind a strict character whitelist. Any letter is rejected outright, which blocks =SUM(A1:A5), =alert(1), and every injection shaped like them.
The script cannot restructure your sheet. The executable code contains exactly two mutating calls, setValue() and setFormula(). There is no insertRow, deleteRow, insertColumn, deleteColumn, insertSheet, deleteSheet, clear, merge, appendRow or setDataValidation anywhere in the file. The worst a bug can do is write a wrong number into one cell you can undo.
So what? Auditing that list — and being able to state it in one sentence — is worth more than any amount of defensive code around a larger surface.
Which Google account to use
The script must run under an account with edit access to the sheet. It does not need to own it, but every extra party is a dependency that can break quietly.
| Setup | Verdict |
|---|---|
| Personal Gmail owns the sheet, same account runs the script | Best. No dependencies. |
| Sheet owned by someone else, you have edit access | Works, but breaks silently if they revoke access |
| Work or Workspace account | Admins can block "Anyone" web app deployment — check first |
| Two accounts, one for the sheet and one for the script | Works, but doubles what can go wrong |
On a Workspace account you find out at the deploy step: the Anyone option under "Who has access" is missing or greyed out. Build on a personal Gmail instead. And be signed into the right account before you start — creating the script under the wrong one is the single most common setup mistake.
Setting it up
About twenty-five minutes end to end, no development environment, and the repository has a guide for each stage plus a troubleshooting page covering every error the script can produce.
What it deliberately does not do
No bank sync, no receipt scanning, no budget alerts. Each needs either credentials or a server, and the point is that there is neither.
Three real limitations are worth knowing before you build it. One cell per category per day — three lunches become one figure with three semicolon-joined remarks, so you cannot later ask what the second lunch on the 12th was; that granularity does not exist in the sheet's design. Renaming a category means updating the Shortcut's list, because the phone cannot know the sheet changed until it asks. And cold start: the first request after about fifteen idle minutes takes three to five seconds while Apps Script wakes up, later ones one to two. The write completes regardless — you can pocket the phone before the notification lands.
iPhone only for now. The backend is a plain HTTP endpoint, so an Android client through Tasker or HTTP Shortcuts would work; it just is not written yet.
Where the AI actually helped
I built this with Claude as a pair-programmer, and the useful part was not code generation. It was the parts of the job I would otherwise have skipped.
The recursive-descent parser is the clearest case. I knew I did not want eval; writing a correct precedence-climbing parser with a character whitelist from scratch, on a weekend, for a personal expense tracker, is exactly the kind of thing that gets abandoned in favour of the unsafe one-liner. Having a second party draft it and then argue about the edge cases — percentages, unary minus, mismatched brackets — made the safe version the cheap option instead of the expensive one.
The second case is the audit. "List every mutating call in this file, and tell me what the worst thing it can do to my spreadsheet is" is a question that takes a person an hour and a model a few seconds, and the answer is what let me write the guarantee in the section above as a fact rather than an intention. The third is the documentation: six guides, a troubleshooting page covering every error string, and a localisation guide for people outside India. Personal projects do not usually get documentation, because by the time the thing works the author no longer needs it.
So what? The tool would exist without the AI. The parser, the audit and the six guides would not — those are the parts that get cut when it is a weekend project with an audience of one.
The Apps Script backend in one file, the blank spreadsheet template, and six guides: setup, the Shortcut build, the spreadsheet, troubleshooting, settings and localisation, and how it works. MIT licensed.
github.com/srivastav-ayush/tap-to-track ↗