Teaching Claude to file my paperwork
May 24, 2026 · 4 min read
I have several "inbox" directories scattered across my filesystem — ~/Documents/Finance/inbox, ~/Documents/Car/inbox, a generic ~/inbox, and so on. Things land there from browser downloads, scanner output, forwarded email attachments, and a pipeline that fetches PDFs from links inside forwarded emails. They have names like attachment (3).pdf, Screenshot 2026-04-12 at 10.15.32.png, or statement.pdf. Useless.
For years I'd let them pile up, then once a quarter sit down and rename, dedupe, and file them by hand. It was the kind of chore I'd put off until I couldn't find a thing I needed.
So I wrote a Claude Code skill called ingest-inbox. It's about 1,100 lines of SKILL.md — mostly a destination catalog ("paystubs go here, vet bills go there"), filename conventions, dedupe rules, and a handful of mode-switches. The interesting part isn't the rules. It's that I no longer have to know the rules.
How it works, mechanically
The skill resolves the inbox path to one of five modes:
| Inbox | Mode | What changes |
|---|---|---|
~/Documents/Finance/inbox |
finance |
Auto-files per account map, runs beancount extraction |
~/Documents/ |
subtree |
Files within that subtree, proposes per group |
~/Documents/inbox |
documents |
Heuristic destinations, asks per group |
~/inbox |
home |
Routes real docs into ~/Documents/inbox/, flags trash |
| anything else | generic |
Identifies, proposes per file |
For every PDF, it runs a local docling container at http://localhost:5001 and writes a .md sidecar — the canonical text-extraction step. It dedupes against the destination (existing OCR'd copies have sidecars; raw inbox downloads don't, and I never want the raw one overwriting the OCR'd one). Filenames get normalized to YYYYMMDD . Duplicates go to _duplicates/ for audit, never deleted outright.
In Finance mode it goes further: extracts every transaction and balance, categorizes against the existing accounts in my beancount ledger (never inventing new ones), and writes a self-contained ingestion note in cleanup/ that I can paste into the ledger. (Follow up blog post to come about how I use plaintextaccounting)
Worked example: an oil change
Earlier today, I got the car serviced. Here's what happened end-to-end without me touching anything until the very last step:
- Valvoline emails the receipt to my Gmail. I forward it to
.@ - Email-ingest pipeline (Gmail → SES → S3 → Lambda → a FastAPI receiver on my homelab) classifies the route as
car, appends the body to~/Documents/Car/vault/2026-05.mdwith a tag, and notices the email contains a link to a PDF invoice. - It follows the link and saves that page if appropriate (e.g. this email from Valvoline had a direct link to the PDF to download). If it's a PDF, drop it into
~/Documents/Car/inbox/with three sidecars:.meta.yml(sender, subject, classifier route),.links.fetched.yml(source URL, sha256, http status), and a.docling.mdit already extracted. - I tell Claude: "ingest the Car inbox." It invokes
ingest-inbox, resolves the path tosubtreemode withSUBTREE=Car, reads the sidecars (so it skips re-running docling), and identifies the file as a Valvoline invoice for the 2023 Forester. - It proposes filing into
~/Documents/Car/Service/20260524 Valvoline Oil Change/, with the PDF renamed to20260524 Valvoline Oil Change.pdfand the sidecar travelling with it. I confirm. mv -n. Done.
What used to be a ten-minute task — open the PDF, figure out what it is, rename it, find the right folder, check for a duplicate, move it — is now me typing one sentence.
Why I bother
The skill itself isn't impressive — it's a destination map plus some bash plus a polite invocation of docling. What's impressive (to me) is the second-order effect: I now forward things to my inbox without dread. The Valvoline email used to be an item on a future chore list. Now it's a 30-second round-trip through the homelab and it ends up in exactly the folder I'd have put it in, with a markdown sidecar I can grep.
The next time I'm at the mechanic and they ask "when was the last oil change", I can answer in five seconds with ls ~/Documents/Car/Service/ | tail. That's the whole point.