Skip to main content

Source: ocean/docs/posthog-slack-integration.md | ✏️ Edit on GitHub

PostHog Slack Integration Guide

This guide covers integrating PostHog analytics with Slack for real-time notifications about user behavior, feature adoption, and business metrics.

Prerequisites

  • PostHog account with admin access
  • Slack workspace admin access
  • Slack incoming webhooks enabled

Setting Up PostHog Webhooks

1. Create Slack Incoming Webhook

First, create a dedicated webhook for PostHog:

  1. Go to your Slack App configuration (or create a new app)
  2. Enable Incoming Webhooks
  3. Add a new webhook to workspace
  4. Select channel: #alerts-analytics
  5. Copy the webhook URL

2. Install PostHog Slack App

In PostHog:

  1. Navigate to Apps in the sidebar

  2. Search for "Slack"

  3. Click "Install"

  4. Configure the app:

    {
    "webhook_url": "YOUR_SLACK_WEBHOOK_URL",
    "include_event_properties": true,
    "format": "blocks"
    }

Creating Analytics Alerts

User Signup Notifications

Monitor new user signups in real-time:

  1. Go to Data Management → Actions

  2. Click "New Action"

  3. Configure:

    • Name: "New User Signup"
    • Match: Event name = user_signed_up
  4. Add Slack notification:

    {
    "channel": "#alerts-analytics",
    "message": "🎉 New user signup: {{person.email}}",
    "include_properties": ["plan", "referral_source", "organization_id"]
    }

Feature Adoption Alerts

Track when users adopt key features:

// Example: First time using billing feature
posthog.capture('feature_first_use', {
feature: 'billing_dashboard',
user_id: user.id,
organization_id: org.id,
})

Configure PostHog Action:

{
"name": "Feature Adoption - Billing",
"match": {
"event": "feature_first_use",
"properties": {
"feature": "billing_dashboard"
}
},
"slack": {
"message": "💳 Organization {{organization_id}} accessed billing for the first time"
}
}

High-Value User Actions

Monitor important business events:

Action: 'High-Value Cart Created'
Conditions:
- Event: cart_updated
- Property: cart_value > 1000
Slack Message: '💰 High-value cart: ${{cart_value}} by {{person.email}}'
Channel: '#alerts-analytics'

Advanced Analytics Alerts

Conversion Funnel Alerts

Monitor funnel drop-offs:

  1. Create a Funnel in PostHog:

    • Step 1: page_view (landing page)
    • Step 2: signup_started
    • Step 3: user_signed_up
    • Step 4: onboarding_completed
  2. Set up alerts for significant drops:

    // PostHog Webhook configuration
    {
    "funnel_id": "YOUR_FUNNEL_ID",
    "alert_threshold": 0.7, // Alert if conversion < 70%
    "check_interval": "hourly",
    "slack_notification": {
    "channel": "#alerts-analytics",
    "message": "⚠️ Funnel conversion dropped below 70%: {{funnel_name}}"
    }
    }

Cohort-Based Alerts

Alert on specific user segments:

// Define cohort: "High-Value Users"
{
"cohort_definition": {
"name": "High-Value Users",
"filters": [
{ "property": "total_spent", "operator": ">", "value": 500 },
{ "property": "subscription_status", "value": "active" }
]
},
"alerts": {
"churn_risk": {
"condition": "no_activity_days > 14",
"slack": {
"message": "🚨 High-value user inactive: {{person.email}} ({{no_activity_days}} days)"
}
}
}
}

Feature Flag Monitoring

Alert on feature flag issues:

// Monitor feature flag performance
posthog.capture('feature_flag_evaluation', {
flag_key: 'new_checkout_flow',
variant: flag_variant,
error_occurred: error !== null,
load_time_ms: loadTime
})

// PostHog Action
{
"name": "Feature Flag Error Rate",
"conditions": {
"event": "feature_flag_evaluation",
"properties": {
"error_occurred": true
}
},
"aggregation": {
"type": "percentage",
"threshold": 5, // Alert if > 5% error rate
"window": "5m"
},
"slack": {
"message": "🚩 Feature flag '{{flag_key}}' has high error rate: {{error_percentage}}%"
}
}

Setting Up Business Metrics Alerts

Revenue Tracking

// Track revenue events
posthog.capture('purchase_completed', {
revenue: 99.99,
currency: 'USD',
product_id: 'pro_plan',
mrr_impact: 99.99
})

// Daily revenue summary
{
"schedule": "daily_9am",
"metric": "sum",
"event": "purchase_completed",
"property": "revenue",
"slack": {
"channel": "#alerts-analytics",
"message": "📊 Daily Revenue: ${{total_revenue}} ({{transaction_count}} transactions)"
}
}

User Engagement Metrics

// Weekly Active Users alert
{
"name": "Weekly Active Users Report",
"schedule": "weekly_monday_9am",
"query": {
"event": "$pageview",
"aggregation": "unique_users",
"date_range": "last_7_days"
},
"comparison": "previous_week",
"slack": {
"message": "📈 WAU: {{current_value}} ({{change_percentage}}% from last week)",
"include_chart": true
}
}

Retention Alerts

// Alert on retention drop
{
"name": "Retention Drop Alert",
"metric": "retention_rate",
"cohort": "new_users_last_month",
"threshold": {
"day_1": 80,
"day_7": 50,
"day_30": 30
},
"slack": {
"message": "📉 Retention below threshold: Day {{retention_day}} = {{retention_rate}}%"
}
}

Custom PostHog to Slack Integration

Create a more sophisticated integration using PostHog webhooks:

// supabase/functions/posthog-slack-webhook/index.ts
import { serve } from 'https://deno.land/std@0.208.0/http/server.ts'

interface PostHogEvent {
event: string
properties: Record<string, any>
timestamp: string
distinct_id: string
person?: {
email?: string
name?: string
}
}

const SLACK_WEBHOOKS = {
analytics: Deno.env.get('SLACK_WEBHOOK_ANALYTICS')!,
alerts: Deno.env.get('SLACK_WEBHOOK_ALERTS')!,
}

// Event routing configuration
const EVENT_ROUTES = {
// High-priority events
critical: {
events: ['payment_failed', 'subscription_cancelled', 'high_value_churn'],
webhook: SLACK_WEBHOOKS.alerts,
format: 'detailed',
},

// Analytics events
analytics: {
events: ['user_signed_up', 'feature_adopted', 'milestone_reached'],
webhook: SLACK_WEBHOOKS.analytics,
format: 'summary',
},
}

serve(async (req) => {
try {
const { event, properties, person } = (await req.json()) as PostHogEvent

// Determine routing
const route = Object.entries(EVENT_ROUTES).find(([_, config]) =>
config.events.includes(event)
)?.[1]

if (!route) {
return new Response('Event not configured for Slack', { status: 200 })
}

// Format message based on event type
const message = formatSlackMessage(event, properties, person, route.format)

// Send to Slack
await fetch(route.webhook, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(message),
})

return new Response('OK', { status: 200 })
} catch (error) {
console.error('PostHog webhook error:', error)
return new Response('Error', { status: 500 })
}
})

function formatSlackMessage(
event: string,
properties: Record<string, any>,
person: any,
format: string
) {
const emoji = getEmojiForEvent(event)
const userName = person?.email || person?.name || properties.distinct_id || 'Unknown User'

if (format === 'detailed') {
return {
text: `${emoji} ${event.replace(/_/g, ' ').toUpperCase()}`,
blocks: [
{
type: 'header',
text: {
type: 'plain_text',
text: `${emoji} ${formatEventName(event)}`,
},
},
{
type: 'section',
fields: [
{
type: 'mrkdwn',
text: `*User:*\n${userName}`,
},
{
type: 'mrkdwn',
text: `*Time:*\n${new Date().toLocaleString()}`,
},
...Object.entries(properties)
.filter(([key]) => !['distinct_id', 'token'].includes(key))
.slice(0, 6)
.map(([key, value]) => ({
type: 'mrkdwn',
text: `*${formatPropertyName(key)}:*\n${value}`,
})),
],
},
],
}
}

// Simple format
return {
text: `${emoji} ${formatEventName(event)}: ${userName}`,
attachments: [
{
color: getColorForEvent(event),
fields: Object.entries(properties)
.filter(([key]) => ['revenue', 'plan', 'feature'].includes(key))
.map(([key, value]) => ({
title: formatPropertyName(key),
value: String(value),
short: true,
})),
},
],
}
}

function getEmojiForEvent(event: string): string {
const emojiMap: Record<string, string> = {
user_signed_up: '🎉',
payment_failed: '💔',
subscription_cancelled: '😢',
feature_adopted: '✨',
milestone_reached: '🏆',
high_value_churn: '🚨',
experiment_started: '🧪',
retention_improved: '📈',
}
return emojiMap[event] || '📊'
}

function formatEventName(event: string): string {
return event
.split('_')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ')
}

function formatPropertyName(prop: string): string {
return prop.replace(/_/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase())
}

function getColorForEvent(event: string): string {
if (event.includes('failed') || event.includes('error')) return 'danger'
if (event.includes('warning') || event.includes('churn')) return 'warning'
return 'good'
}

Slack App Commands

Create interactive Slack commands for PostHog data:

Slash Commands

Configure in your Slack app:

  1. /analytics [metric] - Get quick metrics

    /analytics dau
    > Daily Active Users: 1,234 (+5.2% from yesterday)
  2. /feature-flag [flag_name] - Check feature flag status

    /feature-flag new_checkout
    > Feature Flag "new_checkout": 50% rollout, 234 users active
  3. /user-lookup [email] - Get user analytics

    /user-lookup user@example.com
    > User: Premium plan, last seen 2h ago, 45 events today

Implementation

// Handle Slack slash commands
export async function handleSlackCommand(command: string, text: string) {
switch (command) {
case '/analytics':
return await getQuickMetric(text)

case '/feature-flag':
return await getFeatureFlagStatus(text)

case '/user-lookup':
return await getUserAnalytics(text)

default:
return { text: 'Unknown command' }
}
}

Best Practices

1. Alert Prioritization

  • #alerts-critical: Payment failures, service outages
  • #alerts-analytics: User milestones, daily summaries
  • #alerts-experiments: A/B test results, feature flag updates

2. Message Formatting

Keep messages concise but informative:

Good: "🎉 New signup: john@example.com (Pro plan, Google referral)"
Bad: "Event user_signed_up occurred with properties..."

3. Rate Limiting

Prevent alert fatigue:

// Batch similar events
const eventBatcher = new Map()

function batchEvent(event: string, data: any) {
const batch = eventBatcher.get(event) || []
batch.push(data)
eventBatcher.set(event, batch)

// Send batched notification every 5 minutes
if (batch.length === 1) {
setTimeout(() => sendBatchedNotification(event), 5 * 60 * 1000)
}
}

4. Actionable Insights

Include next steps in alerts:

"📉 Signup conversion dropped to 45% (-10% from last week)
→ Check recent deployments
→ Review A/B test results
→ Analyze drop-off points"

Testing

1. Test Individual Events

// Test from your app
posthog.capture('test_slack_integration', {
test: true,
message: 'Testing PostHog to Slack integration',
timestamp: new Date().toISOString(),
})

2. Test Batch Processing

// Send multiple events to test batching
for (let i = 0; i < 10; i++) {
posthog.capture('batch_test_event', {
index: i,
batch_id: 'test_batch_001',
})
}

3. Test Error Scenarios

// Test error handling
posthog.capture('test_error_event', {
should_fail: true,
error_type: 'webhook_failure',
})

Troubleshooting

Common Issues

  1. "Events not appearing in Slack"

    • Verify webhook URL is correct
    • Check PostHog action configuration
    • Review PostHog logs for errors
  2. "Too many notifications"

    • Implement batching
    • Add event filters
    • Use sampling for high-volume events
  3. "Missing event properties"

    • Ensure properties are captured correctly
    • Check property filters in actions
    • Verify webhook payload format

Next Steps

  1. Set up custom dashboards in Slack using Block Kit
  2. Create scheduled reports for key metrics
  3. Implement two-way sync for user properties
  4. Build Slack bot for interactive analytics queries