B2B SaaS - Vendor Compliance & Document Tracking
COI Vault
A multi-tenant SaaS platform that tracks vendor certificates of insurance, expirations, and compliance - with automated reminders and full audit trails. Built for property managers, condo boards, and general contractors.
Application Screenshot
Replace with actual screenshot
Problem
Property managers and contractors manually track vendor COIs in spreadsheets. They miss expirations, exposing themselves to liability gaps. There's no purpose-built tool for this - just generic document managers that don't understand compliance workflows.
Why It Matters
A single lapsed certificate of insurance can expose a property management company to six-figure liability. This isn't a nice-to-have - it's a compliance requirement that most teams handle with spreadsheets and prayer. COI Vault turns a reactive, error-prone process into a proactive system with automated enforcement.
Architecture Overview
Server-first Next.js application using the App Router pattern. Dashboard pages are Server Components that fetch data directly from Prisma - no client-side fetching, no loading spinners, no waterfall requests. All mutations go through Server Actions with Zod validation and auth checks at every boundary. Multi-tenant isolation is enforced at the data layer: every query filters by organization ID, and every server action verifies org membership before proceeding.
┌──────────────────────────────────────┐
│ Architecture Diagram │
│ Replace with actual diagram │
└──────────────────────────────────────┘
Stack Breakdown
Server Components + Server Actions for zero-waterfall data fetching
End-to-end type safety from database schema to UI
Type-safe ORM with relational data model for multi-tenant architecture
Credentials-based auth with JWT sessions - no session table overhead
Checkout, Billing Portal, and Webhooks for subscription lifecycle
Transactional email for expiry reminders
Edge deployment with scheduled cron jobs for automated reminders
Unit and integration testing
Utility-first styling with zero runtime overhead
Technical Decisions
Server Components for dashboard pages
Dashboard data doesn't need interactivity on initial render. By keeping pages as Server Components, we eliminate client-side fetching entirely - the HTML arrives with data already rendered. This removes loading states, reduces JavaScript bundle size, and makes the app feel instant.
Server Actions for all mutations
Every write operation (create vendor, upload document, change plan) goes through a Server Action with Zod validation. This creates a single enforcement layer - auth checks, plan limits, input validation, and audit logging all happen in one place. No API routes to maintain separately.
JWT sessions over database sessions
For a B2B SaaS with moderate user counts, JWT sessions eliminate a database query on every request. The tradeoff is that session revocation requires token expiry rather than instant invalidation - acceptable for this use case.
Soft deletes with audit trail
In a compliance-focused product, data should never disappear. Every deletion is a soft delete (setting deletedAt), and every action creates an audit log entry. This satisfies B2B compliance requirements and enables full traceability.
Plan enforcement at the action layer
Plan limits (max vendors, documents, seats) are checked inside Server Actions - not in the UI. The UI shows limits for UX, but the server enforces them. This prevents any bypass through direct API calls or UI manipulation.
Tradeoffs
Chose NextAuth credentials provider over OAuth for simplicity - limits social login options but reduces third-party dependencies
In-memory rate limiting instead of Redis - sufficient for current scale, would need Redis for horizontal scaling
Stripe webhook verification happens synchronously - keeps the handler simple but adds latency to webhook processing
Single-region deployment on Vercel - acceptable latency for North American users, would need edge functions for global distribution
Scaling Considerations
Database connection pooling via Prisma + Neon's serverless driver for handling concurrent connections
Audit log table will grow unbounded - needs a retention policy or archival strategy at scale
Cron-based reminders work for hundreds of orgs; at thousands, would need a queue-based system (BullMQ, SQS)
Multi-tenant query isolation relies on application-level orgId filtering - Row-Level Security (RLS) would add database-level enforcement
Explore the code
The full source code, documentation, and architecture decisions are available on GitHub.