Skip to main content

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 chatwoot database 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_dispatch manual trigger, mirrors deploy-metabase.yml structure)
  • 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

  1. GCP Cloud Run deployment of the official Chatwoot Docker image:

    • Service name: chatwoot
    • Environment variables sourced from Doppler
    • PostgreSQL via a new chatwoot database 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 as deploy-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)
  2. 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)
  3. 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 UID
      • name — user display name
      • email — user email
      • businessId — active business ID
      • businessName — active business name
      • role — user role in the business
      • planTier — business plan tier
    • i18next locale synced to Chatwoot widget locale (es / en)
    • Component lives at apps/frontend-pwa/src/components/support/ChatwootWidget.tsx
  4. Docusaurus plugin integration (apps/docs):

    • Install @chatwoot/docusaurus-plugin
    • Add plugin config to docusaurus.config.ts with websiteToken from env var
    • Widget appears on all docs pages
    • No authentication context (public docs, anonymous visitors)
  5. 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

DecisionChoiceReason
HostingGCP Cloud RunConsistent with existing infra; scales to zero
DatabaseNew chatwoot DB on existing shared Cloud SQL instanceCost-effective; same connection pattern as Metabase; no new instance needed
Cache / RedisCloud Memorystore + VPC ConnectorChatwoot hard requirement; VPC Connector is new infra needed for Memorystore access
Process modelSingle Cloud Run service (official entrypoint runs web + Sidekiq)Do NOT override --command; official image manages both processes
EmailSendGrid (existing)Already configured in project
React integrationOfficial JS SDK + custom hookNo extra npm dependency needed
Docusaurus integration@chatwoot/docusaurus-pluginOfficial, 1-line config
CI/CDworkflow_dispatch in deploy-chatwoot.ymlMirrors 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.