I Built FreelanceFlow: A Local-First AI Receipt Manager for Freelancers
This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle
What I Built
FreelanceFlow is a privacy-first AI business assistant for freelancers. It turns a folder of scattered receipts into an organized financial dashboard — without uploading a single file to the cloud.
The problem it solves:
Every freelancer I know has the same pain: receipts scattered across email, phone photos, and random folders. At the end of the month they either spend hours manually entering everything into a spreadsheet, or they hand a mess to their accountant and hope for the best. Neither is good.
FreelanceFlow fixes this with a simple workflow:
- Upload a receipt photo or PDF
- AI extracts the merchant, date, amount, currency, and expense category
- You review and correct before anything is saved — no blind trust in the model
- Dashboard shows income, expenses, net cash flow, and category breakdowns
- Invoice tracker flags unpaid and overdue invoices automatically
- Monthly report generates an AI-written summary and a downloadable CSV
Who would pay for this:
Freelance developers, designers, consultants, and solo founders who hate admin work. The recurring pain is real — recording expenses, chasing invoices, and preparing records for an accountant. A Pro tier at ₹249/month (roughly $3) for higher document limits and invoice reminders is a realistic starting point to validate.
Demo
Local-first app — runs entirely on your machine.
To run it yourself:
# 1. Pull the AI model
ollama pull gemma3:4b
ollama serve
# 2. Backend
cd backend
python -m venv venv
.\venv\Scripts\activate
pip install -r requirements.txt
python seed.py # loads sample data
uvicorn main:app --reload
# 3. Frontend
cd frontend
npm install
npm run dev
Open http://localhost:5173 — the dashboard loads with seeded sample data immediately.
What you'll see:
- Dashboard with income/expense cards, a monthly bar chart, and a category pie chart
- Receipts page — upload any JPG, PNG, or PDF and watch the AI fill in the form
- Invoice tracker with color-coded paid / unpaid / overdue status
- Reports page — pick a month, generate an AI summary, download CSV
Code
thedarkking01
/
FreelanceFlow-AI-Business-Assistant
a privacy-focused AI assistant that helps freelancers manage receipts, invoices, expenses, and monthly financial reports without manually entering every transaction
FreelanceFlow — AI Business Assistant
A local-first AI receipt and expense manager for freelancers.
Upload receipts → AI extracts the details → review → track invoices → generate monthly reports.
Everything runs on your machine. No data leaves your device.
Built for the Hacktoberfest 2026 Open-Source AI Challenge.
Features
Feature
Description
Smart Receipt Scanner
Upload JPG, PNG, or PDF — OCR + Gemma extracts merchant, date, amount, category
Human-in-the-loop Review
AI suggestions are editable before saving — no blind trust in model output
AI Expense Categorization
Software, Hardware, Travel, Food, Office Supplies, Marketing, Other
Financial Dashboard
Income, expenses, net cash flow, category pie chart, monthly bar chart
Invoice Tracker
Record invoices, auto-detect overdue, mark as paid, delete
Monthly Report Generator
AI-written summary + CSV export — fallback summary if Ollama is offline
Privacy by Design
All OCR, AI inference, and storage happen locally — zero telemetry
Tech Stack
Layer
Project structure at a glance:
freelanceflow/
├── backend/
│ ├── main.py # FastAPI entry point
│ ├── database.py # SQLAlchemy models
│ ├── models.py # Pydantic validation schemas
│ ├── seed.py # Sample data loader
│ ├── routes/
│ │ ├── receipts.py # Upload → OCR → AI → confirm
│ │ ├── invoices.py # Invoice CRUD + overdue detection
│ │ ├── dashboard.py # Financial aggregations (pure Python math)
│ │ └── reports.py # AI monthly summary + CSV export
│ └── services/
│ ├── ocr.py # Tesseract + pdfplumber
│ ├── ai_extractor.py # Ollama + Gemma structured extraction
│ └── categorizer.py # Keyword-based category fallback
├── frontend/
│ └── src/
│ ├── pages/ # Dashboard, Receipts, Invoices, Reports
│ ├── components/ # Sidebar navigation
│ └── api/ # Axios client
└── tests/ # 23 tests — validators, math, report logic
How I Built It
The AI pipeline
The core AI workflow is a three-stage pipeline:
Stage 1 — OCR
Before the AI sees anything, the document is converted to plain text. PDFs go through pdfplumber for clean text extraction. Images (JPG, PNG) go through Tesseract OCR. This keeps the pipeline debuggable — you can inspect the raw text at any point.
# services/ocr.py
def extract_text(file_path: str, content_type: str) -> str:
if content_type == "application/pdf":
return extract_text_from_pdf(file_path)
return extract_text_from_image(file_path)
Stage 2 — Structured AI extraction with Gemma
The OCR text is sent to a locally running Gemma 3 4B model via Ollama. The prompt asks for a strict JSON response with defined fields — merchant, date, amount, currency, category, tax. Ollama's format: "json" parameter constrains the output to valid JSON.
# services/ai_extractor.py
response = httpx.post(
"http://localhost:11434/api/generate",
json={"model": "gemma3:4b", "prompt": prompt, "stream": False, "format": "json"},
timeout=60.0,
)
data = json.loads(response.json().get("response", "{}"))
Every field is then validated and sanitized — dates are checked with date.fromisoformat(), amounts must be positive floats, categories must be from the allowed set. If Ollama is offline, the endpoint returns an empty scaffold so the user can fill it in manually.
Stage 3 — Keyword fallback categorization
If Gemma returns "Other" for the category, a keyword-based categorizer runs over the merchant name and raw OCR text as a second pass:
# services/categorizer.py
_KEYWORDS = {
"Software": ["saas", "subscription", "adobe", "github", "aws", "hosting"],
"Travel": ["uber", "ola", "flight", "hotel", "airbnb"],
"Food": ["swiggy", "zomato", "restaurant", "cafe", "coffee"],
# ...
}
This catches common cases even when the AI is uncertain.
Human-in-the-loop review
AI output is never saved directly. Every upload returns a pre-filled editable form. The user corrects any mistakes, then clicks "Confirm & Save." Only confirmed records enter the expense ledger. This is the most important design decision in the whole app.
Financial calculations
All arithmetic — totals, category breakdowns, net cash flow, monthly trends — is done in Python with SQLAlchemy queries. The AI is never asked to do math. This is a hard rule: AI for understanding documents, Python for numbers.
Monthly reports
The report endpoint aggregates the month's data in Python, then sends a structured prompt to Gemma asking for a 3–4 sentence professional summary. If Ollama is unavailable, a rule-based fallback generates a plain-English summary from the same numbers — so the feature always works.
Tech stack
| Layer | Technology | Why |
|---|---|---|
| Frontend | Vite + React + TailwindCSS + Recharts | Fast dev, great charts |
| Backend | Python FastAPI | Async, auto-docs, Pydantic validation |
| AI | Ollama + Gemma 3 4B | Local inference, no API key, open-weight |
| OCR | Tesseract + pdfplumber | Handles both images and PDFs |
| Database | SQLite via SQLAlchemy | Zero config, local file, portable |
Why Does Open Innovation Matter?
Three things that a closed API simply cannot give you:
1. Privacy is a real feature, not a marketing claim
When a freelancer uploads a receipt, it contains their client's name, the amount they were paid, what software they use, where they travel. With a closed cloud API, that data leaves the device. With Ollama + Gemma running locally, the receipt text never touches an external server. The only network call the app makes is to localhost:11434. That's a meaningful difference for anyone working with confidential client information.
2. It works offline and costs nothing to run
No API key. No monthly inference bill that scales with usage. No rate limits. No service going down and breaking your app. Once Gemma is pulled, the app runs indefinitely on your own hardware. For a bootstrapped side project targeting price-sensitive freelancers, this matters enormously — the marginal cost of an AI-powered feature is zero.
3. You can inspect, replace, and improve the model
With a closed API you accept whatever the provider ships. With Ollama you can swap gemma3:4b for llama3.2, mistral, or any other compatible model in one line. You can test different models against your receipt test set and pick the one that performs best for your use case. That kind of control is only possible with open-weight models.
What I Measured
I tested FreelanceFlow against a set of 20 synthetic receipts covering different formats — printed PDFs, handwritten-style images, multi-line receipts, and receipts with tax lines.
| Metric | Result |
|---|---|
| Merchant name accuracy | 17/20 correct (85%) |
| Date extraction accuracy | 19/20 correct (95%) |
| Amount extraction accuracy | 18/20 correct (90%) |
| Category accuracy (AI + keyword fallback) | 16/20 correct (80%) |
| Average processing time per receipt | ~4–6 seconds on CPU |
| Receipts needing manual correction | ~25% |
The 25% manual correction rate is honest and expected. The human-in-the-loop design exists precisely because the model isn't perfect. The goal isn't 100% accuracy — it's to eliminate 75–80% of manual data entry while keeping the user in control of the rest.
Failures were mostly on low-quality images (blurry phone photos) and receipts with unusual formatting. PDF extraction was consistently more reliable than image OCR.
What I Learned
Structured output prompting is worth the effort. Asking Gemma to return JSON with format: "json" and a clear field-by-field prompt description dramatically improved consistency compared to asking for free-form text and parsing it.
The fallback matters more than you think. Ollama wasn't running during one of my test sessions and the whole upload flow broke silently. Adding graceful degradation — empty scaffold for manual entry, rule-based report summary — made the app usable even without the AI layer.
OCR quality is the real bottleneck. The AI extraction is only as good as the text it receives. A blurry phone photo produces garbage OCR output, and no amount of prompt engineering fixes that. Preprocessing images (contrast, deskew) before OCR would be the highest-leverage improvement.
What's Next
- Image preprocessing before OCR (contrast enhancement, deskew) to improve accuracy on phone photos
- Recurring expense detection — flag when the same merchant appears every month
- Invoice reminder notifications — email or desktop notification when due dates approach
- Multi-currency support with exchange rate conversion for international freelancers
- Export to PDF for accountant-ready monthly reports
Running the Tests
cd backend
.\venv\Scripts\activate
python -m pytest ..\tests\ -v
23 passed in 10.15s
tests/test_validators.py — 9 tests (Pydantic model validation)
tests/test_reports.py — 7 tests (month filtering, AI fallback, CSV)
tests/test_dashboard.py — 7 tests (expense totals, net cash flow, overdue)
Top comments (0)