Design Doc: Chatwoot Live Chat & Support Inbox
Feature Name
Merchant Support Layer — Chatwoot (Live Chat + Help Center + Omnichannel Inbox)
Overview
Self-host Chatwoot on GCP and integrate it into both the FlowPOS PWA (apps/frontend-pwa)
and the Docusaurus help center (apps/docs). Chatwoot replaces the need for a third-party
live chat SaaS, provides an omnichannel inbox for the support team (WhatsApp, email, web chat),
and optionally runs an AI agent (Captain) on top of the Docusaurus help content.
Project Context
- Project: FlowPOS
- Deployment target: GCP Cloud Run (Docker image, same infra pattern as backend)
- Database: PostgreSQL — new
chatwootdatabase on the existing shared Cloud SQL instance (same instance used by FlowPOS and Metabase). Does NOT require a new Cloud SQL instance. Follows the same socat TCP→Unix socket connection pattern as Metabase. - Secrets: Doppler (CHATWOOT_SECRET_KEY_BASE, POSTGRES_* vars, SMTP via SendGrid)
- CI/CD: GitHub Actions (
deploy-chatwoot.yml,workflow_dispatchmanual trigger, mirrorsdeploy-metabase.ymlstructure) - Frontend integration points:
apps/frontend-pwa(Vite + React 19, i18next)apps/docs(Docusaurus 3)
- No changes to FlowPOS NestJS backend or PostgreSQL schema
Scope
What this spec must cover
-
GCP Cloud Run deployment of the official Chatwoot Docker image:
- Service name:
chatwoot - Environment variables sourced from Doppler
- PostgreSQL via a new
chatwootdatabase on the existing shared Cloud SQL instance (same instance as FlowPOS and Metabase — no new Cloud SQL instance needed). Connection uses the same socat TCP→Unix socket pattern asdeploy-metabase.yml. Cloud SQL Proxy provided via--add-cloudsql-instances. Do NOT override--command. - Redis via Cloud Memorystore. Requires a VPC Connector on the Cloud Run service, which is new infrastructure not currently used by other FlowPOS services.
- SMTP configured to use SendGrid (already available in the project)
- Custom domain:
support.flowpos.app(or equivalent)
- Service name:
-
Chatwoot initial configuration (documented setup steps, not automated):
- Create account and inbox (Website Live Chat)
- Enable Spanish UI locale
- Connect WhatsApp Business channel (optional at launch)
- Connect email inbox via SendGrid inbound parse (optional at launch)
-
React PWA widget integration (
apps/frontend-pwa):- Install and configure the Chatwoot JavaScript SDK via a React hook or component
- Widget initializes only after Firebase auth resolves and the user is authenticated — not shown to unauthenticated users (lazy init, auth-gated)
- Pass merchant identity data to Chatwoot via
window.$chatwoot.setUser(userId, attributes):userId— Firebase UIDname— user display nameemail— user emailbusinessId— active business IDbusinessName— active business namerole— user role in the businessplanTier— business plan tier
- i18next locale synced to Chatwoot widget locale (
es/en) - Component lives at
apps/frontend-pwa/src/components/support/ChatwootWidget.tsx
-
Docusaurus plugin integration (
apps/docs):- Install
@chatwoot/docusaurus-plugin - Add plugin config to
docusaurus.config.tswithwebsiteTokenfrom env var - Widget appears on all docs pages
- No authentication context (public docs, anonymous visitors)
- Install
-
Environment variable contract:
# Chatwoot server
CHATWOOT_SECRET_KEY_BASE=
CHATWOOT_POSTGRES_HOST=/cloudsql/<project>:<region>:<existing-instance>
CHATWOOT_POSTGRES_DATABASE=chatwoot
CHATWOOT_POSTGRES_USERNAME=chatwoot
CHATWOOT_POSTGRES_PASSWORD=<secret>
CHATWOOT_REDIS_URL=
CHATWOOT_SMTP_ADDRESS=smtp.sendgrid.net
CHATWOOT_SMTP_USERNAME=apikey
CHATWOOT_SMTP_PASSWORD=<SENDGRID_API_KEY>
# Frontend (PWA + Docs)
VITE_CHATWOOT_BASE_URL=https://support.flowpos.app
VITE_CHATWOOT_WEBSITE_TOKEN=
Out of scope
- Changes to NestJS backend or PostgreSQL FlowPOS schema
- Chatwoot Captain AI agent (future milestone after content library is built)
- Chatwoot mobile SDK / React Native widget (future, for Desktop app)
- CRM sync between Chatwoot contacts and FlowPOS customers table
- Custom Chatwoot theme / white-labeling (use default at launch)
Key Technical Decisions
| Decision | Choice | Reason |
|---|---|---|
| Hosting | GCP Cloud Run | Consistent with existing infra; scales to zero |
| Database | New chatwoot DB on existing shared Cloud SQL instance | Cost-effective; same connection pattern as Metabase; no new instance needed |
| Cache / Redis | Cloud Memorystore + VPC Connector | Chatwoot hard requirement; VPC Connector is new infra needed for Memorystore access |
| Process model | Single Cloud Run service (official entrypoint runs web + Sidekiq) | Do NOT override --command; official image manages both processes |
| SendGrid (existing) | Already configured in project | |
| React integration | Official JS SDK + custom hook | No extra npm dependency needed |
| Docusaurus integration | @chatwoot/docusaurus-plugin | Official, 1-line config |
| CI/CD | workflow_dispatch in deploy-chatwoot.yml | Mirrors deploy-metabase.yml; Artifact Registry flowpos-chatwoot/chatwoot; pinned version (not :latest) |
Chatwoot Process Model
The official Chatwoot Docker image runs both the web server (Rails/Puma) and the Sidekiq
worker process via its entrypoint script. Do not override --command in the Cloud Run
deployment — let the entrypoint manage both processes. This keeps the deployment simple:
one service, one image, matching the Metabase pattern.
Local Development
Developers use the staging Chatwoot instance (support.flowpos.app) for widget testing.
No local Docker Compose setup for Chatwoot is required.
To test the widget locally, set these env vars in apps/frontend-pwa/.env.local:
VITE_CHATWOOT_BASE_URL=https://support.flowpos.app
VITE_CHATWOOT_WEBSITE_TOKEN=<token-from-staging-inbox>
For the help center, set in apps/docs/.env:
CHATWOOT_BASE_URL=https://support.flowpos.app
CHATWOOT_WEBSITE_TOKEN=<token-from-staging-inbox>
Speckit Command
Primary:
/speckit.specify Self-host Chatwoot on GCP Cloud Run with a dedicated Cloud SQL database and Memorystore Redis, integrate the live-chat widget into the FlowPOS React PWA (apps/frontend-pwa) via a lazy-loaded ChatwootWidget component that passes authenticated merchant identity, and embed it in the Docusaurus help center (apps/docs) via @chatwoot/docusaurus-plugin — using SendGrid for email and Doppler for secrets, with no changes to the FlowPOS NestJS backend or schema.
Alternative:
/speckit.specify Deploy Chatwoot (Docker) to GCP Cloud Run backed by a separate Cloud SQL PostgreSQL instance and Cloud Memorystore Redis; create a ChatwootWidget.tsx in apps/frontend-pwa that lazily initializes the SDK and syncs i18next locale and authenticated user identity; add @chatwoot/docusaurus-plugin to apps/docs; manage all secrets via Doppler; SendGrid as SMTP provider.
Why the primary is best: The primary captures the three integration surfaces (GCP infra, React PWA, Docusaurus) in a single sentence, calls out the two hardest constraints (merchant identity passthrough + lazy loading), and explicitly states what is NOT changing (NestJS backend/schema) — which prevents Speckit from generating unnecessary migration stubs. The alternative is useful as a fallback if Speckit needs the component path explicitly named.