--- name: posthog-core-workflow-a description: 'Implement PostHog product analytics: event capture, user identification, group analytics, and property management using posthog-js and posthog-node. Trigger: "posthog analytics", "capture events", "track users posthog", "posthog identify", "posthog group analytics", "product analytics". ' allowed-tools: Read, Write, Edit, Bash(npm:*), Grep version: 1.12.0 license: MIT author: Jeremy Longshore tags: - saas - posthog - workflow - analytics compatibility: Designed for Claude Code, also compatible with Codex and OpenClaw --- # PostHog Core Workflow A — Product Analytics ## Overview Primary PostHog workflow covering event capture, user identification, group analytics, and person properties. This is the foundation for all PostHog analytics: capturing what users do, linking events to identified users, and grouping users by company/team for B2B analytics. ## Prerequisites - Completed `posthog-install-auth` setup - `posthog-js` (browser) and/or `posthog-node` (server) installed - Project API key (`phc_...`) configured ## Instructions ### Step 1: Define Event Taxonomy ```typescript // src/analytics/events.ts // Define all events as typed constants for consistency export const EVENTS = { // User lifecycle USER_SIGNED_UP: 'user_signed_up', USER_LOGGED_IN: 'user_logged_in', USER_ONBOARDING_COMPLETED: 'user_onboarding_completed', // Core product actions FEATURE_USED: 'feature_used', ITEM_CREATED: 'item_created', ITEM_UPDATED: 'item_updated', ITEM_DELETED: 'item_deleted', SEARCH_PERFORMED: 'search_performed', EXPORT_COMPLETED: 'export_completed', // Revenue events SUBSCRIPTION_STARTED: 'subscription_started', SUBSCRIPTION_UPGRADED: 'subscription_upgraded', SUBSCRIPTION_CANCELED: 'subscription_canceled', PAYMENT_COMPLETED: 'payment_completed', } as const; // Standard property schema for consistency across events interface BaseProperties { source?: 'web' | 'mobile' | 'api' | 'webhook'; plan_tier?: 'free' | 'pro' | 'enterprise'; duration_ms?: number; } ``` ### Step 2: Capture Events (Browser) ```typescript import posthog from 'posthog-js'; import { EVENTS } from './events'; // Custom event with properties posthog.capture(EVENTS.ITEM_CREATED, { item_type: 'document', source: 'web', plan_tier: 'pro', }); // Timed event (measure duration) const start = performance.now(); await doExpensiveOperation(); posthog.capture(EVENTS.EXPORT_COMPLETED, { format: 'csv', row_count: 1500, duration_ms: Math.round(performance.now() - start), }); // Pageview with custom properties (if capture_pageview: false) posthog.capture('$pageview', { page_title: document.title, referrer: document.referrer, }); ``` ### Step 3: Identify Users and Set Properties ```typescript // After user logs in — links anonymous events to this user posthog.identify('user-456', { // $set properties (persist, overwrite on change) email: 'jane@acme.com', name: 'Jane Smith', plan: 'enterprise', signup_date: '2025-06-15', }); // Update properties later without re-identifying posthog.people.set({ last_active: new Date().toISOString(), total_items: 42, }); // Set properties only if not already set ($set_once) posthog.people.set_once({ first_seen: new Date().toISOString(), original_referrer: document.referrer, }); // Unset properties posthog.people.unset(['deprecated_field']); // Reset on logout (clears distinct_id, starts new anonymous session) posthog.reset(); ``` ### Step 4: Group Analytics (B2B Company Tracking) ```typescript // Associate user with a company group posthog.group('company', 'company-789', { name: 'Acme Corp', industry: 'SaaS', plan: 'enterprise', employee_count: 150, arr: 250000, }); // Events now automatically include company context posthog.capture(EVENTS.FEATURE_USED, { feature_name: 'bulk-export', }); // This event is attributed to both user-456 AND company-789 // Multiple group types posthog.group('team', 'team-alpha', { name: 'Alpha Team' }); ``` ### Step 5: Server-Side Event Capture (posthog-node) ```typescript import { PostHog } from 'posthog-node'; const posthog = new PostHog(process.env.NEXT_PUBLIC_POSTHOG_KEY!, { host: 'https://us.i.posthog.com', }); // Server-side capture (e.g., in API route or webhook handler) function trackServerEvent(userId: string, event: string, properties?: Record) { posthog.capture({ distinctId: userId, event, properties: { ...properties, source: 'api', }, }); } // Identify with server-side properties posthog.identify({ distinctId: 'user-456', properties: { subscription_status: 'active', mrr: 99, }, }); // Group identify from server posthog.groupIdentify({ groupType: 'company', groupKey: 'company-789', properties: { plan: 'enterprise', total_seats: 50, }, }); // CRITICAL: Flush in serverless/edge functions await posthog.flush(); ``` ### Step 6: Create Annotations for Context ```bash set -euo pipefail # Mark a deployment or product change in PostHog curl -X POST "https://app.posthog.com/api/projects/$POSTHOG_PROJECT_ID/annotations/" \ -H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "content": "v2.5.0 deployed — new checkout flow", "date_marker": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'", "scope": "project" }' ``` ## Error Handling | Error | Cause | Solution | |-------|-------|----------| | Events not appearing | `posthog.init` not called | Ensure init runs before any capture | | Anonymous/identified split | Different `distinct_id` across platforms | Use consistent user ID from your auth system | | Group data missing | `posthog.group()` not called | Call `group()` before capture for group attribution | | Server events lost | No `flush()` in serverless | Always call `await posthog.flush()` before response | | Properties not updating | Using `$set_once` for mutable data | Use `posthog.people.set()` for values that change | ## Output - Typed event taxonomy for consistent tracking - Browser event capture with user identification - B2B group analytics linking users to companies - Server-side event capture with proper flushing - Annotations marking deployments and product changes ## Resources - [Capture Events](https://posthog.com/docs/product-analytics/capture-events) - [Identifying Users](https://posthog.com/docs/product-analytics/identify) - [Group Analytics](https://posthog.com/docs/product-analytics/group-analytics) - [Annotations API](https://posthog.com/docs/api/annotations) ## Next Steps For feature flags and experiments, see `posthog-core-workflow-b`.