-
-
FixFlow turns a messy typed, spoken, or photographed complaint into a structured work order.
-
The dispatch board shows every open ticket sorted by priority.
-
Each team gets its own queue — here's the Plumbing team's pending ticket."
-
"Tickets route straight to the right team, or to management if no team fits."
-
"Type it, speak it, or snap a photo — FixFlow figures out where it should go."
-
"Just describe the issue, and FixFlow handles the rest."
Inspiration# FixFlow — Report it. Track it. Get it fixed.
Inspiration
Every hostel WhatsApp group has the same message buried somewhere in it: "AC in room 204 is broken, koi dekh lo please." It gets three thumbs-up reactions, forty unrelated messages pile on top of it, and by the next morning nobody remembers whose job it was to fix it — or whether it ever got fixed at all.
We've all lived some version of this: a university complaint box nobody opens, an office Slack channel where "facilities issues" is just a graveyard of unanswered pings, a landlord group chat that's 90% noise. The problem was never that people didn't report issues — it's that a report and a work order are two completely different things, and nothing in between them exists. A report is a sentence typed in frustration. A work order is: what's broken, where, how urgent, and who owns it.
The hackathon brief asked us not to build another AI demo, but something people would actually use. That line stuck with us, because this is a problem we'd all personally been annoyed by — not an invented use case dressed up to justify an API call.
What it does
FixFlow turns a messy, human complaint — typed, spoken, or photographed — into a structured, tracked maintenance ticket in seconds:
- Someone reports an issue in whatever language or phrasing feels natural — English, Urdu, Hinglish, Roman Urdu, mixed, it doesn't matter.
- Gemini reads the text (and the photo, if attached) and extracts a clean work order: the problem, the location, the category, and the priority.
- FixFlow auto-routes the ticket to the right internal department — Plumbing, Electrical, HVAC, IT, Cleaning, Carpentry, Security — each with its own queue and its own resolution-time target. If a category doesn't map to an in-house team (most buildings don't keep a security office or a carpenter on staff), it falls to a Management queue that can loop in an outside contractor instead.
- Each department has its own portal: a manager picks their team, sees only what's routed to them, and moves tickets through Assigned → In Progress → Resolved.
- The original reporter gets a trackable ticket, and once it's marked resolved, they confirm whether it actually worked. If not, it reopens and jumps up in priority automatically — so a ticket can't quietly die just because someone clicked a status dropdown.
That loop — messy human communication → structured action → tracked outcome, with real accountability at the end instead of a fire-and-forget message — is the whole idea.
How we built it
- Frontend: React 18 + Vite, React Router, a hand-built design system (no CSS framework) — dark glass panels, a gradient accent that visually stands in for "the thing that routes a report to the right place," and a ticket-stub component as the app's signature visual element.
- Backend: Node.js + Express, a small set of REST routes for creating, listing, and updating tickets.
- AI: Google's Gemini API, given both the raw complaint text and an optional photo, with a system prompt constraining it to return one structured JSON object — category, location, priority, and a one-line summary an admin can scan at a glance.
- Voice input: the browser's own Web Speech API, so talking through a complaint needs zero extra API calls.
- Storage: a flat JSON file for the hackathon build — intentionally the only layer that would need to change to swap in a real database later.
- Routing logic: a simple category → department → SLA map. Every category resolves to a due time:
$$ t_{\text{due}} = t_{\text{created}} + h_{\text{SLA}}(\text{category}) $$
and a ticket is flagged overdue the moment $t_{\text{now}} > t_{\text{due}}$ and it hasn't reached a resolved state — which is what drives the "Overdue" badge and the priority bump on reopened tickets.
We deliberately kept the stack boring where it didn't need to be interesting: no database server to stand up, no auth to configure, one free API key, and it runs. The interesting part is entirely in the prompt design and the routing model, not the infrastructure.
Challenges we ran into
- Getting Gemini to be honest about uncertainty. Early on, the model would confidently invent a location or a category when the complaint text genuinely didn't say — "leaking tap" with no room number would sometimes get a hallucinated room anyway. We had to explicitly tell it to say "Not specified" rather than guess, and sanitize its output server-side against a fixed enum instead of trusting free-text categories.
- Making it work with zero setup. We wanted anyone to clone the repo and have it run without begging for API keys first, so we built an offline heuristic keyword classifier as a fallback. It's obviously dumber than Gemini, but it means the demo — and a real deployment with a dead API key — never just breaks.
- Designing for departments that don't exist yet. Not every company has a dedicated electrician or plumber on staff. We didn't want the routing logic to pretend otherwise, so categories that are commonly outsourced are explicitly marked as such, and fall through to a Management queue that's meant to call someone rather than be someone.
- Multilingual input without a translation layer. Getting the model to reliably parse Roman Urdu and Hinglish complaints — where the same sentence might mix three languages mid-thought — took real prompt iteration, since a lot of off-the-shelf classification prompts quietly assume English input.
- Real accountability, not just a status field. The easy version of this app stops at "mark it resolved." We kept coming back to the reopen-and-escalate loop because a status update from a technician isn't proof anything actually got fixed — the reporter's confirmation is.
What we learned
- A good AI feature is invisible: the demo isn't "look, it's using Gemini," it's "look, the ticket already has the right team and the right urgency without anyone typing that in." The moment the AI becomes the spectacle instead of the plumbing, you've built a toy instead of a tool.
- Constraints (an offline fallback, a JSON-file database, no auth) aren't corners cut so much as scope decisions the trick is being explicit about which ones are "fine for now" versus "fine forever," instead of pretending the whole thing is production-ready.
- Designing for a specific person's actual annoyance (the buried WhatsApp message) kept every feature decision grounded. Every time we were tempted to add something clever, the test was: does this help someone's AC actually get fixed faster, or does it just look good in a demo video?
What's next
- Swap the JSON file for a real database and add lightweight staff auth so a dispatch board isn't open to anyone with the URL.
- Push notifications or WhatsApp/SMS alerts to a department the moment a ticket lands, instead of relying on someone having the portal open.
- Active re-escalation for overdue tickets — not just a visual flag, but an automatic nudge or reassignment when a team misses its SLA.
- Let a company configure its own department list and SLAs at signup, since not every building has the same set of in-house teams.
Built With
- express.js
- node.js
- react
- render
- vercel


Log in or sign up for Devpost to join the conversation.