Skip to main content

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:
{
  "name": "user_prompt",
  "session_id": "conv_abc123",
  "user_id": "user_456",
  "metadata": {
    "prompt": "How do I reset my password?"
  }
}

Field Reference

FieldRequiredDescription
nameYesEvent name (e.g., “user_message”, “search_query”). Used to categorize events.
session_idAuto-generatedYour conversation/thread ID. Critical for journey tracking. If not provided, Turret generates a UUID and returns it in the response.
user_idAuto-generatedYour user’s ID. Enables cross-session analysis. If not provided, Turret generates a UUID and returns it in the response.
metadataYesObject 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:
{
  "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:
-H "X-API-Key: your-api-key"
Never expose your API key in client-side code. Always make requests from your backend services.

Code Examples

Minimal Integration

The simplest integration - just send the message, Turret handles the rest:
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
Store the returned IDs and include them in subsequent messages:
// 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:
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

# 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:
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:
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:
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:
TypeDescriptionUsage
textFree-form text contentSemantically clustered into topics
enumCategorical values with limited optionsAvailable for segmentation/filtering
numberNumeric valuesAvailable for aggregation
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 for more details.

Event Naming Best Practices

Choose descriptive, consistent names for your events:

Good Names

  • “user_prompt”
  • “search_query”
  • “chat_message”
  • “feedback_submitted”

Avoid

  • “Event1”, “Event2”
  • “user_action”
  • “something_happened”
  • Names that are too generic

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:
// 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:
// 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:
{
  "name": "search_query",
  "metadata": {
    "query": "Marvel funko pops"
  }
}

Support Tickets

Track support conversations - use ticket ID as session_id:
{
  "name": "support_message",
  "session_id": "ticket_12345",
  "user_id": "customer_789",
  "metadata": {
    "prompt": "I can't log into my account after the update"
  }
}