The problem
The feature you need is always one tier up.
A relative runs a small business and invoices overseas customers. Every time they needed one more thing, like recurring bills or a payment reminder, their invoicing software asked them to upgrade.
It’s the usual SaaS pricing trick: the basic plan is just short of useful. For a one-person business sending a few dozen invoices a month, that adds up to a monthly bill for features they’ll never use.
My bet was that AI has made software cheap enough to build for exactly one person. Instead of finding a cheaper vendor, I built the tool they needed: their invoice layout, their tax fields, their way of getting paid.
Own your tools, own your data.The one-line brief, from the project README
The whole promise on one screen: polished invoices, saved customers and items, sending from your own Gmail, recurring billing.Who it’s for
Built for my relative first, and then anyone who wants to host it themselves.
Primary
My relative
Runs a small owner-operated business, billing overseas buyers. Needs clean tax invoices, reminders and a record of who has paid. Does not want to learn accounting software.
Secondary
Their customers’ finance teams
Buyers who keep asking for copies of invoices and what’s still owed. They get a private read-only page instead of another email thread.
Wider
Small sellers who’d rather own their tools
The code is public under an MIT license, with a quick-start guide and support for several businesses in one login.
InferredScale
Tens of invoices a month
Deliberately small. The design optimises for getting one invoice right quickly, not for bulk billing or teams.
Use cases
How they use it in a month.
The monthly invoiceOwner, start of month
Pick the customer, pick saved items, and price, tax, due date, bank details and export notes fill themselves in. Download the PDF or send it from their own Gmail.
The late payerOwner, week three
Reminders are scheduled with the invoice: 3 days before, on the day, and a week after. They send themselves, with the PDF attached, and stop the moment it’s paid.
The partial transferOwner, on a bank alert
Record the amount, method and reference. The amount defaults to what’s still owed, not the invoice total. Paid in full gets a PAID stamp, on screen and on the PDF.
The retainerA client billed on the 1st
A monthly schedule creates the invoice automatically. If the server was off, it catches up on the runs it missed.
“Can you resend that?”The buyer’s accounts team
The owner switches on a private link for that customer. The buyer sees what’s outstanding, overdue and paid, and downloads any PDF. No account needed.
The product
An invoice in about a minute.
It does three things: create an invoice, send it and get paid. Every other feature makes one of those quicker.
New invoice. Items come from a saved catalogue, the due date follows from the terms, and bank and export notes pre-fill.Recording a partial bank transfer. The amount defaults to the balance.The PDF the customer receives: real text, not an image, about 4 KB. Retainers that bill themselves, with Run now and Pause.Tax invoices that match
A PDF layout copied from the one the business already used, generated on the server as real text so it stays crisp and tiny.
Send from your own Gmail
Invoices come from the owner’s address, not a no-reply. Optional: without it, Download PDF still works.
Each payment stored on its own
That makes partial payments, repeat payments and undo easy.
Several businesses, one login
Each has its own numbering, logo, currency symbol, tax label and default terms.
Accountant-ready export
Excel export filtered by financial year (April to March), quarter or a custom range.
Reminders that know the date
Reminder wording changes with how late the invoice is, and cancels itself when paid.
How it works
I kept it small, so one person can run it.
One Node server, one React app with no build step and one Postgres database.
Owner
The app
Invoices, customers, recurring billing and settings, in the browser.
Server
Express
Makes PDFs and Excel files, sends Gmail, runs every request as the signed-in user.
Database
Supabase Postgres
Keeps each user’s data separate, numbers invoices without gaps, calculates balances.
Scheduler
Daily job
Creates due recurring invoices, then sends due reminders.
Buyer
Private portal
A read-only page showing only that customer’s invoices.
Node + ExpressReact (no build step)Supabase Postgres + StoragepdfkitexceljsGmail APIVercel
What the buyer sees through their private link. Built and working locally, not yet released.Product decisions
The main decisions.
Build a narrow tool instead of buying a bigger plan
WhyThe relative needed about six features. Paying for a tier to get one of them made no sense when building all six was feasible.
Trade-offNo vendor support or roadmap. Whoever runs it owns the upkeep.
Freeze seller and buyer details on each invoice
WhyAn invoice is a legal document. Editing a customer’s address later must never rewrite an invoice that was already sent.
Trade-offSome duplicated data per invoice. Logos are stored by reference to keep that small.
Let the database number invoices without gaps
WhyTax authorities expect unbroken invoice sequences. If creating an invoice fails, its number is given back instead of skipped.
Trade-offMore careful transaction code than a simple counter.
Start with one local file, then move to a real database
WhyVersion one stored everything in a single JSON file: no account, no cloud. Four weeks later it moved to Postgres so it could run anywhere, with sign-in and data separated per user.
Trade-offIt gave up the original “no cloud, no account” simplicity. The README still needs to catch up.
A secret link instead of customer accounts
WhyBuyers won’t create an account to view one invoice. A long, unguessable link that the owner can replace or switch off is enough.
Trade-offAnyone holding the link can view those invoices, so replacing and revoking it had to be one click.
Dates as plain calendar days
WhyIn India’s timezone, reminders fired a day early and a 1 February schedule rolled to 28 February. All date maths now ignores timezones, with tests run under six of them.
Trade-offA small refactor across every place that touched dates.
Where it stands
Working, open source, with two features still unreleased.
Built
- Invoices, customers and a saved item catalogue
- Real-text PDFs with a PAID stamp
- Sending from Gmail with editable templates
- Full and partial payments, with undo
- Monthly recurring invoices with catch-up
- Scheduled payment reminders
- Several businesses per login
- Excel export
- Sign-in with password, email link or passkey
- Deployed on Vercel
In progress
- Customer portal (built and working locally, not yet released)
- Timezone-safe dates, with tests across six timezones
Next
- Custom fields (the data model already reserves space)
- Tax rates as a managed list
20
commits, all public on GitHub
~9k
lines of hand-written code
4 KB
size of a full tax-invoice PDF
48
steps in the end-to-end API test
- 23 Jun 2026First version: the whole app on a single local file
- 25 JunOpen-sourced under MIT, README rewritten around the story
- 19 JulMoved to Postgres with tests proving nothing changed
- 20 JulPer-user data separation, passkeys, password reset
- 21 JulDeployed on Vercel
- 26 AugCustomer portal and timezone fix built, not yet released
What I learned
What building it taught me.
- The fastest way to understand a SaaS pricing page is to build the features behind it. Most of them were a day of work each.
- Building for one real person made decisions easy. Every feature had a name attached to it, and anything without one didn’t get built.
- Dates are where small tools break. The timezone bug was invisible in tests recorded in UTC and obvious to anyone in India.
Next in the lab
The Aspiration Network
Classmates post what they’re working toward and the network helps them get there. Reputation comes only from help that actually went somewhere.