- Add new routers: admin, appointments, campaigns - Add storage service and logging config - Add migrations directory and test suite with pytest config - Add supabase_migration_features.sql - Update models, dependencies, config, and existing routers - Remove whatsapp_service (deleted) - Update pyproject.toml and uv.lock dependencies Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contexta — AI Chatbot SaaS Platform
A full-stack SaaS platform that lets businesses build custom AI chatbots from their documents, publish them to a marketplace, and optionally export self-hostable code.
Architecture at a Glance
contexta_be/ → FastAPI backend (Python 3.11)
contexta_fe/ → React + TypeScript frontend (Vite)
Key tech stack:
- Backend: FastAPI, Supabase (auth + DB), Qdrant (vectors), LangChain
- Frontend: React 18, TypeScript, Tailwind CSS, TanStack Query, Zustand
- AI: Fireworks AI (free tier), OpenAI, Anthropic, Google Gemini
- Payments: Stripe
- Infra: Docker, Railway/Vercel-ready
Prerequisites
| Tool | Version | Purpose |
|---|---|---|
| Python | 3.11+ | Backend runtime |
| Node.js | 18+ | Frontend build |
| Git | any | Version control |
External services you need to set up (all have free tiers):
| Service | What for | Free tier? |
|---|---|---|
| Supabase | Database + Auth | ✅ Yes |
| Qdrant Cloud | Vector search | ✅ Yes (1GB) |
| Fireworks AI | LLM (free models) | ✅ Yes |
| OpenAI | Embeddings + GPT-4 | Pay-per-use |
| Stripe | Payments | ✅ Test mode |
Part 1: Supabase Setup (Database + Auth)
1.1 Create a Supabase project
- Go to supabase.com → New project
- Save your Project URL and both keys (anon + service_role)
1.2 Run the database schema
- In your Supabase dashboard → SQL Editor
- Open
contexta_be/supabase_schema.sql - Paste the entire file → Run
This creates all tables, RLS policies, and triggers.
1.3 Enable Email Auth
- In Supabase → Authentication → Providers
- Ensure "Email" is enabled
- Optionally disable "Confirm email" for faster testing
Part 2: Qdrant Setup (Vector Database)
Option A: Qdrant Cloud (recommended)
- Go to cloud.qdrant.io → Create cluster (free tier)
- Get your Cluster URL and API Key
Option B: Local Qdrant (for development)
docker run -p 6333:6333 qdrant/qdrant
Then set QDRANT_URL=http://localhost:6333 and leave QDRANT_API_KEY empty.
Part 3: Backend Setup (contexta_be)
3.1 Clone and configure
cd contexta_be
cp .env.example .env
Edit .env with your credentials:
# App
APP_ENV=development
APP_SECRET_KEY=change-this-to-a-random-string-in-production
ALLOWED_ORIGINS=http://localhost:5173,http://localhost:3000
# Supabase (from your project settings)
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=eyJ...
SUPABASE_SERVICE_ROLE_KEY=eyJ...
# Qdrant
QDRANT_URL=https://your-cluster.qdrant.io
QDRANT_API_KEY=your-key-here
# LLM - MINIMUM REQUIRED: OpenAI (for embeddings)
OPENAI_API_KEY=sk-...
# LLM - Optional but recommended
FIREWORKS_API_KEY=fw_... # Free models for starter plan
ANTHROPIC_API_KEY=sk-ant-... # Claude models
GOOGLE_API_KEY=AIza... # Gemini models
# Stripe (use test keys for development)
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
STRIPE_STARTER_PRICE_ID=price_...
STRIPE_PRO_PRICE_ID=price_...
3.2 Create virtual environment and install
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -r requirements.txt
3.3 Start the backend
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
The API will be available at:
- API: http://localhost:8000
- Swagger docs: http://localhost:8000/docs
- Health check: http://localhost:8000/health
3.4 Docker alternative
# Copy and edit .env first
cp .env.example .env
# Edit .env...
# Start with Docker (includes Qdrant locally)
docker-compose up -d
Part 4: Frontend Setup (contexta_fe)
4.1 Install dependencies
cd contexta_fe
npm install
4.2 Configure environment
cp .env.example .env
.env content:
VITE_API_URL=http://localhost:8000
VITE_APP_NAME=Contexta
4.3 Start the frontend
npm run dev
Frontend available at: http://localhost:5173
Part 5: Stripe Setup (Payments)
5.1 Create Stripe products
- Go to dashboard.stripe.com → Products
- Create Starter product → Add price → $39/month recurring
- Create Pro product → Add price → $119/month recurring
- Copy the Price IDs (start with
price_) → paste into backend.env
5.2 Set up webhook (for local testing)
Install Stripe CLI:
# Mac
brew install stripe/stripe-cli/stripe
# Others: https://stripe.com/docs/stripe-cli
Forward webhooks to local:
stripe listen --forward-to localhost:8000/api/v1/billing/webhook
Copy the webhook signing secret → paste into STRIPE_WEBHOOK_SECRET in .env
5.3 Test a subscription
Use Stripe test card: 4242 4242 4242 4242 with any future expiry and any CVC.
Part 6: Testing the Full Flow
6.1 Create an account
- Open http://localhost:5173
- Click "Get started free" or go to http://localhost:5173/signup
- Fill in company name, email, password
6.2 Create a chatbot
- After login → Dashboard → New Chatbot
- Enter a name and configure settings
- Click Save
6.3 Upload documents
- In the chatbot builder → Documents tab
- Drag and drop a PDF, DOCX, or CSV file
- Wait for processing (status changes to "completed" with chunk count)
⚠️ Document processing requires a valid OpenAI API key for embeddings and a working Qdrant instance.
6.4 Test in preview mode
- In the chatbot builder → Preview tab
- Type a question related to your uploaded document
- The chatbot should answer based on the document content
6.5 Publish to marketplace (requires paid plan)
- Subscribe to Starter or Pro (Stripe checkout)
- Back in Dashboard → click Publish on your chatbot
- Visit http://localhost:5173/marketplace to see it listed
6.6 Export code (Pro plan)
In the chatbot builder → click Export Code button → downloads a ZIP containing:
backend/— Complete FastAPI app with your vectorsfrontend/— React TypeScript chat widgetsetup.py— Interactive setup wizardQUICK_START.md— 5-minute deployment guide
API Reference
The complete API is documented via Swagger at http://localhost:8000/docs when the backend is running.
Key endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/auth/signup |
Create account |
| POST | /api/v1/auth/login |
Login |
| GET | /api/v1/auth/me |
Current user |
| GET | /api/v1/chatbots |
List your chatbots |
| POST | /api/v1/chatbots |
Create chatbot |
| PUT | /api/v1/chatbots/{id} |
Update chatbot |
| POST | /api/v1/chatbots/{id}/publish |
Publish to marketplace |
| POST | /api/v1/chatbots/{id}/export |
Download code export (Pro) |
| POST | /api/v1/chatbots/{chatbot_id}/documents |
Upload document |
| POST | /api/v1/chat/{chatbot_id} |
Send chat message |
| GET | /api/v1/marketplace/chatbots |
Browse marketplace |
| POST | /api/v1/billing/checkout |
Create Stripe checkout |
Deployment
Backend → Railway
# Install Railway CLI
npm i -g @railway/cli
cd contexta_be
railway login
railway init
railway up
Set all environment variables in Railway dashboard → Variables tab.
Frontend → Vercel
npm i -g vercel
cd contexta_fe
vercel
Set VITE_API_URL to your Railway backend URL in Vercel environment variables.
Update CORS
Once deployed, update ALLOWED_ORIGINS in backend to include your Vercel domain:
ALLOWED_ORIGINS=https://your-app.vercel.app
Minimal Setup (Testing Only, No Real AI)
If you want to test the UI without real API keys, you can:
- Run only the frontend
- Mock the API responses
For the quickest backend test without Qdrant/OpenAI, comment out the vector store initialization in startup and the RAG processing in document upload. The chat will still work but won't return relevant answers.
Environment Variables Reference
Backend (contexta_be/.env)
| Variable | Required | Description |
|---|---|---|
SUPABASE_URL |
✅ | Supabase project URL |
SUPABASE_ANON_KEY |
✅ | Supabase anon key |
SUPABASE_SERVICE_ROLE_KEY |
✅ | Supabase service role key |
QDRANT_URL |
✅ | Qdrant cluster URL |
QDRANT_API_KEY |
⚠️ | Required for Qdrant Cloud |
OPENAI_API_KEY |
✅ | Required for embeddings |
FIREWORKS_API_KEY |
⚠️ | For free-tier Llama/Mixtral models |
ANTHROPIC_API_KEY |
⚠️ | For Claude models (Pro plan) |
GOOGLE_API_KEY |
⚠️ | For Gemini models (Pro plan) |
STRIPE_SECRET_KEY |
⚠️ | For billing (use test key in dev) |
STRIPE_WEBHOOK_SECRET |
⚠️ | For Stripe webhooks |
STRIPE_STARTER_PRICE_ID |
⚠️ | Stripe price ID for Starter plan |
STRIPE_PRO_PRICE_ID |
⚠️ | Stripe price ID for Pro plan |
ALLOWED_ORIGINS |
✅ | Comma-separated CORS origins |
APP_SECRET_KEY |
✅ | Random secret for production |
Frontend (contexta_fe/.env)
| Variable | Required | Description |
|---|---|---|
VITE_API_URL |
✅ | Backend API URL |
Project Structure
contexta_be/
├── app/
│ ├── main.py # FastAPI app + router registration
│ ├── config.py # Settings + plan limits
│ ├── database.py # Supabase client
│ ├── models.py # Pydantic models
│ ├── dependencies.py # Auth middleware
│ ├── routers/
│ │ ├── auth.py # Signup, login, me
│ │ ├── chatbots.py # CRUD + publish + export
│ │ ├── documents.py # File upload + processing
│ │ ├── chat.py # RAG chat endpoint
│ │ ├── marketplace.py # Public chatbot listing
│ │ └── billing.py # Stripe checkout + webhooks
│ └── services/
│ ├── vector_store.py # Qdrant operations
│ ├── embeddings.py # OpenAI embeddings
│ ├── document_processor.py # PDF/DOCX/CSV parsing
│ ├── llm.py # Multi-provider LLM routing
│ ├── rag.py # RAG pipeline
│ └── code_export.py # ZIP code generation
├── supabase_schema.sql # Database schema (run once)
├── requirements.txt
├── Dockerfile
└── docker-compose.yml
contexta_fe/
├── src/
│ ├── main.tsx # Entry point
│ ├── App.tsx # Router + routes
│ ├── index.css # Tailwind + global styles
│ ├── types/index.ts # TypeScript interfaces
│ ├── services/api.ts # API client (axios)
│ ├── store/authStore.ts # Zustand auth state
│ ├── lib/utils.ts # Helpers + constants
│ ├── components/
│ │ ├── ui.tsx # Reusable UI (Button, Input, Card...)
│ │ ├── Layout.tsx # App shell with sidebar
│ │ └── ChatInterface.tsx # Chat UI with sources
│ └── pages/
│ ├── LandingPage.tsx
│ ├── AuthPages.tsx # Login + Signup
│ ├── DashboardPage.tsx
│ ├── ChatbotBuilderPage.tsx # Builder + docs + preview
│ ├── MarketplacePage.tsx
│ ├── PricingPage.tsx
│ └── SettingsPage.tsx
├── package.json
├── vite.config.ts
└── tailwind.config.js
Troubleshooting
"CORS error" in browser
→ Check ALLOWED_ORIGINS in backend .env includes your frontend URL
"Could not connect to Qdrant"
→ Check QDRANT_URL is correct and the cluster is running
→ For local Docker: ensure the qdrant service is started
"Invalid or expired token" on API calls
→ The Supabase JWT has expired. Log out and log back in.
→ Check SUPABASE_SERVICE_ROLE_KEY is correct
Document processing stuck on "processing"
→ Check OpenAI API key is valid
→ Check Qdrant connection
→ Check backend logs: docker logs contexta-api or terminal output
"Upgrade to publish" even after Stripe payment
→ Ensure Stripe webhook is configured and received the checkout.session.completed event
→ Check backend logs for webhook processing
→ For local testing, use stripe listen --forward-to localhost:8000/api/v1/billing/webhook
Chat returns "I don't have information about that"
→ Document processing may have failed — check document status in builder → The uploaded content may not be relevant to the question → Try re-uploading the document
Development Tips
- Backend hot reload:
uvicorn app.main:app --reloadwatches for file changes - Frontend hot reload:
npm run devautomatically reloads - API testing: Use http://localhost:8000/docs (Swagger UI)
- Database inspection: Supabase dashboard → Table Editor
- Vector inspection: Qdrant dashboard at http://localhost:6333/dashboard (local only)
License
MIT — build freely, use commercially.