Deterministic tax computation you can trust, wrapped in an AI experience people actually want to use.
Demo Video · Quick Start · API Reference · Architecture
Filing taxes in India means juggling two regimes, a dozen deduction sections, AIS/26AS reconciliation, and financial documents scattered across PDFs, screenshots, and bank exports. A CA fixes all of that for a fee. TaxAI does it in software — it turns raw CSVs and photographed documents into a reconciled, regime-compared, rebate-and-cess-correct tax computation, with a conversational assistant sitting on top of the same numbers the engine computed (not a hallucinated guess).
The core design bet: the LLM never does arithmetic. Groq is used for document classification, OCR-to-CSV conversion, and natural-language answers — every rupee of tax owed is computed by deterministic Python in tax_engine.py and the FY-specific slab tables in tax_rules/. That split is what makes the numbers auditable.
| 🧠 AI Tax Assistant | Conversational Q&A grounded in the user's own computed dashboard data — refunds, deductions, GST, regime comparisons — with a hard refusal for anything non-tax |
| 📄 Multi-Format Document Intake | CSV or photographed documents (.png/.jpg/.webp/.tif/.bmp) across 9 document types — bank statements, AIS, Form 16, sales/purchase registers, capital gains statements, interest certificates, rent summaries, deduction proofs |
| 🔎 OCR → LLM → Structured CSV | Tesseract extracts raw text from images, Groq converts it into a document-type-aware CSV schema, which then flows through the same validated parser as native CSV uploads |
| ⚖️ Old vs New Regime Engine | Progressive slab computation for both regimes with FY 2024-25 rules, Section 87A rebate (₹12,500 @ ≤₹5L old / ₹25,000 @ ≤₹7L new), 4% Health & Education Cess, and automatic "pick the cheaper regime" mode |
| 🧾 Job-Based Filing Pipeline | Create a filing job → attach documents → process → review reconciliation issues → approve → export an ITR-ready PDF, with full status tracking (uploaded → parsed → normalized → reconciled → computed → needs_review → approved → exported) |
| 💳 Transaction Intelligence | Every bank/register row is parsed, merchant-normalized, Groq-categorized (with confidence scores), and persisted individually for the transactions dashboard |
| 📊 GST & Business Bookkeeping | For small-business profiles: ledger generation, P&L, balance sheet, and GST liability computation from sales/purchase registers |
| 🔐 Hardened by Design | JWT auth with mandatory expiry, startup secret validation (refuses to boot on default/weak secrets), IDOR-safe data isolation, upload size limits, and password-length DoS protection — see Security |
flowchart LR
subgraph Client["🖥️ Frontend — React 18 + Vite + Tailwind"]
UI["Landing · Auth · Dashboard · Upload<br/>Tax Center · Filing · Transactions · AI Assistant"]
end
subgraph API["⚙️ Backend — FastAPI"]
AUTHR["auth.py<br/>register / login / me"]
TAXR["tax_api.py<br/>analyze · jobs · transactions · dashboard"]
SEC["security.py<br/>JWT issue + verify"]
end
subgraph Services["🧠 Service Layer"]
ING["document_ingestion.py<br/>schema validation & CSV parsing"]
OCR["image_ocr.py<br/>Tesseract OCR"]
AI["groq_ai.py / ai_extraction.py<br/>classification + enrichment"]
ENGINE["tax_engine.py + tax_rules/<br/>slabs · rebate · cess"]
JOBS["tax_jobs.py<br/>filing job orchestration"]
BOOKS["bookkeeping.py / gst.py<br/>ledger, P&L, GST"]
end
subgraph Data["🗄️ Persistence"]
DB[("PostgreSQL (prod)<br/>SQLite (tests)<br/>via SQLAlchemy")]
end
GROQ[["☁️ Groq Cloud API"]]
UI -- "Bearer JWT over HTTPS" --> TAXR
UI -- "credentials" --> AUTHR
AUTHR --> SEC
TAXR --> SEC
TAXR --> ING
TAXR --> OCR
OCR --> AI
ING --> AI
AI -- HTTPS --> GROQ
TAXR --> JOBS
JOBS --> ENGINE
JOBS --> BOOKS
JOBS --> DB
AUTHR --> DB
TAXR --> DB
Why this split matters: the service layer has no knowledge of HTTP, and tax_engine.py has no knowledge of Groq or the database — it's pure functions over pandas.DataFrames and dicts. That's what makes 12+ tax-computation regression tests possible without spinning up a server.
This is the actual data flow behind POST /api/tax-assistant/analyze and the job-based process endpoint — traced end-to-end during a full correctness audit of the repo.
flowchart TD
A["📤 Upload document<br/>CSV or image"] --> B{File type?}
B -- image --> C["Tesseract OCR<br/>→ Groq text-to-CSV"]
B -- CSV --> D["Schema validation<br/>document_ingestion.py"]
C --> D
D --> E["Classify by document_type<br/>bank_statement · ais · form16 · sales/purchase_register<br/>interest_certificate · rent_summary · capital_gains_statement · deduction_proof"]
E --> F["Groq enrichment (bank rows only)<br/>merchant · category · confidence"]
F --> G["Normalize into canonical frames<br/>bank_transactions · ais_entries · form16_summaries"]
G --> H["Merge special-document income into AIS<br/>certificates are authoritative for their income type<br/>— replaces, never adds, to prevent double-counting"]
H --> I["build_tax_computation()<br/>tax_engine.py"]
I --> J["Old regime slabs"]
I --> K["New regime slabs"]
J --> L{"regime == auto?<br/>pick lower liability"}
K --> L
L --> M["Apply Section 87A rebate<br/>+ 4% Health & Education Cess"]
M --> N["Income / deduction breakdown<br/>+ regime recommendation"]
N --> O["✅ JSON response<br/>tax_result + business_result"]
Correctness note: capital gains are surfaced under
special_rate_incomewith an explicit compliance warning rather than silently taxed at slab rates — the engine does not (yet) apply STCG/LTCG special-rate schedules, and says so instead of guessing.
sequenceDiagram
actor U as User
participant F as React SPA
participant A as /api/auth
participant S as security.py
participant DB as Database
U->>F: PAN + email + mobile + password
F->>A: POST /register
A->>A: Validate PAN/email/mobile regex, 8–128 char password
A->>DB: INSERT user (PBKDF2-SHA256 hash)
Note over A,DB: Duplicate email/PAN/mobile → 409, not a 500
A->>S: create_access_token(user.id)
S-->>A: JWT (HS256, exp: 15 min, type="access")
A-->>F: 201 + access_token
U->>F: GET /jobs (later, authenticated)
F->>A: Authorization: Bearer <JWT>
A->>S: decode_access_token()
S->>S: verify signature · exp · type == "access"
S-->>A: user_id
A->>DB: SELECT ... WHERE user_id = :id
Note over A,DB: every job/transaction/document query<br/>is scoped to the caller's user_id
DB-->>A: caller's rows only
A-->>F: 200
- No boot on weak secrets — the app refuses to start if
SECRET_KEY/JWT_SECRET_KEYare missing, short, or a known placeholder (change-this-secret-key,secret, etc.) - JWTs always expire — token creation raises rather than issuing a non-expiring token if
JWT_ACCESS_TOKEN_EXPIRES_MINUTES <= 0, and thetypeclaim is checked on every request - IDOR-safe by construction — every job/transaction lookup filters by
(job_id, user_id)together, neverjob_idalone; verified with a dedicated IDOR test suite - Upload limits enforced server-side —
MAX_UPLOAD_BYTES(default 10 MB) is checked against the actual bytes read, not theContent-Lengthheader - Password DoS guarded — passwords are capped at 128 characters before hashing (PBKDF2 cost scales with input length)
- CORS configured correctly —
allow_credentials=Falsepaired with explicit origin allowlisting, avoiding the wildcard-origin + credentials footgun
erDiagram
USERS ||--o{ TAX_FILING_JOBS : owns
TAX_FILING_JOBS ||--o{ TAX_DOCUMENT_UPLOADS : contains
TAX_FILING_JOBS ||--o{ TRANSACTION_RECORDS : contains
TAX_DOCUMENT_UPLOADS ||--o{ TRANSACTION_RECORDS : "parsed into"
USERS {
int id PK
string name
string pancard_number UK
string email UK
string mobile_number UK
string password_hash
}
TAX_FILING_JOBS {
int id PK
string job_id UK "UUID, public identifier"
int user_id FK
string profile_type "individual | small_business"
string regime_preference "old | new | auto"
string status "uploaded..exported"
json processing_result
}
TAX_DOCUMENT_UPLOADS {
int id PK
int job_id FK
string document_type
string source_name
text raw_content
string parse_status
}
TRANSACTION_RECORDS {
int id PK
int job_id FK
int document_upload_id FK
string transaction_date
float amount
string txn_type "income | expense"
string category
float confidence
}
Cascade deletes are wired so removing a TaxFilingJob cleans up its documents and transactions automatically — no orphaned rows.
|
Backend
|
Frontend
|
TaxAI/
├── Backend/
│ ├── app.py # FastAPI app factory + lifespan + CORS
│ ├── auth.py # register / login / me
│ ├── tax_api.py # analyze, jobs, transactions, dashboard, PDF export
│ ├── security.py # JWT issue + verify
│ ├── config.py # env config + startup secret validation
│ ├── models.py # User, TaxFilingJob, TaxDocumentUpload, TransactionRecord
│ ├── services/
│ │ ├── tax_engine.py # regime-agnostic slab computation
│ │ ├── tax_rules/ # FY-specific slab tables (e.g. individual_fy_2024_25.py)
│ │ ├── tax_jobs.py # job pipeline: attach → process → approve
│ │ ├── tax_assistant.py # one-shot /analyze pipeline + AI Q&A fallback
│ │ ├── document_ingestion.py # per-document-type schema validation
│ │ ├── document_catalog.py # multi-document parsing orchestration
│ │ ├── image_ocr.py # Tesseract OCR → Groq CSV conversion
│ │ ├── groq_ai.py # Groq HTTP/SDK client + label allowlisting
│ │ ├── ai_extraction.py # canonical tax-model extraction
│ │ ├── transactions.py # per-row transaction sync + dashboard summaries
│ │ ├── bookkeeping.py / gst.py # ledger, P&L, balance sheet, GST
│ │ ├── reconciliation.py # cross-document consistency checks
│ │ ├── itr_mapper.py # canonical model → ITR draft
│ │ └── optimization.py # tax-saving recommendations
│ └── tests/ # pytest suite (auth, security, IDOR, tax, uploads, groq)
└── Frontend/
└── src/
├── pages/ # Landing, Auth, Dashboard, Upload, TaxCenter,
│ # Filing, Transactions, AIAssistant, Settings
├── components/ # AppLayout, ProtectedRoute
└── context/ # AuthContext
cd Backend
pip install -r requirements.txtCreate Backend/.env:
SECRET_KEY=replace-with-32+-random-characters
JWT_SECRET_KEY=replace-with-a-different-32+-random-string
JWT_ACCESS_TOKEN_EXPIRES_MINUTES=15
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/taxai
GROQ_API_KEY=your-groq-key # optional — falls back to heuristics if unset
GROQ_ENABLED=true # optional, default false
CORS_ALLOWED_ORIGINS=http://localhost:5173The app will not start without a sufficiently random
SECRET_KEY/JWT_SECRET_KEY— this is intentional (see Security posture).
python app.py
# → http://localhost:8000 (docs at /docs, health at /health)cd Frontend
npm install
npm run dev
# → http://localhost:5173cd Backend
python -m pytest tests/ -vAuth — /api/auth
| Method | Path | Description |
|---|---|---|
POST |
/register |
Create account (name, PAN, email, mobile, password) → JWT |
POST |
/login |
Login with email or PAN + password → JWT |
GET |
/me |
Current user profile (requires Authorization: Bearer) |
Tax Assistant — /api/tax-assistant
| Method | Path | Description |
|---|---|---|
GET |
/options |
Supported profile types, regimes, document types, provider status |
POST |
/analyze |
One-shot analysis from inline CSV documents (JSON body) |
POST |
/analyze-files |
One-shot analysis from uploaded files (multipart) |
POST |
/ask |
Ask the AI assistant a tax question, grounded in the caller's dashboard |
POST |
/jobs |
Create a filing job |
GET |
/jobs |
List the caller's filing jobs |
POST |
/jobs/{job_id}/documents |
Attach documents (CSV or image) to a job |
POST |
/jobs/{job_id}/process |
Run the full pipeline: parse → reconcile → compute → draft ITR |
GET |
/jobs/{job_id}/review |
Full processing result + reconciliation issues |
POST |
/jobs/{job_id}/approve |
Approve the reviewed draft |
GET |
/jobs/{job_id}/export/itr-pdf |
Export an ITR-draft PDF (job must be approved) |
GET |
/dashboard/financial-data |
Latest processed job, shaped for the dashboard UI |
GET |
/transactions |
Paginated transaction list (capped at 500/request), filterable by job_id |
Every route above except /register, /login, and /options requires Authorization: Bearer <token> and is scoped to the authenticated user.
57 passed in ~2.6s
| Suite | Covers |
|---|---|
test_auth.py |
Registration validation, duplicate-conflict handling, login, password bounds |
test_security.py |
Secret validation, JWT expiry enforcement, expired-token rejection |
test_idor.py |
Cross-user job access is blocked on review, upload, process, and list endpoints |
test_tax_computation.py |
Rebate thresholds, regime slab math, special-document income inclusion, AIS double-counting prevention |
test_uploads.py |
Upload size limits, valid-CSV acceptance, transaction pagination caps |
test_groq_ai.py |
Partial-batch failure accounting, label/category allowlist coercion |
- ITR e-filing portal integration
- Live AIS / Form 26AS verification against the IT department
- Full GST return filing support
- Special-rate capital gains computation (STCG/LTCG schedules)
- Investment & tax-saving recommendation engine
- Kawaljeet Singh
- Harsh Pachauri
- Akshit Maheshwari
- Divyam Gupta
Built for educational and hackathon purposes.