Governed analytics Q&A over WhatsApp using Luzmo /AIPrompt (agent: analyst), with per-persona embed row filters and optional chart PNG replies.



A WhatsApp bot that answers dataset questions via Luzmo’s hosted /AIPrompt API (agent: analyst), replies with WhatsApp-formatted text, and uploads chart PNGs when Luzmo returns a visualization.
Synthetic/demo data and a phone allowlist only — not production identity linking.
WhatsApp Business Solution Terms restrict AI providers when AI is the primary functionality (Meta decides). Prefer demo recipients with EEA or Brazil country codes, or obtain written legal/Meta clearance for other regions (e.g. UK +44). Do not treat a non-EEA demo as policy-cleared without that clearance.
WhatsApp Business Solution Data must not train shared AI models. Confirm Luzmo’s Trust Center posture before any customer-data use. Keep the bot dataset-specific; refuse clearly off-topic / general-purpose prompts.
node:sqlite)npm install
cp .env.example .env
cp config/allowlist.example.json config/allowlist.json
# Edit .env and allowlist.json with real values / demo phones
.env and config/allowlist.json are gitignored — never commit secrets or real phone numbers.
| Variable | Purpose |
|---|---|
PORT |
HTTP port (default 3000) |
PUBLIC_BASE_URL |
Tunnel HTTPS origin, no path (e.g. https://abc.ngrok-free.dev) |
WHATSAPP_ACCESS_TOKEN |
Prefer a System User token (not 24h temp) |
WHATSAPP_PHONE_NUMBER_ID |
Cloud API phone number ID |
WHATSAPP_VERIFY_TOKEN |
Webhook verify token you choose |
WHATSAPP_APP_SECRET |
Meta app secret (signature verification) |
WHATSAPP_GRAPH_VERSION |
e.g. v25.0 |
LUZMO_API_KEY / LUZMO_API_TOKEN |
Owner credentials — mint embeds only |
LUZMO_HOST |
API host (default https://api.luzmo.com) |
LUZMO_DATASET_ID |
Shared dataset UUID |
LUZMO_TENANT_COLUMN_ID |
Column id used in embed filters |
LUZMO_THEME_ID |
Optional theme UUID or built-in id. Empty/unset = no theme |
LUZMO_TIMEZONE_ID |
IANA timezone for /AIPrompt (default UTC) |
ALLOWLIST_PATH |
Path to allowlist JSON (default ./config/allowlist.json) |
SQLITE_PATH |
SQLite file for dedupe + conversations |
CONVERSATION_IDLE_MINUTES |
Luzmo conversation idle TTL (default 60) |
AIPROMPT_TIMEOUT_MS |
/AIPrompt timeout (default 120000) |
WHATSAPP_WABA_ID is not required at runtime.
ngrok http 3000 or cloudflared tunnel --url http://localhost:3000.PUBLIC_BASE_URL to the tunnel HTTPS origin.https://<tunnel>/webhooks/whatsapp.WHATSAPP_VERIFY_TOKEN.messages.npm run verify-config
npm run dev
# or
npm run build && npm start
Health: GET /healthz
npm run smoke:meta -- 32470000001
npm run smoke:luzmo -- "What is total revenue?"
npm run themes:list
What is total revenue? Show me a chart.Break that down by month./reset, then a pronoun-only follow-up loses context.WhatsApp -> signed webhook -> persist/dedupe by wamid -> 200
-> per-identity serial queue
-> allowlist persona
-> mint embed (dataset + tenant filter)
-> /aiprompt SSE (buffer to completion)
-> WhatsApp text (+ PNG via Media API using SAME embed token)
/AIPrompt (never owner credentials).LUZMO_THEME_ID is applied only on PNG export.node:sqlite (experimental flag on npm start / npm run dev).npm test
See createAIPrompt.md. Sibling pattern: the Luzmo Analyst Slackbot in this repository.
Take your data products to the next level with Luzmo.