An intelligent property maintenance operations platform that combines a structured complaint-management workflow with AI-assisted triage, semantic duplicate detection, and computer-vision analysis of maintenance photo evidence — with every AI output reviewed by a human administrator before it affects an operational decision.
Live Application: nivara-intelligence.vercel.app Backend API: nivara-ai-production-9539.up.railway.app
Most complaint-tracking tools for residential societies or property teams stop at a status board: a resident opens a ticket, an admin closes it. Nivara is built around the observation that a facilities team's real bottleneck isn't recording complaints — it's triaging them: figuring out what category and severity a report actually is, noticing when three residents are describing the same leak in different words, and judging whether a submitted photo actually shows what the description claims.
Nivara keeps the operational core of a maintenance tracker — registration, complaint submission with photo evidence, status lifecycle, notices, notifications, an admin dashboard — and layers three assistive AI capabilities on top of it:
- Text-based complaint triage using a Gemini LLM to suggest category, severity, urgency, and dispatch priority.
- Semantic duplicate detection using vector embeddings and cosine similarity, so related incidents surface even when the wording is completely different.
- Visual intelligence via a dedicated FastAPI/PyTorch microservice that classifies maintenance photos and explains its reasoning with Grad-CAM heatmaps. Every one of these is designed as a recommendation layer, not a decision layer. The system never mutates a complaint's status, category, or priority automatically — it stores AI output alongside the complaint and asks a human administrator to review and act on it.
- Structured operations, not a form dump. Complaints carry an immutable status history, configurable overdue detection, priority levels, and category-based filtering — the operational backbone a facilities team actually needs.
- AI-assisted triage, not AI-decided triage. Gemini analyzes complaint text and proposes category, severity, urgency, and priority with a confidence score and stated reasoning — an admin decides whether to apply it.
- Semantic duplicate detection. Cosine similarity over text embeddings catches related incidents that keyword search would miss (e.g. "water dripping from ceiling" vs. "bathroom pipe leaking").
- Visual evidence analysis with explainability. A separate computer-vision service classifies uploaded photos into maintenance categories and generates Grad-CAM heatmaps showing which regions of the image drove the prediction, instead of returning an opaque label.
- Human-in-the-loop by design. AI predictions are stored as metadata (
aiTriage,aiEmbedding, visual analysis results) alongside the complaint record, never written into its operational fields directly — a deliberate accountability boundary, not a missing feature.
- Register and log in (JWT-based authentication)
- Submit maintenance complaints with category, description, and an optional photo
- Track personal complaint status and history
- View the notice board (important notices pinned first)
- View an in-app notification center for status changes and important notices
- Provide feedback confirming or correcting a visual AI prediction on their own complaint
- Filter and search all complaints by category, status, priority, date range, and overdue state
- Update complaint status (within a defined lifecycle) and priority
- Monitor overdue complaints (configurable via
OVERDUE_DAYS) - View an operational dashboard: totals, status breakdown, overdue count, "needs attention" items, resolution metrics, a maintenance health score, complaint trends, and recurring issue insights
- Run AI complaint triage on a complaint and review the structured recommendation
- Run semantic duplicate detection against existing complaints
- Run visual analysis on an attached complaint photo and review the Grad-CAM explanation
- Publish, edit, and delete notices — optionally marked "important" to trigger resident email notifications
- Bootstrap admin accounts via a secure CLI utility (not through public registration)
Input → Model → Structured Output → Administrator Review.
An admin triggers analysis on a complaint (POST /api/admin/complaints/:id/ai-triage). The complaint's category and description are sent to a Gemini model, which returns a structured, schema-validated response: predicted category, severity (Low / Medium / High / Critical), urgency (Low / Normal / Urgent / Emergency), a recommended dispatch priority, a confidence score, and short operational reasoning. This output is stored in the complaint's aiTriage field — separate from the complaint's actual status, priority, and category fields — and surfaced in the admin UI for review. The admin decides whether to apply any of it.
Text → Embedding → Cosine Similarity → Ranked Suggestions.
When an admin requests duplicate detection (POST /api/admin/complaints/:id/find-duplicates), the complaint's normalized text (Category: ... \n Description: ...) is converted into a vector embedding using a Gemini embedding model. That vector is compared against the stored embeddings of other complaints using cosine similarity. Matches above a configurable threshold (AI_DUPLICATE_THRESHOLD, default 0.85) are returned as ranked suggestions, capped by AI_DUPLICATE_LIMIT (default 5). Embeddings are computed once and cached on the complaint (aiEmbedding); if a stored embedding was produced by a since-changed model, it is regenerated automatically. This is a similarity-scoring approach rather than keyword matching, so it can surface duplicates described in completely different language. Duplicates are presented as suggestions with a match confidence — complaints are never automatically merged.
Photo → Classification + Localization → Explanation.
Uploaded complaint photos can be sent to a dedicated FastAPI/PyTorch microservice (ml-service) for analysis (POST /api/admin/complaints/:id/visual-analysis):
- Classification — an EfficientNet-B0 backbone with a custom classification head, trained via transfer learning, predicts one of a fixed set of property-maintenance visual classes (e.g. water leakage, wall/ceiling damage, garbage/waste, electrical hazard, broken infrastructure, lift/door damage, parking/road damage, other).
- Detection — a YOLO-compatible object detection component supports localized fault detection for specific component types.
- Explainability — Grad-CAM (Gradient-weighted Class Activation Mapping) generates a heatmap over the image showing which regions most influenced the model's prediction, rather than returning a bare label.
- Feedback loop — residents and admins can confirm or correct a visual prediction (
POST /api/complaints/:id/visual-feedback), and that feedback is stored for future review rather than being applied as an automatic model update. - Model lifecycle — model status is tracked explicitly (e.g. untrained / trained / ready / failed), so the system does not represent a model as production-ready without it actually having been trained and validated.
When a complaint has both a text description and an attached photo, the backend acts as the orchestration layer: it holds the Gemini text-triage result and the visual-intelligence result side by side as complaint metadata, giving an administrator both signals at once when deciding how to act. This synthesis happens at the presentation/decision layer — it does not collapse into a single automated verdict that changes the complaint's operational state.
Resident submits complaint text + optional photo
│
├──────────────► Gemini text triage ──────► category / severity / urgency / priority + reasoning
│
└──────────────► Visual Intelligence service
│
├── EfficientNet-B0 classification
├── YOLO-compatible detection
└── Grad-CAM explanation
│
▼
Combined text + visual evidence
│
▼
Administrator review (human decision)
│
▼
Status / priority / category update (manual)
flowchart TD
A[React + Vite Frontend] -->|REST, JWT Bearer| B[Express Backend]
subgraph Backend["Express Backend"]
B --> B1[Authentication / RBAC]
B --> B2[Complaint APIs]
B --> B3[AI Orchestration]
B --> B4[Dashboard Analytics]
B --> B5[Notifications - Nodemailer]
B --> B6[Cloudinary - Photo Storage]
B --> B7[(MongoDB via Mongoose)]
end
B3 --> C1[Gemini Text Triage]
B3 --> C2[Gemini Embeddings]
B3 --> D[FastAPI / PyTorch Visual Intelligence Service]
subgraph VisualAI["Visual Intelligence Microservice"]
D --> D1[EfficientNet-B0 Classification]
D --> D2[YOLO-Compatible Detection]
D --> D3[Grad-CAM Explainability]
end
The React SPA calls the Express API with an Authorization: Bearer <token> header. Express validates JWTs, applies role middleware, and persists data in MongoDB through Mongoose. Photo uploads go through Multer (in-memory) and are stored in Cloudinary, with only the resulting URL saved to the database. Dashboard analytics are computed on the backend, not in the browser. The visual-intelligence workload is isolated in its own FastAPI service rather than embedded in the Node process.
| Layer | Technologies |
|---|---|
| Frontend | React, Vite, React Router, Axios, Tailwind CSS, Recharts |
| Backend | Node.js, Express.js, MongoDB, Mongoose, JWT, bcrypt, Multer, Cloudinary, Nodemailer |
| AI / ML | Google Gemini (text triage + embeddings), FastAPI, PyTorch, Torchvision, EfficientNet-B0, YOLO-compatible detection, Grad-CAM |
Only technologies actually present in the codebase are listed here — see Environment Variables for how each is configured.
Nivara-AI/
├── backend/
│ ├── src/
│ │ ├── config/
│ │ ├── controllers/
│ │ ├── middleware/
│ │ ├── models/
│ │ ├── routes/
│ │ ├── services/
│ │ │ └── ai/ # provider abstraction (Gemini + mock providers)
│ │ ├── utils/
│ │ ├── app.js
│ │ └── server.js
│ └── test/
│ └── api.test.js
├── frontend/
│ └── src/
│ ├── components/
│ ├── auth/
│ ├── resident/
│ ├── admin/
│ ├── layouts/
│ ├── services/
│ ├── context/
│ ├── utils/
│ ├── App.jsx
│ └── main.jsx
├── ml-service/ # FastAPI/PyTorch visual intelligence microservice
├── docs/
├── .env.example
├── README.md
└── SYSTEM_DESIGN.md
A resident submits:
"Water is leaking from the ceiling in my bathroom."
with an attached photo.
- Text path: the description is sent to Gemini, which returns a structured triage: category
Plumbing, severityHigh, urgencyUrgent, a recommended priority, a confidence score, and short reasoning — stored incomplaint.aiTriage. - Visual path (if a photo is attached): the image is sent to the visual-intelligence microservice, which classifies it (e.g.
Water Leakage) and returns a Grad-CAM heatmap highlighting the region driving that prediction. - Synthesis: the backend holds both results as complaint metadata rather than as complaint state.
- Human decision: an administrator opens the complaint, reviews the AI triage panel and the visual evidence side by side, and manually applies (or overrides) the category, severity, priority, or status.
At no point does the system change
status,priority, orcategoryon its own — those fields only change through an explicit administrator action.
- JWT authentication — login and registration issue JWTs signed with
JWT_SECRET;requireAuthmiddleware validates tokens on protected routes. - Role-based authorization —
requireRole("resident")/requireRole("admin")gate role-specific routes; all/api/admin/*endpoints reject residents with403 Forbidden. - Ownership checks — complaint detail access verifies the requesting resident owns the complaint before returning it.
- Admin-only AI endpoints — AI triage, duplicate detection, and visual analysis are restricted to authenticated admins; visual feedback is available to both residents and admins.
- Server-side API keys —
GEMINI_API_KEYand other provider credentials are used only on the backend and never exposed to the client. - Controlled AI provider failure — provider outages or timeouts return a controlled
503rather than disrupting complaint submission or status-lifecycle operations. - Registration role integrity — public registration always creates
residentaccounts; client-supplied role values are ignored. Administrator accounts are provisioned only through a backend CLI bootstrap utility. - Human-in-the-loop as a security property, not just a UX choice — because AI output never writes directly into operational fields, an AI error (a bad triage suggestion, a false-positive duplicate, a misclassified photo) cannot silently change a complaint's real status or priority.
| Group | Endpoint | Access | Notes |
|---|---|---|---|
| Auth | POST /api/auth/register |
Public | { name, email, password, confirmPassword } |
POST /api/auth/login |
Public | { email, password } |
|
GET /api/auth/me |
Authenticated | Current user profile | |
| Resident Complaints | POST /api/complaints |
Resident | multipart/form-data: category, description, optional photo |
GET /api/complaints/my |
Resident | List own complaints | |
GET /api/complaints/:id |
Authenticated | Ownership-checked for residents | |
POST /api/complaints/:id/visual-feedback |
Authenticated | Confirm/correct a visual AI prediction | |
| Admin Complaints | GET /api/admin/complaints |
Admin | Filters: category, status, priority, from, to, overdue, search |
GET /api/admin/complaints/:id |
Admin | Full complaint detail | |
PATCH /api/admin/complaints/:id/status |
Admin | { status, note } |
|
PATCH /api/admin/complaints/:id/priority |
Admin | { priority } |
|
| AI Intelligence | POST /api/admin/complaints/:id/ai-triage |
Admin | Generates and stores structured AI triage |
POST /api/admin/complaints/:id/find-duplicates |
Admin | Cosine-similarity duplicate search | |
POST /api/admin/complaints/:id/visual-analysis |
Admin | EfficientNet-B0 classification + Grad-CAM | |
| Dashboard | GET /api/admin/dashboard |
Admin | Query trendDays (7 / 30 / 90) |
| Notices | GET /api/notices |
Authenticated | List notices |
POST /api/admin/notices |
Admin | { title, content, isImportant } |
|
PATCH /api/admin/notices/:id |
Admin | Edit notice | |
DELETE /api/admin/notices/:id |
Admin | Delete notice | |
| Notifications | GET /api/complaints/notifications |
Authenticated | Recent status-change and notice notifications |
Response shape:
{ "success": true, "message": "Message", "data": {} }Error shape:
{ "success": false, "message": "Complaint not found" }| Model | Key Fields |
|---|---|
| User | name, email, passwordHash, role, timestamps |
| Complaint | residentId, category, description, photoUrl, status, priority, isOverdue, resolvedAt, aiTriage, aiEmbedding, visualFeedback, timestamps |
| StatusHistory (embedded in Complaint) | status, changedBy, note, timestamp |
| Notice | title, content, isImportant, createdBy, timestamps |
Every complaint carries an immutable, append-only statusHistory: each status change adds a new entry rather than rewriting the previous one, so the full lifecycle of a complaint is auditable.
Complaint lifecycle: Open → In Progress, Open → Resolved, In Progress → Resolved. Once Resolved, further status changes are rejected by the backend.
- Node.js 20+
- Python 3.x (for the visual-intelligence microservice)
- MongoDB (local instance or MongoDB Atlas)
- A Cloudinary account (for photo uploads)
- An SMTP provider (e.g. Gmail SMTP, Brevo) for email notifications
- A Google AI Studio API key (for Gemini)
cd backend
npm install
cp ../.env.example .env
npm run seed # optional — seeds demo residents and complaints
npm run create-admin # provisions an administrator account
npm run dev # runs at http://localhost:5000cd frontend
npm install
npm run dev # runs at http://localhost:5173cd ml-service
uvicorn app.main:app --reload --port 8001cd backend
npm testBackend tests live in backend/test/api.test.js.
Use .env.example at the repository root as the template. Real secrets are never committed.
| Variable | Purpose |
|---|---|
PORT |
Backend server port |
MONGODB_URI |
MongoDB connection string |
JWT_SECRET |
Secret used to sign JWTs |
JWT_EXPIRES_IN |
JWT expiry (e.g. 7d) |
CLOUDINARY_CLOUD_NAME / CLOUDINARY_API_KEY / CLOUDINARY_API_SECRET |
Cloudinary photo storage credentials |
SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASS / EMAIL_FROM |
SMTP configuration for Nodemailer notifications |
OVERDUE_DAYS |
Days after which an unresolved complaint is flagged overdue |
CLIENT_URL |
Frontend origin (used server-side, e.g. for email links) |
VITE_API_URL |
Backend API base URL consumed by the frontend build |
AI_PROVIDER |
gemini or mock |
GEMINI_API_KEY |
Google AI Studio API key |
AI_MODEL |
Gemini model used for text triage |
AI_DUPLICATE_THRESHOLD |
Cosine similarity cutoff for duplicate suggestions (default 0.85) |
AI_DUPLICATE_LIMIT |
Max duplicate suggestions returned (default 5) |
The visual-intelligence microservice (ml-service) is configured separately; consult ml-service/ for its own configuration as it evolves independently of the Node backend's .env.
| Component | Platform |
|---|---|
| Frontend | Vercel — nivara-intelligence.vercel.app |
| Backend API | Railway — nivara-ai-production-9539.up.railway.app |
| Database | MongoDB Atlas |
For a Vercel frontend deployment, set the root directory to frontend, build command to npm run build, output directory to dist, and VITE_API_URL to the deployed backend's /api URL. For a Railway/Render backend deployment, set the root directory to backend, build command to npm install, start command to npm start, and configure all backend environment variables listed above.
Local development always uses http://localhost:5000 (backend) and http://localhost:5173 (frontend) — the production URLs above are only in effect once deployed.
cd backend
npm testAutomated tests currently cover the backend API (backend/test/api.test.js). There is no automated frontend or ml-service test suite documented in the repository at this time.
- Human-in-the-loop AI — every AI output (triage, duplicate suggestions, visual classification) is stored as metadata alongside a complaint, never written into its operational fields automatically, so a wrong AI prediction cannot silently change what happens to a ticket.
- Provider abstraction — AI calls go through a service layer (
backend/src/services/ai/) rather than being called directly from controllers, so the Gemini provider can be swapped for a deterministic mock provider in tests or offline environments. - Semantic similarity over keyword matching — duplicate detection is embedding-based specifically because residents describe the same physical problem in very different words.
- Visual intelligence isolated as its own microservice — the PyTorch/EfficientNet workload runs in a dedicated FastAPI service rather than inside the Node process, keeping the ML runtime and its dependencies decoupled from the request-handling backend.
- Explicit model lifecycle status — visual models carry an explicit status (untrained / trained / ready / failed) so the system doesn't represent an unvalidated model as production-ready.
- Cloudinary for photo storage — images are externalized to Cloudinary rather than stored in MongoDB, keeping the database lean and photo delivery fast.
- Role-based access control — admin capabilities, including all AI endpoints, are gated behind
role === "admin", with public registration unable to self-assign that role. - Graceful AI failure — AI provider outages return a controlled
503rather than blocking or corrupting core complaint operations.
- AI triage, duplicate detection, and visual analysis are assistive — they depend on Gemini and the visual-intelligence service being reachable and correctly configured; if either is misconfigured, the corresponding feature degrades to a controlled error rather than working silently.
- Visual model quality depends on the training data behind the EfficientNet-B0 classifier; the project tracks model status explicitly rather than claiming production accuracy it hasn't earned.
- Email delivery depends on valid SMTP credentials being configured.
- Photo upload requires valid Cloudinary credentials; without them the backend returns a clear configuration error rather than silently failing.
- There are no real-time updates or background job schedulers — dashboard and complaint data are computed on request, by design, to keep the deployment footprint simple.
(None of the following exist today — this is a list of directions the current architecture could reasonably support.)
- Expanded, domain-specific training data for the visual classifier to improve real-world accuracy
- Broader visual class coverage and refined YOLO-based detection for specific components
- Background job processing for AI analysis instead of on-demand synchronous calls
- Real-time complaint and dashboard updates (e.g. via WebSockets)
- Deeper multimodal reasoning that jointly conditions text and visual signals rather than presenting them side by side
- Automated SLA forecasting based on historical resolution data
- Maintenance cost estimation informed by complaint category and severity trends
Contributions are welcome! Please open an issue or submit a pull request for any improvements or bug fixes.
This repository does not currently declare an explicit license. Until one is added, all rights are reserved by the author by default — check with the repository owner before reuse.
Developed by Sameer Senapati 🚀




