# Advanced Features

Advanced features include group analytics, LLM analytics, product tours, conversations, toolbar, and event listeners for extended functionality.

## Group Analytics

Group analytics allows you to track organizations, companies, teams, or any entity that groups users together.

### Associate User with Group

Associates the user with a group and optionally sets group properties.

```typescript { .api }
/**
 * Associates the user with a group
 * @param groupType - Type of group (e.g., 'company', 'team', 'organization')
 * @param groupKey - Unique identifier for the group
 * @param groupPropertiesToSet - Properties to set on the group
 */
function group(
    groupType: string,
    groupKey: string,
    groupPropertiesToSet?: Properties
): void;
```

**Usage Examples:**

```typescript
// Associate user with company
posthog.group('company', 'acme-inc');

// Associate with properties
posthog.group('company', 'acme-inc', {
    name: 'Acme Inc',
    plan: 'enterprise',
    employees: 500,
    industry: 'technology'
});

// Multiple group types
posthog.group('company', 'acme-inc');
posthog.group('team', 'engineering');
posthog.group('project', 'project-alpha');

// Update group properties
posthog.group('company', 'acme-inc', {
    mrr: 50000,
    last_active: Date.now()
});
```

### Get Current Groups

Gets all current group memberships for the user.

```typescript { .api }
/**
 * Gets all current group memberships
 * @returns Object mapping group types to group keys
 */
function getGroups(): Record<string, any>;
```

**Usage Examples:**

```typescript
// Get all groups
const groups = posthog.getGroups();
console.log('User groups:', groups);
// Output: { company: 'acme-inc', team: 'engineering' }

// Check specific group
const groups = posthog.getGroups();
if (groups.company === 'acme-inc') {
    showEnterpriseFeatures();
}

// Use in conditional logic
function getGroupPlan() {
    const groups = posthog.getGroups();
    return groups.company ? 'group' : 'individual';
}
```

### Reset Groups

Clears all group associations.

```typescript { .api }
/**
 * Clears all group associations
 */
function resetGroups(): void;
```

**Usage Examples:**

```typescript
// Clear all groups
posthog.resetGroups();

// Clear on user action
function leaveOrganization() {
    posthog.resetGroups();
    posthog.reset(); // Also reset user
}
```

## LLM Analytics

Track Large Language Model (LLM) interactions, traces, feedback, and metrics.

### Capture Trace Feedback

Captures user feedback for an LLM trace.

```typescript { .api }
/**
 * Captures user feedback for an LLM trace
 * @param traceId - Unique identifier for the LLM trace
 * @param userFeedback - Feedback text from the user
 */
function captureTraceFeedback(
    traceId: string | number,
    userFeedback: string
): void;
```

**Usage Examples:**

```typescript
// Capture positive feedback
posthog.captureTraceFeedback('trace-123', 'This response was helpful!');

// Capture negative feedback
posthog.captureTraceFeedback('trace-456', 'Response was not accurate');

// Capture with structured feedback
const feedback = JSON.stringify({
    rating: 5,
    comment: 'Excellent response',
    helpful: true
});
posthog.captureTraceFeedback('trace-789', feedback);

// Usage in UI
function FeedbackButtons({ traceId }) {
    return (
        <>
            <button onClick={() => {
                posthog.captureTraceFeedback(traceId, 'positive');
            }}>👍</button>
            <button onClick={() => {
                posthog.captureTraceFeedback(traceId, 'negative');
            }}>👎</button>
        </>
    );
}
```

### Capture Trace Metric

Captures a metric for an LLM trace.

```typescript { .api }
/**
 * Captures a metric for an LLM trace
 * @param traceId - Unique identifier for the LLM trace
 * @param metricName - Name of the metric
 * @param metricValue - Value of the metric
 */
function captureTraceMetric(
    traceId: string | number,
    metricName: string,
    metricValue: string | number | boolean
): void;
```

**Usage Examples:**

```typescript
// Capture response time
posthog.captureTraceMetric('trace-123', 'response_time_ms', 1250);

// Capture token usage
posthog.captureTraceMetric('trace-123', 'tokens_used', 450);

// Capture quality metrics
posthog.captureTraceMetric('trace-123', 'relevance_score', 0.95);
posthog.captureTraceMetric('trace-123', 'coherence_score', 0.88);

// Boolean metrics
posthog.captureTraceMetric('trace-123', 'used_context', true);
posthog.captureTraceMetric('trace-123', 'hallucination_detected', false);

// Track LLM interaction
async function generateAIResponse(prompt) {
    const traceId = `trace-${Date.now()}`;
    const startTime = Date.now();

    const response = await llm.generate(prompt);

    const duration = Date.now() - startTime;

    // Capture metrics
    posthog.captureTraceMetric(traceId, 'response_time_ms', duration);
    posthog.captureTraceMetric(traceId, 'tokens_used', response.tokens);
    posthog.captureTraceMetric(traceId, 'model', response.model);

    return { traceId, response };
}
```

## Product Tours

Product tours are guided walkthroughs that help users discover features. Tours can be displayed automatically based on conditions or programmatically controlled through the API.

**Note**: Product tours require the tours feature to be enabled in your PostHog configuration.

```typescript
// Enable product tours
posthog.init('token', {
    disable_product_tours: false // default is true
});
```

### Get All Product Tours

Fetches all product tours from the server.

```typescript { .api }
/**
 * Gets all product tours
 * @param callback - Callback receiving tours and load status
 * @param forceReload - Force reload from server
 */
function getProductTours(
    callback: ProductTourCallback,
    forceReload?: boolean
): void;
```

**Usage Example:**

```typescript
posthog.productTours.getProductTours((tours, context) => {
    if (context.isLoaded) {
        console.log('All tours:', tours);
        tours.forEach(tour => {
            console.log(`Tour: ${tour.name}, Status: ${tour.display_frequency}`);
        });
    } else {
        console.error('Failed to load tours:', context.error);
    }
}, false); // Don't force reload
```

### Get Active Product Tours

Fetches product tours that should be shown to the current user based on conditions.

```typescript { .api }
/**
 * Gets active product tours that match current conditions
 * @param callback - Callback receiving tours and load status
 */
function getActiveProductTours(callback: ProductTourCallback): void;
```

**Usage Example:**

```typescript
posthog.productTours.getActiveProductTours((tours, context) => {
    if (context.isLoaded) {
        console.log('Active tours:', tours);
        tours.forEach(tour => {
            console.log(`Tour: ${tour.name}`);
        });
    } else {
        console.error('Failed to load tours:', context.error);
    }
});
```

### Show Product Tour

Displays a specific product tour by ID.

```typescript { .api }
/**
 * Shows a product tour by its ID
 * @param tourId - Unique identifier for the tour
 */
function showProductTour(tourId: string): void;
```

**Usage Example:**

```typescript
// Show a specific tour
posthog.productTours.showProductTour('onboarding-tour-123');
```

### Preview Tour

Force loads and displays a tour, ignoring all display conditions. Useful for testing.

```typescript { .api }
/**
 * Previews a tour, ignoring display conditions
 * @param tour - Tour configuration object
 */
function previewTour(tour: ProductTour): void;
```

**Usage Example:**

```typescript
// Preview a tour configuration
const testTour = {
    id: 'test-tour',
    name: 'Test Tour',
    type: 'product_tour',
    steps: [...]
};

posthog.productTours.previewTour(testTour);
```

### Dismiss Product Tour

Dismisses the currently displayed tour.

```typescript { .api }
/**
 * Dismisses the current tour
 */
function dismissProductTour(): void;
```

**Usage Example:**

```typescript
// User clicks close button
posthog.productTours.dismissProductTour();
```

### Navigate Tour Steps

Controls navigation through tour steps.

```typescript { .api }
/**
 * Moves to the next step in the current tour
 */
function nextStep(): void;

/**
 * Moves to the previous step in the current tour
 */
function previousStep(): void;
```

**Usage Example:**

```typescript
// Custom navigation buttons
document.getElementById('tour-next')?.addEventListener('click', () => {
    posthog.productTours.nextStep();
});

document.getElementById('tour-prev')?.addEventListener('click', () => {
    posthog.productTours.previousStep();
});
```

### Reset Tours

Resets tour completion state for users.

```typescript { .api }
/**
 * Resets a specific tour so it can be shown again
 * @param tourId - Tour ID to reset
 */
function resetTour(tourId: string): void;

/**
 * Resets all tours for the current user
 */
function resetAllTours(): void;
```

**Usage Example:**

```typescript
// Reset a specific tour
posthog.productTours.resetTour('onboarding-tour-123');

// Reset all tours (e.g., for testing)
posthog.productTours.resetAllTours();
```

### Cancel Pending Tour

Cancels a tour that's waiting to be displayed.

```typescript { .api }
/**
 * Cancels a pending tour
 * @param tourId - Tour ID to cancel
 */
function cancelPendingTour(tourId: string): void;
```

**Usage Example:**

```typescript
// Cancel a tour before it's shown
posthog.productTours.cancelPendingTour('feature-announcement-tour');
```

### Product Tour Types

```typescript { .api }
/**
 * Product tour configuration
 */
interface ProductTour {
    /**
     * Unique tour identifier
     */
    id: string;

    /**
     * Tour name
     */
    name: string;

    /**
     * Tour description
     */
    description?: string;

    /**
     * Tour type
     */
    type: 'product_tour';

    /**
     * Auto-launch the tour
     */
    auto_launch?: boolean;

    /**
     * Tour steps
     */
    steps: ProductTourStep[];

    /**
     * Display conditions
     */
    conditions?: ProductTourConditions;

    /**
     * Visual appearance
     */
    appearance?: ProductTourAppearance;

    /**
     * Display frequency
     */
    display_frequency?: ProductTourDisplayFrequency;
}

/**
 * Product tour step
 */
interface ProductTourStep {
    /**
     * Step ID
     */
    id: string;

    /**
     * Step type
     */
    type: ProductTourStepType;

    /**
     * CSS selector for target element
     */
    selector?: string;

    /**
     * How user progresses to next step
     */
    progressionTrigger: 'button' | 'click';

    /**
     * Step content (markdown)
     */
    content: JSONContent | null;

    /**
     * Step content (HTML)
     */
    contentHtml?: string;

    /**
     * Survey question in step
     */
    survey?: ProductTourSurveyQuestion;

    /**
     * Step buttons
     */
    buttons?: ProductTourStepButtons;

    /**
     * Banner configuration
     */
    bannerConfig?: ProductTourBannerConfig;
}

/**
 * Product tour step type
 */
type ProductTourStepType = 'element' | 'modal' | 'survey' | 'banner';
```

## Conversations

In-app conversations provide support and messaging capabilities.

**Note**: Conversations are managed through PostHog's conversations feature and require backend configuration.

```typescript { .api }
// Enable/disable conversations
posthog.init('token', {
    disable_conversations: false
});
```

### Conversation Types

```typescript { .api }
/**
 * Conversations remote configuration
 */
interface ConversationsRemoteConfig {
    /**
     * Whether conversations are enabled
     */
    enabled: boolean;

    /**
     * Whether the widget is enabled
     */
    widgetEnabled?: boolean;

    /**
     * API token for conversations
     */
    token: string;

    /**
     * Greeting text
     */
    greetingText?: string;

    /**
     * Widget color
     */
    color?: string;

    /**
     * Placeholder text
     */
    placeholderText?: string;

    /**
     * Require email from users
     */
    requireEmail?: boolean;

    /**
     * Collect user name
     */
    collectName?: boolean;
}

/**
 * Message in conversation
 */
interface Message {
    /**
     * Message ID
     */
    id: string;

    /**
     * Message content
     */
    content: string;

    /**
     * Author type
     */
    author_type: MessageAuthorType;

    /**
     * Author name
     */
    author_name?: string;

    /**
     * Creation timestamp
     */
    created_at: string;

    /**
     * Whether message is private
     */
    is_private: boolean;
}

/**
 * Message author type
 */
type MessageAuthorType = 'customer' | 'AI' | 'human';

/**
 * Support ticket
 */
interface Ticket {
    /**
     * Ticket ID
     */
    id: string;

    /**
     * Ticket status
     */
    status: TicketStatus;

    /**
     * Last message preview
     */
    last_message?: string;

    /**
     * Last message timestamp
     */
    last_message_at?: string;

    /**
     * Number of messages
     */
    message_count: number;

    /**
     * Creation timestamp
     */
    created_at: string;

    /**
     * All messages
     */
    messages?: Message[];

    /**
     * Unread message count
     */
    unread_count?: number;
}

/**
 * Ticket status
 */
type TicketStatus = 'new' | 'open' | 'pending' | 'on_hold' | 'resolved';
```

**Note**: Conversations API methods are accessed through the `posthog.conversations` namespace. The methods below are available once conversations are enabled and loaded.

### Show Widget

Shows the conversations widget (button and chat panel).

```typescript { .api }
/**
 * Shows the conversations widget
 */
function show(): void;
```

**Usage Example:**

```typescript
// Show conversations widget
posthog.conversations.show();
```

### Hide Widget

Hides the conversations widget completely.

```typescript { .api }
/**
 * Hides the conversations widget
 */
function hide(): void;
```

**Usage Example:**

```typescript
// Hide conversations widget
posthog.conversations.hide();
```

### Check Widget Availability

Checks if conversations are available and loaded.

```typescript { .api }
/**
 * Checks if conversations are available
 * @returns True if conversations are loaded and available
 */
function isAvailable(): boolean;
```

**Usage Example:**

```typescript
if (posthog.conversations.isAvailable()) {
    console.log('Conversations ready');
    posthog.conversations.show();
} else {
    console.log('Conversations not available');
}
```

### Check Widget Visibility

Checks if the widget is currently visible.

```typescript { .api }
/**
 * Checks if the widget is currently visible
 * @returns True if widget is visible
 */
function isVisible(): boolean;
```

**Usage Example:**

```typescript
if (posthog.conversations.isVisible()) {
    console.log('Widget is shown');
}
```

### Get Current Ticket ID

Gets the ID of the current active ticket.

```typescript { .api }
/**
 * Gets the current active ticket ID
 * @returns Ticket ID or null if no active ticket
 */
function getCurrentTicketId(): string | null;
```

**Usage Example:**

```typescript
const ticketId = posthog.conversations.getCurrentTicketId();
if (ticketId) {
    console.log('Active ticket:', ticketId);
}
```

### Get Widget Session ID

Gets the widget session ID for tracking.

```typescript { .api }
/**
 * Gets the widget session ID
 * @returns Widget session ID or null
 */
function getWidgetSessionId(): string | null;
```

**Usage Example:**

```typescript
const sessionId = posthog.conversations.getWidgetSessionId();
console.log('Widget session:', sessionId);
```

### Reset Conversations

Resets conversations state.

```typescript { .api }
/**
 * Resets conversations state
 */
function reset(): void;
```

**Usage Example:**

```typescript
// Reset conversations on logout
function logout() {
    posthog.conversations.reset();
    posthog.reset();
}
```

### Send Message

Sends a message in a conversation.

```typescript { .api }
/**
 * Sends a message in a conversation
 * @param message - Message content
 * @param traits - Optional user identification data
 * @returns Promise with send result
 */
function sendMessage(
    message: string,
    traits?: UserProvidedTraits
): Promise<SendMessageResponse>;
```

**Usage Example:**

```typescript
// Send a message
await posthog.conversations.sendMessage('Hello, I need help!', {
    email: 'user@example.com',
    name: 'John Doe'
});
```

### Get Messages

Retrieves messages for a conversation or ticket.

```typescript { .api }
/**
 * Gets messages for a ticket
 * @param ticketId - Optional ticket ID (uses current conversation if not provided)
 * @param options - Optional query options
 * @returns Promise with messages
 */
function getMessages(
    ticketId?: string,
    options?: GetMessagesOptions
): Promise<GetMessagesResponse>;
```

**Usage Example:**

```typescript
// Get messages for current conversation
const response = await posthog.conversations.getMessages();
console.log('Messages:', response.messages);

// Get messages for specific ticket
const ticketMessages = await posthog.conversations.getMessages('ticket-123');
```

### Get Tickets

Retrieves all tickets for the current user.

```typescript { .api }
/**
 * Gets all tickets for the current user
 * @param options - Optional query options
 * @returns Promise with tickets
 */
function getTickets(
    options?: GetTicketsOptions
): Promise<GetTicketsResponse>;
```

**Usage Example:**

```typescript
// Get all tickets
const response = await posthog.conversations.getTickets();
console.log('Tickets:', response.tickets);

// Filter tickets by status
const openTickets = response.tickets.filter(
    ticket => ticket.status === 'open'
);
```

### Mark as Read

Marks a ticket as read.

```typescript { .api }
/**
 * Marks a ticket as read
 * @param ticketId - Ticket ID to mark as read
 * @returns Promise with result
 */
function markAsRead(ticketId: string): Promise<MarkAsReadResponse>;
```

**Usage Example:**

```typescript
// Mark ticket as read
await posthog.conversations.markAsRead('ticket-123');
```

### Conversations Usage Pattern

```typescript
// Initialize with conversations enabled
posthog.init('token', {
    disable_conversations: false
});

// Wait for conversations to load
// Then use the API
async function initConversations() {
    // Check if conversations are available
    if (posthog.conversations) {
        // Get existing tickets
        const { tickets } = await posthog.conversations.getTickets();
        console.log('User has', tickets.length, 'tickets');

        // Send a new message
        await posthog.conversations.sendMessage('I need support', {
            email: 'user@example.com'
        });

        // Get messages for a ticket
        const { messages } = await posthog.conversations.getMessages(tickets[0].id);
        console.log('Ticket messages:', messages);
    }
}
```

## Toolbar

The PostHog toolbar provides visual analytics and feature management capabilities.

### Load Toolbar

Loads the PostHog toolbar for visual analytics and editing.

```typescript { .api }
/**
 * Loads the PostHog toolbar
 * @param params - Toolbar parameters
 * @returns True if toolbar loaded successfully
 */
function loadToolbar(params: ToolbarParams): boolean;
```

**Usage Examples:**

```typescript
// Load toolbar with API key
posthog.loadToolbar({
    apiKey: 'your-api-key'
});

// Load with user email
posthog.loadToolbar({
    apiKey: 'your-api-key',
    userEmail: 'user@example.com'
});

// Load for admin users only
if (user.isAdmin) {
    posthog.loadToolbar({
        apiKey: process.env.POSTHOG_API_KEY,
        userEmail: user.email
    });
}

// Load via URL parameter
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get('posthog_toolbar') === 'true') {
    posthog.loadToolbar({
        apiKey: 'your-api-key'
    });
}
```

### Toolbar Types

```typescript { .api }
/**
 * Toolbar parameters
 */
interface ToolbarParams {
    /**
     * PostHog API key
     */
    apiKey?: string;

    /**
     * User email for toolbar
     */
    userEmail?: string;

    /**
     * Additional toolbar parameters
     */
    [key: string]: any;
}
```

## Event Listeners

Register callbacks for various PostHog events.

### Event Captured Listener

Registers a callback that fires whenever an event is captured.

```typescript { .api }
/**
 * Registers an event listener
 * @param event - Event type (currently only 'eventCaptured')
 * @param cb - Callback function
 * @returns Function to unsubscribe from callback
 */
function on(
    event: 'eventCaptured',
    cb: (data: CaptureResult) => void
): () => void;
```

**Usage Examples:**

```typescript
// Listen to all events
const unsubscribe = posthog.on('eventCaptured', (data) => {
    console.log('Event:', data.event);
    console.log('Properties:', data.properties);
});

// Stop listening
unsubscribe();

// Filter specific events
posthog.on('eventCaptured', (data) => {
    if (data.event === 'purchase_completed') {
        showThankYou();
    }
});

// Send to external service
posthog.on('eventCaptured', (data) => {
    if (data.event.startsWith('$')) {
        return; // Skip internal events
    }

    sendToExternalAnalytics(data);
});
```

### Feature Flags Listener

Registers a callback for when feature flags are loaded or updated.

```typescript { .api }
/**
 * Registers a callback for when feature flags are loaded
 * @param callback - Function to call when flags are loaded
 * @returns Function to unsubscribe from callback
 */
function onFeatureFlags(callback: FeatureFlagsCallback): () => void;
```

See [Feature Flags](./feature-flags.md) for details.

### Surveys Listener

Registers a callback for when surveys are loaded.

```typescript { .api }
/**
 * Registers a callback for when surveys are loaded
 * @param callback - Function to call when surveys are loaded
 * @returns Function to unsubscribe from callback
 */
function onSurveysLoaded(callback: SurveyCallback): () => void;
```

See [Surveys](./surveys.md) for details.

### Session ID Listener

Registers a callback for when the session ID changes.

```typescript { .api }
/**
 * Registers a callback for when the session ID changes
 * @param callback - Function to call when session changes
 * @returns Function to unsubscribe from callback
 */
function onSessionId(callback: SessionIdChangedCallback): () => void;
```

See [Session Recording](./session-recording.md) for details.

## Utility Methods

### Get Page View ID

Gets the unique ID for the current page view.

```typescript { .api }
/**
 * Gets the unique ID for the current page view
 * @returns Page view ID or undefined
 */
function getPageViewId(): string | undefined;
```

**Usage Example:**

```typescript
const pageViewId = posthog.getPageViewId();
console.log('Page view:', pageViewId);
```

### Push to Queue

Pushes a deferred operation to the queue (mainly for snippet usage).

```typescript { .api }
/**
 * Pushes a deferred operation to the queue
 * @param item - Operation to push
 */
function push(item: SnippetArrayItem): void;
```

**Usage Example:**

```typescript
// Used primarily with the snippet loader
posthog.push(['identify', 'user-123']);
```

### To String

Returns a string representation of the PostHog instance.

```typescript { .api }
/**
 * Returns a string representation of the instance
 * @returns String representation (typically the instance name)
 */
function toString(): string;
```

**Usage Example:**

```typescript
// Get instance name
const instanceName = posthog.toString();
console.log('Instance:', instanceName); // 'posthog' or custom name
```

## Integrations

### Sentry Integration

PostHog provides a Sentry integration class for connecting error tracking with PostHog analytics.

```typescript { .api }
/**
 * Sentry integration class
 * Use this with Sentry SDK to link errors with PostHog sessions
 */
class SentryIntegration {
    // Integration class for Sentry error tracking
}

/**
 * Creates a Sentry integration instance
 * @param options - Configuration options for the integration
 * @returns SentryIntegration instance
 */
function sentryIntegration(options?: SentryIntegrationOptions): SentryIntegration;
```

**Usage Example:**

```typescript
import * as Sentry from '@sentry/browser';
import posthog, { sentryIntegration } from 'posthog-js';

// Initialize PostHog
posthog.init('your-api-key');

// Initialize Sentry with PostHog integration
Sentry.init({
    dsn: 'your-sentry-dsn',
    integrations: [
        sentryIntegration({
            posthog: posthog,
            organization: 'my-org',
            projectId: 123456
        })
    ]
});

// Errors will now be linked between Sentry and PostHog
try {
    riskyOperation();
} catch (error) {
    // Both PostHog and Sentry will capture this
    Sentry.captureException(error);
    posthog.captureException(error);
}
```

## Common Patterns

### Multi-Tenant Applications

Track group-level analytics for SaaS applications:

```typescript
// On user login
async function onUserLogin(user) {
    // Identify user
    posthog.identify(user.id, {
        email: user.email,
        name: user.name
    });

    // Associate with organization
    posthog.group('organization', user.organizationId, {
        name: user.organization.name,
        plan: user.organization.plan,
        seat_count: user.organization.seats,
        created_at: user.organization.createdAt
    });

    // Associate with team
    if (user.teamId) {
        posthog.group('team', user.teamId, {
            name: user.team.name,
            role: user.team.role
        });
    }

    // Reload flags with group context
    posthog.reloadFeatureFlags();
}

// Track organization-level events
function trackOrganizationEvent(eventName, properties = {}) {
    const groups = posthog.getGroups();

    posthog.capture(eventName, {
        ...properties,
        organization_id: groups.organization,
        team_id: groups.team
    });
}
```

### LLM Application Tracking

Comprehensive LLM interaction tracking:

```typescript
class LLMTracker {
    async trackGeneration(prompt, context = {}) {
        const traceId = this.generateTraceId();
        const startTime = Date.now();

        try {
            // Capture generation start
            posthog.capture('llm_generation_started', {
                trace_id: traceId,
                prompt_length: prompt.length,
                ...context
            });

            // Generate response
            const response = await this.llm.generate(prompt);

            // Capture metrics
            const duration = Date.now() - startTime;
            posthog.captureTraceMetric(traceId, 'response_time_ms', duration);
            posthog.captureTraceMetric(traceId, 'tokens_used', response.usage.total_tokens);
            posthog.captureTraceMetric(traceId, 'model', response.model);
            posthog.captureTraceMetric(traceId, 'temperature', response.temperature);

            // Capture completion
            posthog.capture('llm_generation_completed', {
                trace_id: traceId,
                success: true
            });

            return { traceId, response };
        } catch (error) {
            // Capture error
            posthog.captureException(error, {
                trace_id: traceId,
                context: 'llm_generation'
            });

            posthog.capture('llm_generation_failed', {
                trace_id: traceId,
                error: error.message
            });

            throw error;
        }
    }

    trackFeedback(traceId, feedback) {
        posthog.captureTraceFeedback(traceId, feedback);

        // Also capture as event for analysis
        posthog.capture('llm_feedback_received', {
            trace_id: traceId,
            feedback: feedback
        });
    }

    generateTraceId() {
        return `trace-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
    }
}

// Usage
const tracker = new LLMTracker();

async function generateResponse(userPrompt) {
    const { traceId, response } = await tracker.trackGeneration(
        userPrompt,
        { user_id: currentUser.id }
    );

    return { traceId, response };
}

function handleFeedback(traceId, rating, comment) {
    const feedback = JSON.stringify({ rating, comment });
    tracker.trackFeedback(traceId, feedback);
}
```

### Admin Toolbar Integration

Load toolbar for admin users:

```typescript
function initializeAdminFeatures(user) {
    if (user.role === 'admin' || user.role === 'developer') {
        // Load toolbar
        posthog.loadToolbar({
            apiKey: process.env.POSTHOG_API_KEY,
            userEmail: user.email
        });

        // Add toolbar toggle to UI
        addToolbarToggle();
    }
}

function addToolbarToggle() {
    const toggle = document.createElement('button');
    toggle.textContent = 'Toggle PostHog Toolbar';
    toggle.onclick = () => {
        // Toolbar can be toggled with keyboard shortcut
        // or programmatically loaded
        posthog.loadToolbar({
            apiKey: process.env.POSTHOG_API_KEY
        });
    };

    document.body.appendChild(toggle);
}
```

### Event Pipeline Integration

Forward events to external systems:

```typescript
// Initialize external integrations
const integrations = {
    segment: new SegmentClient(),
    amplitude: new AmplitudeClient(),
    mixpanel: new MixpanelClient()
};

// Forward all PostHog events
posthog.on('eventCaptured', (data) => {
    // Skip internal events
    if (data.event.startsWith('$')) {
        return;
    }

    // Forward to other platforms
    Object.values(integrations).forEach(integration => {
        integration.track(data.event, data.properties);
    });
});

// Selective forwarding
posthog.on('eventCaptured', (data) => {
    const criticalEvents = [
        'purchase_completed',
        'signup_completed',
        'subscription_changed'
    ];

    if (criticalEvents.includes(data.event)) {
        // Send to data warehouse
        sendToWarehouse(data);

        // Alert monitoring system
        sendToMonitoring(data);
    }
});
```

### Custom Event Enrichment

Enrich events with additional context:

```typescript
posthog.on('eventCaptured', async (data) => {
    // Skip if already enriched
    if (data.properties._enriched) {
        return;
    }

    // Add user context
    const user = await getCurrentUser();
    if (user) {
        data.properties.user_plan = user.plan;
        data.properties.user_cohort = user.cohort;
        data.properties.user_tenure_days = calculateTenure(user.createdAt);
    }

    // Add session context
    data.properties.session_duration = getSessionDuration();
    data.properties.pages_viewed = getPageViewCount();

    // Add technical context
    data.properties.connection_type = navigator.connection?.effectiveType;
    data.properties.device_memory = navigator.deviceMemory;

    // Mark as enriched
    data.properties._enriched = true;
});
```

### Real-Time Analytics Dashboard

Build real-time monitoring:

```typescript
class AnalyticsDashboard {
    constructor() {
        this.events = [];
        this.maxEvents = 100;

        this.setupListeners();
    }

    setupListeners() {
        // Track all events
        posthog.on('eventCaptured', (data) => {
            this.addEvent(data);
        });

        // Track session changes
        posthog.onSessionId((sessionId) => {
            this.updateSessionInfo(sessionId);
        });

        // Track flag changes
        posthog.onFeatureFlags((flags) => {
            this.updateFlags(flags);
        });
    }

    addEvent(data) {
        this.events.unshift({
            ...data,
            timestamp: Date.now()
        });

        if (this.events.length > this.maxEvents) {
            this.events.pop();
        }

        this.render();
    }

    updateSessionInfo(sessionId) {
        console.log('Session changed:', sessionId);
        this.render();
    }

    updateFlags(flags) {
        console.log('Flags updated:', flags);
        this.render();
    }

    render() {
        // Update dashboard UI
        this.renderEventList();
        this.renderMetrics();
    }

    renderEventList() {
        // Display recent events
        console.table(this.events.slice(0, 10));
    }

    renderMetrics() {
        // Calculate and display metrics
        const eventCounts = this.events.reduce((acc, e) => {
            acc[e.event] = (acc[e.event] || 0) + 1;
            return acc;
        }, {});

        console.log('Event counts:', eventCounts);
    }
}

// Initialize dashboard
const dashboard = new AnalyticsDashboard();
```

## Advanced Group Analytics Patterns

### Hierarchical Groups

```typescript { .api }
// Handle nested organizational structures
interface OrganizationHierarchy {
    company: string;
    division: string;
    department: string;
    team: string;
}

function setOrganizationContext(user: User, hierarchy: OrganizationHierarchy) {
    // Set all group levels
    posthog.group('company', hierarchy.company, {
        company_name: user.companyName,
        company_size: user.companySize,
        industry: user.industry
    });
    
    posthog.group('division', hierarchy.division, {
        division_name: user.divisionName,
        division_head: user.divisionHead
    });
    
    posthog.group('department', hierarchy.department, {
        department_name: user.departmentName,
        budget: user.departmentBudget
    });
    
    posthog.group('team', hierarchy.team, {
        team_name: user.teamName,
        team_size: user.teamSize
    });
    
    // Reload flags to get group-based targeting
    posthog.reloadFeatureFlags();
}
```

### Group Property Management

```typescript { .api }
// Update group properties dynamically
class GroupPropertyManager {
    updateCompanyMetrics(companyId: string, metrics: {
        mrr?: number;
        userCount?: number;
        activeUsers?: number;
    }) {
        posthog.group('company', companyId, {
            mrr: metrics.mrr,
            user_count: metrics.userCount,
            active_users: metrics.activeUsers,
            last_updated: new Date().toISOString()
        });
    }
    
    incrementUsage(companyId: string, feature: string) {
        posthog.capture('feature_used', {
            feature: feature
        });
        
        // Update company-level usage tracking
        posthog.group('company', companyId, {
            [`${feature}_usage_count`]: 1  // PostHog will increment
        });
    }
}
```

### Multi-Tenant Analytics

```typescript { .api }
// Complete multi-tenant tracking
class MultiTenantAnalytics {
    identifyTenant(user: User, tenant: Tenant) {
        // Identify user
        posthog.identify(user.id, {
            email: user.email,
            name: user.name,
            role: user.role
        });
        
        // Associate with tenant
        posthog.group('tenant', tenant.id, {
            tenant_name: tenant.name,
            plan: tenant.plan,
            created_at: tenant.createdAt,
            user_count: tenant.userCount,
            mrr: tenant.mrr,
            industry: tenant.industry,
            region: tenant.region
        });
        
        // Track tenant-level events
        posthog.capture('tenant_session_started', {
            tenant_id: tenant.id,
            user_role: user.role
        });
        
        // Reload flags for tenant-specific features
        posthog.reloadFeatureFlags();
    }
    
    trackTenantEvent(event: string, properties: Properties = {}) {
        const groups = posthog.getGroups();
        
        posthog.capture(event, {
            ...properties,
            tenant_id: groups.tenant
        });
    }
    
    switchTenant(newTenant: Tenant) {
        // Clear old group associations
        posthog.resetGroups();
        
        // Set new tenant
        posthog.group('tenant', newTenant.id, {
            tenant_name: newTenant.name,
            plan: newTenant.plan
        });
        
        // Track switch
        posthog.capture('tenant_switched', {
            new_tenant_id: newTenant.id
        });
        
        // Reload flags
        posthog.reloadFeatureFlags();
    }
}
```

## Advanced LLM Analytics

### Comprehensive LLM Tracking

```typescript { .api }
// Complete LLM interaction tracking
interface LLMTrace {
    id: string;
    model: string;
    prompt: string;
    response?: string;
    startTime: number;
    endTime?: number;
    error?: Error;
}

class LLMAnalytics {
    private traces = new Map<string, LLMTrace>();
    
    startTrace(model: string, prompt: string): string {
        const traceId = `trace_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
        
        this.traces.set(traceId, {
            id: traceId,
            model,
            prompt,
            startTime: Date.now()
        });
        
        // Capture trace start
        posthog.capture('llm_trace_started', {
            trace_id: traceId,
            model: model,
            prompt_length: prompt.length,
            prompt_hash: this.hashPrompt(prompt)
        });
        
        return traceId;
    }
    
    endTrace(traceId: string, response: string, usage: {
        promptTokens: number;
        completionTokens: number;
        totalTokens: number;
    }) {
        const trace = this.traces.get(traceId);
        if (!trace) return;
        
        trace.response = response;
        trace.endTime = Date.now();
        
        const duration = trace.endTime - trace.startTime;
        
        // Capture metrics
        posthog.captureTraceMetric(traceId, 'duration_ms', duration);
        posthog.captureTraceMetric(traceId, 'prompt_tokens', usage.promptTokens);
        posthog.captureTraceMetric(traceId, 'completion_tokens', usage.completionTokens);
        posthog.captureTraceMetric(traceId, 'total_tokens', usage.totalTokens);
        posthog.captureTraceMetric(traceId, 'tokens_per_second', usage.totalTokens / (duration / 1000));
        
        // Capture completion
        posthog.capture('llm_trace_completed', {
            trace_id: traceId,
            model: trace.model,
            response_length: response.length,
            success: true
        });
    }
    
    errorTrace(traceId: string, error: Error) {
        const trace = this.traces.get(traceId);
        if (!trace) return;
        
        trace.error = error;
        trace.endTime = Date.now();
        
        // Capture error
        posthog.captureException(error, {
            trace_id: traceId,
            model: trace.model,
            prompt_length: trace.prompt.length
        });
        
        posthog.capture('llm_trace_failed', {
            trace_id: traceId,
            error: error.message
        });
    }
    
    captureFeedback(traceId: string, rating: number, comment?: string) {
        const feedbackData = JSON.stringify({
            rating: rating,
            comment: comment,
            timestamp: new Date().toISOString()
        });
        
        posthog.captureTraceFeedback(traceId, feedbackData);
        
        // Also track as event for analysis
        posthog.capture('llm_feedback', {
            trace_id: traceId,
            rating: rating,
            has_comment: !!comment
        });
    }
    
    private hashPrompt(prompt: string): string {
        // Simple hash for prompt deduplication
        return prompt.split('').reduce((hash, char) => {
            return ((hash << 5) - hash) + char.charCodeAt(0);
        }, 0).toString(36);
    }
}

// Usage
const llm = new LLMAnalytics();

async function generateResponse(prompt: string) {
    const traceId = llm.startTrace('gpt-4', prompt);
    
    try {
        const result = await openai.chat.completions.create({
            model: 'gpt-4',
            messages: [{ role: 'user', content: prompt }]
        });
        
        llm.endTrace(traceId, result.choices[0].message.content!, {
            promptTokens: result.usage!.prompt_tokens,
            completionTokens: result.usage!.completion_tokens,
            totalTokens: result.usage!.total_tokens
        });
        
        return { traceId, response: result.choices[0].message.content };
    } catch (error) {
        llm.errorTrace(traceId, error as Error);
        throw error;
    }
}

// Track feedback
function handleUserFeedback(traceId: string, thumbsUp: boolean, comment?: string) {
    const rating = thumbsUp ? 5 : 1;
    llm.captureFeedback(traceId, rating, comment);
}
```

### LLM Cost Tracking

```typescript { .api }
// Track LLM costs and usage
interface TokenPricing {
    prompt: number;    // Price per 1K tokens
    completion: number;  // Price per 1K tokens
}

const modelPricing: Record<string, TokenPricing> = {
    'gpt-4': { prompt: 0.03, completion: 0.06 },
    'gpt-3.5-turbo': { prompt: 0.001, completion: 0.002 }
};

function captureTraceWithCost(
    traceId: string,
    model: string,
    usage: { promptTokens: number; completionTokens: number }
) {
    const pricing = modelPricing[model];
    
    if (pricing) {
        const promptCost = (usage.promptTokens / 1000) * pricing.prompt;
        const completionCost = (usage.completionTokens / 1000) * pricing.completion;
        const totalCost = promptCost + completionCost;
        
        posthog.captureTraceMetric(traceId, 'prompt_cost_usd', promptCost);
        posthog.captureTraceMetric(traceId, 'completion_cost_usd', completionCost);
        posthog.captureTraceMetric(traceId, 'total_cost_usd', totalCost);
    }
    
    posthog.captureTraceMetric(traceId, 'prompt_tokens', usage.promptTokens);
    posthog.captureTraceMetric(traceId, 'completion_tokens', usage.completionTokens);
}
```

## Testing Advanced Features

### Testing Group Analytics

```typescript { .api }
describe('Group Analytics', () => {
    beforeEach(() => {
        posthog.resetGroups();
    });
    
    it('associates user with groups', () => {
        posthog.group('company', 'test-company', {
            name: 'Test Company'
        });
        
        const groups = posthog.getGroups();
        expect(groups.company).toBe('test-company');
    });
    
    it('resets groups on logout', () => {
        posthog.group('company', 'test-company');
        posthog.resetGroups();
        
        const groups = posthog.getGroups();
        expect(Object.keys(groups).length).toBe(0);
    });
});
```

### Testing LLM Analytics

```typescript { .api }
describe('LLM Analytics', () => {
    it('captures trace feedback', () => {
        const traceId = 'test-trace-123';
        
        posthog.captureTraceFeedback(traceId, 'positive');
        
        expect(posthog.capture).toHaveBeenCalledWith(
            '$ai_feedback',
            expect.objectContaining({
                trace_id: traceId
            })
        );
    });
    
    it('captures trace metrics', () => {
        const traceId = 'test-trace-123';
        
        posthog.captureTraceMetric(traceId, 'tokens', 100);
        
        expect(posthog.capture).toHaveBeenCalledWith(
            '$ai_metric',
            expect.objectContaining({
                trace_id: traceId,
                metric_name: 'tokens',
                metric_value: 100
            })
        );
    });
});
```

## Troubleshooting Advanced Features

### Group Analytics Not Working

```typescript { .api }
// Debug group associations
function debugGroups() {
    console.log('Current groups:', posthog.getGroups());
    
    // Test group association
    posthog.group('test_group', 'test-key', {
        test_property: 'test_value'
    });
    
    console.log('After test group:', posthog.getGroups());
    
    // Verify group events are captured
    posthog.on('eventCaptured', (data) => {
        if (data.event === '$groupidentify') {
            console.log('Group identify event:', data);
        }
    });
    
    // Trigger a group event
    posthog.capture('test_event_with_group');
}
```

### LLM Traces Not Appearing

```typescript { .api }
// Debug LLM trace capture
function debugLLMTraces() {
    const testTraceId = 'debug-trace-123';
    
    // Test feedback capture
    posthog.captureTraceFeedback(testTraceId, 'test feedback');
    
    // Test metric capture
    posthog.captureTraceMetric(testTraceId, 'test_metric', 42);
    
    // Listen for events
    posthog.on('eventCaptured', (data) => {
        if (data.event === '$ai_feedback' || data.event === '$ai_metric') {
            console.log('LLM event captured:', data);
        }
    });
}
```

### Toolbar Not Loading

```typescript { .api }
// Debug toolbar loading
function debugToolbar() {
    console.log('Current URL:', window.location.href);
    console.log('Has __posthog param:', window.location.search.includes('__posthog'));
    
    // Try loading toolbar
    const result = posthog.loadToolbar({});
    console.log('Toolbar load result:', result);
    
    // Check toolbar config
    console.log('Toolbar disabled:', posthog.config.advanced_disable_toolbar_metrics);
    
    // Force load with specific intent
    posthog.loadToolbar({
        userIntent: 'heatmaps'
    });
}
```

## Performance Considerations

### Optimize Group Tracking

```typescript { .api }
// Batch group updates
async function updateMultipleGroups(updates: Array<{
    type: string;
    key: string;
    properties: Properties;
}>) {
    // Pause flag reloading during batch
    posthog.featureFlags.setReloadingPaused(true);
    
    // Update all groups
    for (const { type, key, properties } of updates) {
        posthog.group(type, key, properties);
    }
    
    // Resume and reload once
    posthog.featureFlags.setReloadingPaused(false);
    posthog.reloadFeatureFlags();
}
```

### Optimize Event Listeners

```typescript { .api }
// Clean up event listeners properly
class EventListenerManager {
    private unsubscribers: Array<() => void> = [];
    
    addListener(type: 'eventCaptured' | 'featureFlags' | 'sessionId', callback: any) {
        let unsubscribe: () => void;
        
        if (type === 'eventCaptured') {
            unsubscribe = posthog.on('eventCaptured', callback);
        } else if (type === 'featureFlags') {
            unsubscribe = posthog.onFeatureFlags(callback);
        } else {
            unsubscribe = posthog.onSessionId(callback);
        }
        
        this.unsubscribers.push(unsubscribe);
        return unsubscribe;
    }
    
    removeAllListeners() {
        this.unsubscribers.forEach(unsubscribe => unsubscribe());
        this.unsubscribers = [];
    }
}

// Usage in React
function usePostHogListener(callback: (data: CaptureResult) => void) {
    useEffect(() => {
        const unsubscribe = posthog.on('eventCaptured', callback);
        return unsubscribe;  // Cleanup on unmount
    }, [callback]);
}
```

## Edge Cases

### Group Reset on User Switch

```typescript { .api }
// Handle organization switching
async function switchOrganization(newOrgId: string) {
    // Get current groups for logging
    const oldGroups = posthog.getGroups();
    
    // Capture switch event before reset
    posthog.capture('organization_switched', {
        old_organization: oldGroups.company,
        new_organization: newOrgId
    });
    
    // Reset all groups
    posthog.resetGroups();
    
    // Load new organization
    const newOrg = await loadOrganization(newOrgId);
    
    // Set new groups
    posthog.group('company', newOrg.id, {
        name: newOrg.name,
        plan: newOrg.plan,
        user_count: newOrg.userCount
    });
    
    // Reload flags for new organization context
    posthog.reloadFeatureFlags();
}
```

### Trace ID Generation Best Practices

```typescript { .api }
// Generate unique, traceable trace IDs
class TraceIdGenerator {
    generate(prefix: string = 'trace'): string {
        const timestamp = Date.now();
        const random = Math.random().toString(36).substring(2, 15);
        const sessionId = posthog.get_session_id().substring(0, 8);
        
        return `${prefix}_${timestamp}_${sessionId}_${random}`;
    }
    
    // Validate trace ID format
    isValid(traceId: string): boolean {
        return /^[a-z]+_\d+_[a-f0-9]+_[a-z0-9]+$/.test(traceId);
    }
}

const traceIdGen = new TraceIdGenerator();

// Usage
const traceId = traceIdGen.generate('llm');
if (traceIdGen.isValid(traceId)) {
    // Use trace ID
}
```

### Feedback with Sentiment Analysis

```typescript { .api }
// Enhanced feedback tracking with sentiment
interface FeedbackData {
    rating: number;
    comment?: string;
    sentiment?: 'positive' | 'neutral' | 'negative';
    categories?: string[];
}

function captureFeedbackWithSentiment(traceId: string, feedback: FeedbackData) {
    // Determine sentiment from rating if not provided
    const sentiment = feedback.sentiment || (
        feedback.rating >= 4 ? 'positive' :
        feedback.rating <= 2 ? 'negative' :
        'neutral'
    );
    
    // Capture structured feedback
    const feedbackJson = JSON.stringify({
        rating: feedback.rating,
        comment: feedback.comment,
        sentiment: sentiment,
        categories: feedback.categories,
        timestamp: new Date().toISOString()
    });
    
    posthog.captureTraceFeedback(traceId, feedbackJson);
    
    // Also capture as event for aggregation
    posthog.capture('llm_feedback_detailed', {
        trace_id: traceId,
        rating: feedback.rating,
        sentiment: sentiment,
        has_comment: !!feedback.comment,
        categories: feedback.categories?.join(',')
    });
}
```

## Security Considerations

### Sanitize Group Properties

```typescript { .api }
// Sanitize sensitive group properties
function sanitizeGroupProperties(properties: Properties): Properties {
    const sensitive = ['password', 'api_key', 'secret', 'token'];
    const sanitized = { ...properties };
    
    for (const key of Object.keys(sanitized)) {
        if (sensitive.some(s => key.toLowerCase().includes(s))) {
            delete sanitized[key];
        }
    }
    
    return sanitized;
}

// Usage
posthog.group('company', 'company-123', sanitizeGroupProperties({
    name: 'Company Name',
    api_key: 'secret',  // Will be removed
    plan: 'enterprise'
}));
```

### Validate Trace Data

```typescript { .api }
// Validate and sanitize trace data
function captureSafeTraceFeedback(traceId: string, feedback: string) {
    // Validate trace ID format
    if (!traceId || traceId.length > 255) {
        console.error('Invalid trace ID');
        return;
    }
    
    // Sanitize feedback (remove PII)
    const sanitized = feedback
        .replace(/\b[\w.-]+@[\w.-]+\.\w+\b/g, '[EMAIL]')
        .replace(/\b\d{3}-\d{2}-\d{4}\b/g, '[SSN]')
        .replace(/\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/g, '[CARD]');
    
    posthog.captureTraceFeedback(traceId, sanitized);
}
```

## See Also

- [Group Analytics in Events](./events.md) - Capturing group-level events
- [Feature Flags](./feature-flags.md) - Group-based feature flags
- [Identification](./identification.md) - User and group identification
- [Sentry Integration](./sentry-integration.md) - Error tracking integration
- [Toolbar](./toolbar.md) - Debugging with the toolbar
- [Types](./types.md) - Advanced type definitions
