Chatwoot live chat (marketing pages)
The public marketing pages carry a Chatwoot Cloud live-chat
bubble — the "Contact Us" channel for visitors, pricing questions, and careers
applicants. Everything the repo owns is already wired; this doc gets the Chatwoot side
to a professional setup on the free (Hacker) tier, with most of it automated by
scripts/chatwoot-setup.sh.
Split of responsibilities
The repo owns: the widget loader
(chatwoot-widget.tsx, mounted in
public-layout.tsx so only marketing
pages get it), the public config endpoint (GET /api/public/chatwoot —
PublicChatwootController),
the CSP whitelist in frontend/nginx.conf, and the
provisioning script.
The Chatwoot dashboard owns: the inbox, greeting, pre-chat form, out-of-office, labels, canned responses, automation. Replies come from humans in the dashboard or the mobile app; there is no bot answering on its own.
The widget is off by default everywhere: with no CHATWOOT_WEBSITE_TOKEN the
endpoint answers 204 and the frontend injects nothing — no script, no iframe, no
connection to Chatwoot. CI and e2e therefore never see the widget.
Setup
Chatwoot Cloud gates API access tokens behind paid plans (Profile Settings → Access Token says so outright on the free tier). So there are two paths:
- Path A — manual dashboard checklist: the free-tier path. ~20 minutes of clicking; every item below is a standard free feature.
- Path B —
scripts/chatwoot-setup.sh: only if you're on a paid Cloud plan or ever run self-hosted Chatwoot (where the API is free). Same outcome as Path A, idempotent, prints the website token at the end. Credentials (CHATWOOT_API_TOKENfrom Profile Settings → Access Token,CHATWOOT_ACCOUNT_IDfrom the dashboard URL) go in the gitignored repo-root.envor the shell — never in compose files or GitHub Secrets.
Path A — manual checklist (free tier)
Work top to bottom; items marked (if shown) move between plans as Chatwoot reshuffles tiers — skip without guilt if your dashboard doesn't offer them.
1. Website inbox — Settings → Inboxes → Add Inbox → Website:
- Website name
MotorPH Enterprise, URLhttps://motorphenterprise.com - Widget color
#1A3470(brand.800 — matches the site's primary buttons) - Welcome heading
Questions about MotorPH?, taglinePayroll, pricing, or careers — ask away. - After creation, the Configuration tab holds the website token — this is the value
for
CHATWOOT_WEBSITE_TOKEN(a short alphanumeric string, not a JWT).
2. Inbox settings, on the same inbox:
- Settings tab: enable the channel greeting:
Hi — you've reached MotorPH Enterprise. Ask us anything about payroll, pricing, or careers. - Pre Chat Form tab: enable; message
Leave your email so we can reply even if you close the tab.; email required, name optional. - Business Hours tab (if shown): enable, timezone Asia/Manila, Mon–Fri 09:00–18:00;
unavailable message:
We're offline right now. Leave your email and your question and we'll reply within one business day.
3. Labels — Settings → Labels → add four:
| Label | Color | Purpose |
|---|---|---|
marketing | #1A3470 | Load-bearing — the widget calls setLabel('marketing'); the slug must match exactly |
careers | #2E7D32 | Load-bearing — same, for /careers* pages |
pricing | #F4C300 | Triage: plan/price questions |
support | #5B80C2 | Triage: product help |
4. Canned responses — Settings → Canned Responses → add these (short code → content),
then trigger them by typing / in any reply box:
pricing→ Plans are per employee per month: Starter ₱99 (up to 25 employees), Growth ₱149 (up to 150 employees, more modules), Enterprise custom. Every plan starts with a 14-day free trial, no card required: https://motorphenterprise.com/pricingtrial→ You can create your own workspace in about a minute — free for 14 days, no card required: https://motorphenterprise.com/signupselfhost→ Yes — MotorPH is self-hostable. One Docker Compose file brings up the whole stack (Spring Boot backend, PostgreSQL, nginx frontend) on your own VPS. Source and deployment guide: https://github.com/jomariabejo/MotorPH-Enterprise-Payroll-System-v2forms→ MotorPH generates BIR Forms 2316, 1601-C, and the 1604-C alphalist (with its .DAT file), plus SSS, PhilHealth, and Pag-IBIG remittance reports — straight from payroll data, no re-encoding. Details: https://motorphenterprise.com/solutions/gov-formscareers→ Open roles are listed at https://motorphenterprise.com/careers — you can apply directly from the posting and your application goes straight into our recruitment pipeline.demo→ The fastest way to see the product is a trial workspace — free for 14 days, no card required: https://motorphenterprise.com/signup. The careers page at https://motorphenterprise.com/careers is also a live demo of the recruitment module.thanks→ Thanks for reaching out! This chat stays open if anything else comes up — or email [email protected].followup→ Just checking in — did that cover what you needed? Happy to dig deeper.
5. Automation (if shown) — Settings → Automation → new rule "Label pricing
questions": when a conversation is created and the message content contains
pricing / price / plan / cost → add label pricing.
6. Don't skip these three:
- Allowed domains (if shown on the inbox) —
motorphenterprise.com,stage.motorphenterprise.com,localhost:5173. - Notifications — Profile Settings → Notifications: enable email + push for conversation created. An unanswered chat is worse than no chat widget.
- The mobile app — install Chatwoot's iOS/Android app and sign in. On a one-person team this is the single highest-leverage step in this doc: it turns "reply within one business day" into "reply in five minutes", which is what actually converts.
Pro playbook on the free tier
What makes a chat channel feel professional is response behavior, not paid features:
-
Answer with
/. Type/in the reply box to insert a canned response, then edit one sentence to make it personal. Fast and accurate beats typing plan prices from memory (and getting them wrong). -
Resolve aggressively. After the visitor's question is answered, send
/thanksand hit Resolve (Cmd/Ctrl+Alt+E). A clean inbox means new chats never drown. Press?in the dashboard for all shortcuts. -
Let the labels work for you. The widget auto-labels
marketingvscareers; the automation addspricing. Once a week, open Reports → Overview and filter conversations by thepricinglabel — that list is your sales-objection backlog and a direct feed into FAQ/pricing-page copy. -
Keep business hours honest. The out-of-office + required email means off-hours visitors become leads instead of bounces. Don't set 24/7 hours you can't staff.
-
Paid features you can skip, and what covers the gap:
Paid feature Free-tier substitute Captain AI bot The pre-chat form + /-canned responses + the site's FAQ section already answer the common questionsSLA timers Mobile push notifications — you are the SLA More than 2 agents Two seats: you + one collaborator is plenty at this stage Advanced reports Weekly label-filtered review in Reports → Overview -
Two seats are free — invite a collaborator (Agents → Add) so vacations don't go unanswered.
Where the token goes
| Environment | Where | Then |
|---|---|---|
| Local dev | CHATWOOT_WEBSITE_TOKEN=... in the repo-root .env | docker compose up -d motorph_payroll_backend |
| Staging | STAGE_ENV_FILE GitHub Secret | next deploy rewrites /srv/motorph/.env.stage |
| Production | PROD_ENV_FILE GitHub Secret | next deploy rewrites /srv/motorph/.env |
The website token is a short alphanumeric string (not a JWT — an invalid token shows up
as a 404 on app.chatwoot.com/widget plus an X-Frame-Options refusal in the console).
The VPS env files are rewritten from the Secrets on every pipeline run — a hand-edit
on the VPS lasts one deploy. Keep the filled master in the password manager
(env-vars.md).
Order matters: the CSP whitelist must be deployed to an environment before its token is configured, or the browser blocks the widget with console CSP violations. Roll out stage first, verify, then production.
Verification checklist
- No token:
curl -i localhost:5173/api/public/chatwoot→ 204; zero Chatwoot requests in the network tab. - Token set (backend restarted; frontend image rebuilt if the CSP change wasn't deployed
yet): bubble appears on
/,/pricing,/careersafter first idle/interaction; absent on/login,/signup, and every authenticated page; console shows no CSP violations. - Footer "Contact Us" opens the chat panel (falls back to mailto when the widget is off).
- Incognito: pre-chat form asks for email; a sent message appears in the dashboard;
a
/careersconversation carries thecareerslabel; the greeting fires; outside business hours the out-of-office message shows instead. - Reply from the dashboard with a
/canned response — it renders in the visitor's widget. - Attachments: have an agent send an image from the dashboard. Chatwoot Cloud may serve
uploads from a storage CDN; if the image breaks with a CSP console error, add that
exact origin to
img-srcin frontend/nginx.conf.
Disabling
Blank the token (Secret or .env), redeploy/restart the backend. The endpoint goes back
to 204 and the widget vanishes — no frontend change needed.