QResolve
QR-based asset fault reporting, ticketing and a programmatic SEO marketplace for India’s maintenance industry
Visit live project- Feb to Jun 2026
- ~4 months
- Commits
- 76
- Programmatic SEO pages
- ~108
- Supabase Edge Functions
- 6

Overview
QResolve is a two-product platform for India's facility and asset maintenance market. The Directory is a public, SEO-driven listing of verified providers; Relay is a multi-tenant operations console with QR-tagged assets, no-login public fault reporting, ticket triage, AI insights and Razorpay subscription billing. They share one Supabase project and form a deliberate flywheel: the directory generates leads for providers, providers adopt Relay to handle them, measurable ticket throughput earns a "Verified by Relay" badge, and the badge lifts directory ranking.
Two products, one platform
QResolve Directory
qresolve.com
A public, SEO-driven directory of verified maintenance providers across 12 service categories and 8 metros.
Next.js 16 (App Router), React 19, Tailwind v4
QResolve Relay
relay.qresolve.com
A multi-tenant SaaS operations console — QR-tagged assets, no-login public fault reporting, ticket triage, AI insights and Razorpay subscription billing.
Vite + React 18 + TypeScript, Supabase
The challenge
A whole industry running on WhatsApp threads and spreadsheets
India's maintenance market — lifts, HVAC, EV chargers, vending machines, fire safety, CCTV — runs almost entirely on WhatsApp threads, phone calls and spreadsheets. QResolve was scoped to attack three compounding failures at once: a frictionless public reporting surface, an auditable ticket system behind it, and a discovery front end that rewards the operators who actually use the system.
- Reporting friction — the person who notices a broken lift is almost never a system user, so any flow needing an app install or login simply does not get used
- No accountability trail — a fault reported over WhatsApp has no ticket ID, no priority and no timestamped resolution, so providers cannot prove SLA performance
- No discovery layer — facility managers searching for "lift maintenance in Pune" find directory spam, not operators with demonstrable track records
What we built
Two frontends, one Postgres database, and authorization enforced in the database itself
The directory needs server-side rendering for Google; the operations console needs a fast, stateful SPA. Splitting them meant each could use the right tool without compromise, while providers, profile claims, assets and issues stayed in a single Postgres schema — no sync layer, no data drift.
Every operation requiring a secret — Razorpay keys, the Gemini key, service-role database access — lives in a Deno Edge Function, so the browser never holds anything more sensitive than the Supabase anon key and the public Razorpay Key ID. Tenancy isolation is enforced in Postgres RLS policies rather than application code, so a bug in a React component cannot leak another organization's assets.
What we delivered
The public reporting flow
The single most important screen in the platform, and the only one designed for someone who will use it exactly once. No app, no signup, no login — a QR sticker opens a two-step mobile form. The QR URL carries asset name, location and org ID as query params alongside the UUID, so if the database fetch fails on flaky venue Wi-Fi the form still renders with real context instead of a dead end. Asset types define their own fault options, photo evidence uploads to Supabase Storage, and the reporter gets a TKT-XXXXXX receipt. A QR that resolves to nothing redirects to the directory with an unmapped-QR marker, turning a dead scan into a lead.
Asset management and QR printing
Full CRUD over assets with a two-level category and asset-type taxonomy. QR codes render client-side pointing at the public report URL, with three print-ready PDF templates — Classic, Minimal and Bold — laid out at exact A5 dimensions and exported via html2pdf.js.
Issue triage
A tabbed queue (All, Open, In Progress, Resolved) with search and filters, one-tap status transitions that stamp resolution time automatically, and a revenue-impact flag that lets operators separate "the lobby lift is down" from "a light bulb is out" independently of the reporter’s own priority claim.
AI prescriptive insights
An Edge Function verifies the caller’s JWT, pulls up to 50 assets and 90 days of issues, and compresses them into a token-efficient text summary rather than dumping raw JSON — deliberate cost control. Gemini 2.5 Flash returns exactly three insights, each a different type, with impact and confidence ratings. Results cache per organization so the panel populates instantly on revisit.
Subscription billing
Five Edge Functions and a database-driven plan catalog, built on one principle: Razorpay is the source of truth for subscription state and the frontend never writes it. Nine Razorpay event types map onto four internal states, webhook signatures are HMAC-verified against the raw body before anything else runs, and every processed event ID is stored for idempotency so a replayed webhook cannot double-apply.
Downgrade safety
A pre-flight check answers the question most SaaS billing flows skip: what happens to the data you already have when your plan lapses? It surfaces the excess asset count and recommendations before the user confirms cancellation, and RLS policies enforce the same rule in Postgres — a lapsed org loses write access to assets while reads stay intact. They never lose their data.
Programmatic SEO directory
12 service categories across 8 metros plus national coverage generate roughly 108 landing pages, each with dynamically generated titles, descriptions and canonicals. Organization, WebSite, CollectionPage and LocalBusiness structured data, a sitemap that queries Supabase at build time so every provider page is indexed automatically, and GA4 with a typed event wrapper.
Admin console and account tiers
An eight-tab internal dashboard covering users, organizations, taxonomy, assets, issues and claim requests. Admin and user credentials are validated against the intended login path, so a wrong-surface login fails loudly rather than silently downgrading. A per-organization asset-limit override lets the client run sales demos without engineering involvement.
Engineering challenges
The problems that took real work to solve. Tap any one to read how.
Problem
The value proposition depends on strangers filing tickets, but granting anon INSERT on issues naively also grants the SELECT that Supabase’s RETURNING clause needs — which would expose every tenant’s ticket data.
Solution
The issue UUID is generated client-side so the app never reads the row back, and the photo uploads before the insert so its URL is part of the initial payload. Anonymous users can write, but can never read. Exactly the asymmetry the feature needs.
Delivery timeline
Foundation
Feb 2026
Supabase schema, enums, RLS policies, auth, org onboarding, initial dashboard and report UI
Multi-tenancy & admin
Feb–Mar 2026
Admin dashboard, admin auth separation, moderation, asset-type taxonomy, issue-type flows
QR & print
Mar 2026
QR generation, three PDF templates, print pipeline, dynamic template selection
Directory & SEM
Mar 2026
Next.js directory, category and city architecture, search, claim-your-profile flow, owner dashboard
Intelligence & polish
Mar–Apr 2026
Gemini AI insights, dynamic reports with charts, photo attachments, mobile fixes, domain split
Growth instrumentation
Apr 2026
Sitemap, robots, JSON-LD, Open Graph, Meta Pixel, asset edit flows
Commercial features
Apr 2026
Account tier management, admin Danger Zone, demo-account tooling
Monetization
May 2026
Razorpay subscriptions end to end, webhook pipeline, downgrade validation, subscription-aware RLS
Launch analytics
Jun 2026
Google Analytics 4 in production, post-launch UX smoothing
Security posture
Tenant isolation
RLS on every org-scoped table, keyed on an org-membership check
Privilege escalation
Admin checks via a SECURITY DEFINER function against an explicit allowlist — never a client-supplied claim
Anonymous access
Write-only INSERT on issues; no anon SELECT, UPDATE or DELETE anywhere
Secret handling
Razorpay secret, webhook secret, Gemini key and service-role key exist only as Edge Function secrets
Webhook authenticity
HMAC signature verified against the raw body before any parsing or database access
Replay protection
Idempotency guard storing the last processed webhook event ID on the subscription row
Entitlement enforcement
Subscription status checked in RLS policies, not just the UI — a lapsed org cannot write even via direct API calls
Account moderation
Ban status checked at sign-in with immediate sign-out; cascade deletes on user removal
Results
No-login
Fault Reporting
QR scan to filed ticket in two steps, no app install
~108
SEO Landing Pages
12 categories × 8 metros, auto-indexed from the database
9 → 4
Billing States Mapped
Razorpay events reduced to four internal states, idempotently
76
Commits in ~4 Months
Feb to Jun 2026, schema to production payments
Outcomes
- A production-deployed two-product platform with live payment processing, serving public traffic and paying subscribers.
- A reporting flow that removes every barrier between noticing a fault and filing it — no app, no login, a two-step form on a phone.
- Billing infrastructure resilient to the failure modes that actually occur in production: replayed webhooks, out-of-order events, uncharged cancellations, expired plans with excess data.
- A programmatic SEO surface of roughly 108 landing pages plus unlimited provider pages, fully annotated with structured data and auto-indexed.
- Operator tooling — tier overrides, moderation, claim approval — that lets the client run sales demos and manage the platform without engineering involvement.
- Nine internal documentation files covering deployment, integration, testing checklists and go-live runbooks, so the client is not dependent on the build team to operate or extend the system.
Related work
Want something like QResolve?
Share your goals and we will come back with a clear plan and estimate.


