Audit AI/SaaS tool spend for startup engineering teams and surface where seats and plans are overpaying — with deterministic, finance-defensible math.
Live: spentsight.vercel.app
Startups often stack Cursor, Copilot, Claude, ChatGPT, Gemini, Windsurf, and vendor APIs without a clear view of seat utilisation versus list pricing. Finance teams need numbers they can defend; LLM-invented prices break that trust. SpendSight was built as a lead-generation audit for Credex: show the savings first, then capture a lead.
- Multi-tool audit across common AI coding and chat products (plans, seats, usage inputs)
- Deterministic pricing engine — recommendations map to a hardcoded pricing registry, not model guesses
- Optional short narrative summary via Gemini (with a deterministic fallback when no API key is set)
- Shareable audit result pages persisted in Supabase
- Email capture after results (Resend), not before — value before the ask
- Zustand form state with
persistso mid-flow reloads keep progress - Jest coverage for the audit engine
flowchart LR
Browser -->|form + Zustand persist| NextApp[Next.js App Router]
NextApp -->|POST /api/audit/create| AuditAPI
AuditAPI --> Engine[AuditEngine TypeScript]
Engine --> Registry[PRICING_REGISTRY]
AuditAPI -->|optional summary| Gemini[Gemini 2.0 Flash]
AuditAPI --> Supabase[(Supabase Postgres)]
NextApp -->|POST /api/leads/create| LeadsAPI
LeadsAPI --> Resend[Resend email]
LeadsAPI --> Supabase
Client uses the Supabase anon key (RLS). Server routes use the service-role client for writes. Rate limiting is in-memory (per instance) — fine for MVP, not a global Redis limit.
| Layer | Choice |
|---|---|
| App | Next.js 16 (App Router), React 19, TypeScript |
| UI | Tailwind CSS 4, Radix/shadcn-style components |
| State | Zustand (persist) |
| Forms | react-hook-form + Zod |
| Data | Supabase (Postgres) |
| Resend | |
| Narrative LLM | Gemini 2.0 Flash (optional) |
| Tests | Jest + ts-jest |
- Zero AI for the money math.
AuditEngineis pure TypeScript overPRICING_REGISTRY. An LLM hallucinating a list price would destroy product trust. AI is only used for a short summary paragraph. - Results before email. The email gate sits after the savings hero so users have already seen value.
- Split Supabase clients. Browser anon vs server service-role — never mix them.
- In-memory rate limits. Simpler than Redis for MVP; resets on cold start. Documented trade-off for later Upstash/Redis.
- Node.js 20+
- A Supabase project
- Optional:
GEMINI_API_KEY,RESEND_API_KEY
git clone https://github.com/darkNIGHT669/spentsight.git
cd spentsight
npm installCreate .env.local (names only — fill with your own values):
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=
GEMINI_API_KEY=
RESEND_API_KEY=
NEXT_PUBLIC_BASE_URL=http://localhost:3000
npm run devOpen http://localhost:3000.
npm testspentsight/
├── app/ # App Router pages + API routes
│ ├── api/audit/ # Create audit + optional LLM summary
│ ├── api/leads/ # Lead capture + email
│ └── audit/[auditId]/ # Shareable results
├── components/ # Form + results UI
├── lib/
│ ├── audit-engine.ts # Deterministic pricing logic
│ ├── pricing-registry.ts
│ └── supabase/ # client vs server clients
├── store/ # Zustand audit form store
└── __tests__/ # Audit engine unit tests
- Unit tests focus on
AuditEnginerecommendation behaviour (__tests__/audit-engine.test.ts) npm run lintvia ESLint (Next config)
Push to main — Vercel auto-deploys via GitHub integration. Set the same env var names in the Vercel project settings. Ensure NEXT_PUBLIC_BASE_URL matches the production host for share links.