RAG-Based AI Chatbot
A multi-tenant, white-label AI chatbot platform for websites. One codebase serves every customer, and each gets a branded widget that answers questions using retrieval-augmented generation (RAG) over their own uploaded documents.

The Problem
The goal was a chatbot that knows a customer's business, matches their brand, and lives on their website, sold as a single multi-tenant platform instead of a bespoke build for every customer.
The Starting Point
Built from scratch as a multi-tenant SaaS platform from day one, so branding, billing, and per-tenant data isolation are part of the core architecture instead of being retrofitted onto a single-customer chatbot later.
The Solution
Each tenant gets a branded chat widget (colors, logo, name, position), their own uploaded knowledge base (PDF, DOCX, TXT, web pages), their own system prompt, and either the platform's shared Anthropic key or their own key on the top plan. The branding runs deeper than a color swap. A demo travel site's widget introduces itself as "Journey, Driftwood's trip planner" in a coral-branded header, while a demo cloud-hosting site's widget is "Byte," a purple-branded support bot with a completely different opening line. Same underlying widget, two distinct products. The widget ships in two parts: a tiny (~8KB) vanilla JS snippet that customers paste into their site, which injects a shadow-DOM iframe for CSS/DOM isolation from the host page, and the chat UI itself, which lives inside that iframe. A retrieval-augmented generation pipeline embeds each user message, retrieves the top five tenant-scoped chunks by cosine similarity from pgvector, and streams a Claude response back over SSE.
Architecture
Engineering Work
Multi-tenant isolation is enforced at the database layer, not just in application code. Row Level Security is enabled on every Supabase table, anonymous reads are blocked by default, and every API route re-verifies the authenticated tenant_id server-side, so cross-tenant data access is architecturally impossible rather than merely checked for. The widget's security model is deliberately layered. allowed_domains drives a Content-Security-Policy frame-ancestors header on the widget route itself, which is the actual enforced boundary, applied by the browser, so an unlisted site simply cannot render the widget. /api/chat separately validates the request's Origin and cross-checks it against that same allowed-domains list for attribution. /api/widget-config is intentionally left open-CORS, since it only returns public branding data with no secrets and has to be fetchable before any iframe exists; the real cost and abuse surface is /api/chat, which is where the strict checks live. Known gaps are tracked rather than hidden: the BYOK Anthropic API key is currently stored in plaintext, flagged as needing a key-management decision before Scale-plan customers rely on it, and the internal /api/admin/* routes still go through an unnecessary HTTP hop slated to collapse into direct server-side calls.
AI Contribution
Retrieval-augmented generation runs over each tenant's own uploaded documents: chunked into 512-token segments with 50-token overlap, embedded with OpenAI's text-embedding-3-small (1536 dimensions), and retrieved at query time via tenant-scoped cosine similarity in pgvector. The retrieved chunks, the tenant's own system prompt, and recent message history are assembled into a single prompt and answered by Claude, streamed back to the widget over SSE. Claude also has a search_web tool available for questions the tenant's own uploaded documents don't cover. Each call is logged with its input, result, and elapsed time, an 18-match web search resolving in 342ms, for example, giving that fallback path the same auditability as the RAG retrieval step.
Results
Phase 1 (core RAG pipeline, embed widget, admin tenant/document management, message logging) and most of Phase 2 (Stripe billing and webhooks, plan-based rate limiting, an admin analytics dashboard, document management) are built; onboarding email automation is the one Phase 2 item still open. Phase 3 (a customer-facing self-serve portal, custom domains, webhook notifications, prompt A/B testing) hasn't started yet. Roughly 80 files at MVP: 6 API routes, 12 React components, 8 lib utilities, 6 test files, 4 migrations.
Final Product


