> ## 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.

# Tracking Events

> Learn how to track user events with metadata for semantic analysis and journey tracking

## Overview

Turret's event tracking captures user interactions along with free-form text metadata. The API analyzes this metadata to automatically group similar events into semantic clusters and track how users flow through different topics within conversations.

## Request Body

Every event sent to Turret has the following structure:

```json theme={null}
{
  "name": "user_prompt",
  "session_id": "conv_abc123",
  "user_id": "user_456",
  "metadata": {
    "prompt": "How do I reset my password?"
  }
}
```

## Field Reference

| Field        | Required       | Description                                                                                                                                               |
| ------------ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`       | Yes            | Event name (e.g., "user\_message", "search\_query"). Used to categorize events.                                                                           |
| `session_id` | Auto-generated | Your conversation/thread ID. Critical for journey tracking. If not provided, Turret generates a UUID and returns it in the response.                      |
| `user_id`    | Auto-generated | Your user's ID. Enables cross-session analysis. If not provided, Turret generates a UUID and returns it in the response.                                  |
| `metadata`   | Yes            | Object containing the data to cluster. The key (e.g., "prompt") becomes the clustering dimension. The value is the text that gets semantically clustered. |

## Response

Every successful request returns the IDs used for the event:

```json theme={null}
{
  "user_id": "550e8400-e29b-41d4-a716-446655440000",
  "session_id": "6fa459ea-ee8a-3ca4-894e-db77e160355e"
}
```

**Store these IDs** and include them in subsequent requests to maintain session continuity. This is how Turret connects multiple messages into a conversation journey.

## Authentication

All requests must include your API key in the `X-API-Key` header:

```bash theme={null}
-H "X-API-Key: your-api-key"
```

<Warning>
  Never expose your API key in client-side code. Always make requests from your backend services.
</Warning>

## Code Examples

### Minimal Integration

The simplest integration - just send the message, Turret handles the rest:

```javascript theme={null}
const response = await fetch('https://api.useturret.com/track', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': 'your-api-key'
  },
  body: JSON.stringify({
    name: 'user_message',
    metadata: {
      prompt: userMessage
    }
  })
});

const { user_id, session_id } = await response.json();
// Store these for subsequent messages in this conversation
```

### Recommended: Persist IDs for Journey Tracking

Store the returned IDs and include them in subsequent messages:

```javascript theme={null}
// First message - let Turret generate IDs
let sessionId = null;
let userId = null;

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

  const data = await response.json();

  // Store IDs for subsequent messages
  sessionId = data.session_id;
  userId = data.user_id;

  return data;
}
```

### Using Your Own IDs

If you already have conversation/user IDs, pass them directly:

```javascript theme={null}
await fetch('https://api.useturret.com/track', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': 'your-api-key'
  },
  body: JSON.stringify({
    name: 'user_message',
    session_id: myConversationId,  // Your existing conversation ID
    user_id: myUserId,             // Your existing user ID
    metadata: {
      prompt: userMessage
    }
  })
});
// Response still returns the IDs, but they'll match what you sent
```

### cURL

```bash theme={null}
# First message (no IDs - Turret generates them)
curl -X POST https://api.useturret.com/track \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "user_message",
    "metadata": {
      "prompt": "How do I reset my password?"
    }
  }'

# Response:
# {"user_id":"550e8400-e29b-41d4-a716-446655440000","session_id":"6fa459ea-ee8a-3ca4-894e-db77e160355e"}

# Subsequent messages (include IDs from response)
curl -X POST https://api.useturret.com/track \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "user_message",
    "session_id": "6fa459ea-ee8a-3ca4-894e-db77e160355e",
    "user_id": "550e8400-e29b-41d4-a716-446655440000",
    "metadata": {
      "prompt": "Thanks! How do I change my email?"
    }
  }'
```

### JavaScript

Using fetch in Node.js:

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

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

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const data = await response.json();

    // Store IDs for subsequent messages
    this.sessionId = data.session_id;
    this.userId = data.user_id;

    return data;
  }

  // Call this when starting a new conversation
  newConversation() {
    this.sessionId = null;
  }
}

// Usage
const turret = new TurretClient(process.env.TURRET_API_KEY);

// First message - IDs auto-generated
await turret.trackMessage('How do I reset my password?');

// Subsequent messages - same session
await turret.trackMessage('Thanks! How do I change my email?');

// New conversation
turret.newConversation();
await turret.trackMessage('What are your pricing plans?');
```

### Python

Using the requests library:

```python theme={null}
import requests
import os

class TurretClient:
    def __init__(self, api_key):
        self.api_key = api_key
        self.session_id = None
        self.user_id = None

    def track_message(self, prompt):
        response = requests.post(
            'https://api.useturret.com/track',
            headers={
                'X-API-Key': self.api_key,
                'Content-Type': 'application/json'
            },
            json={
                'name': 'user_message',
                'session_id': self.session_id,  # None on first call
                'user_id': self.user_id,        # None on first call
                'metadata': {'prompt': prompt}
            }
        )
        response.raise_for_status()
        data = response.json()

        # Store IDs for subsequent messages
        self.session_id = data['session_id']
        self.user_id = data['user_id']

        return data

    def new_conversation(self):
        """Call this when starting a new conversation"""
        self.session_id = None

# Usage
turret = TurretClient(os.getenv('TURRET_API_KEY'))

# First message - IDs auto-generated
turret.track_message('How do I reset my password?')

# Subsequent messages - same session
turret.track_message('Thanks! How do I change my email?')

# New conversation
turret.new_conversation()
turret.track_message('What are your pricing plans?')
```

## Multiple Metadata Keys

You can include additional context in your metadata:

```javascript theme={null}
await fetch('https://api.useturret.com/track', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': 'your-api-key'
  },
  body: JSON.stringify({
    name: 'chat_message',
    session_id: threadId,
    metadata: {
      prompt: userMessage,           // Text - clusters by user intent
      detected_language: 'spanish',  // Enum - used for segmentation
      platform: 'ios',               // Enum - used for segmentation
      response_time_ms: 1250         // Number - for aggregation
    }
  })
});
```

### Automatic Field Type Detection

Turret automatically analyzes your metadata fields and classifies them:

| Type       | Description                             | Usage                                |
| ---------- | --------------------------------------- | ------------------------------------ |
| **text**   | Free-form text content                  | Semantically clustered into topics   |
| **enum**   | Categorical values with limited options | Available for segmentation/filtering |
| **number** | Numeric values                          | Available for aggregation            |

<Info>
  Field types are detected automatically when you first send events with new metadata keys. You can override these classifications in Settings → Metadata if needed. See [Segmentation](/essentials/segmentation) for more details.
</Info>

## Event Naming Best Practices

Choose descriptive, consistent names for your events:

<CardGroup cols={2}>
  <Card title="Good Names" icon="check">
    * "user\_prompt"
    * "search\_query"
    * "chat\_message"
    * "feedback\_submitted"
  </Card>

  <Card title="Avoid" icon="x">
    * "Event1", "Event2"
    * "user\_action"
    * "something\_happened"
    * Names that are too generic
  </Card>
</CardGroup>

## How Clustering Works

When you send an event:

1. **Embedding Generation**: Turret generates a vector embedding for the metadata value (e.g., the prompt text) using an embedding model.

2. **Semantic Clustering**: Events are grouped into clusters based on embedding similarity. Similar prompts end up in the same cluster, even if they use different words:
   * "How do I reset my password?" and "I forgot my login credentials" → Same cluster

3. **Cluster Labeling**: Clusters are automatically labeled with human-readable descriptions using an LLM (e.g., "Password Reset Requests").

## How Journey Tracking Works

When events share the same `session_id`, Turret tracks transitions between clusters:

1. User asks about pricing (Cluster A)
2. User asks about features (Cluster B)
3. User asks about pricing again (Cluster A)

This creates a journey: **A → B → A**

Turret provides insights like:

* **Starting topics**: What do users ask about first?
* **Common follow-ups**: After topic X, users often ask about Y
* **Back-and-forth patterns**: Users bounce between topics A and B

## Rate Limits

Turret has generous rate limits to accommodate high-volume applications:

* **Free trial**: 250k total events (within 14 days)
* **Pro plan**: 1,000,000 events per month
* **Business plan**: 10,000,000 events per month
* **Enterprise**: Custom limits

If you exceed your rate limit, you'll receive a `429 Too Many Requests` response.

## Error Handling

Handle potential errors gracefully:

```javascript theme={null}
// Check response status
if (response.status === 429) {
  console.log('Rate limit exceeded, backing off...');
  // Implement exponential backoff
} else if (response.status === 401) {
  console.log('Invalid API key');
} else if (response.status === 400) {
  console.log('Invalid request format');
}
```

## Common Use Cases

### LLM Chatbot

Track each message a user sends to your AI chatbot:

```javascript theme={null}
// When user sends a message to your chatbot
const { session_id, user_id } = await turret.trackMessage(userMessage);

// Store session_id with your conversation state
// Include it in all subsequent messages in this conversation
```

### Search Applications

Track search queries to understand what users are looking for:

```json theme={null}
{
  "name": "search_query",
  "metadata": {
    "query": "Marvel funko pops"
  }
}
```

### Support Tickets

Track support conversations - use ticket ID as session\_id:

```json theme={null}
{
  "name": "support_message",
  "session_id": "ticket_12345",
  "user_id": "customer_789",
  "metadata": {
    "prompt": "I can't log into my account after the update"
  }
}
```
