Skip to content

Repository files navigation

Chatty Chatty

Chatty by PersonaliAI

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.

License: MIT CI Next.js FastAPI Supabase LiveKit MCP Docker PRs Welcome

Chatty Cloud (hosted) · Documentation · Quick Start · Managed deployment guide · Voice worker guide · Platform Runbook · Features · MCP Server · Architecture · Contributing


Table of contents

Why Chatty

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

✨ Features

  • 💬 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 telemetry

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

Architecture

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
Loading
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

Deployment profiles

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.

Deployment-platform compatibility

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
Docker Managed-Supabase Docker Compose Supported now for the API + frontend; put TLS in front.
Railway Railway Docker Compose guide Supported as two services; Railway maps the API and frontend Dockerfiles separately.
Render render.yaml / Render Blueprint reference Blueprint included; it creates the API + frontend services and prompts for secrets.
Heroku 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.

📋 Requirements

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.

🚀 Quick Start (Docker Compose)

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 frontend

Dashboard: http://localhost:3000 - sign up, create a bot, and the widget embed snippet is generated for you under bot settings.

📖 Self-Hosting, Step by Step

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.

Step 1 - Create and configure Supabase

  1. Go to supabase.com, sign up, and click New Project. The free tier is enough to run Chatty (upgrade later if you outgrow it).
  2. Pick a region close to where you'll deploy the backend, set a database password, and wait for provisioning (~2 minutes).
  3. Once the project is ready, go to Project Settings → API and note down:
    • Project URL (https://xxxxx.supabase.co)
    • anon public key
    • service_role key (keep this one secret - it bypasses row-level security)
  4. 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.

Step 2 - Apply the database schema

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.

Step 3 - Get your LLM key (Gemini, free tier)

Chatty defaults to Google's Gemini (generous free tier, no credit card required to start):

  1. Go to aistudio.google.com/apikey.
  2. Sign in with a Google account and click Create API key.
  3. Copy the key - you'll paste it into GEMINI_API_KEY in 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.

Step 4 - Configure environment variables

Three files, one per surface:

cp backend/.env.example backend/.env
cp frontend/.env.example frontend/.env
cp .env.example .env

Open 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.

Step 5 - Run it

docker compose up --build backend frontend

First 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 at http://localhost:8000/docs)

Step 6 - Verify the install

  1. 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; check docker compose logs backend.
  2. Open http://localhost:3000, click Sign up, and create an account (this goes through Supabase Auth against your own project).
  3. Create your first bot from the dashboard. Add a knowledge source (paste some text or a URL) and try it in the Playground tab.
  4. 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.

Step 7 - Optional features

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.

Step 8 - Production deployment without Cloud Run or Firebase App Hosting

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.

Step 9 - Updating

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

Troubleshooting

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).

💻 Local Development (without Docker)

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 8000

Frontend:

cd frontend
npm install
npm run dev

Voice worker (optional):

cd backend
python voice-agent/voice_worker.py dev

MCP Server & Agent Control

Chatty 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.

Environment Variable Reference

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.

🧪 Testing & CI

.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-blocking pip-audit dependency 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.

🔒 Security

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).

🤝 Contributing

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.

📄 License

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.

Releases

Packages

Used by

Contributors

Languages