Fusionchat is a modern, cloud-native customer engagement platform that enables businesses to deliver seamless, AI-powered chat experiences across multiple channels. Built on a serverless architecture with Next.js, Convex backend, Clerk authentication and a shared UI library based on shadcn/ui. Fusionchat combines enterprise-grade security with developer-friendly extensibility.
- apps/
- web/ — Admin/dashboard app (Next.js 15, App Router, Clerk, Convex)
- widget/ — Hosted chat widget app (Next.js 15, embedded in an iframe)
- embed/ — Lightweight embed script (Vite) to place the widget on any site
- packages/
- backend/ — Convex functions, schema, and integrations (AI, VAPI, AWS)
- ui/ — Shared UI component library (shadcn/ui-based)
- math/ — Example TS lib (build + exports setup)
- eslint-config/ — Centralized ESLint configs
- typescript-config/ — Centralized TypeScript configs
- Security — see Security Quickstart and SECURITY.md
- Next.js App Router across web and widget apps with server components by default.
- Auth via Clerk:
- Edge
middleware.tsgatekeeping routes inapps/web/. ClerkProviderinitialized inapps/web/app/layout.tsx.
- Edge
- Data & Realtime via Convex:
- Client:
ConvexProviderWithClerkwired inapps/web/components/providers.tsx(requiresNEXT_PUBLIC_CONVEX_URL). - Backend: Convex functions in
packages/backend/convex/, schema inpackages/backend/convex/schema.ts.
- Client:
- Embedding strategy:
apps/embed/embed.tsinjects a floating button and loadsapps/widgetin an iframe using aWIDGET_URLandorganizationIdquery param.
- Shared UI:
@workspace/uiexportsglobals.css, components, hooks, and lib for consumption in apps viatranspilePackages.
- Monorepo tooling:
- pnpm workspaces, Turbo for tasks and caching, shared TS and ESLint configs.
Start here to keep deployments safe. See
SECURITY.mdfor the full policy.
- Store secrets in environment managers (Vercel/Convex/AWS), not in git. Use
*.env.exampletemplates.- Configure Clerk correctly and protect routes via
apps/web/middleware.ts.- Set
NEXT_PUBLIC_CONVEX_URLto the correct environment for each app.- Validate postMessage origins in production for the embed/widget pairing.
- Rotate any leaked keys immediately and purge history if necessary.
flowchart LR
subgraph "Client Sites"
E["Embed Script (Vite)"]
I["Widget App (Next.js)"]
end
subgraph "Monorepo Apps"
W["Web App (Next.js)"]
I
end
subgraph "Shared Packages"
UI["@workspace/ui (shadcn/ui)"]
B["@workspace/backend (Convex)"]
end
E --> I
I --> E
W --> UI
I --> UI
W --> B
I --> B
subgraph "External Services"
Clerk["Clerk"]
OpenAI["OpenAI"]
Vapi["VAPI"]
AWS["AWS Secrets Manager"]
end
W <--> Clerk
B <--> AWS
B <--> OpenAI
B <--> Vapi
- See
packages/backend/README.md#api-referencefor a detailed list of Convex public/private/system functions and argument schemas. - See
packages/backend/README.md#web-app-→-backend-usage-mapfor howapps/webmodules call these endpoints using@workspace/backend/_generated/apiwithuseQuery,useMutation, anduseAction.
- Create/Select a Convex project for production.
- Set production environment variables in Convex (e.g., Clerk issuer domain, secrets managed via AWS if applicable).
- Push functions:
# From repo root or backend package
pnpm --filter @workspace/backend dev # for local
# For production deployment, use Convex dashboard/CLI per docs- Update app envs with the production
CONVEX_URLand ensureNEXT_PUBLIC_CONVEX_URLpoints to it.
- Import
apps/webandapps/widgetinto Vercel as separate projects or a monorepo with two deployments. - Build command:
pnpm build - Install command:
pnpm install - Output:
.next - Node version: 20+
- Env vars to configure in Vercel:
- Both apps:
NEXT_PUBLIC_CONVEX_URL(production Convex URL) - Web app only: Clerk keys/URLs (
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,CLERK_SECRET_KEY, and sign-in/up URLs)
- Both apps:
- Set
transpilePackagesalready configured innext.config.mjsfor@workspace/ui.
- Build
apps/embed:
pnpm --filter embed build- Publish
apps/embed/dist/to your CDN (e.g., Cloudflare R2 + public endpoint, Vercel Static Hosting, S3 + CloudFront). - At build time, set
VITE_WIDGET_URLto your production Widget domain soEMBED_CONFIG.WIDGET_URLis correct. - Usage on client sites:
<script
src="https://your-cdn.com/embed.js"
data-organization-id="org_123"
data-position="bottom-right"
defer
></script>- Optional programmatic control via
window.FusionchatWidget:
window.FusionchatWidget.show();
window.FusionchatWidget.hide();
window.FusionchatWidget.destroy();
window.FusionchatWidget.init({ organizationId: "org_456", position: "bottom-left" });- Do not commit secrets; use Vercel/Convex/AWS secret managers.
- For VAPI and other integrations, store secret values in AWS Secrets Manager and reference the secret name in Convex plugins (see
packages/backend/convex/private/plugins.ts).
- Verify
NEXT_PUBLIC_CONVEX_URLand Clerk URLs resolve in production. - Confirm iframe origin messaging between Embed and Widget works (correct production origins).
- Validate Convex public endpoints respond and session validation flows correctly for widget traffic.
- Node.js >= 20
- pnpm >= 10
- Convex CLI:
npm i -g convex - Optional: Vercel CLI if deploying to Vercel
- Install dependencies at the repo root:
pnpm install- Configure environment variables:
- Each app/package has its own
.env.local(do not commit secrets). - Required variables (non-exhaustive):
# apps/web/.env.local
NEXT_PUBLIC_CONVEX_URL=...
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=...
CLERK_SECRET_KEY=...
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL=/
NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL=/
# packages/backend/.env.local
CONVEX_DEPLOYMENT=dev:your-deployment
CONVEX_URL=https://<your-project>.convex.cloud
CLERK_JWT_ISSUER_DOMAIN=https://<your-clerk-domain>.clerk.accounts.dev
CLERK_SECRET_KEY=...
OPENAI_API_KEY=...
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
AWS_REGION=us-east-1Security note: Never commit real secrets. Use environment managers (Vercel/Convex secrets) in production.
- Run the stack locally (from repo root):
# Start backend Convex dev in a separate terminal
pnpm --filter @workspace/backend dev
# Start web app
pnpm --filter web dev
# Start widget app
pnpm --filter widget dev
# Start embed script dev server
pnpm --filter embed devPorts used by default:
- web: 3000
- widget: 3001
- embed (Vite): 3002
Alternatively, use Turbo to orchestrate:
pnpm dev- Next.js 15 App Router with Clerk middleware (
apps/web/middleware.ts). - Root layout:
apps/web/app/layout.tsxsets fonts,ClerkProvider, and Convex provider. - Redirects
/to/conversationsviaapps/web/next.config.mjs. - Modules organized under
apps/web/modules/(auth, customization, dashboard, files, integrations, plugins). - Hooks: example
useIsMobileinapps/web/hooks/use-mobile.ts.
Run:
pnpm --filter web dev- Hosted widget app rendered inside an iframe by the embed script.
- Minimal routing in
apps/widget/app/, providers inapps/widget/components/providers.tsx.
Run:
pnpm --filter widget dev- Vite-built vanilla TS snippet that injects a floating chat button and an iframe.
- Core file:
apps/embed/embed.ts. - Reads attributes from the
<script>tag such asdata-organization-idand optionaldata-position.
Run:
pnpm --filter embed devEmbed usage example:
<script
src="https://your-cdn.com/embed.js"
data-organization-id="org_123"
data-position="bottom-right"
defer
></script>Optionally, control it programmatically:
window.FusionchatWidget.show();
window.FusionchatWidget.hide();
window.FusionchatWidget.destroy();
window.FusionchatWidget.init({ organizationId: "org_456", position: "bottom-left" });- Convex schema:
packages/backend/convex/schema.tsdefines tables for subscriptions, widget settings, plugins, conversations, contact sessions, and users with indices. - Convex functions: organized under
public/,private/, andsystem/. - Auth config:
packages/backend/convex/auth.config.tsandusers.tshelpers. - Integrations:
@convex-dev/agent,@convex-dev/rag,@vapi-ai/server-sdk,@ai-sdk/openaiand AWS SDK for secrets/storage.
Run:
pnpm --filter @workspace/backend dev- shadcn/ui-based library exporting components, hooks, lib utilities, and Tailwind globals.
- Consumed via
@workspace/ui/*withtranspilePackagesconfigured in appnext.config.mjs.
Usage example:
import { Button } from "@workspace/ui/components/button";- App Router in
app/directory; default Server Components. Add"use client"where interactivity is needed. - Layouts via
layout.tsxand nested layouts for shared UI. - Styling: Tailwind CSS + shadcn UI. Import
@workspace/ui/globals.cssin root layout. - Data fetching: Server Components async/await on the server; client-side state via Jotai/React Hook Form; Convex React hooks on the client.
- Forms & Validation: React Hook Form + Zod; validate both client and server-side where applicable.
- TypeScript: strict mode; type every function param and return; avoid
any. - Files & naming: kebab-case dirs/files; components/types PascalCase; hooks prefixed
use*. - Organization:
- Services (API calls) →
src/services/(or module-localservices/) - Helpers →
src/utils/ - Hooks →
src/hooks/
- Services (API calls) →
- Root scripts (Turbo):
pnpm build— Build all packages/appspnpm dev— Run all apps in dev mode (non-cached)pnpm lint— Lint allpnpm format— Prettier format
- Per-app scripts in
package.jsonof each app.
- Do not commit secrets. Use
.env.locallocally and secret managers in production. - Clerk keys, OpenAI, and AWS credentials are sensitive. Rotate if exposed.
- Limit logs to avoid leaking PII. Follow least-privilege on AWS IAM.
- Missing
NEXT_PUBLIC_CONVEX_URL: the web app will throw early inapps/web/components/providers.tsx. Ensure Convex dev is running and URL is set. - Auth redirect loop: verify
apps/web/middleware.tspublic routes and your Clerk configuration/URLs. - Module import errors from
@workspace/ui: ensuretranspilePackagesincludes"@workspace/ui"and that the package builds. - Convex schema mismatch: re-run Convex dev and push functions if needed.
- Add detailed app-level READMEs for
web,widget, andembedwith screenshots. - Expand backend docs with function references and sequence diagrams.
- Add CONTRIBUTING.md and PR templates.
Proprietary FusionwaveAI. All rights reserved.