Telemetry Standards
supabase/supabase
PostHog event tracking standards for Supabase Studio. An agent skill from supabase/supabase.
PostHog analytics, event tracking, feature flags, dashboards
$ npx skills add alinaqi/maggy --skill posthog-analytics -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install alinaqi/maggy posthog-analytics --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/alinaqi/maggy.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/posthog-analytics .claude/skills/posthog-analytics && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "posthog-analytics" agent skill from https://github.com/alinaqi/maggy/tree/main/skills/posthog-analytics into .claude/skills/posthog-analytics/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "posthog-analytics", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/alinaqi/maggy/tree/main/skills/posthog-analyticsType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add alinaqi/maggy --skill posthog-analytics -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install alinaqi/maggy posthog-analytics --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/alinaqi/maggy.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/posthog-analytics .agents/skills/posthog-analytics && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "posthog-analytics" agent skill from https://github.com/alinaqi/maggy/tree/main/skills/posthog-analytics into .agents/skills/posthog-analytics/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "posthog-analytics", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add alinaqi/maggy --skill posthog-analytics -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install alinaqi/maggy posthog-analytics --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/alinaqi/maggy.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/posthog-analytics .cursor/skills/posthog-analytics && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "posthog-analytics" agent skill from https://github.com/alinaqi/maggy/tree/main/skills/posthog-analytics into .cursor/skills/posthog-analytics/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "posthog-analytics", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/alinaqi/maggy.git --path skills/posthog-analytics--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add alinaqi/maggy --skill posthog-analytics -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install alinaqi/maggy posthog-analytics --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/alinaqi/maggy.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/posthog-analytics .gemini/skills/posthog-analytics && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "posthog-analytics" agent skill from https://github.com/alinaqi/maggy/tree/main/skills/posthog-analytics into .gemini/skills/posthog-analytics/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "posthog-analytics", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install alinaqi/maggy posthog-analyticsInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add alinaqi/maggy --skill posthog-analytics -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/alinaqi/maggy.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/posthog-analytics .github/skills/posthog-analytics && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "posthog-analytics" agent skill from https://github.com/alinaqi/maggy/tree/main/skills/posthog-analytics into .github/skills/posthog-analytics/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "posthog-analytics", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add alinaqi/maggy --skill posthog-analytics -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install alinaqi/maggy posthog-analytics --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/alinaqi/maggy.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/posthog-analytics .opencode/skills/posthog-analytics && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "posthog-analytics" agent skill from https://github.com/alinaqi/maggy/tree/main/skills/posthog-analytics into .opencode/skills/posthog-analytics/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "posthog-analytics", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
posthog-analyticsPostHog analytics, event tracking, feature flags, dashboards
Posthog Analytics is an agent skill from alinaqi/maggy. PostHog analytics, event tracking, feature flags, dashboards
Its SKILL.md is about 5.2k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Data & Analytics, covering Product analytics. It works with PostHog. The repository describes itself as: What started as an opinionated Claude Code setup kit is now an autonomous AI engineering command center. The licence is MIT.
Read from SKILL.md and the folder at commit 72a456e. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
npmpipFrom the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
us.i.posthog.comAlso links to:
posthog.comFrom URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
POSTHOG_API_KEYNEXT_PUBLIC_POSTHOG_KEYVITE_POSTHOG_KEYFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Posthog Analytics loads about 5.2k tokens when it runs. Until then it costs about 20 tokens; SKILL.md has 192 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check noted patterns worth knowing about, such as sudo or a known installer.
# .env.local (Next.js) - SAFE: These are meant to be public# .env (Backend) - Keep privateAutomated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from alinaqi/maggy at commit 72a456e, republished under its MIT licence (© alinaqi). 192 words, ~5,231 tokens.
.claude/skills/posthog-analytics/SKILL.md (or your agent's skills folder).For implementing product analytics with PostHog - event tracking, user identification, feature flags, and project-specific dashboards.
Sources: PostHog Docs | Product Analytics | Feature Flags
Measure what matters, not everything.
Analytics should answer specific questions:
Don't track everything. Track what informs decisions.
npm install posthog-js// lib/posthog.ts
import posthog from 'posthog-js';
export function initPostHog() {
if (typeof window !== 'undefined' && !posthog.__loaded) {
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST || 'https://us.i.posthog.com',
person_profiles: 'identified_only', // Only create profiles for identified users
capture_pageview: false, // We'll handle this manually for SPA
capture_pageleave: true,
loaded: (posthog) => {
if (process.env.NODE_ENV === 'development') {
posthog.debug();
}
},
});
}
return posthog;
}
export { posthog };// app/providers.tsx
'use client';
import { useEffect } from 'react';
import { usePathname, useSearchParams } from 'next/navigation';
import { initPostHog, posthog } from '@/lib/posthog';
export function PostHogProvider({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
initPostHog();
}, []);
// Track pageviews
useEffect(() => {
if (pathname) {
let url = window.origin + pathname;
if (searchParams.toString()) {
url += `?${searchParams.toString()}`;
}
posthog.capture('$pageview', { $current_url: url });
}
}, [pathname, searchParams]);
return <>{children}</>;
}// app/layout.tsx
import { PostHogProvider } from './providers';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<PostHogProvider>
{children}
</PostHogProvider>
</body>
</html>
);
}// src/posthog.ts
import posthog from 'posthog-js';
posthog.init(import.meta.env.VITE_POSTHOG_KEY, {
api_host: import.meta.env.VITE_POSTHOG_HOST || 'https://us.i.posthog.com',
person_profiles: 'identified_only',
});
export { posthog };// src/main.tsx
import { PostHogProvider } from 'posthog-js/react';
import { posthog } from './posthog';
ReactDOM.createRoot(document.getElementById('root')!).render(
<PostHogProvider client={posthog}>
<App />
</PostHogProvider>
);pip install posthog# analytics/posthog_client.py
import posthog
from functools import lru_cache
@lru_cache()
def get_posthog():
posthog.project_api_key = os.environ["POSTHOG_API_KEY"]
posthog.host = os.environ.get("POSTHOG_HOST", "https://us.i.posthog.com")
posthog.debug = os.environ.get("ENV") == "development"
return posthog
# Usage
def track_event(user_id: str, event: str, properties: dict = None):
ph = get_posthog()
ph.capture(
distinct_id=user_id,
event=event,
properties=properties or {}
)
def identify_user(user_id: str, properties: dict):
ph = get_posthog()
ph.identify(user_id, properties)npm install posthog-node// lib/posthog.ts
import { PostHog } from 'posthog-node';
const posthog = new PostHog(process.env.POSTHOG_API_KEY!, {
host: process.env.POSTHOG_HOST || 'https://us.i.posthog.com',
});
// Flush on shutdown
process.on('SIGTERM', () => posthog.shutdown());
export { posthog };
// Usage
export function trackEvent(userId: string, event: string, properties?: Record<string, any>) {
posthog.capture({
distinctId: userId,
event,
properties,
});
}
export function identifyUser(userId: string, properties: Record<string, any>) {
posthog.identify({
distinctId: userId,
properties,
});
}# .env.local (Next.js) - SAFE: These are meant to be public
NEXT_PUBLIC_POSTHOG_KEY=phc_xxxxxxxxxxxxxxxxxxxx
NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
# .env (Backend) - Keep private
POSTHOG_API_KEY=phc_xxxxxxxxxxxxxxxxxxxx
POSTHOG_HOST=https://us.i.posthog.comAdd to credentials.md patterns:
'POSTHOG_API_KEY': r'phc_[A-Za-z0-9]+',// Identify on signup
async function handleSignup(email: string, name: string) {
const user = await createUser(email, name);
posthog.identify(user.id, {
email: user.email,
name: user.name,
created_at: user.createdAt,
plan: 'free',
});
posthog.capture('user_signed_up', {
signup_method: 'email',
});
}
// Identify on login
async function handleLogin(email: string) {
const user = await authenticateUser(email);
posthog.identify(user.id, {
email: user.email,
name: user.name,
plan: user.plan,
last_login: new Date().toISOString(),
});
posthog.capture('user_logged_in');
}
// Reset on logout
function handleLogout() {
posthog.capture('user_logged_out');
posthog.reset(); // Clears identity
}// Standard properties to track
interface UserProperties {
// Identity
email: string;
name: string;
// Lifecycle
created_at: string;
plan: 'free' | 'pro' | 'enterprise';
// Engagement
onboarding_completed: boolean;
feature_count: number;
// Business
company_name?: string;
company_size?: string;
industry?: string;
}
// Update properties when they change
posthog.capture('$set', {
$set: { plan: 'pro' },
});// Format: [object]_[action]
// Use snake_case, past tense for actions
// ✅ Good event names
'user_signed_up'
'feature_created'
'subscription_upgraded'
'onboarding_completed'
'invite_sent'
'file_uploaded'
'search_performed'
'checkout_started'
'payment_completed'
// ❌ Bad event names
'click' // Too vague
'ButtonClick' // Not snake_case
'user signup' // Spaces
'creatingFeature' // Not past tense// === AUTHENTICATION ===
posthog.capture('user_signed_up', {
signup_method: 'google' | 'email' | 'github',
referral_source: 'organic' | 'paid' | 'referral',
});
posthog.capture('user_logged_in', {
login_method: 'google' | 'email' | 'magic_link',
});
posthog.capture('user_logged_out');
posthog.capture('password_reset_requested');
// === ONBOARDING ===
posthog.capture('onboarding_started');
posthog.capture('onboarding_step_completed', {
step_name: 'profile' | 'preferences' | 'first_action',
step_number: 1,
total_steps: 3,
});
posthog.capture('onboarding_completed', {
duration_seconds: 120,
steps_skipped: 0,
});
posthog.capture('onboarding_skipped', {
skipped_at_step: 2,
});
// === FEATURE USAGE ===
posthog.capture('feature_used', {
feature_name: 'export' | 'share' | 'duplicate',
context: 'dashboard' | 'editor',
});
posthog.capture('[resource]_created', {
resource_type: 'project' | 'document' | 'team',
// Resource-specific properties
});
posthog.capture('[resource]_updated', {
resource_type: 'project',
fields_changed: ['name', 'description'],
});
posthog.capture('[resource]_deleted', {
resource_type: 'project',
});
// === BILLING ===
posthog.capture('pricing_page_viewed', {
current_plan: 'free',
});
posthog.capture('checkout_started', {
plan: 'pro',
billing_period: 'monthly' | 'annual',
price: 29,
});
posthog.capture('subscription_upgraded', {
from_plan: 'free',
to_plan: 'pro',
mrr_change: 29,
});
posthog.capture('subscription_downgraded', {
from_plan: 'pro',
to_plan: 'free',
reason: 'too_expensive' | 'missing_features' | 'not_using',
});
posthog.capture('subscription_cancelled', {
plan: 'pro',
reason: 'string',
feedback: 'string',
});
// === ERRORS ===
posthog.capture('error_occurred', {
error_type: 'api_error' | 'validation_error' | 'network_error',
error_message: 'string',
error_code: 'string',
page: '/dashboard',
});// hooks/useTrack.ts
import { useCallback } from 'react';
import { posthog } from '@/lib/posthog';
export function useTrack() {
const track = useCallback((event: string, properties?: Record<string, any>) => {
posthog.capture(event, {
...properties,
timestamp: new Date().toISOString(),
});
}, []);
return { track };
}
// Usage
function CreateProjectButton() {
const { track } = useTrack();
const handleCreate = async () => {
track('project_creation_started');
try {
const project = await createProject();
track('project_created', {
project_id: project.id,
template_used: project.template,
});
} catch (error) {
track('project_creation_failed', {
error_message: error.message,
});
}
};
return <button onClick={handleCreate}>Create Project</button>;
}// Check feature flag (client-side)
import { useFeatureFlagEnabled } from 'posthog-js/react';
function NewFeature() {
const showNewUI = useFeatureFlagEnabled('new-dashboard-ui');
if (showNewUI) {
return <NewDashboard />;
}
return <OldDashboard />;
}
// With payload
import { useFeatureFlagPayload } from 'posthog-js/react';
function PricingPage() {
const pricingConfig = useFeatureFlagPayload('pricing-experiment');
// pricingConfig = { price: 29, showAnnual: true }
return <Pricing config={pricingConfig} />;
}// app/dashboard/page.tsx
import { PostHog } from 'posthog-node';
import { cookies } from 'next/headers';
async function getFeatureFlags(userId: string) {
const posthog = new PostHog(process.env.POSTHOG_API_KEY!);
const flags = await posthog.getAllFlags(userId);
await posthog.shutdown();
return flags;
}
export default async function Dashboard() {
const cookieStore = cookies();
const userId = cookieStore.get('user_id')?.value;
const flags = await getFeatureFlags(userId);
return (
<div>
{flags['new-dashboard'] && <NewFeature />}
</div>
);
}// Track experiment exposure
function ExperimentComponent() {
const variant = useFeatureFlagEnabled('checkout-experiment');
useEffect(() => {
posthog.capture('experiment_viewed', {
experiment: 'checkout-experiment',
variant: variant ? 'test' : 'control',
});
}, [variant]);
return variant ? <NewCheckout /> : <OldCheckout />;
}## Essential SaaS Dashboards
### 1. Acquisition Dashboard
**Questions answered:** Where do users come from? What converts?
Insights to create:
- [ ] Signups by source (daily/weekly trend)
- [ ] Signup conversion rate by landing page
- [ ] Time from first visit to signup
- [ ] Signup funnel: Visit → Signup Page → Form Start → Complete
### 2. Activation Dashboard
**Questions answered:** Are new users getting value?
Insights to create:
- [ ] Onboarding completion rate
- [ ] Time to first key action
- [ ] Activation rate (% reaching "aha moment" in first 7 days)
- [ ] Drop-off by onboarding step
- [ ] Feature adoption in first session
### 3. Engagement Dashboard
**Questions answered:** How are users using the product?
Insights to create:
- [ ] DAU/WAU/MAU trends
- [ ] Feature usage heatmap
- [ ] Session duration distribution
- [ ] Actions per session
- [ ] Power users vs casual users
### 4. Retention Dashboard
**Questions answered:** Are users coming back?
Insights to create:
- [ ] Retention cohorts (D1, D7, D30)
- [ ] Churn rate by plan
- [ ] Reactivation rate
- [ ] Last action before churn
- [ ] Features correlated with retention
### 5. Revenue Dashboard
**Questions answered:** Is the business growing?
Insights to create:
- [ ] MRR trend
- [ ] Upgrades vs downgrades
- [ ] Trial to paid conversion
- [ ] Revenue by plan
- [ ] LTV by acquisition source## Essential E-Commerce Dashboards
### 1. Conversion Funnel
Insights to create:
- [ ] Full funnel: Browse → PDP → Add to Cart → Checkout → Purchase
- [ ] Cart abandonment rate
- [ ] Checkout drop-off by step
- [ ] Payment failure rate
### 2. Product Performance
Insights to create:
- [ ] Product views → purchases (by product)
- [ ] Add to cart rate by category
- [ ] Search → purchase correlation
- [ ] Cross-sell effectiveness
### 3. Customer Dashboard
Insights to create:
- [ ] Repeat purchase rate
- [ ] Average order value trend
- [ ] Customer lifetime value
- [ ] Purchase frequency distribution## Essential Content Dashboards
### 1. Consumption Dashboard
Insights to create:
- [ ] Content views by type
- [ ] Read/watch completion rate
- [ ] Time on content
- [ ] Scroll depth distribution
### 2. Engagement Dashboard
Insights to create:
- [ ] Shares by content
- [ ] Comments per article
- [ ] Save/bookmark rate
- [ ] Return visits to same content
### 3. Growth Dashboard
Insights to create:
- [ ] New vs returning visitors
- [ ] Email signup rate
- [ ] Referral traffic sources## Essential AI App Dashboards
### 1. Usage Dashboard
Insights to create:
- [ ] Queries per user per day
- [ ] Token usage distribution
- [ ] Response time p50/p95
- [ ] Error rate by query type
### 2. Quality Dashboard
Insights to create:
- [ ] User feedback (thumbs up/down)
- [ ] Regeneration rate (user asked for new response)
- [ ] Edit rate (user modified AI output)
- [ ] Follow-up query rate
### 3. Cost Dashboard
Insights to create:
- [ ] Token cost per user
- [ ] Cost by model
- [ ] Cost by feature
- [ ] Efficiency trends (value/cost)When setting up analytics for a project:
1. First, check existing dashboards:
- Use `dashboards-get-all` to list current dashboards
2. Create project-appropriate dashboards:
- Use `dashboard-create` with descriptive name
3. Create insights for each dashboard:
- Use `query-run` to test queries
- Use `insight-create-from-query` to save
- Use `add-insight-to-dashboard` to organize
4. Set up key funnels:
- Signup funnel
- Onboarding funnel
- Purchase/conversion funnel// Example: Creating SaaS dashboards via MCP
// 1. Create dashboard
const dashboard = await mcp_posthog_dashboard_create({
name: "Activation Metrics",
description: "Track new user activation and onboarding",
tags: ["saas", "activation"],
});
// 2. Create insights
const signupFunnel = await mcp_posthog_query_run({
query: {
kind: "InsightVizNode",
source: {
kind: "FunnelsQuery",
series: [
{ kind: "EventsNode", event: "user_signed_up", name: "Signed Up" },
{ kind: "EventsNode", event: "onboarding_started", name: "Started Onboarding" },
{ kind: "EventsNode", event: "onboarding_completed", name: "Completed Onboarding" },
{ kind: "EventsNode", event: "first_value_action", name: "First Value" },
],
dateRange: { date_from: "-30d" },
},
},
});
// 3. Save and add to dashboard
const insight = await mcp_posthog_insight_create_from_query({
name: "Signup to Activation Funnel",
query: signupFunnel.query,
favorited: true,
});
await mcp_posthog_add_insight_to_dashboard({
insightId: insight.id,
dashboardId: dashboard.id,
});// Opt-out handling
export function handleCookieConsent(consent: boolean) {
if (consent) {
posthog.opt_in_capturing();
} else {
posthog.opt_out_capturing();
}
}
// Check consent status
const hasConsent = posthog.has_opted_in_capturing();
// Initialize with consent check
posthog.init(key, {
opt_out_capturing_by_default: true, // Require explicit opt-in
respect_dnt: true, // Respect Do Not Track
});// ❌ NEVER track these
posthog.capture('event', {
password: '...', // Credentials
credit_card: '...', // Payment info
ssn: '...', // Government IDs
medical_info: '...', // Health data
full_address: '...', // Detailed location
});
// ✅ OK to track
posthog.capture('event', {
country: 'US', // General location
plan: 'pro', // Product info
feature_used: 'export', // Usage
});// lib/analytics.ts
const SENSITIVE_KEYS = ['password', 'token', 'secret', 'credit', 'ssn'];
function sanitizeProperties(props: Record<string, any>): Record<string, any> {
return Object.fromEntries(
Object.entries(props).filter(([key]) =>
!SENSITIVE_KEYS.some(sensitive => key.toLowerCase().includes(sensitive))
)
);
}
export function safeCapture(event: string, properties?: Record<string, any>) {
posthog.capture(event, sanitizeProperties(properties || {}));
}// Disable in development
if (process.env.NODE_ENV === 'development') {
posthog.opt_out_capturing();
// Or use debug mode
posthog.debug();
}// playwright/fixtures.ts
import { test as base } from '@playwright/test';
export const test = base.extend({
page: async ({ page }, use) => {
// Mock PostHog to capture events
await page.addInitScript(() => {
window.capturedEvents = [];
window.posthog = {
capture: (event, props) => {
window.capturedEvents.push({ event, props });
},
identify: () => {},
reset: () => {},
};
});
await use(page);
},
});
// In tests
test('tracks signup event', async ({ page }) => {
await page.goto('/signup');
await page.fill('[name=email]', 'test@example.com');
await page.click('button[type=submit]');
const events = await page.evaluate(() => window.capturedEvents);
expect(events).toContainEqual({
event: 'user_signed_up',
props: expect.objectContaining({ signup_method: 'email' }),
});
});// Enable toolbar for debugging
posthog.init(key, {
// ...
loaded: (posthog) => {
if (process.env.NODE_ENV === 'development') {
posthog.debug();
// Toolbar available via PostHog dashboard
}
},
});// Log all events in development
posthog.init(key, {
_onCapture: (eventName, eventData) => {
if (process.env.NODE_ENV === 'development') {
console.log('PostHog Event:', eventName, eventData);
}
},
});## Must-Track Events
### Acquisition
- [ ] `page_viewed` (automatic with capture_pageview)
- [ ] `user_signed_up`
- [ ] `user_logged_in`
### Activation
- [ ] `onboarding_started`
- [ ] `onboarding_step_completed`
- [ ] `onboarding_completed`
- [ ] `first_[key_action]` (your "aha moment")
### Engagement
- [ ] `[feature]_used`
- [ ] `[resource]_created`
- [ ] `search_performed`
- [ ] `invite_sent`
### Revenue
- [ ] `pricing_page_viewed`
- [ ] `checkout_started`
- [ ] `subscription_upgraded`
- [ ] `subscription_cancelled`
### Retention
- [ ] `session_started`
- [ ] `feature_[x]_used` (power features)| Project Type | Key Dashboards |
|---|---|
| SaaS | Acquisition, Activation, Engagement, Retention, Revenue |
| E-Commerce | Conversion Funnel, Product Performance, Customer LTV |
| Content | Consumption, Engagement, Growth |
| AI/LLM | Usage, Quality, Cost |
| Mobile App | Installs, Onboarding, DAU/MAU, Crashes |
// Auto-enriched by PostHog
$current_url
$browser
$device_type
$os
// Add these yourself
user_plan // 'free' | 'pro' | 'enterprise'
user_role // 'admin' | 'member'
company_id // For B2B
feature_context // Where in the app© alinaqi, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in skills/posthog-analytics of alinaqi/maggy.
Open the folder on GitHubat commit 72a456e
We found 1 copy of this SKILL.md (exact, near-identical or edited) in other folders, from 1 other GitHub owner. This page covers the copy in alinaqi/maggy, which our catalogue first saw on October 7, 2026.
Posthog Analytics next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Posthog Analytics this skillalinaqi/maggy | 707 | 1 repos | ~5.2k | Automated safety check: Notes | MIT | |
| Telemetry Standardssupabase/supabase | 111k | — | ~2k | Automated safety check: Pass | Apache-2.0 | |
| Prod TelemetryUsefulSoftwareCo/executor | 4.1k | — | ~1.9k | Automated safety check: Pass | MIT | |
| Posthog Product Health Auditboardsesh/boardsesh | 164 | — | ~2.1k | Automated safety check: Pass | Apache-2.0 | |
| Posthog Instrumentationlangfuse/langfuse | 36k | — | ~3.2k | Automated safety check: Pass | Custom licence | |
| Creating Box Plot InsightsPostHog/posthog | 40k | — | ~1.1k | Automated safety check: Pass | Custom licence |
supabase/supabase
PostHog event tracking standards for Supabase Studio. An agent skill from supabase/supabase.
UsefulSoftwareCo/executor
Query Executor's production telemetry — Axiom traces (executor-cloud dataset), prod Postgres via PlanetScale, PostHog product analytics — through the Executor MCP.
boardsesh/boardsesh
Mine Boardsesh's PostHog telemetry (error tracking, session recordings, product analytics) with a multi-agent workflow, then file verified, deduplicated, severity-labelled GitHub issues.
langfuse/langfuse
Product analytics with posthog. An agent skill from langfuse/langfuse.
PostHog/posthog
Creates product analytics or SQL-backed box plot insights in PostHog.
PostHog/posthog
Explains how to query PostHog data, defaulting to typed runners for supported product analytics.
alinaqi/maggy
AI Engine Optimization - semantic triples, page templates, content clusters for AI citations
alinaqi/maggy
Claude Code Agent Teams - default team-based development with strict TDD pipeline enforcement
alinaqi/maggy
Latest AI models reference - Claude, OpenAI, Gemini, Eleven Labs, Replicate
alinaqi/maggy
Android Java development with MVVM, ViewBinding, and Espresso testing
alinaqi/maggy
Android Kotlin development with Coroutines, Jetpack Compose, Hilt, and MockK testing
alinaqi/maggy
AI-driven testing agent that auto-discovers, generates, executes, evaluates, and fixes tests for any project type
Works with
Categories
PostHog analytics, event tracking, feature flags, dashboards. Posthog Analytics is an agent skill from alinaqi/maggy.
Posthog Analytics fits situations like: tasks that involve Product analytics.
Run `npx skills add alinaqi/maggy --skill posthog-analytics -a claude-code`. Or copy the skill folder (skills/posthog-analytics in alinaqi/maggy) into .claude/skills/posthog-analytics in your project. Claude Code loads it when a task matches its description.
Run `npx skills add alinaqi/maggy --skill posthog-analytics -a codex`. Or copy the skill folder (skills/posthog-analytics in alinaqi/maggy) into .agents/skills/posthog-analytics in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add alinaqi/maggy --skill posthog-analytics -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/posthog-analytics, .gemini/skills/posthog-analytics, .github/skills/posthog-analytics and .opencode/skills/posthog-analytics in your project.
Going by SKILL.md and its folder, Posthog Analytics needs the command-line tools its instructions call (npm and pip) and credentials named POSTHOG_API_KEY, NEXT_PUBLIC_POSTHOG_KEY and VITE_POSTHOG_KEY. Our summary lists: Python 3; Node.js; A credential in NEXT_PUBLIC_POSTHOG_KEY; A credential in VITE_POSTHOG_KEY.
SKILL.md names 2 domains. In commands or code: us.i.posthog.com; the agent is likely to contact it when it follows the instructions. As links in the text: posthog.com. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found notes only (mentions a .env file), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.
Posthog Analytics is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 5.2k tokens (SKILL.md is roughly 21k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Posthog Analytics: Telemetry Standards (supabase/supabase, 111k stars), Prod Telemetry (UsefulSoftwareCo/executor, 4.1k stars), Posthog Product Health Audit (boardsesh/boardsesh, 164 stars) and Posthog Instrumentation (langfuse/langfuse, 36k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
alinaqi (a GitHub user) maintains it in alinaqi/maggy, which has 707 GitHub stars. The repository holds 71 skills in this directory. The repository was last updated on September 24, 2026.
Source: alinaqi/maggy on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.