DEV Community

jiahao luo for AI Invoice Maker

Posted on • Originally published at dev.to

I built a free, no-login, unlimited AI invoice tool. Here are the 4 features and the 5 5 3 dunning matrix behind it.

Cross-post notice: this is the canonical version. Reposts on Medium, Hashnode, and Indie Hackers will link back here.

A few months ago I shipped a free invoice generator. The kind of tool freelancers find at 11pm when a client wants an invoice and they don't want to register on a CRM just to send a PDF.

The whole product runs under four hard constraints I refused to break:

  • 100% online — open the URL, use it. No install, no desktop app, no browser extension. Works on any laptop, tablet, or phone with a browser.
  • Unlimited use — no per-invoice cap, no "after 5 invoices please sign up," no monthly quota. Generate one a day, generate a hundred a day. Same tool either way.
  • No login — anonymous by default. No email capture, no account creation, no password reset flows, no session cookies tied to identity. Your data lives in your browser's localStorage; close the tab and nothing is sent anywhere we don't show you.
  • Free, period — no paid tier, no credit card, no "premium templates," no watermark on PDFs.

Those four together are the actual product. Everything else (AI features, customization, multi-document) is what I built given those constraints, not in spite of them.

Generating invoices is the easy part. The hard parts are:

  1. Filling the damn form (people will give up halfway and bounce)
  2. Making it look like theirs, not a generic template
  3. Actually getting paid 30 days later when the client goes quiet
  4. Not having to switch tools when they need a receipt or a quote next week

This post is a build-in-public walkthrough of the four AI features I shipped to address each of those, the architectural decision that surprised me most (a relationship-aware dunning matrix), and a few real costs from the prompt design.

Tool is at ainvoicemaker.com — open it, it works. Nothing to sign up for.


1. Smart Paste — text or image → form fields, in 5 seconds

The single highest-friction step of every invoice tool I've ever used: filling the form. Client name, address, line items, rates, dates. 12-20 fields of typing, every time, often pulled from an email.

So Smart Paste eats the email instead.

Smart Paste bar at the top of the invoice form, with a textarea, image-upload button, and example chips

You paste anything — a brief, an email thread, a Slack message, a Notion doc — and the AI extracts what it can:

  • Client business name, address, contact email
  • Line items (description + quantity + unit price)
  • Currency (detected from $ / € / £ / ¥ or country mentions)
  • Due date (from "net 30", "by Friday", "end of month")
  • Project / invoice context

It also accepts screenshots (4MB max). Camera button → upload photo of a handwritten estimate, an email screenshot, or a competitor's invoice you're rebuilding. Same AI pipeline, vision input.

The implementation honest part

I started with one big prompt asking for the entire invoice JSON in one shot. It hallucinated currencies and made up tax rates. Switching to an extraction-only prompt (pull entities, don't reason about them) and a separate validation pass cut hallucinations to near zero on real-world inputs.

SYSTEM (extraction only):
You extract structured fields from unstructured text. You do
NOT infer, calculate, or assume. If a field isn't in the
input, return null for it. Never invent currencies, tax
rates, or amounts. Never round.

INPUT: {raw text or OCR'd image}

OUTPUT: JSON matching the invoice schema. Empty fields = null.
Enter fullscreen mode Exit fullscreen mode

Three-line discipline, one big behavior change: the model stopped trying to "be helpful" and just reported what it actually saw.

Smart Paste works for invoice, receipt, quote, superbill, and proforma — same component, same API endpoint, the document type is just a parameter.


2. Customizable preview — 14+ knobs, no design skill required

Generic invoice tools all spit out the same Stripe-blue / black-Helvetica template. That's fine for one-off jobs but it's not yours — and clients pattern-match the template they see most.

Customize sheet open: 3 style presets (Minimal / Professional / Branded), 8 brand-color swatches, density selector (Compact / Standard / Roomy), and per-section toggles

The customize sheet has:

  • 3 paper styles — classic, modern, minimal
  • 8 brand colors — pre-tuned for invoice contrast (no guesswork on accessibility)
  • Paper size — A4 or Letter (auto-detected from locale, override-able)
  • Column visibility — show/hide tax, discount, notes columns based on whether you actually use them
  • Logo upload — PNG with transparent background, auto-fits header
  • 13 languages — full UI + invoice copy translations (not just labels — totals row, tax line, payment terms)
  • Currency — 50+ currencies with proper formatting (e.g. €1.234,56 vs $1,234.56)
  • Typography — serif / sans / mono mode

The whole customize sheet is pure form state — no save button, no "draft" concept. Every change updates the live preview pane on the right within 100ms. Refresh and you're back to defaults; you only "commit" by downloading the PDF or sending it.

That decision (no save state) cut roughly 40% of complexity out of the product. No accounts, no drafts, no "where did my invoice go" support questions. The local browser is the source of truth, and recent invoices are remembered in localStorage for re-use.


3. AI Payment Reminders — a 5 × 5 × 3 dunning matrix

This is the part I'm proudest of architecturally, so I'll go deeper.

The naive way to do reminders is one tone, generic email, click-and-send. The slightly-less-naive way is N tones (friendly / firm / etc), pick one, send.

What I shipped is a 5 × 5 × 3 matrix:

  • 5 tones — soft, standard, firm, final, demand
  • 5 escalation steps — first reminder through last notice
  • 3 relationship types — new, loyal, going-silent

Payment Reminder generator with 4 tone tabs (Gentle / Second reminder / Firm / Final notice), each auto-recommended by days overdue, with full email body preview

The matrix:

                     Step 1  Step 2  Step 3   Step 4   Step 5
Relationship:
  new (default)       soft   standard firm   final    demand
  loyal              soft   soft     standard firm    final
  going_silent       soft   standard firm    final    demand
Enter fullscreen mode Exit fullscreen mode

A loyal customer (3+ paid invoices, on time) gets benefit of doubt and escalates one step slower than a new client. A "going-silent" customer (was responsive, now isn't) gets standard escalation. A new client gets standard escalation but with no past relationship to lean on.

Why this matters: the cost of using "firm" too early on a loyal $50K/year client is higher than the cost of using "soft" too long on a new $500 client. The matrix encodes the asymmetric risk so the user doesn't have to think about it.

The "demand" tone never threatens legal action

Step 5 is "demand" — the strongest tone the system will produce. It explicitly does not include phrases like "small claims," "collections agency," or "legal action." Reasoning:

  • Legal threats from a freelancer often violate their own contract or the client's jurisdiction
  • AI-generated legal threats are an unbounded liability surface for the tool
  • Most freelancers don't actually want to escalate; they want a strong-but-defensible last email before they decide to escalate

If you want to threaten legal action, you write that part yourself. The AI gets you up to "this is unacceptable and I need a date." You decide whether the next email mentions a lawyer.

Multi-language with per-tone, per-language prompts

This is where it gets unglamorous. Each (tone, language) pair has its own system prompt because:

  • "Firm" in Japanese is grammatically and culturally different from "firm" in German
  • A polite German chase email is more direct than a polite English one
  • Some languages (Korean, Japanese) have honorifics that change every sentence based on relationship — collapsing them under "firm" loses the entire point

So SYSTEM_PROMPTS[tone][language] is a 2D lookup, not one big prompt with a {language} variable. ~50 prompts to maintain, but the output quality difference is significant — bilingual freelancers in the user pool spotted machine-translated tone within 2-3 emails.

Two reminder modes: self-nudge or auto-chase

The matrix is the content layer — what the email says. There's a second design decision on the delivery side: who actually hits send?

Send & get paid modal showing two delivery modes:

When an invoice goes overdue, the user picks one of two modes:

Mode A — Remind me to chase (self-nudge). The system emails you a nudge with a 1-click AI draft. You read it, edit if needed, and send it from your own mailbox. Keeps your voice, your relationship, your control.

Mode B — Auto-chase the client. The system drafts and sends the reminder directly to the client on the matrix-recommended schedule. You can pause anytime, mark paid anytime, or override the tone. Hands-off until they pay.

Most users start in Mode A and switch to Mode B for clients who go fully silent — the relationship signal already says "this one isn't worth your hand-holding anymore."

The interesting design problem: Mode B has to be trustworthy enough to delegate without being aggressive enough to torch a relationship. The matrix already encodes that asymmetry, which is why Mode B exists at all. A naive "AI auto-sends emails to your clients" feature without the relationship + escalation logic would be a recipe for damaged accounts.

Real chase email samples

Same fictional invoice — Acme Corp, $2,400, 14 days overdue, invoice #INV-1042 — through tones 1-4:

Tone 1 — soft (gentle nudge)

Enter fullscreen mode Exit fullscreen mode

Tone 2 — standard (professional follow-up)

Enter fullscreen mode Exit fullscreen mode

Tone 3 — firm

Enter fullscreen mode Exit fullscreen mode

Tone 4 — final notice

Enter fullscreen mode Exit fullscreen mode

(Tone 5 — demand — looks similar to "final notice" but stripped of conciliatory language. Reserved for clients who have ignored all prior steps. Notably, it still does not threaten legal action.)

The cost numbers

Per-reminder cost (Claude 3.5 Sonnet via OpenRouter):

tokens cost
System prompt ~400 $0.0012
User context (invoice + history) ~600 $0.0018
Output (email body) ~250 $0.0038
Total per draft ~1,250 ~$0.0068

About 2/3 of a cent per chase email. At freelance volumes (5-30 chases/month per active user) the marginal cost is rounding error. The bigger cost is keeping prompts tuned across 13 languages.

If you want to try the dunning generator without going through the invoice flow first, there's a standalone Payment Reminder generator that takes invoice number + amount + days overdue and outputs the email directly.


4. Multi-document — same engine, six form factors

Once Smart Paste and the preview engine were stable for invoices, every other document type came almost free:

Document What it does Try it
Invoice Standard invoice with line items, taxes, discounts /
Receipt Payment-received version, "PAID" stamp + transaction details /receipt
Quote / Estimate Pre-work pricing, optional "Convert to Invoice" on accept /quote
Superbill US medical billing — NPI / CPT / ICD-10 fields, HIPAA-conscious defaults /superbill-generator
Contract Service agreement, scope + payment terms + termination, AI-fillable /contract
NDA Mutual or one-way, jurisdiction-aware boilerplate /nda
Payment Reminder Standalone chase-email generator /payment-reminder-generator

Same Smart Paste, same theme, same engine — different schema configurations:

Receipt page using the same form/preview framework as invoice — only the schema and labels change

Quote page — same framework, different doctype, with extra

They all share:

  • Same Smart Paste extraction (the document type is a parameter)
  • Same theme system (your brand color carries across all six)
  • Same PDF export (no per-document templates to maintain)
  • Same localStorage memory (your last-used "From" details auto-fill)

Adding a seventh document type now costs about a day of work — schema definition + a small profile config. The framework cost was high upfront, the marginal cost is near zero now.


The dashboard ties it together

Dashboard listing invoices on this device with status badges (Sent / Overdue / Paid) and a

Because there's no login, the dashboard is per-device — saved entirely in localStorage. It groups your local history into:

  • Overdue — past due date, top of the list
  • Due soon — under 7 days
  • Sent — awaiting payment
  • Drafts — not yet sent

Each row gets a one-click Generate reminder button (drops you into the dunning generator with all invoice context pre-filled) and Mark as paid (closes the loop, also gives the AI relationship signal — paid-on-time history feeds into the loyal classifier next time).

This is the closest thing to "an account" the tool has, and it works without any account because everything is local.


The stack

For anyone curious about the build:

  • Frontend — Next.js 15 (App Router), TypeScript strict, Tailwind, shadcn/ui
  • AI provider — OpenRouter (single key, automatic fallback). Default model: Claude 3.5 Sonnet for invoice extraction + reminder generation. Lighter classification tasks fall through to GPT-4o-mini.
  • Database — Supabase Postgres (only used for the auto-chase delivery queue; everything else is localStorage)
  • PDF rendering — client-side, no server roundtrip — same React tree that renders the live preview
  • Hosting — Alibaba Cloud HK + Cloudflare CDN
  • Test — Playwright E2E (currently 226 tests, including a chained "switch industry then template then language" test that has caught more bugs than any unit test ever has)
  • Cost shape — fixed (server + Cloudflare) ~$30/month. Variable (AI calls) ~$0.007/draft × volume. At zero revenue this is comfortably under personal-project budget.

The single biggest leverage point: putting AI behind a unified src/lib/ai/client.ts interface from day one. Every feature (Smart Paste, dunning, future stuff) goes through the same client, gets the same fallback + cost tracking + prompt-version logging, free of charge.


What I'd do differently

1. Ship the dunning matrix sooner. I spent 6 weeks polishing the customize sheet before any reminder feature existed. The customize sheet is delightful but the chase emails are what made users come back — it's the only feature where I get unsolicited "this saved me $X" emails.

2. Constraint-based prompts > example-based prompts. Almost every prompt I tried to seed with examples ended up copying the example's structure too literally. Replacing examples with hard constraints ("don't use exclamation marks", "no Sorry to bother you", "subject line must include invoice number") gave more variety and tighter tone separation.

3. The relationship signal is the unfair advantage. Most invoice tools are document generators. Adding the relationship dimension (new vs loyal vs going-silent) turned this into a workflow tool. It's also the hardest part to copy because you need usage history before the signal exists.


Try it

Open the URL. That's the whole onboarding.

  • No install, no extension, runs in any browser
  • No login, no email capture, no account
  • Unlimited — generate as many invoices, receipts, quotes, contracts as you need
  • Free forever — no paid tier, no credit card, no watermark

Free invoice generator with AI payment reminders

If you build something similar, want to compare prompt structures, or have a (tone, language) pair where my output is wrong, comments below or @ me on Dev.to.


Cross-posted to Medium, Hashnode, and Indie Hackers. Canonical version is on Dev.to.

Top comments (1)

Collapse
 
peacebinflow profile image
PEACEBINFLOW

The 5×5×3 dunning matrix is the kind of thing that, once you see it laid out, feels almost obvious — of course the right tone depends on who the client is and how far overdue they are, and of course a machine can encode that logic better than someone staring at an overdue invoice at 11pm trying to decide how assertive to sound. But the part that's genuinely subtle is the "loyal" classification slowing the escalation curve. That's not just a convenience feature. That's encoding an asymmetric risk calculation into the tool: the cost of annoying a reliable $50K/year client with a tone that's too firm too early is orders of magnitude higher than the cost of being too lenient with a new $500 client who was never going to pay anyway. Most reminder tools flatten that distinction. They treat all overdue invoices as equivalent, because the system doesn't know the difference.

The downstream implication I'm turning over is that this makes the tool's value compound with use. The first invoice you generate, the relationship classifier has no signal — everyone's "new." By the tenth invoice, the system has a history of who paid on time and who didn't, and it can start making finer-grained decisions without you having to manually mark anything. That's a quiet kind of lock-in. Not the hostile kind, where your data is held hostage. The organic kind, where the tool gets smarter the more you use it, and switching to a competitor means resetting back to zero signal. It's a defensibility that doesn't require accounts or walled gardens or SaaS pricing tiers. The data is in localStorage. You could walk away anytime. But the accumulated relationship intelligence doesn't come with you.

I'm curious — at what threshold did the "loyal" classifier feel reliable? Three paid invoices on time sounds like a good starting heuristic, but I could imagine edge cases where someone's been a client for years and always pays 15 days late, and that's actually fine because they're consistent. Do you have a way for users to override the classification, or is the heuristic fixed and invisible?