Local cheque and invoice management for Sri Lankan SMBs: invoice photos in (web or WhatsApp) → human verify → AI-assisted cheque bundling → cash-flow timing → print.
Problem. Many businesses still pay suppliers with cheques. Invoices arrive as photos, amounts and due dates get retyped, several invoices are grouped under an LKR ceiling, and cash must sit in the bank before the cheque is presented — including CBSL holidays.
Solution. Zenith keeps that loop in one Flask + SQLite app, with a human in the loop before any invoice is accepted, and Python guardrails after the AI proposes bundles.
- Login — single-user password gate (
APP_PASSWORD) - Invoice upload — Gemini vision extraction, then human verification
- WhatsApp inbox — Meta Cloud API photos, whitelist, manual Send to AI
- Cheque bundling — LKR ceiling grouping, drag-and-drop, chat assistant
- Guardrails — CBSL holidays and amount ceiling in Python (
core/guardrails.py) - Cash flow — when money must be in the bank
- Cheque print — formatted cheque output
- Analytics — reports after cheque commits
- Languages — English, Sinhala, Tamil
| Agent | Role |
|---|---|
| 1 Vision | Invoice document check + OCR (Gemini) |
| 2 Anomaly | Pre-verify rules and findings before the human accepts the invoice |
| 3 Strategist | Cheque plan via Gemini tool-calling over bundling + dates |
| 4 Reviewer | Plain-language explanation of the plan (Gemini) |
Not an LLM: holiday and ceiling checks run in Python after Agent 3. If AI is off or fails, fallback bundling is core/bundling.py (compute_bundles).
Optional extra: agentic/ orchestrator (USE_AGENTIC_ORCHESTRATOR). Standard web + WhatsApp inbox paths do not depend on it.
Intake → verify → draft bundle → preview → commit. Patterns can feed the bundling assistant.
Full walkthrough: docs/WORKFLOW.md. Schema: database/DATABASE.md.
| Layer | Choice |
|---|---|
| App | Python 3.11+, Flask |
| Data | SQLite |
| OCR / strategist / reviewer | Google Gemini |
| Bundling chat + analyst | OpenAI (optional) |
| Tool-calling | LangChain |
| Meta Cloud API (Twilio / Node bridge optional) | |
| ReportLab | |
| Vector patterns (optional) | ChromaDB |
- Python 3.11+
pip- (Optional) Gemini and/or OpenAI API keys
- (Optional) Meta WhatsApp app + a public HTTPS URL (e.g. cloudflared) for live intake
No paid APIs required.
pip install -r requirements.txt
copy .env.example .envOn Linux/macOS use cp .env.example .env.
In .env set at least:
APP_PASSWORD=demo
USE_FAKE_AI=trueThen:
python scripts/init_db.py
python scripts/seed_sample_invoices.py
python app.pyOpen http://127.0.0.1:5000 and sign in with APP_PASSWORD.
Zenith is a Python Flask app with SQLite, file uploads, and optional WhatsApp webhooks. Netlify and Vercel cannot run it (those hosts are for static / Node frontends).
Use a Python host and submit that HTTPS URL:
-
Push this repo to GitHub (do not commit
.envordont upload/). -
Create a web service on Render (Blueprint:
render.yaml) or Railway / Fly.io / any VPS with theDockerfile. -
Set environment variables (dashboard, not the repo):
Variable Value FLASK_ENVproductionHOST0.0.0.0FLASK_SECRET_KEYa long random string APP_PASSWORDthe login you give judges USE_FAKE_AItruefor a no-key demo, orfalseplusGEMINI_API_KEY -
First boot creates the SQLite database and sample invoices if the DB file is missing.
-
Open
https://YOUR-SERVICE.onrender.com— you should see login. Health check:/health.
Render’s free filesystem is wiped on every redeploy/restart — that leaves broken rows with missing invoice photos. To keep data with SQLite:
-
Use a paid web service (Starter or higher) — persistent disks are not available on Free.
-
Attach a disk mounted at
/var/data(Blueprintrender.yamlalready does this). -
Set these env vars (also in
render.yaml):Variable Value DATABASE_PATH/var/data/invoice_cheque.dbUPLOAD_FOLDER/var/data/invoicesINBOUND_QUEUE_DIR/var/data/inbound_queueCHROMA_PERSIST_DIR/var/data/chroma -
Set
APP_PASSWORDyourself in the Render dashboard (do not leave it auto-generated if you care about a stable login). -
Redeploy. Startup logs should show
db=/var/data/invoice_cheque.dbanduploads=/var/data/invoices.
If you are already on Free: upgrade the service → add Disk → set the env vars above → redeploy. Data lost on previous Free redeploys cannot be recovered.
Local python app.py is unchanged (HOST=127.0.0.1 in .env).
Same as quick start, then in .env:
USE_FAKE_AI=falseGEMINI_API_KEY— document check + OCR (Agent 1) and Agents 3–4OPENAI_API_KEY— bundling chat assistant and analyst (optional)
See .env.example for every variable.
python app.pyOptional. The web upload path works without WhatsApp.
-
Set
META_WHATSAPP_TOKEN,META_PHONE_NUMBER_ID,META_VERIFY_TOKENin.env. -
Tunnel Flask:
cloudflared tunnel --url http://127.0.0.1:5000
-
In Meta Developer Console → WhatsApp → Configuration, set the webhook to
https://YOUR-TUNNEL/webhook/whatsappwith the same verify token. Subscribe tomessages. -
In the app: Invoices → WhatsApp settings — whitelist supplier numbers.
-
Suppliers send invoice photos → they land in WhatsApp photos → Send to AI → human verify.
Local mock (no Meta): USE_WHATSAPP_MOCK=true and MOCK_IMAGE_PATH=path/to/test.jpg, then python whatsapp_agent.py.
Optional Node bridge: whatsapp-bridge/README.md — not required for Meta production.
SQLite file: database/invoice_cheque.db (gitignored). Rebuild with python scripts/init_db.py.
pip install pytest
python -m pytest agentic/tests core/tests agents/tests routes/tests -q| Name | GitHub |
|---|---|
| Sithil Yapa | sithilyapa717 |
| Yohan Ruchitha | yohanruchitha2-star |
| Nithika Senanayake | senanayakenithikas-ux |
| Vinudi Sanaya | VinudiSanaya |
| Chamodi Madurasinghe | ChamodiMadurasinghe |
This project is licensed under the MIT License.

