Inspiration
The global jewelry industry is a $300+ billion market that still runs on tools built for general retail. Walk into a jeweler — anywhere from Mumbai to Antwerp to New York — and you'll find them juggling Excel for stock, paper books for artisan wages, a separate accounting app for GST/VAT, a third tool for hallmark records, a fourth for loyalty cards, and a fifth (if any) for their website. Nothing talks to anything else.
Jewelry is also uniquely hard. Stock isn't just "items" — it's metals (by purity), stones (by 4Cs), and finished goods (by SKU + weight + making charges + live metal rate + tax). A single ring sale touches 8 different pricing inputs, 3 tax components, a hallmark ID, a customer loyalty ledger, and an inventory adjustment that must be karat-aware. Karigars (artisans) are paid by weight delivered, not by hours. Customers pay in installments through gold savings schemes that span 11 months. Every gram has to be accounted for from supplier to showroom to customer to potential buy-back.
We wanted to build the system the industry should already have: one platform that speaks jewelry natively, works for jewelers anywhere in the world, and handles every part of the lifecycle from raw metal to retail to compliance to ecommerce.
What it does
CaratFlow is a multi-tenant jewelry ERP + commerce platform. One backend, one database (with strict per-tenant isolation), and four front-ends — admin web dashboard, B2C storefront, customer-facing website, and mobile apps — all sharing the same data and business logic.
Core ERP (admin)
- Inventory — Karat-aware metal stock, gemstone stock by 4Cs, finished-goods SKUs, batch/lot tracking for diamonds, RFID/barcode/QR support, multi-location with inter-branch transfers, 5 valuation methods (FIFO, weighted average, last purchase, gross profit, current market), serialized item tracking, stock-take workflows with variance detection.
- Manufacturing — Multi-level BOM with explosion, MRP, 9-stage job order lifecycle, artisan ("karigar") management with daily wages, metal-balance ledger (issued vs returned vs wasted), WIP tracking, QC checkpoints with rework loops, tunch (purity) and wastage calculations, assembly/disassembly.
- Retail / POS — Touch-optimized POS with live metal rate pricing, weighing-scale serial driver (Mettler Toledo SICS, A&D, generic RS-232), barcode scanning, layaway, repair orders, custom orders linked to manufacturing, old-gold buy-back with melting workflow, appraisals, gift cards, label printing (ZPL + TSPL), customer-facing display via SSE.
- Financial — Double-entry GL, chart of accounts, AP/AR with aging, multi-currency, bank reconciliation, tax engine (CGST/SGST/IGST split for India, configurable for VAT/sales-tax in other countries), TDS/TCS threshold tracking, P&L, balance sheet, trial balance, cash flow, cost centers.
- CRM — Customer 360, purchase history, loyalty engine (points + tiers + earning rules), digital passbook, segmentation, lead pipeline, campaign management, birthday/anniversary auto-reminders, WhatsApp/SMS/email notifications via real provider integrations, occasion tracking, customer feedback.
- Wholesale — Purchase orders with approval workflow, 3-way matching goods receipt, outgoing/incoming consignment ("memo") tracking gram-by-gram, supplier management with performance metrics, agent commission engine (percentage / per unit / per weight), credit limits, outstanding tracking with aging buckets.
- Compliance — HUID (Hallmark Unique ID) tracking, BIS hallmark submission workflow, gemstone certificate management (GIA, IGI, HRD, SGL), full chain-of-custody traceability, Kimberley Process compliance for diamonds, conflict-free sourcing documentation, insurance policy tracking, compliance audits.
- Reporting — Configurable dashboards, sales/inventory/manufacturing/CRM reports, demand forecasting, statistical reorder calculation, custom report builder, scheduled email delivery, tax reports, audit trail.
B2C storefront + customer experience
- Public catalog with category browse, product detail with image gallery, real-time price ticker
- Search (typo-tolerant via Meilisearch) + voice search (Web Speech API, English/Hindi/Tamil locales)
- Wishlist with price-drop alerts, product compare, AR virtual try-on, 360° product view
- Cart, coupons, BNPL/EMI checkout, multiple payment gateways (Razorpay, Stripe, PayU)
- Live video consultation — customers can request a 1-on-1 video shopping session with a store consultant via auto-generated meeting rooms
- Customer portal — orders, returns, address book, KYC, gold savings scheme dashboard, loyalty points
- AI chatbot for shopping assistance + AI-powered product recommendations
- Pre-order / backorder management with back-in-stock alerts
- Abandoned cart recovery via WhatsApp/SMS/email
Mobile apps (React Native + Expo)
Four role-based apps from one codebase:
- Sales staff app — POS billing, barcode scan, customer lookup, today's KPIs, biometric authentication for high-value sales (Face ID / fingerprint)
- Owner app — KPI dashboard, approval workflows, report summaries, daily metal rate entry
- Customer app — digital passbook, loyalty, scheme tracking, catalog, store locator
- Field agent app — collection tracking, visit logging, order booking, commission dashboard
Hardware integration
Real device drivers (not stubs): weighing scales (multiple protocols), barcode scanners, label printers, customer-facing displays (Server-Sent Events stream), biometric attendance webhooks (ZKTeco/ESSL), RFID reader hooks.
Region-aware compliance (India + extensible)
Girvi (gold mortgage loans) with KYC + interest accrual + auction workflow, kitty/chit schemes, gold savings schemes (pay 11 get 12), real Aadhaar/PAN eKYC via aggregator API, real NIC e-Invoice IRP submission via GSP, automatic CGST/SGST/IGST split, e-Way Bill generation, GSTR-1/3B worksheets. The compliance layer is pluggable so VAT-based countries (UAE, EU, UK) can be added by writing one tax provider.
How we built it
Architecture
A Turborepo + pnpm monorepo with strict module boundaries: packages/ db -- Prisma 6, MySQL 8, split schemas per domain (24 .prisma files) shared-types -- Zod schemas, TS types, domain events utils -- Money, Weight, Purity, Tax, Validators, Date helpers ui -- shadcn/ui-based component library
apps/ api -- NestJS 11, 27 modules, tRPC v11 + REST + Swagger web -- Next.js 15 admin dashboard (App Router, React 19) storefront -- Next.js 15 B2C site mobile -- React Native + Expo SDK 52, 4 role-based apps
Tech stack
- Backend: NestJS 11, Prisma 6 (split schemas per domain — 24 files), MySQL 8, BullMQ + Redis for async jobs, Meilisearch for search, S3-compatible blob storage
- API: tRPC v11 internal (end-to-end type safety, no codegen), REST + OpenAPI for external integrations
- Frontend: Next.js 15 App Router, React 19, Tailwind CSS 4, shadcn/ui, TanStack Query, Zustand
- Mobile: Expo SDK 52, React Native, NativeWind, expo-camera, expo-secure-store, expo-local-authentication
- Real-time: Socket.IO gateway with tenant-scoped rooms, broadcasts whitelisted domain events to subscribed admin clients
- Testing: Vitest (~3,500 unit + integration tests), Playwright (~700 browser E2E), autocannon perf benchmarks
- CI: GitHub Actions
Key design decisions
- Money as integers — All monetary values stored in the smallest currency unit (paise/cents/fils). No floating-point rounding errors, ever.
- Weights as integers — All weights stored in milligrams. Display unit (g, ct, tola, troy oz) is a user preference applied at render time.
- Multi-tenant from day one —
tenantIdon every table, extracted from JWT in middleware, enforced by every query via aTenantAwareServicebase class. - Event-driven cross-module communication — Modules never import from each other. They communicate via typed BullMQ events (40+ event types in a typed
DomainEventunion). Adding a new module never requires touching existing ones. - Split Prisma schemas — Each domain owns its own
.prismafile. Cross-domain references go through anchor entities (Tenant, Customer, Product, Location, Account) incore.prisma.
Challenges we ran into
The pricing math. A single jewelry sale line is
(metal_rate × net_weight) + making_charges + wastage + stone_value + GST(metal) + GST(making), where each component can be a percentage or a fixed amount, and the final tax depends on whether the buyer is in the same state. Getting this right with BigInt paise arithmetic — and proving it with property-based tests — took multiple iterations.Karat-aware inventory. Gold isn't fungible. 22K gold stock and 18K gold stock are different SKUs but related: melting one converts into the other at a known ratio. Stock movements have to track gross weight, fine weight, and purity together, and valuation methods have to know which one to apply.
Multi-tenant isolation under load. Every Prisma call must filter by
tenantId. We built aTenantAwareServicebase class with atenantWhere(tenantId, extra)helper, audited every single Prisma call site (2,200+) for leaks, and added regression tests that prove cross-tenant queries returnNotFoundException. Zero leaks found, but the audit took a full session.tRPC v10 → v11 migration. Our admin dashboard inherited a tRPC v10 client that was incompatible with TanStack Query v5. We had to bump the entire stack, rewrite the client, and verify ~95 admin pages still typed cleanly against the rebuilt
AppRouter.React 19 + Radix Dialog. A subtle type clash where
@types/react@18(pulled in by mobile) and@types/react@19(used by web) caused Dialog primitives to fail JSX validation. We worked around it with aJSXComponent<P>cast in our shared UI package.Hardware on multiple platforms. Weighing scales talk RS-232, barcode scanners are HID keyboard wedges, label printers want ZPL or TSPL, biometric devices push HTTP webhooks, customer-facing displays need a real-time stream. We built one abstraction (
IDevice<TRead, TWrite>) and per-device drivers, withserialportloaded dynamically so the API still boots on hosts without the native module.Real integrations vs sandbox limits. We wired Cashfree (Aadhaar/PAN eKYC), Masters India (NIC e-Invoice GSP), metals.dev (live gold/silver rates), SendGrid (email), MSG91/Twilio (SMS), and WhatsApp Business Cloud API (messaging). Every aggregator has different sandbox quirks. We landed on a pluggable provider interface for each integration so a tenant can swap providers without code changes.
PDF generation. Rendering a GST-compliant invoice with item images, HUID, and HSN/CGST/SGST split inside a headless browser, while keeping the puppeteer Chromium binary out of small Docker images, required a
@Global()PDF service with a cached browser singleton and graceful shutdown.
Accomplishments that we're proud of
- One codebase covers the entire jewelry value chain — supplier intake, manufacturing, retail POS, B2C ecommerce, mobile sales, customer self-service, and compliance — with no duplication of business logic.
- Real device drivers, not stubs — actual scale protocol parsers, real ZPL/TSPL label rendering, real SSE streams, real biometric webhook normalization.
- Real third-party integrations — WhatsApp Cloud, SendGrid, Twilio/MSG91, Cashfree eKYC, Masters India e-Invoice, metals.dev rate poller — each with a pluggable provider interface and graceful local-only fallback.
- Multi-tenant proven secure — every Prisma call audited, regression tests for cross-tenant access, zero leaks across 2,200+ call sites.
- Type-safe end to end — tRPC v11 means the frontend knows the exact shape of every backend procedure at compile time. Renaming a field in the API immediately fails the build in every page that uses it.
- ~3,500 tests passing across API, web, mobile, storefront — plus ~700 Playwright browser tests and an autocannon performance suite.
- Voice search, AR try-on, AI chatbot, biometric mobile auth, video consultation — features customers expect in 2026, all in MVP form on day one.
What we learned
- Domain modeling beats clever code. The hours we spent getting
MoneyUtil,WeightUtil, andPurityUtilright paid back ten-fold across the rest of the codebase. Every downstream feature got to assume "the math is correct." - Boundaries are the feature. Strict module + import boundaries (apps can't import each other; modules talk via events) made it possible for many people to work in parallel without merge hell.
- Type safety scales. A 27-module backend with 90+ admin pages and a mobile app would be impossible to refactor without end-to-end types. A single
tRPC AppRouterrename propagates through 130+ files in seconds. - Multi-tenant is a discipline, not a feature. It has to be enforced at every layer (middleware, service base class, query helper, regression tests) or it leaks. There is no "we'll add it later."
- Test infra is production code. When 89 tests started failing because page titles changed, fixing them was as important as shipping the new pages.
- Pluggable providers > hardcoded integrations. Every aggregator we touched (SMS, eKYC, e-Invoice, rates) has a sandbox quirk. A clean provider interface let us swap between them — and gracefully degrade when credentials are missing.
The big bet
We want CaratFlow to be the default platform any jeweler — from a single-shop Mumbai kaarigar to a 200-store Antwerp chain to a Tiffany subsidiary in Tokyo — can install and run their entire business on. One tenant signup, one schema, one set of conventions, and a UI that speaks jewelry. That's the thing the industry doesn't have, and that's what we're shipping.
Built With
- amazon-web-services
- autocannon
- bcryptjs
- bullmq
- cashfree-verification-api
- delhivery
- docker
- docker-compose
- expo-camera
- expo-local-authentication
- expo-notifications
- expo-router
- expo-secure-store
- expo.io
- github-actions
- javascript
- jitsi-meet
- jwt
- let's-encrypt
- linux
- lucide-react
- masters-india-gsp
- meilisearch
- meta-graph-api
- metals.dev
- mettler-toledo-sics
- minio
- msg91
- mysql
- nativewind
- nestjs
- next.js
- nginx
- nic-e-invoice-irp
- node.js
- openapi
- payu
- playwright
- pnpm
- prisma
- puppeteer
- radix-ui
- razorpay
- react
- react-19
- react-native
- recharts
- redis
- rest
- sendgrid
- serialport
- server-sent-events
- shadcn/ui
- shiprocket
- shopify-api
- socket.io
- stripe
- swagger
- swc
- tailwind-css
- tanstack-query
- tanstack-table
- testing-library
- trpc
- ts-node
- tspl
- turborepo
- twilio
- typescript
- upi
- vite
- vitest
- web-speech-api
- whatsapp-business-cloud-api
- zkteco
- zod
- zpl
- zustand
Log in or sign up for Devpost to join the conversation.