# In-depth: Conversations The Sensay API provides access to conversation data and messages through specialized endpoints that use cursor-based pagination for efficient navigation through large message histories. Analytics endpoints provide aggregated insights into your replicas' usage patterns and performance metrics. ## Conversation mentions The mentions endpoint allows you to retrieve conversation messages grouped into "mention groups" (containing replica replies and context) and "placeholder groups" (representing collapsed user-only messages). ## Conversation messages The messages endpoint allows you to retrieve conversation messages. This is useful for expanding placeholders received from the mentions endpoint. ### Cursor-based pagination Conversation endpoints use cursor-based pagination with unique identifiers (UUIDs) to navigate through datasets. This approach is efficient for large datasets and handles concurrent changes gracefully. #### Parameters - `limit`: Number of items to fetch (1-100) - `beforeUUID`: Return items before this UUID (excluding the UUID itself) - `afterUUID`: Return items after this UUID (excluding the UUID itself) ### Basic usage **Get the first page (latest mentions):** ```bash curl -X GET "https://api.sensay.io/v1/replicas/{replicaUUID}/conversations/{conversationUUID}/mentions?limit=20" \ -H "X-ORGANIZATION-SECRET: $ORGANIZATION_SECRET" \ -H "X-API-Version: $API_VERSION" \ -H "Content-Type: application/json" ``` **Response:** ```json { "success": true, "items": [ { "type": "mention", "messages": [ { "uuid": "03db5651-cb61-4bdf-9ef0-89561f7c9c53", "content": "Hello, how are you?", "role": "user", "createdAt": "2024-09-24T09:09:55.66709+00:00", "source": "web", "replicaUUID": "f0e4c2f7-ae27-4b35-89bf-7cf729a73687" }, { "uuid": "04ea3f1b-df72-4c98-a8f1-2ef820b5c94d", "content": "I'm doing well, thank you for asking!", "role": "assistant", "createdAt": "2024-09-24T09:10:15.33421+00:00", "source": "web", "replicaUUID": "f0e4c2f7-ae27-4b35-89bf-7cf729a73687" } ] }, { "type": "placeholder", "count": 15 } ], "count": 2 } ``` ### Navigation **Get the next page (older mentions):** ```bash # Use the UUID of the last message from the previous response curl -X GET "https://api.sensay.io/v1/replicas/{replicaUUID}/conversations/{conversationUUID}/mentions?limit=20&beforeUUID=03db5651-cb61-4bdf-9ef0-89561f7c9c53" \ -H "X-ORGANIZATION-SECRET: $ORGANIZATION_SECRET" \ -H "X-API-Version: $API_VERSION" \ -H "Content-Type: application/json" ``` **Get newer mentions:** ```bash # Use the UUID of the first message from the current page curl -X GET "https://api.sensay.io/v1/replicas/{replicaUUID}/conversations/{conversationUUID}/mentions?limit=20&afterUUID=ec46b4db-3f0d-4392-b0f5-9fe327922e8a" \ -H "X-ORGANIZATION-SECRET: $ORGANIZATION_SECRET" \ -H "X-API-Version: $API_VERSION" \ -H "Content-Type: application/json" ``` #### Implementation pattern ```javascript async function loadNextPage(lastMessageUUID) { const response = await fetch( `/mentions?limit=20&beforeUUID=${lastMessageUUID}` ); return response.json(); } async function loadPreviousPage(firstMessageUUID) { const response = await fetch( `/mentions?limit=20&afterUUID=${firstMessageUUID}` ); return response.json(); } // Usage const firstPage = await fetch('/mentions?limit=20'); const data = await firstPage.json(); // Get the last message UUID for next page const lastMessage = data.items .filter(item => item.type === 'mention') .flatMap(mention => mention.messages) .pop(); if (lastMessage) { const nextPage = await loadNextPage(lastMessage.uuid); } ``` ### Placeholder expansion **Expand a placeholder chronologically before a known message UUID:** ```bash # Use the UUID of the first message after the placeholder curl -X GET "https://api.sensay.io/v1/replicas/{replicaUUID}/conversations/{conversationUUID}/messages?limit=20&beforeUUID=03db5651-cb61-4bdf-9ef0-89561f7c9c53" \ -H "X-ORGANIZATION-SECRET: $ORGANIZATION_SECRET" \ -H "X-API-Version: $API_VERSION" \ -H "Content-Type: application/json" ``` **Expand a placeholder chronologically after a known message UUID:** ```bash # Use the UUID of the last message before the placeholder curl -X GET "https://api.sensay.io/v1/replicas/{replicaUUID}/conversations/{conversationUUID}/mentions?limit=20&afterUUID=ec46b4db-3f0d-4392-b0f5-9fe327922e8a" \ -H "X-ORGANIZATION-SECRET: $ORGANIZATION_SECRET" \ -H "X-API-Version: $API_VERSION" \ -H "Content-Type: application/json" ``` #### Implementation pattern ```javascript async function expandPlaceholderBefore(firstMessageUUID) { const response = await fetch( `/messages?limit=20&afterUUID=${firstMessageUUID}` ); return response.json(); } async function expandPlaceholderAfter(lastMessageUUID) { const response = await fetch( `/messages?limit=20&afterUUID=${lastMessageUUID}` ); return response.json(); } // Usage const mentions = await fetch('/mentions?limit=20'); const { items } = await mentions.json(); const placeholders = items.reduce((acc, item, ix, array) => { if (item.type === 'placeholder') { const mentionBefore = array[ix - 1] const mentionAfter = array[ix + 1] const firstMessageBeforePlaceholder = mentionBefore?.messages.at(-1).uuid const firstMessageAfterPlaceholder = mentionAfter?.messages.at(-1).uuid acc.push({ ...item, firstMessageBeforePlaceholder, firstMessageAfterPlaceholder }) } return acc; }, []) // Expand the most recent placeholder const messages = await fetch(`/messages?afterUUID=${placeholders.at(-1).firstMessageBeforePlaceholder}`) ``` ## Understanding message types ### Mention items Mentions represent groups of messages where an interaction with the replica occurred: - `type`: Always "mention" - `messages`: Message items that are part of this mention ### Message items Messages represent actual conversation content with full details: - `uuid`: Unique identifier for cursor navigation - `content`: The message text - `role`: Either "user" or "assistant" - `createdAt`: Timestamp when the message was created - `source`: Where the message originated (web, telegram, discord, etc.) - `replicaUUID`: The replica involved in this conversation ### Placeholder items Placeholders represent collapsed groups of messages to improve navigation: - `type`: Always "placeholder" - `count`: Number of messages represented by this placeholder Placeholders typically represent stretches of user-only messages between assistant interactions, allowing you to focus on the most relevant parts of long conversations. ## Best practices ### Performance 1. **Choose appropriate limits:** - 10-50 items for UI lists - Up to 100 for data processing 2. **Cache responses when possible:** - Cursor positions are stable and cache-friendly - Message content rarely changes once created ### Error handling ```javascript // Handle invalid cursors try { const response = await fetch(`/mentions?beforeUUID=${uuid}`); if (response.status === 400) { // UUID doesn't exist in this conversation // Fall back to loading from the beginning return fetch('/mentions?limit=20'); } } catch (error) { console.error('Conversation pagination error:', error); } ``` ### UI considerations 1. **Loading states:** Always show loading indicators during pagination requests 2. **Empty states:** Handle conversations with no assistant interactions 3. **Error states:** Provide retry mechanisms for failed requests 4. **Real-time updates:** Consider how new messages affect cursor positions ## Conversation analytics The Sensay API provides analytics endpoints that offer insights into conversation patterns and usage across different communication channels for your replicas. ### Conversation Analytics overview The Conversation Analytics endpoints help you understand how your replicas are being used. They provide aggregated data about conversation volumes across conversation sources, and over time. ### Historical conversation analytics The historical analytics endpoint provides cumulative conversation count data over the last 30 days, showing how conversation volume grows over time for your replica. #### Basic usage **Get historical conversation data:** ```bash curl -X GET "https://api.sensay.io/v1/replicas/{replicaUUID}/analytics/conversations/historical" \ -H "X-ORGANIZATION-SECRET: $ORGANIZATION_SECRET" \ -H "X-API-Version: $API_VERSION" \ -H "Content-Type: application/json" ``` **Response:** ```json { success: true, items: [ { "date": "2025-05-01", "cumulativeConversations": 100 }, { "date": "2025-05-02", "cumulativeConversations": 103 }, { "date": "2025-05-03", "cumulativeConversations": 108 }, { "date": "2025-05-04", "cumulativeConversations": 115 } ] } ``` #### Understanding historical data - **Cumulative counts**: Each day shows the total number of conversations created up to that date - **30-day window**: Always returns exactly 30 data points for the last 30 days - **Historical inclusion**: Conversations from before the 30-day period are included in the cumulative totals - **Date format**: Dates are returned in YYYY-MM-DD format in ascending chronological order - **Aggregation boundaries**: All date boundaries are based on UTC midnight #### Implementation pattern ```javascript async function getHistoricalData(replicaUUID) { const response = await fetch( `/v1/replicas/${replicaUUID}/analytics/conversations/historical`, { headers: { 'X-ORGANIZATION-SECRET': process.env.ORGANIZATION_SECRET, 'X-API-Version': '2025-03-25', 'Content-Type': 'application/json' } } ); if (!response.ok) { throw new Error(`Analytics request failed: ${response.status}`); } return response.json(); } // Usage const data = await getHistoricalData('f0e4c2f7-ae27-4b35-89bf-7cf729a73687'); // Calculate daily growth const dailyGrowth = data.slice(1).map((day, index) => ({ date: day.date, newConversations: day.cumulativeConversations - data[index].cumulativeConversations })); ``` ### Source analytics The source analytics endpoint shows how conversations are distributed across different communication channels as of today, helping you understand which platforms your replica is most active on. #### Basic usage **Get conversation source distribution:** ```bash curl -X GET "https://api.sensay.io/v1/replicas/{replicaUUID}/analytics/conversations/sources" \ -H "X-ORGANIZATION-SECRET: $ORGANIZATION_SECRET" \ -H "X-API-Version: $API_VERSION" \ -H "Content-Type: application/json" ``` **Response:** ```json { "success": true, "items": [ { "source": "telegram", "conversations": 245 }, { "source": "discord", "conversations": 123 }, { "source": "web", "conversations": 89 }, { "source": "embed", "conversations": 34 } ] } ``` #### Available sources - `telegram`: Conversations from Telegram bot interactions - `discord`: Conversations from Discord bot interactions - `web`: Conversations from the web interface (sensay.io) - `embed`: Conversations from embedded widgets on websites #### Understanding source data - **Active sources only**: Only communication channels with at least one conversation are included - **Total conversations**: Each source shows the total number of conversations initiated from that platform - **Empty results**: Replicas with no conversations return an empty array #### Implementation pattern ```javascript async function getSourceAnalytics(replicaUUID) { const response = await fetch( `/v1/replicas/${replicaUUID}/analytics/conversations/sources`, { headers: { 'X-ORGANIZATION-SECRET': process.env.ORGANIZATION_SECRET, 'X-API-Version': '2025-03-25', 'Content-Type': 'application/json' } } ); if (!response.ok) { throw new Error(`Analytics request failed: ${response.status}`); } return response.json(); } // Usage const { items: sources } = await getSourceAnalytics('f0e4c2f7-ae27-4b35-89bf-7cf729a73687'); // Calculate percentages const totalConversations = sources.reduce((sum, source) => sum + source.conversations, 0); const sourcePercentages = sources.map(source => ({ ...source, percentage: ((source.conversations / totalConversations) * 100).toFixed(1) })); ``` ### Analytics Best practices #### Performance considerations 1. **Cache analytics data**: Analytics queries can be resource-intensive, so cache results when possible 2. **Rate limiting**: Be mindful of API rate limits when requesting analytics for multiple replicas 3. **Batch processing**: When analyzing multiple replicas, consider implementing batch processing with delays #### Error handling ```javascript async function safeAnalyticsRequest(endpoint, replicaUUID) { try { const response = await fetch(`/v1/replicas/${replicaUUID}/analytics/${endpoint}`); if (response.status === 404) { // Replica doesn't exist or no access return null; } if (response.status === 403) { // Insufficient permissions throw new Error('Access denied to replica analytics'); } if (!response.ok) { throw new Error(`Analytics request failed: ${response.status}`); } return await response.json(); } catch (error) { console.error('Analytics request error:', error); throw error; } } ``` ### Data visualization Historical data works well with line charts to show conversation growth trends: ```javascript // Prepare data for chart libraries function prepareHistoricalChart(data) { return { labels: data.map(d => d.date), datasets: [{ label: 'Cumulative Conversations', data: data.map(d => d.cumulativeConversations), borderColor: 'rgb(75, 192, 192)', tension: 0.1 }] }; } ``` Source data works well with pie charts or bar charts: ```javascript // Prepare data for source distribution charts function prepareSourceChart(data) { return { labels: data.map(d => d.source), datasets: [{ label: 'Conversations by Source', data: data.map(d => d.conversations), backgroundColor: [ 'rgb(255, 99, 132)', 'rgb(54, 162, 235)', 'rgb(255, 205, 86)', 'rgb(75, 192, 192)' ] }] }; } ``` ### Limitations - **Experimental status**: These endpoints are currently in experimental phase and may change - **Limited timeframe**: Historical data is limited to the last 30 days - **Aggregate data only**: Analytics provide summary statistics, not individual conversation details - **Access control**: Analytics are subject to the same replica access permissions as other endpoints