> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useturret.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Best Practices

> Get the most value from Turret with these proven strategies and patterns

## Event Design Principles

### Structure Your Events Consistently

Use a consistent structure across your application:

<CardGroup cols={2}>
  <Card title="Good Structure" icon="check">
    ```json theme={null}
    {
      "name": "user_prompt",
      "session_id": "conv_abc123",
      "user_id": "user_456",
      "metadata": {
        "prompt": "actual user input"
      }
    }
    ```
  </Card>

  <Card title="Avoid" icon="x">
    ```json theme={null}
    {
      "name": "event",
      "metadata": {
        "data": "mixed content",
        "stuff": "unclear purpose"
      }
    }
    ```
  </Card>
</CardGroup>

### Maintain Session Continuity

The `session_id` connects events into a conversation journey. Turret auto-generates it if not provided, but you must persist and reuse it:

```javascript theme={null}
class TurretTracker {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.sessionId = null;
    this.userId = null;
  }

  async track(prompt) {
    const response = await fetch('https://api.useturret.com/track', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': this.apiKey
      },
      body: JSON.stringify({
        name: 'user_message',
        session_id: this.sessionId,  // null on first call
        user_id: this.userId,
        metadata: { prompt }
      })
    });

    const data = await response.json();
    // Store for subsequent messages
    this.sessionId = data.session_id;
    this.userId = data.user_id;
  }

  startNewConversation() {
    this.sessionId = null;  // Next track() gets new session_id
  }
}
```

<Tip>
  **Already have conversation IDs?** Pass your existing conversation/thread ID as `session_id` - Turret will use it directly instead of generating one.
</Tip>

### Focus on User-Generated Text

Turret works best with free-form text that users actually write or say:

**Excellent for Turret:**

* User prompts and questions
* Search queries
* Support requests
* Feedback and comments

**Less valuable for Turret:**

* System-generated messages
* Enum values or categories
* Technical logs
* Numeric data

## Session ID Best Practices

### Option 1: Let Turret Generate IDs

If you don't have existing conversation IDs, let Turret generate them:

```javascript theme={null}
// First message - no session_id
const response1 = await turret.track({ prompt: 'What pricing plans do you offer?' });
const sessionId = response1.session_id;  // Store this!

// Subsequent messages - include session_id
await turret.track({ prompt: 'What features are in the pro plan?', session_id: sessionId });
await turret.track({ prompt: 'Can I get a discount?', session_id: sessionId });
```

This creates a journey: **Pricing → Features → Discounts**

### Option 2: Use Your Own IDs

If you already have conversation IDs (most apps do), pass them directly:

```javascript theme={null}
// Your app already has a conversation ID
const myConversationId = conversation.id;

// Pass it to Turret - it uses your ID, not a generated one
await turret.track({
  prompt: 'What pricing plans do you offer?',
  session_id: myConversationId,
  user_id: currentUser.id
});
```

### What to Avoid

```javascript theme={null}
// BAD: Different session_id for each message (breaks journeys)
await turret.track({ prompt: 'Question 1' });  // Gets session A
await turret.track({ prompt: 'Question 2' });  // Gets session B - NOT connected!

// BAD: Hardcoded session_id (mixes all users together)
await turret.track({ prompt: 'Question', session_id: 'default' });

// GOOD: Store and reuse session_id
const { session_id } = await turret.track({ prompt: 'Question 1' });
await turret.track({ prompt: 'Question 2', session_id });  // Connected!
```

### Cross-Session Analysis with user\_id

Track the same user across multiple conversations:

```javascript theme={null}
// Monday: User starts a conversation (new session)
const { user_id } = await turret.track({ prompt: 'How do I set up the integration?' });
// Store user_id with your user record

// Wednesday: Same user, new conversation
await turret.track({
  prompt: 'The integration stopped working',
  user_id: storedUserId  // Same user, but new session_id (omitted = auto-generated)
});
```

This reveals patterns like: users who ask about setup often return with issues.

## Metadata Design

### Keep Metadata Simple

Focus on the text you want to cluster:

```json theme={null}
{
  "name": "user_prompt",
  "session_id": "conv_abc123",
  "metadata": {
    "prompt": "How do I integrate your API with my React app?"
  }
}
```

### Add Context When Helpful

You can include additional metadata for context (only the `prompt` field is clustered):

```json theme={null}
{
  "name": "user_prompt",
  "session_id": "conv_abc123",
  "user_id": "user_456",
  "metadata": {
    "prompt": "How do I integrate your API with my React app?",
    "detected_language": "english",
    "platform": "web"
  }
}
```

### Use Natural Language

Store text as users actually wrote it:

<CardGroup cols={2}>
  <Card title="Preserve Original" icon="check">
    ```json theme={null}
    {
      "prompt": "hey, can u help me fix this bug?"
    }
    ```
  </Card>

  <Card title="Avoid Over-Processing" icon="x">
    ```json theme={null}
    {
      "prompt": "HELP FIX BUG"
    }
    ```
  </Card>
</CardGroup>

## Event Timing

### Track at the Right Moments

Capture events when you have the most complete information:

<AccordionGroup>
  <Accordion icon="clock" title="User Input Events">
    Track immediately after user submits text (prompts, questions, search queries)
  </Accordion>

  <Accordion icon="brain" title="AI Response Events">
    Track after generating a response if you want to analyze AI outputs
  </Accordion>

  <Accordion icon="heart" title="Feedback Events">
    Track when users provide explicit feedback or ratings
  </Accordion>
</AccordionGroup>

## Data Quality

### Validate Before Sending

Ensure your events contain meaningful data:

```javascript theme={null}
function validateEvent(name, sessionId, metadata) {
  // Check for required fields
  if (!name || !metadata) return false;
  
  // Ensure metadata has text content
  const hasText = Object.values(metadata).some(value => 
    typeof value === 'string' && value.trim().length > 0
  );
  
  if (!hasText) return false;
  
  // Warn if session_id is missing (recommended field)
  if (!sessionId) {
    console.warn('session_id missing - journey tracking disabled');
  }
  
  return true;
}
```

### Handle Sensitive Data

Be careful with personally identifiable information (PII):

```javascript theme={null}
function sanitizeMetadata(metadata) {
  return Object.fromEntries(
    Object.entries(metadata).map(([key, value]) => [
      key,
      typeof value === 'string' ? 
        value.replace(/\b\d{3}-\d{2}-\d{4}\b/g, '[SSN]')
             .replace(/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, '[EMAIL]')
        : value
    ])
  );
}
```

## Performance Optimization

### Use Async Tracking

Don't block your main application flow:

```javascript theme={null}
async function trackEventAsync(name, sessionId, userId, metadata) {
  // Use setTimeout to avoid blocking
  setTimeout(async () => {
    try {
      await trackEvent(name, sessionId, userId, metadata);
    } catch (error) {
      console.error('Event tracking failed:', error);
      // Don't let tracking failures break your app
    }
  }, 0);
}
```

### Implement Circuit Breakers

Protect your application from API failures:

```javascript theme={null}
class EventTracker {
  constructor() {
    this.failures = 0;
    this.maxFailures = 5;
    this.resetTime = 60000; // 1 minute
    this.lastFailureTime = 0;
  }
  
  async trackEvent(name, sessionId, userId, metadata) {
    if (this.isCircuitOpen()) {
      console.log('Circuit breaker open, skipping event');
      return;
    }
    
    try {
      await this.sendEvent(name, sessionId, userId, metadata);
      this.onSuccess();
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }
  
  isCircuitOpen() {
    return this.failures >= this.maxFailures && 
           Date.now() - this.lastFailureTime < this.resetTime;
  }
  
  onSuccess() {
    this.failures = 0;
  }
  
  onFailure() {
    this.failures++;
    this.lastFailureTime = Date.now();
  }
}
```

## Analytics Strategy

### Start Simple, Evolve

Begin with basic events and add complexity over time:

**Phase 1: Minimal (Just Track Messages)**

```json theme={null}
{
  "name": "user_message",
  "metadata": {
    "prompt": "user's actual question or request"
  }
}
```

Store the returned `session_id` for subsequent messages.

**Phase 2: Connect to Your User System**

```json theme={null}
{
  "name": "user_message",
  "session_id": "your-conversation-id",
  "user_id": "your-user-id",
  "metadata": {
    "prompt": "user's actual question or request"
  }
}
```

**Phase 3: Additional Context**

```json theme={null}
{
  "name": "user_message",
  "session_id": "your-conversation-id",
  "user_id": "your-user-id",
  "metadata": {
    "prompt": "user's actual question or request",
    "platform": "mobile",
    "language": "en"
  }
}
```

### Monitor Data Quality

Regularly check that your events are providing value:

* **Topic diversity**: Are you seeing varied, meaningful topics?
* **Journey insights**: Are session\_ids producing useful journey patterns?
* **Event distribution**: Are some event types dominating?
* **Actionability**: Can you make product decisions from the insights?

## Common Patterns

### The Chat Application Pattern

Track each message in a conversation:

```javascript theme={null}
class ChatTracker {
  constructor(turretApiKey) {
    this.turret = new TurretClient(turretApiKey);
  }

  async onUserMessage(userMessage) {
    // Track the user's message
    await this.turret.track({
      name: 'user_message',
      session_id: this.sessionId,  // null on first message
      user_id: this.userId,
      metadata: { prompt: userMessage }
    }).then(data => {
      this.sessionId = data.session_id;
      this.userId = data.user_id;
    });
  }

  startNewConversation() {
    this.sessionId = null;  // New conversation = new session
  }
}
```

### The Search Application Pattern

Track search queries - each search session is a conversation:

```javascript theme={null}
// Each search session gets its own session_id
const { session_id } = await turret.track({
  name: 'search_query',
  metadata: { query: searchTerm }
});

// If user refines their search, include session_id
await turret.track({
  name: 'search_query',
  session_id,
  metadata: { query: refinedSearchTerm }
});
```

### The Support Ticket Pattern

Use ticket ID as session\_id for easy correlation:

```javascript theme={null}
await turret.track({
  name: 'support_message',
  session_id: ticketId,      // Your existing ticket ID
  user_id: customerId,       // Your existing customer ID
  metadata: { prompt: customerMessage }
});
```

## Measuring Success

### Key Metrics to Track

* **Topic insights actioned**: How many topic discoveries lead to product changes?
* **Journey optimization**: Have journey insights improved user flows?
* **User satisfaction correlation**: Do insights correlate with satisfaction improvements?
* **Support ticket reduction**: Are you catching issues before they become support requests?

### Regular Review Process

1. **Weekly**: Review new and growing topics, check journey patterns
2. **Monthly**: Analyze topic trends and journey correlations
3. **Quarterly**: Assess impact of insights-driven decisions
4. **Annually**: Review overall analytics strategy and goals

<Tip>
  The most successful teams treat Turret as a continuous insight engine. Combining topic clustering with journey tracking reveals powerful patterns that neither could surface alone.
</Tip>
