Building a Back Office SaaS with AI: Invoice, Email Reminders & Scheduling in 3 Days
Tags: #SaaS #FastAPI #AI #BackOfficeAutomation #PostgreSQL #Python #BuildLog
Introduction
"Can we automate small business back office operations with AI?" — that's the question that started this project.
We call it AI Back Office Pack. Here's how we built Phases 1–3.
What We Built
AI Back Office Pack is a modular back office automation SaaS for small businesses. Pick the modules you need and combine them.
| Module | Function |
|---|---|
| Module 1 | Invoice Management |
| Module 2 | Email Reminders |
| Module 3 | Scheduling & Meeting Room Management |
| Module 4 | HR & Attendance (in progress) |
| Module 5 | Document Management (planned) |
Infrastructure
infra server (internal)
├── API Server port 4000 (FastAPI + Python)
├── Dashboard port 4001 (React 18 + Tailwind)
├── PostgreSQL port 5433 (persistence)
└── MinIO port 9000/9001 (file storage)
Key decision: everything behind Nginx reverse proxy with relative paths.
The dashboard calls /api/ instead of hardcoded localhost:4000, so it works from any machine without changing config.
Module 1: Invoice Management
The first module we built. Core features: CRUD + status workflow (draft → approved → sent).
Design choices:
- PDFs stored in MinIO (S3-compatible), metadata in PostgreSQL
-
tenant_idon every table for multi-tenant isolation - Multi-currency schema from day one
We hit a currency display bug after launch — everything showed $ instead of ¥. Currency fix became Phase 4's first task.
Module 2: Email Reminders
Automatically send payment reminder emails for invoices approaching their due date.
Invoice due date → 3 days before / 1 day before / day-of → auto email
Implementation notes:
- Scheduler: Python
APScheduler, jobs registered on startup - Template engine generates body (company name, amount, due date)
- Sent history in DB to prevent duplicate sends
Email reminders had the highest demo impact — users feel automation when an email arrives in their inbox.
Module 3: Scheduling & Meeting Room Management
Meeting room booking with external calendar sync (Google Calendar / Outlook).
Hardest part: time overlap checking.
SELECT * FROM room_bookings
WHERE room_id = $1
AND start_time < $2
AND end_time > $3;
Simple query, but timezone handling took several iterations. The server was running in JST while storing timestamps in UTC — classic timezone mix. Fixed by normalizing everything to UTC at the DB layer.
Tests: 25/25 PASS
All modules, all tests passing.
Module 1 (Invoice) ✅ 9/9
Module 2 (Reminder) ✅ 7/7
Module 3 (Schedule) ✅ 9/9
─────────────────────────────────
Total ✅ 25/25
We followed a TDD-adjacent flow: API schema → tests → implementation. When the schema is fixed upfront, AI-generated implementations stay on track.
The Dashboard Headache
The React dashboard had localhost:4000 hardcoded for all API calls. When Linou opened it in a browser on a different PC — no connection.
Fix: change all API calls to relative paths → route /api/ through Nginx to FastAPI.
location /api/ {
proxy_pass http://127.0.0.1:4000/;
}
After that fix: "Login works! Invoices are showing!" — Phase 3 done.
Lessons Learned
1. Schema first, everything else follows
Fixing the DB schema and API schema before writing code makes AI implementation prompts simple: "implement CRUD for this schema." Clear spec = predictable output.
2. Tests are the spec
Writing tests first makes "what are we building" concrete. It doesn't matter how the implementation is written as long as tests pass — ideal for AI/human collaboration.
3. The localhost trap
Hardcoded localhost will happen. Add "test from a different machine" to your checklist from day one.
What's Next
- Module 4: HR & Attendance (awaiting requirements confirmation)
- Currency bug fix:
$→¥ - Stripe payment integration (Phase 4)
Published: 2026-03-05 / Author: Jack
Top comments (0)