← Back to Case Studies Internal Tools

Multi-Tenant CRM for a Field Demo Program

A specialty-foods CPG ran hundreds of in-store demos a month on an aging vertical SaaS plus spreadsheets. Built a multi-tenant CRM — admin scheduling portal, ambassador self-service app (PWA + native iOS), payments, and reporting — with tenant isolation enforced in the database and verified by 126 live security probes.

Next.js TypeScript Supabase Postgres RLS Vercel Capacitor Playwright

Problem

A specialty-foods CPG runs an in-store demo program: hundreds of events a month across six regions of a national grocery footprint, staffed by a roster of field brand ambassadors. The program lived in an aging industry SaaS plus a constellation of spreadsheets.

The daily pain was concrete. Reassigning one event to a different ambassador wasn’t supported — the workaround was duplicate the event, then decline the original, which polluted reporting and confused the field team. Ambassadors had no self-service way to publish their availability. Post-event reports, photos, and payments each lived somewhere different, and program status lived in the coordinators’ heads.

Solution

A purpose-built CRM with two faces on one database:

Admin portal — event scheduling (single and bulk), campaigns, venue management with address autocomplete, a color-coded month calendar, a live check-in board that updates in real time as ambassadors arrive, click-to-pay payment links, and a wage/expense split with year-end 1099 CSV export.

Ambassador portal — an availability calendar (the feature that unlocked everything else), shift accept/decline, open-shift claiming, geo + selfie check-in, post-event reports with photo upload, and payment notifications. Delivered as an installable PWA and a thin native iOS app on TestFlight — the native shell wraps the live web app, so every web deploy updates the app instantly.

The fix for the original complaint is almost anticlimactic: reassigning an event is now one edit, with automatic notifications to both ambassadors.

Architecture

Next.js app (56 routes)  →  Vercel (deploy on push)


Supabase
    ├── Postgres + Row-Level Security  ──→ tenant isolation in the DB, not the app
    ├── Realtime                       ──→ live check-in board
    └── Storage (tenant-scoped)        ──→ report photos, receipts, training docs


Ambassador clients: installable PWA + Capacitor iOS shell (TestFlight, push-ready)

Key Decisions

Tenant isolation in the database, not the application. Every table carries row-level security; the app can have bugs without leaking a tenant’s data. The test suite proves it live: 126 RLS isolation probes run against the production database, attempting cross-tenant reads and writes on every surface.

The recipient-pin pattern. Audits kept finding the same class of hole: a table referencing another tenant-scoped row lets an admin in Company B plant a row pointing at Company A’s data. The fix — RLS that pins both the row’s tenant and the referenced row’s tenant — became a standing pattern applied across five surfaces.

PWA-first, thin native wrapper. One codebase, instant updates, and an App Store presence — without maintaining a separate mobile app.

Soft-archive over hard-delete. Events archive with notifications instead of disappearing, so history and reporting survive schedule churn.

White-label built in. Branding (logo, name) comes from the tenant record. A fictional second tenant runs as a permanent sales demo and as a live guard against single-tenant assumptions creeping in.

Results

  • The duplicate-and-decline workaround is gone — reassignment is one edit with auto-notifications, verified by end-to-end tests against production
  • Live pilot underway: real venues loaded, real weekend shifts scheduled
  • 126/126 live tenant-isolation probes pass; an independent adversarial verification pass confirmed all ten audited items
  • Security audit: zero critical findings; hardening applied (framework CVE patch, CSP headers, credential rotation, upload limits)
  • Multi-tenant and self-serve onboarding ready — the next customer is a signup, not a rebuild

How This Scales

  • Push notifications — device tokens and the APNs sender are shipped; enabling is a credential drop
  • Email dispatch — notification fan-out is env-gated behind a provider key
  • More tenants — self-serve company creation exists; per-tenant branding is automatic
  • Deeper payments — click-to-pay links today; processor integration is an add-on, not a rework

Tech Stack

  • App: Next.js (App Router, 56 routes), TypeScript
  • Data: Supabase — Postgres with Row-Level Security, Realtime, tenant-scoped Storage
  • Mobile: installable PWA + Capacitor iOS shell (TestFlight)
  • Verification: Playwright end-to-end suite + live RLS isolation probes against production
  • Hosting: Vercel, deploy-on-push

Need something similar?

I've built this before. Let's talk about adapting it for your needs.