💡 Inspiration
India produces millions of tonnes of plastic, e-waste and battery waste every year. Under the Extended Producer Responsibility (EPR) rules, every brand owner, importer and producer must collect and recycle a share of what it sells, and prove it with recycling certificates filed on the CPCB portal.
In practice, compliance teams run this on spreadsheets, email threads and WhatsApp messages. Fake or inflated recycling certificates are a known problem. The same certificate gets sold to more than one company, and recyclers "issue" more than their plants can process. Miss a target or a deadline and the company pays Environmental Compensation. We wanted one place where a compliance team can see what it owes, prove what it has done, and catch fraud before it becomes a liability.
🚀 What it does
EPRFlow is a multi-tenant SaaS for EPR compliance under India's Plastic Waste, E-Waste and Battery Waste Management Rules.
- 📊 Material data: enter or bulk-import (CSV/Excel) what was placed on the market, by brand, plant, GSTIN and state. Every bad row is reported with its row, column and value, and duplicates are rejected so nothing is counted twice.
- 🎯 Automatic EPR targets: a rules engine works out each category's obligation, minimum recycling share, recycled-content requirement and Environmental Compensation risk.
- 🤖 AI document extraction: upload a recycling certificate or invoice and Google Gemini or Anthropic Claude reads it. A reviewer checks each value next to the document, and low-confidence fields are highlighted.
- 🕵️ Certificate fraud detection: checks that run across all organisations on EPRFlow flag reused certificate numbers, identical files, suspended or expired recyclers, recyclers claiming more than their registered capacity, impossible dates and abnormal prices.
- 📄 One-click reports: the CPCB annual return (XLSX/PDF), obligation summary, material and certificate registers, and the audit log export.
- 📅 Compliance calendar: statutory deadlines are generated from the rules, with email reminders 30, 7 and 1 days before and WhatsApp share links.
- 👥 Roles and consultants: Admin, Compliance Manager, Consultant and Viewer roles. A consultant can manage several client companies from one login.
- 🧾 Full audit trail: every change is logged, and every report download gets a SHA-256 fingerprint.
🛠️ How we built it
Stack: Next.js 16 (App Router) · TypeScript · Tailwind CSS v4 + shadcn/ui · PostgreSQL (Neon) + Prisma 7 · Auth.js v5 · Zod · Gemini / Claude APIs · Vercel (hosting, Cron, Blob) · Vitest + Playwright.
We built it in phases: foundation → data entry → targets → certificates & fraud → AI extraction → reports & reminders → landing page and consultant view.
1. Business logic as pure, tested functions. Everything that decides a number lives in src/lib/domain/ with no database or framework imports, so it is fully unit-tested. For each waste category, the obligation engine computes:
$$ \text{Base} = \frac{Q_{FY-1} + Q_{FY-2}}{2} \quad \text{(pre-consumer quantities excluded)} $$
$$ \text{Target} = \text{Base} \times \text{EPR}\% \qquad \text{Min. recycling} = \text{Target} \times \text{Recycling}\% $$
$$ \text{EC risk} = \max(0,\ \text{Target} - \text{Fulfilled}) \times \text{EC rate} $$
No regulatory number is hard-coded. Every percentage, look-back period and deadline comes from a versioned RegulationRule table. When CPCB changes a rule, an admin edits it, and old calculations keep the rule version they were built with.
2. Fraud checks across organisations without leaking data. For example, a recycler is flagged when the certificates it issued across all buyers exceed its registered capacity:
$$ \sum_{\text{buyers}} \text{certified kg}_{\text{recycler, category, FY}} > \text{capacity} $$
A price is flagged when it differs from the platform median by more than 50% (with at least 5 samples):
$$ \frac{|p - \tilde{p}|}{\tilde{p}} > 0.5 $$
These checks read only aggregates. A company is told that a certificate number has already been claimed elsewhere, never by whom.
3. Tenant isolation by construction. Feature code never touches the raw database client. It gets a tenant-scoped Prisma client that adds organizationId to every query and rejects writes to other tenants. An ESLint rule blocks importing the raw client anywhere else.
4. AI with a human in the loop. Gemini and Claude get the same prompt and JSON schema, every reply is validated with Zod, and nothing is saved until a reviewer approves it. Approved certificates then go through the same approval flow and fraud checks as manually entered ones.
🧗 Challenges we ran into
- Regulations change, and the numbers must be verifiable. We couldn't hard-code percentages, so we designed versioned rules. Seed values are marked unverified, and every screen that uses them shows a warning until an admin verifies them.
- Detecting fraud across companies while keeping their data private. Each check had to be phrased as an aggregate question ("is this number used anywhere else?") instead of a lookup into another tenant's data.
- LLM reliability. Gemini sometimes returned
503 overloadedduring testing. We added a retry on a fallback model, a clear message for reviewers instead of a raw error code, and a manual-entry path so an upload never gets stuck. - Serverless limits. Vercel has a 4.5 MB request limit and a read-only filesystem. Files are checked by their actual bytes, fingerprinted, and stored privately in Vercel Blob, with a Postgres fallback for local development.
- Reminders must never double-send. The daily cron can overlap with itself, so each reminder is claimed in the database before the email goes out.
- Bulk imports without double counting. Validation errors had to point to the exact spreadsheet row and column, and rows that duplicate existing data had to be rejected.
📚 What we learned
- How India's EPR system works: categories, certificate trading, CPCB returns and Environmental Compensation, and why fraud is so easy in it.
- Keeping business logic pure and separate from the framework made complex rules easy to test and safe to change.
- Multi-tenant security is best enforced by the architecture (a scoped client plus a lint rule) rather than remembered in every query.
- Using LLMs in production means schema validation, fallbacks and human review, not trusting raw output.
- Next.js 16 App Router patterns such as server actions and
after()for background work, plus serverless deployment with Vercel Cron and Neon Postgres.
🔮 What's next for EPRFlow
- Automatic WhatsApp reminders (currently share links)
- Direct sync with the CPCB EPR portal
- Recycler-side accounts so certificates can be verified at the source
- Payments and self-serve plan upgrades
- Verified regulatory values reviewed with compliance experts
Built With
- express.js
- gemini
- neon
- nexjs
- node.js
- nodemailer
- postgresql
- react
- sql
- typescript
- zod