Open-source AI customer support: chat widget + real-time voice agent + a full MCP server, grounded in your own knowledge base.
Run the application containers on your own host while keeping Supabase Auth, Postgres, Storage, and Realtime managed. The same deployment contract works on a VPS, Railway, Render, or another Docker host without touching the live Supabase project.
Chatty Cloud (hosted) · Documentation · Quick Start · Managed deployment guide · Voice worker guide · Platform Runbook · Features · MCP Server · Architecture · Contributing
- Why Chatty
- Features
- Architecture
- Requirements
- Quick Start (Docker Compose)
- Self-Hosting, Step by Step
- Local Development (without Docker)
- Managed-Supabase platform runbook
- MCP Server & Agent Control
- Environment Variable Reference
- Testing & CI
- Security
- Contributing
- License
Every hosted chatbot SaaS charges per-seat or per-message and holds your conversation data. Chatty is open-core: run it yourself for free, or use Chatty Cloud - our own hosted version of this exact repo, if you'd rather skip the ops work. Either way you get the same feature set - streaming chat, a real-time voice agent, RAG over your own documents, meeting booking, lead capture, WhatsApp/Slack channels, and a full MCP server so an AI agent can run the whole dashboard for you.
| Closed-source SaaS chatbots | Chatty | |
|---|---|---|
| Your conversation data | Lives on their servers, always | Your Supabase project - whether you self-host or use Chatty Cloud |
| Pricing | Per-seat / per-message, no free tier | Self-host for free, or a straightforward hosted plan on Chatty Cloud |
| LLM | Locked to their model | Bring your own - Gemini by default, or BYOK OpenAI/Anthropic/OpenRouter |
| Voice agent | Usually a separate, pricier tier | Included, same knowledge base as chat |
| Agent/automation access | Usually none, or a paid add-on | Full MCP server included - 55 tools, OAuth 2.0 secured |
| Source code | Closed | MIT licensed - fork it, audit it, extend it, run it anywhere |
- 💬 Embeddable chat widget - one
<script>tag, streaming replies, works on any website - 🎙️ Real-time voice agent - phone-call-style conversations via LiveKit, same brain as the chat widget
- 📚 RAG over your own knowledge base - upload PDF/DOCX/PPTX/XLSX, crawl URLs, auto-chunked and embedded
- 🛠️ Tool-calling - books real meetings (Google Meet/Microsoft Teams/Zoom links), captures leads, checks a calendar
- 🔌 Omnichannel - WhatsApp and Slack, in addition to the web widget
- 🔑 BYOK - default is Gemini (generous free tier); swap in your own OpenAI/Anthropic/OpenRouter key per bot
- 🤖 MCP server - connect Claude, ChatGPT, or any MCP client and run the entire dashboard from a conversation: create bots, edit flows, run campaigns, manage leads, configure voice, and more, all as 55 callable tools secured by OAuth 2.0 + PKCE (see MCP Server & Agent Control)
- 📊 Dashboard - manage bots, inbox/conversations, knowledge sources, booking rules, campaigns, and channel connections
Campaign configuration is persisted in chatty_campaigns. Widget deployments can
record impression, click, and conversion events through
POST /api/widget/campaign-events with bot_id, campaign_id, and an optional
idempotency_key (retries with the same key are safe). Dashboard users can read
recomputed metrics from GET /api/bots/{bot_id}/campaigns/{campaign_id}/analytics.
The 20260925220000_chatty_campaign_events.sql migration creates the durable
ledger and indexes; apply it before enabling campaign optimization in production.
Campaign drafts also support a validated schedule_config (once, hourly,
daily, or weekly, with an IANA timezone) and the authenticated dashboard
endpoint POST /api/bots/{bot_id}/campaigns/audience-suggest can generate a
bounded segment, intent threshold, and rationale before a campaign is saved.
- 🐳 One-command managed self-host -
docker compose up, point it at a Supabase project, done
The public repository has one canonical application layout: frontend/ contains the Next.js dashboard and widget, while
backend/ contains the FastAPI API, workers, integrations, and migrations. The application runs against a managed
Supabase project (the same contract used by Chatty Cloud). The optional voice worker can use LiveKit Cloud or the
isolated self-hosted LiveKit media-plane stack under backend/voice-agent; that stack does not replace Supabase,
Postgres, Auth, Storage, or the Chatty API.
flowchart TB
classDef actor fill:#f8fafc,stroke:#64748b,color:#0f172a
classDef edge fill:#eff6ff,stroke:#2563eb,color:#1e3a8a
classDef app fill:#ecfdf5,stroke:#059669,color:#064e3b
classDef data fill:#f0fdfa,stroke:#0f766e,color:#134e4a
classDef integration fill:#fff7ed,stroke:#ea580c,color:#7c2d12
classDef deploy fill:#f5f3ff,stroke:#7c3aed,color:#4c1d95
subgraph clients["Clients and channels"]
direction LR
visitor(("Website visitor")):::actor
operator(("Workspace operator")):::actor
channels["WhatsApp · Slack · MCP"]:::actor
end
subgraph edge["Public edge"]
direction LR
tls["TLS · domains · rate limits"]:::edge
web["Next.js frontend"]:::app
end
subgraph runtime["Chatty runtime"]
direction LR
api["FastAPI API · chat · RAG · webhooks"]:::app
voice["Voice worker · LiveKit Agents"]:::app
end
subgraph managed["Managed Supabase — default profile"]
direction LR
auth["Supabase Auth"]:::data
postgres["Postgres + pgvector · RLS"]:::data
storage["Supabase Storage"]:::data
realtime["Supabase Realtime"]:::data
end
subgraph integrations["Optional integrations"]
direction TB
llm["LLM providers"]:::integration
calendar["Calendar providers"]:::integration
channelsApi["WhatsApp / Slack"]:::integration
livekit["LiveKit Cloud or self-hosted"]:::integration
billing["Billing + webhooks"]:::integration
end
subgraph targets["Deployment targets"]
direction LR
docker["Docker Compose / VPS"]:::deploy
railway["Railway"]:::deploy
render["Render Blueprint"]:::deploy
heroku["Heroku-style host"]:::deploy
end
visitor --> tls
operator --> web
channels --> api
tls --> web
web -->|HTTPS| api
api --> voice
api --> auth
api --> postgres
api --> storage
api --> realtime
api --> llm
api --> calendar
api --> channelsApi
voice --> livekit
api --> billing
targets -. runs .-> runtime
linkStyle default stroke:#64748b,stroke-width:1.5px
chatty/
├── frontend/ Next.js - dashboard, embeddable widget, widget.js loader
├── backend/ FastAPI - chat/RAG/bookings/channels/OAuth/MCP API
│ ├── app/ Routers, core (auth/security/db helpers), schemas
│ ├── plugins/ Google/Microsoft integrations, RAG, widget orchestration
│ ├── supabase/ Database schema and migrations (applied in order)
│ ├── scripts/ apply_migrations.py and other one-off ops scripts
│ ├── voice-agent/ LiveKit voice worker agent + self-hosted VPS Docker stack
│ └── tests/ pytest smoke + unit tests
└── docker-compose.yml
| Profile | Data and identity services | When to use |
|---|---|---|
managed_supabase (default) |
Supabase Auth, Postgres, Storage, pgvector, and RLS | Chatty Cloud and every supported application deployment |
| Voice media plane (optional) | LiveKit + private Redis on a VPS | Keep audio media on your own VPS while the application data remains in Supabase |
For voice deployment, follow backend/voice-agent/README.md and
frontend/docs/guides/voice-self-hosting.mdx.
The managed-Supabase deployment contract is two Docker services (API + frontend).
The root docker-compose.yml runs both services without provisioning a second
database. The complete platform runbook, including secret handling, domains,
health checks, verification, rollback, and troubleshooting, is in
docs/SELF_HOST_MANAGED_SUPABASE.md.
| Platform | Official deployment guide | Current repository status |
|---|---|---|
| Managed-Supabase Docker Compose | Supported now for the API + frontend; put TLS in front. | |
| Railway Docker Compose guide | Supported as two services; Railway maps the API and frontend Dockerfiles separately. | |
render.yaml / Render Blueprint reference |
Blueprint included; it creates the API + frontend services and prompts for secrets. | |
| Heroku container runtime | Supported as two container apps; no full-stack Button is claimed. |
For every supported application deployment keep DEPLOYMENT_PROFILE=managed_supabase. LiveKit self-hosting is an
independent media-plane choice configured only in backend/voice-agent/.env; it does not introduce a second database,
object store, or authentication system.
| Version | Needed for | |
|---|---|---|
| Docker + Docker Compose | 24+ | Quick Start (recommended path) |
| Python | 3.11+ | Backend, without Docker |
| Node.js | 20+ | Frontend, without Docker |
| Supabase account | free tier | Database (Postgres + Auth + Storage) |
| Google AI Studio API key | free tier | Default LLM (Gemini) |
Everything else (LiveKit for voice, WhatsApp/Slack tokens, Google/Microsoft OAuth, Lemon Squeezy billing, Sentry, Upstash Redis) is optional - each env var you leave blank just disables that one feature; nothing else breaks.
The fastest path from clone to a running instance. See Self-Hosting, Step by Step below if you want the full walkthrough with screenshots-in-words and troubleshooting.
git clone --recurse-submodules https://github.com/PersonaliAI/chatty.git
cd chatty
# Optional plugin and SDK source checkouts are independent submodules.
# For an existing clone, use: git submodule update --init --recursive
# 1. Apply the database schema to a Supabase project you've already created
cd backend && pip install psycopg2-binary
python scripts/apply_migrations.py "postgresql://postgres:YOUR_PASSWORD@YOUR_HOST:5432/postgres"
cd ..
# 2. Configure environment variables
cp backend/.env.example backend/.env # fill in Supabase + Gemini keys
cp frontend/.env.example frontend/.env # fill in Supabase URL + anon key
cp .env.example .env # same NEXT_PUBLIC_* values (used at Docker build time)
# 3. Run it
docker compose up --build backend frontendDashboard: http://localhost:3000 - sign up, create a bot, and the widget embed snippet is generated for you under bot settings.
A fuller walkthrough than the Quick Start above - read this if it's your first time, or if something in Quick Start didn't work.
- Go to supabase.com, sign up, and click New Project. The free tier is enough to run Chatty (upgrade later if you outgrow it).
- Pick a region close to where you'll deploy the backend, set a database password, and wait for provisioning (~2 minutes).
- Once the project is ready, go to Project Settings → API and note down:
- Project URL (
https://xxxxx.supabase.co) anonpublickeyservice_rolekey (keep this one secret - it bypasses row-level security)
- Project URL (
- Go to Project Settings → Database → Connection string → URI. Use the Session pooler or direct connection string (not the transaction pooler - schema migrations need a persistent session). You'll use this once, in the next step.
Chatty ships ordered SQL migration files under backend/supabase/migrations/. Apply them all in order with the included script - it's idempotent (tracks what's already applied in a _migrations_log table), so it's always safe to re-run after pulling updates:
cd backend
pip install psycopg2-binary
python scripts/apply_migrations.py "postgresql://postgres:YOUR_PASSWORD@YOUR_HOST:5432/postgres"You should see output like:
<current migration count> migration files found
applied 20260510084430_initial_schema.sql
applied 20260511120000_slice2_billing_and_integrations.sql
...
Done - applied 67 new, skipped 0 already-applied.
If a migration fails partway through, the script stops and prints which file failed - fix the reported issue (usually a stale connection or an already-modified table from manual tinkering) and re-run; already-applied files are skipped automatically.
Chatty defaults to Google's Gemini (generous free tier, no credit card required to start):
- Go to aistudio.google.com/apikey.
- Sign in with a Google account and click Create API key.
- Copy the key - you'll paste it into
GEMINI_API_KEYin the next step.
Prefer a different model? Every bot supports BYOK (OpenAI, Anthropic, or OpenRouter) configurable per-bot from the dashboard once it's running - you don't need to decide this now.
Three files, one per surface:
cp backend/.env.example backend/.env
cp frontend/.env.example frontend/.env
cp .env.example .envOpen backend/.env and fill in, at minimum:
SUPABASE_URL=https://xxxxx.supabase.co
SUPABASE_SECRET_KEY=<secret key from Step 1>
SUPABASE_DB_HOST=<from the connection string in Step 1>
SUPABASE_DB_PASSWORD=<your database password from Step 1>
GEMINI_API_KEY=<from Step 3>
# Generate each of these once with the command shown next to it:
FUNCTION_SECRET=<python -c "import secrets; print(secrets.token_urlsafe(32))">
BYOK_ENCRYPTION_KEY=<python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())">Open frontend/.env and .env (repo root) and fill in the matching NEXT_PUBLIC_SUPABASE_URL / NEXT_PUBLIC_SUPABASE_ANON_KEY (same values as above - the frontend only ever sees the public anon key, never service_role).
Every other variable in backend/.env.example is optional and documented inline - voice, WhatsApp, Slack, Google/Microsoft OAuth, billing, error monitoring, and more. Leave them blank to start; each one unlocks exactly one feature. See the full Environment Variable Reference below for what each does.
If you're deploying under your own domain (not just running locally), also set CHATTY_BACKEND_URL and CHATTY_FRONTEND_URL in backend/.env to your real domains - these are the OAuth issuer and MCP resource identifiers; leaving them at the default breaks OAuth/MCP client discovery once you're not running on localhost. See MCP Server & Agent Control.
docker compose up --build backend frontendFirst build takes a few minutes (installing Python/Node dependencies, compiling the Next.js production bundle). Subsequent runs are fast - Docker caches layers.
- Dashboard:
http://localhost:3000 - Backend API:
http://localhost:8000(interactive docs athttp://localhost:8000/docs)
- Open
http://localhost:8000/docs- you should see the FastAPI Swagger UI listing every endpoint. If this doesn't load, the backend container failed to start; checkdocker compose logs backend. - Open
http://localhost:3000, click Sign up, and create an account (this goes through Supabase Auth against your own project). - Create your first bot from the dashboard. Add a knowledge source (paste some text or a URL) and try it in the Playground tab.
- Copy the embed snippet from the bot's settings and drop it into any HTML page - the widget should load and respond using your knowledge base.
If any of these steps fail, see Troubleshooting below.
Each of these is opt-in - set the relevant env vars in backend/.env and restart (docker compose up --build backend):
| Feature | Env vars | Notes |
|---|---|---|
| Voice agent | LIVEKIT_URL, LIVEKIT_API_KEY, LIVEKIT_API_SECRET |
Free tier at cloud.livekit.io, or self-host LiveKit. Run the worker from backend/voice-agent as a separate service; the managed-Supabase compose keeps it opt-in. |
| WhatsApp channel | WHATSAPP_VERIFY_TOKEN, WHATSAPP_ACCESS_TOKEN |
Meta Cloud API - see Meta's developer docs. |
| Slack channel | SLACK_SIGNING_SECRET |
From your Slack app's Basic Information page. |
| Google Calendar/Gmail booking | GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET |
Create OAuth credentials at console.cloud.google.com/apis/credentials. |
| Microsoft 365 booking | MICROSOFT_CLIENT_ID, MICROSOFT_CLIENT_SECRET |
Register an app in Azure AD. Real Teams online-meeting links, not a placeholder. |
| Real Zoom meeting links | ZOOM_ACCOUNT_ID, ZOOM_CLIENT_ID, ZOOM_CLIENT_SECRET |
Server-to-Server OAuth app in the Zoom Marketplace (meeting:write:meeting scope). A single backend-wide credential - no per-user connect flow. Without this, bookings fall back to a placeholder link. |
| Billing / paid plans | LEMONSQUEEZY_API_KEY + related vars |
Only relevant if you're reselling access; skip entirely for internal/free use. |
| Error monitoring | SENTRY_DSN |
Optional - leave blank and errors just go to stdout/logs. |
The supported production shape is two Docker services (API + frontend) backed
by the same managed Supabase project. The full copy/paste runbook for each
platform is SELF_HOST_MANAGED_SUPABASE.md.
It covers DEPLOYMENT_PROFILE=managed_supabase, secret-manager setup, domains,
health checks, production verification, rollback, and security boundaries.
The retired provider-neutral Postgres/Redis/object-store stack is intentionally
not part of this repository. Do not use old install-self-host.ps1 instructions;
the supported self-hosted component is the LiveKit voice media plane described
in backend/voice-agent/README.md.
git pull
cd backend && python scripts/apply_migrations.py "postgresql://..." # picks up any new migrations, skips old ones
cd .. && docker compose up --build backend frontend| Symptom | Likely cause / fix |
|---|---|
docker compose up fails immediately on backend with RuntimeError: ... environment variable is required |
SUPABASE_URL or SUPABASE_SECRET_KEY is missing from backend/.env - these two fail startup on purpose rather than silently falling back to nothing. |
| Backend starts but BYOK/booking/other features error at request time | GEMINI_API_KEY, FUNCTION_SECRET, or BYOK_ENCRYPTION_KEY is blank - these don't crash startup, but the specific feature that needs them fails when you actually use it. Fill them in per Step 4. |
| Migration script hangs or times out | You're using the transaction pooler connection string. Switch to the Session pooler or direct connection string from Supabase's Connection string page. |
| Dashboard loads but sign-up fails | NEXT_PUBLIC_SUPABASE_URL/NEXT_PUBLIC_SUPABASE_ANON_KEY in frontend/.env don't match the project you ran migrations against. |
| Widget embeds but never replies | Check docker compose logs backend for a Gemini API error - usually an invalid/missing GEMINI_API_KEY, or the free tier's rate limit. |
| MCP/OAuth client can't discover the server once deployed | CHATTY_BACKEND_URL is still the default (https://api.chatty.personaliai.com) instead of your own domain - set it explicitly once you're not on localhost. |
| Voice agent never joins the call | LIVEKIT_URL/LIVEKIT_API_KEY/LIVEKIT_API_SECRET unset, or the voice-worker service isn't running (docker compose up --build with no service names starts it; docker compose up backend frontend doesn't). |
Still stuck? Open an issue with your docker compose logs output (redact secrets first).
Backend:
cd backend
python -m venv .venv && .venv\Scripts\activate # or source .venv/bin/activate on macOS/Linux
pip install -r requirements.txt
uvicorn main:app --reload --port 8000Frontend:
cd frontend
npm install
npm run devVoice worker (optional):
cd backend
python voice-agent/voice_worker.py devChatty ships a full Model Context Protocol server - connect Claude, ChatGPT, or any MCP-compatible client and run the entire dashboard from a conversation instead of clicking through screens.
- Auth: OAuth 2.0 authorization-code flow with mandatory PKCE (RFC 7636), dynamic client registration (RFC 7591), and authorization server metadata discovery (RFC 8414) - no API key to paste into a config file.
- Scopes:
read,write,knowledge,voice,actions,admin- a client only gets what it's granted. - 55 tools across 10 categories: bot lifecycle, customizer & design, visual flow builder, proactive campaigns, voice agent, knowledge base & RAG, inbox & human takeover, leads/calendar/meetings, analytics & self-healing, and settings/guardrails/BYOK/team RBAC.
- Real data only - every tool reads and writes the exact same tables the dashboard does; nothing simulated.
Connect by pointing an MCP client at:
{
"mcpServers": {
"chatty": {
"url": "https://your-backend-domain/mcp"
}
}
}The client opens a normal OAuth consent screen on first connect. If you're self-hosting under your own domain, set CHATTY_BACKEND_URL (and CHATTY_FRONTEND_URL, for the consent screen redirect) in backend/.env to your real URLs first - see Step 4.
Every environment variable is documented inline in backend/.env.example and frontend/.env.example - what it's for, where to get it, and what happens if you leave it blank. The tables below are the short version.
Backend - required to start:
| Variable | Purpose |
|---|---|
SUPABASE_URL, SUPABASE_SECRET_KEY |
Database + auth connection; the secret key stays server-side |
SUPABASE_DB_HOST, SUPABASE_DB_PASSWORD |
Direct Postgres connection for a few operations PostgREST can't do |
FUNCTION_SECRET |
Signs widget origin tokens + OAuth state JWTs; gates internal cron/admin endpoints |
BYOK_ENCRYPTION_KEY |
Encrypts customer-supplied BYOK API keys at rest |
GEMINI_API_KEY |
Default LLM |
Backend - optional, one per feature: voice (LiveKit), WhatsApp, Slack, Google/Microsoft OAuth (calendar booking), Zoom (Server-to-Server OAuth), transactional email (OneSignal), web crawl (Jina), billing (Lemon Squeezy), rate limiting (Upstash Redis), error monitoring (Sentry), and CHATTY_BACKEND_URL/CHATTY_FRONTEND_URL (only needed once you're deployed under your own domain - see MCP Server & Agent Control). Full list with setup links: backend/.env.example.
Frontend - required:
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY |
Must match the backend's Supabase project |
NEXT_PUBLIC_BACKEND_URL |
Where the frontend calls the API - http://localhost:8000 locally, your real backend URL in production |
Frontend - optional: billing portal (Lemon Squeezy), Firebase Analytics. Full list: frontend/.env.example.
.github/workflows/ci.yml runs on every push/PR to main:
- Backend: compile-check (
python -m compileall), the full pytest suite (backend/tests/), and a non-blockingpip-auditdependency scan. - Frontend: TypeScript typecheck, ESLint, a widget design-token drift check, and a production build.
Run the backend suite locally:
cd backend
pip install -r requirements.txt pytest
python -m pytest tests/ -q.github/dependabot.yml watches pip/npm/Docker/GitHub Actions dependencies weekly.
Found a security issue? Please don't open a public GitHub issue for it. Instead, use GitHub's private reporting: this repo's Security tab → Report a vulnerability. We'll respond as quickly as we can.
SUPABASE_SECRET_KEY, FUNCTION_SECRET, BYOK_ENCRYPTION_KEY, and every OAuth/API client secret should be treated as production credentials - never commit a filled-in .env file (all three .env.example templates are already gitignored under their real names).
Issues and PRs welcome - this is a young project and there are rough edges. See CONTRIBUTING.md for the development setup and good first areas: additional STT/TTS provider plugins, additional channel integrations, deployment guides for platforms beyond Docker Compose, and docs.
MIT - see LICENSE. Use it, fork it, ship it commercially - attribution appreciated but not required.
If Chatty is useful to you, star the repo ⭐ - it helps other people find it.