A Slack bot that lets your team chat with PostHog analytics data. Ask questions in natural language and get answers powered by PostHog's MCP server and Claude or GPT-4.
@mentionthe bot in any Slack thread to start a conversation- Follow-up messages in subscribed threads are handled automatically (no need to re-tag)
/posthog <query>slash command for quick one-off queries- Read-only — only uses PostHog's read-only MCP tools (no writes)
- Channel allowlist — optionally restrict to specific channels
- Audit logging — structured JSON logs for every query
- Health status page at
/— shows MCP connectivity and env var status
cd posthog-slack-bot
npm installcp .env.example .envFill in .env:
| Variable | Where to find it |
|---|---|
POSTHOG_API_KEY |
PostHog → Settings → API keys → Personal API keys |
SLACK_BOT_TOKEN |
Slack app config → OAuth & Permissions → Bot User OAuth Token |
SLACK_SIGNING_SECRET |
Slack app config → Basic Information → Signing Secret |
ANTHROPIC_API_KEY |
console.anthropic.com |
OPENAI_API_KEY |
platform.openai.com (if not using Anthropic) |
- Go to api.slack.com/apps → Create New App → From manifest
- Use the manifest below, replacing
YOUR_URLwith your deployment URL:
display_information:
name: PostHog Bot
features:
bot_user:
display_name: posthog-bot
always_online: true
slash_commands:
- command: /posthog
url: YOUR_URL/api/chat-webhook
description: Query PostHog analytics
usage_hint: "<your question>"
oauth_config:
scopes:
bot:
- app_mentions:read
- chat:write
- channels:history
- groups:history
- im:history
- mpim:history
- reactions:write
- users:read
settings:
event_subscriptions:
request_url: YOUR_URL/api/chat-webhook
bot_events:
- app_mention
- message.channels
- message.groups
- message.im
- message.mpim
interactivity:
is_enabled: false
org_deploy_enabled: false
socket_mode_enabled: false
token_rotation_enabled: false# Expose localhost to the internet (for Slack webhooks)
ngrok http 3000
# Update Slack app's webhook URLs to your ngrok URL, then:
npm run devOpen http://localhost:3000 to see the health status page.
@posthog-bot how many events did we get today?
The bot subscribes to the thread — follow-up messages don't require re-tagging:
break it down by event name
Prefix any message with aside (case-insensitive) to have the bot silently ignore it — useful for side conversations in an active thread:
aside @alice can you double-check that number?
/posthog signups this week
Set ALLOWED_CHANNEL_IDS to a comma-separated list of Slack channel IDs to restrict where the bot responds:
ALLOWED_CHANNEL_IDS=C01234ABCDE,C05678FGHIJ
If unset, the bot responds in all channels it's invited to.
| Variable | Dev | Prod |
|---|---|---|
REDIS_URL unset |
In-memory (lost on restart) | — |
REDIS_URL set |
Redis | Redis |
Set REDIS_URL=redis://localhost:6379 (or your Redis provider URL) for production.
npm run dev # Start Next.js dev server
npm run typecheck # TypeScript type check
npm run lint # ESLint
npm run test # Run tests (vitest)
npm run build # Production buildDeploy to any Node.js host (Vercel, Railway, Fly.io, etc.):
- Set all required environment variables
- Point your Slack app's webhook URLs to
https://your-domain.com/api/chat-webhook - Point
/posthogslash command URL to the same endpoint - Use the health page at
https://your-domain.comfor uptime monitoring
Vercel / serverless:
REDIS_URLis required. Serverless functions are stateless — without Redis, thread subscriptions are lost between invocations and follow-up messages won't trigger responses. Upstash, Railway, and Vercel KV all work.
app/
page.tsx # Health/status page
api/chat-webhook/
route.ts # Slack webhook handler (delegates to Chat SDK)
src/
bot.ts # Chat SDK instance + event handlers
agent.ts # Model selection + system prompt loading
posthog-mcp.ts # PostHog MCP client (Bearer token auth)
system-prompt.md # Editable system prompt
The bot uses:
- Chat SDK for Slack event handling and threading
- Vercel AI SDK for LLM calls with MCP tool use
- PostHog MCP for analytics tools
- All credentials are environment variables — never committed
- PostHog MCP is read-only (write tools are not enabled)
- System prompt enforces: never expose PII, always aggregate data
- Slack webhook signatures are verified by the Chat SDK
- Audit logs (stdout JSON) capture every query for compliance