CodingIdeas.ai
← Browse All Ideas

Build Guide

How to Build an Invoice App

Freelancers send 500 million invoices per year — and 30% are paid late because the invoice was a Word doc attached to a Gmail.

Intermediate3–7 days

Invoice tools are evergreen, paid, and low-churn. Build one and you'll learn PDF generation, Stripe webhooks, recurring billing logic, and email deliverability.

Data model

The core tables you'll need before writing any UI.

Userid, email, business_name, address, tax_number, logo_url
Clientid, user_id, name, email, address, currency
Invoiceid, user_id, client_id, number, status (draft/sent/viewed/paid/overdue), due_date, subtotal, tax_rate, total, notes, stripe_payment_link, sent_at, paid_at
LineItemid, invoice_id, description, quantity, unit_price, total

Build order

The sequence that minimises rewrites — build in this order.

1

Invoice builder UI

Build the invoice form: your business details, client selector, line items table (description, qty, unit price, total auto-calculated), tax rate, and notes. Show a live preview alongside.

2

PDF generation

Use @react-pdf/renderer to define the invoice layout as React components. Call pdf().toBlob() to generate a PDF on demand. Show a download button and a preview iframe.

3

Client management

Create a clients table. Add a client selector to the invoice form. On selecting a client, auto-fill their address and currency. Add a simple client list page.

4

Invoice numbering and status

Auto-increment invoice numbers (INV-001, INV-002). Add a status field: draft → sent → viewed → paid → overdue. Update status when: you send it, the client opens it, Stripe confirms payment.

5

Send by email with pay-by-link

Create a Stripe Payment Link for the invoice total. Send an email via Resend with the invoice PDF attached and the pay link. Track when the link is opened using a redirect through your app.

6

Automatic payment reminders

A daily cron job checks for invoices where due_date < today AND status != paid. For each overdue invoice, send a polite reminder email with the pay link. Cap at 3 reminders.

7

Recurring invoices

Add a recurring boolean and interval (monthly/weekly) to invoices. A cron job duplicates the invoice on the correct date, resets status to draft, increments the number, and optionally auto-sends.

Done when

Observable behaviors that confirm V1 is complete — verify each one before you ship.

  • User creates an invoice with 3 line items and the PDF renders correctly within 3 seconds

  • Email with PDF attachment arrives in the client's inbox within 60 seconds of sending

  • Stripe pay link in the email works and marks the invoice paid on webhook receipt

  • Overdue reminder fires for invoices past their due date that are still unpaid

  • Dashboard opens with 2 demo invoices (one paid, one overdue) — no blank state on login

First Run Requirement

Seed 2 demo invoices and a demo client (Acme Corp) on first login so the dashboard shows real data immediately. One invoice is paid, one is overdue — both states are visible.

Build it with AI — Architect Prompt

Paste this into Claude, Cursor, Windsurf, or any AI coding tool. It includes the full context of this guide — data model, build order, done conditions, and pitfalls — so the AI starts with everything it needs.

ClaudeCursorWindsurfCopilotGemini
architect-prompt.txt
<context>
App: Invoice App
Difficulty: Intermediate
Estimated build time: 3–7 days
Tech stack (intermediate): Next.js + Supabase + Stripe + react-pdf + Resend — full pay-by-link flow

Data model:
  User: id, email, business_name, address, tax_number, logo_url
  Client: id, user_id, name, email, address, currency
  Invoice: id, user_id, client_id, number, status (draft/sent/viewed/paid/overdue), due_date, subtotal, tax_rate, total, notes, stripe_payment_link, sent_at, paid_at
  LineItem: id, invoice_id, description, quantity, unit_price, total

FIRST RUN REQUIREMENT:
Seed 2 demo invoices and a demo client (Acme Corp) on first login so the dashboard shows real data immediately. One invoice is paid, one is overdue — both states are visible.

DONE WHEN — verify each before marking V1 complete:
  □ User creates an invoice with 3 line items and the PDF renders correctly within 3 seconds
  □ Email with PDF attachment arrives in the client's inbox within 60 seconds of sending
  □ Stripe pay link in the email works and marks the invoice paid on webhook receipt
  □ Overdue reminder fires for invoices past their due date that are still unpaid
  □ Dashboard opens with 2 demo invoices (one paid, one overdue) — no blank state on login

Recommended build order:
  1. Define API contract + schema + seed data (always first)
  2. Invoice builder UI — Build the invoice form: your business details, client selector, line items table (description, qty, unit price, total auto-calculated), tax rate, and notes. Show a live preview alongside.
  3. PDF generation — Use @react-pdf/renderer to define the invoice layout as React components. Call pdf().toBlob() to generate a PDF on demand. Show a download button and a preview iframe.
  4. Client management — Create a clients table. Add a client selector to the invoice form. On selecting a client, auto-fill their address and currency. Add a simple client list page.
  5. Invoice numbering and status — Auto-increment invoice numbers (INV-001, INV-002). Add a status field: draft → sent → viewed → paid → overdue. Update status when: you send it, the client opens it, Stripe confirms payment.
  6. Send by email with pay-by-link — Create a Stripe Payment Link for the invoice total. Send an email via Resend with the invoice PDF attached and the pay link. Track when the link is opened using a redirect through your app.
  7. Automatic payment reminders — A daily cron job checks for invoices where due_date < today AND status != paid. For each overdue invoice, send a polite reminder email with the pay link. Cap at 3 reminders.
  8. Recurring invoices — Add a recurring boolean and interval (monthly/weekly) to invoices. A cron job duplicates the invoice on the correct date, resets status to draft, increments the number, and optionally auto-sends.
  9. End-to-end verification — walk every Done When condition above (always last)

Known pitfalls to avoid:
  - Generating PDF on the server without a headless browser — @react-pdf/renderer works in Node.js without Puppeteer. Don't reach for Puppeteer (which needs a full Chrome install) until you outgrow react-pdf.
  - Marking invoices paid based on email reply — only trust Stripe webhooks (payment_intent.succeeded) to mark an invoice paid. Never mark it paid based on a customer email or manual input without verification.
  - Forgetting to handle partial payments — if a client pays $500 of a $1000 invoice via bank transfer, your status machine needs a partial state. Add amount_paid alongside the status field.
</context>

<role>
You are a Senior Full-Stack Engineer and product architect who has shipped production Invoice Apps before. You know exactly where developers get stuck and how to structure the project to avoid rewrites.
</role>

<task id="step-1-clarify">
Before writing any code or spec, ask me 3–5 clarifying questions that will meaningfully change the architecture. Focus on: scale expectations, auth requirements, platform (web / mobile / both), must-have vs nice-to-have features for the MVP, and any hard constraints (budget, deadline, existing infrastructure).

<example>
BAD: "What tech stack do you want to use?" — too broad, doesn't change architecture decisions.
GOOD: "Do you need real-time sync across devices, or is single-device with periodic refresh acceptable? This decides whether we use WebSockets or simple REST polling and significantly affects infrastructure complexity."
</example>

⚠️ Do NOT start planning or writing code until I answer. Present the questions, then stop and wait.
</task>

<task id="step-2-architect">
After I answer your questions, produce the following in order:

1. TECHNICAL SPECIFICATION
   - System architecture using → to show data and event flow
   - Core data model (refer to data model in <context>)
   - API contract — list ALL routes with request/response shapes BEFORE any implementation
     WHY: agreeing on contracts first prevents rewrites when frontend and backend shapes diverge
   - External APIs and integration points

2. MVP PLAN in three phases:

   SETUP   ✦ Step 1 (always): API contract + schema migration + seed data
     Done when: schema is migrated, seed script runs, app starts with demo data, zero manual setup.

   CORE FEATURES — build in this exact order:
   2. Invoice builder UI
   3. PDF generation
   4. Client management
   5. Invoice numbering and status
   6. Send by email with pay-by-link
   7. Automatic payment reminders
   8. Recurring invoices

   DONE WHEN — one observable condition per feature:
   □ User creates an invoice with 3 line items and the PDF renders correctly within 3 seconds
   □ Email with PDF attachment arrives in the client's inbox within 60 seconds of sending
   □ Stripe pay link in the email works and marks the invoice paid on webhook receipt
   □ Overdue reminder fires for invoices past their due date that are still unpaid
   □ Dashboard opens with 2 demo invoices (one paid, one overdue) — no blank state on login

   STRETCH GOALS — post-launch additions that are explicitly out of V1 scope.

3. BLOCKER ANALYSIS
   Flag: API rate limits, auth complexity, scope risks, cold-start problems, top 2–3 failure modes.

   FIRST RUN REQUIREMENT: Seed 2 demo invoices and a demo client (Acme Corp) on first login so the dashboard shows real data immediately. One invoice is paid, one is overdue — both states are visible.
   ✓ Done when: app launches with seed data and the full user journey works without any manual setup.

<self_check>
Before presenting your output, verify:
□ Every answer I gave to clarifying questions is reflected in the spec
□ Step 1 is ALWAYS: API contract + schema + seed — never UI first
□ Done When criteria are observable user behaviors, not internal states
□ The plan is realistic for one person to ship within 3–7 days
□ All known pitfalls from <context> are addressed in the spec
</self_check>
</task>

<task id="step-2.5-agents-md">
Generate an AGENTS.md file at the project root. This file is read automatically by Claude Code, Cursor, Windsurf, and all major AI coding tools at the start of every session.

Include:
- Project overview (2–3 sentences)
- Tech stack with exact version numbers
- Folder structure with one-line descriptions per directory
- Key architectural decisions and the WHY behind each
- Coding conventions: camelCase components, kebab-case files, max 200 lines per file, one concern per file
- Available commands: dev, build, test, lint, db:migrate, db:seed
- MVP scope boundaries — features explicitly out of V1

Note in the file: "Cursor users: symlink .cursorrules → AGENTS.md. Claude Code users: symlink CLAUDE.md → AGENTS.md."
</task>

<task id="step-3-implement">
Implement the full project in the exact order from step 2. For each step:
- Write complete code (no placeholders or TODOs)
- Confirm the step works before moving to the next

<constraints>
- Step 1 is ALWAYS: define all API routes + TypeScript request/response interfaces + run schema migration
  WHY: agreeing on contracts before writing a single component prevents shape mismatches that require rewrites
- Final step is ALWAYS: start the app and walk every Done When condition from <context> end-to-end
  WHY: a spec that passes unit tests but breaks the core user journey is not done
- Max 200 lines per file. WHY: every file must fit in one AI context window for complete reasoning
- One concern per file. WHY: mixing auth logic into API routes makes it impossible to reuse — extract to lib/auth.ts
- TypeScript strict mode, no `any` types. WHY: catches data shape mismatches at compile time, not in production
- All database queries in server components or API routes only. WHY: keeps credentials server-side
- All environment variables documented in .env.example. WHY: next developer sets up in under 5 minutes
</constraints>
</task>

How to use this prompt

  1. 1.Copy the prompt above
  2. 2.Open Claude, Cursor (Cmd+L), or Windsurf
  3. 3.Paste the prompt and send — the AI will ask 3–5 clarifying questions
  4. 4.Answer the questions, then it generates your full project spec
  5. 5.Continue in the same session to implement step by step

Common mistakes

What trips up most developers building this for the first time.

⚠️

Generating PDF on the server without a headless browser — @react-pdf/renderer works in Node.js without Puppeteer. Don't reach for Puppeteer (which needs a full Chrome install) until you outgrow react-pdf.

⚠️

Marking invoices paid based on email reply — only trust Stripe webhooks (payment_intent.succeeded) to mark an invoice paid. Never mark it paid based on a customer email or manual input without verification.

⚠️

Forgetting to handle partial payments — if a client pays $500 of a $1000 invoice via bank transfer, your status machine needs a partial state. Add amount_paid alongside the status field.

Recommended tech stack

Pick the level that matches your experience.

Beginner

Next.js + react-pdf + Resend — invoices with PDF and email, no payment processing

Intermediate

Next.js + Supabase + Stripe + react-pdf + Resend — full pay-by-link flow

Advanced

Next.js + Supabase + Stripe + Puppeteer (PDF) + Twilio — professional billing suite

Take it further — related ideas

Each comes with revenue math, a full build guide, and a prompt to paste into Claude or Cursor.