> This page is for Voice APIs.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://dev.hume.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://dev.hume.ai/_mcp/server.

# Chat History

EVI records detailed conversation histories, enabling developers to review and analyze past chat sessions. This guide
introduces **Chats** and **Chat Groups**, and explains how to retrieve chat transcripts, expression measurements, and
reconstructed audio.

> **Warning**
>
> If [data retention is disabled](/docs/resources/privacy#zero-data-retention-and-data-usage-options), chat history
> will not be recorded. This means past chat data and audio reconstructions will no longer be accessible or
> retrievable.

## Chats vs Chat Groups

EVI organizes conversation history into two levels: **Chats** and **Chat Groups**.

* **Chats** represent individual sessions, beginning when a WebSocket connection is established and ending when it
  closes. Each chat contains the messages and events recorded during that session.
* **Chat Groups** link related chats to maintain continuity across multiple sessions. A group can contain one or more
  chats, allowing conversations to persist even when users disconnect and reconnect.

By default, a new chat session creates a new chat group. If the session resumes a previous conversation, the new chat
is added to the existing chat group, preserving the full interaction history and context across sessions.

### Fetching Chats & Chat Groups

Each Chat has a unique `chat_id` and a `chat_group_id` that links it to its corresponding Chat Group. Similarly, each
Chat Group has its own ID, allowing you to retrieve individual sessions or entire sequences of related interactions.

**Chat ID**

Use the [list Chats](/reference/speech-to-speech-evi/chats/list-chats) endpoint to fetch chats. The returned `chat_id`
can be used to fetch chat details or resume a previous session.

#### cURL

```cURL
curl -G https://api.hume.ai/v0/evi/chats \
  -H "X-Hume-Api-Key: <YOUR_API_KEY>" \
  -d page_number=0 \
  -d page_size=10 \
  -d ascending_order=false
```

#### TypeScript

```TypeScript
import { HumeClient } from "hume";

const client = new HumeClient({ apiKey: "<YOUR_API_KEY>" });
const chats = await client.empathicVoice.chats.listChats({
  pageNumber: 0,
  pageSize: 10,
  ascendingOrder: false
});
```

#### Python

```Python
from hume import HumeClient

client = HumeClient(api_key="<YOUR_API_KEY>")
chats = client.empathic_voice.chats.list_chats(
    page_number=0,
    page_size=10,
    ascending_order=False,
)
```

**Chat Group ID**

Every chat includes a `chat_group_id` that identifies the group it belongs to. To fetch chat groups directly, use the
[list Chat Groups](/reference/speech-to-speech-evi/chat-groups/list-chat-groups) endpoint. This is useful for
retrieving all chats that are part of an ongoing conversation.

#### cURL

```cURL
curl -G https://api.hume.ai/v0/evi/chat_groups \
  -H "X-Hume-Api-Key: <YOUR_API_KEY>" \
  -d page_number=0 \
  -d page_size=1 \
  -d ascending_order=false
```

#### TypeScript

```TypeScript
import { HumeClient } from "hume";

const client = new HumeClient({ apiKey: "<YOUR_API_KEY>" });
const chatGroups = await client.empathicVoice.chats.listChatGroups({
  pageNumber: 0,
  pageSize: 10,
  ascendingOrder: false
});
```

#### Python

```Python
from hume import HumeClient

client = HumeClient(api_key="<YOUR_API_KEY>")
chat_groups = client.empathic_voice.chat_groups.list_chat_groups(
    page_number=0,
    page_size=10,
    ascending_order=False,
)
```

**From `chat_metadata`**

You can also extract both IDs at the start of every session via the
[chat\_metadata](https://dev.hume.ai/reference/speech-to-speech-evi/chat#receive.ChatMetadata) message. This
is useful for associating downstream actions or data with the active chat session.

#### chat\_metadata

```json {3,4}
{
  "type": "chat_metadata",
  "chat_group_id": "369846cf-6ad5-404d-905e-a8acb5cdfc78",
  "chat_id": "470a49f6-1dec-4afe-8b61-035d3b2d63b0",
  "request_id": "73c75efd-afa2-4e24-a862-91096b0961362258039"
}
```

### Viewing Chats in the Platform UI

You can also explore chat history and retrieve Chat IDs directly through the Platform UI:

1. Visit the [Chat history page](https://app.hume.ai/evi/chats) to see a paginated list of past chats. Each entry
   displays key information such as the Chat ID, timestamp, event count, and duration.

   ![Platform UI chat history page](/_fern-img/b2f77aa038b2505e21feb93bd49d20d94780217a95a1726b25b2e433613d9d58.webp)

2. Click **"Open details"** on any chat to view its full details. The chat details page includes the Chat ID, Chat Group ID,
   start and end timestamps, duration, status, associated Config ID (if applicable), and a paginated list of recorded
   chat events.

   ![Platform UI chat details page](/_fern-img/bd8d2dbff8e4d0cafba211b38d92a03b444084c5ef9156a184c3fcb030dc5df1.webp)

## Chat Events

Each Chat consists of a sequence of predefined events that represent everything that occurred during the session.

The table below outlines each event type and its purpose.

<table>
  <tbody>
    <tr>
      <th>
         Chat Event 
      </th>

      <th>
         Description 
      </th>
    </tr>

    <tr>
      <td>
         

        `SYSTEM_PROMPT`

         
      </td>

      <td>
         The system prompt used to initialize the session. 
      </td>
    </tr>

    <tr>
      <td>
         

        `CHAT_START_MESSAGE`

         
      </td>

      <td>
         Marks the beginning of the chat session. 
      </td>
    </tr>

    <tr>
      <td>
         

        `USER_RECORDING_START_MESSAGE`

         
      </td>

      <td>
         Marks when the client began streaming audio. 
      </td>
    </tr>

    <tr>
      <td>
         

        `USER_MESSAGE`

         
      </td>

      <td>
         A message sent by the user. 
      </td>
    </tr>

    <tr>
      <td>
         

        `USER_INTERRUPTION`

         
      </td>

      <td>
         A user-initiated interruption while the assistant is speaking. 
      </td>
    </tr>

    <tr>
      <td>
         

        `AGENT_MESSAGE`

         
      </td>

      <td>
         A response generated by the assistant. 
      </td>
    </tr>

    <tr>
      <td>
         

        `SESSION_SETTINGS`

         
      </td>

      <td>
        Marks when the client sent a [session\_settings](/reference/speech-to-speech-evi/chat#send.SessionSettings) message.
      </td>
    </tr>

    <tr>
      <td>
         

        `FUNCTION_CALL`

         
      </td>

      <td>
         A record of a tool invocation by the assistant. 
      </td>
    </tr>

    <tr>
      <td>
         

        `FUNCTION_CALL_RESPONSE`

         
      </td>

      <td>
         The result of a previously invoked function or tool. 
      </td>
    </tr>

    <tr>
      <td>
         

        `PAUSE_ONSET`

         
      </td>

      <td>
        Marks when the client sent a [`pause_assistant_message`](/reference/speech-to-speech-evi/chat#send.PauseAssistantMessage).
      </td>
    </tr>

    <tr>
      <td>
         

        `RESUME_ONSET`

         
      </td>

      <td>
        Marks when the client sent a [`resume_assistant_message`](/reference/speech-to-speech-evi/chat#send.ResumeAssistantMessage).
      </td>
    </tr>

    <tr>
      <td>
         

        `CHAT_END_MESSAGE`

         
      </td>

      <td>
         Indicates the end of the chat session. 
      </td>
    </tr>
  </tbody>
</table>

### Fetching Chat Events

The Chat Events API lets you retrieve detailed event data for a specific Chat or an entire Chat Group. Each event
represents a message, action, or system signal recorded during a session. You can use these endpoints to reconstruct
transcripts, analyze interactions, and extract emotion predictions.

#### Fetching events for a Chat

Use the [/chats/\{chat\_id}/events](/reference/speech-to-speech-evi/chats/list-chat-events) endpoint to fetch
events for a single Chat:

#### cURL

```cURL
curl -G https://api.hume.ai/v0/evi/chats/<YOUR_CHAT_ID> \
  -H "X-Hume-Api-Key: <YOUR_API_KEY>" \
  -d page_number=0 \
  -d page_size=10 \
  -d ascending_order=false
```

#### TypeScript

```TypeScript
import { HumeClient } from "hume";
import { ReturnChatEvent } from "hume/api/resources/empathicVoice";

async function fetchAllChatEvents(chatId: string): Promise<ReturnChatEvent[]> {
  const client = new HumeClient({ apiKey: process.env.HUME_API_KEY });
  const allChatEvents: ReturnChatEvent[] = [];

  // Retrieve an async iterator over all chat events
  const chatEventsIterator = await client.empathicVoice.chats.listChatEvents(chatId, {
    pageNumber: 0, // Start from the first page
  });

  // Collect all events from the iterator
  for await (const chatEvent of chatEventsIterator) {
    allChatEvents.push(chatEvent);
  }

  return allChatEvents;
}
```

#### Python

```Python
from hume import HumeClient
from hume.empathic_voice.types import ReturnChatEvent

async def fetch_all_chat_events(chat_id: str) -> list[ReturnChatEvent]:
    client = AsyncHumeClient(api_key=os.environ.get("HUME_API_KEY"))

    all_chat_events: list[ReturnChatEvent] = []
    # The response is an iterator over chat events
    response = await client.empathic_voice.chats.list_chat_events(id=chat_id, page_number=0)
    async for event in response:
        all_chat_events.append(event)
    return all_chat_events
```

#### Fetching events for a Chat Group

Use the [/chat\_groups/\{chat\_group\_id}/events](/reference/speech-to-speech-evi/chat-groups/list-chat-group-events)
endpoint to fetch events across Chats within a Chat Group:

#### cURL

```cURL
curl -G https://api.hume.ai/v0/evi/chats/<YOUR_CHAT_GROUP_ID> \
  -H "X-Hume-Api-Key: <YOUR_API_KEY>" \
  -d page_number=0 \
  -d page_size=10 \
  -d ascending_order=false
```

#### TypeScript

```TypeScript
import { HumeClient } from "hume";
import { ReturnChatEvent } from "hume/api/resources/empathicVoice";

async function fetchAllChatGroupEvents(chatGroupId: string): Promise<ReturnChatEvent[]> {
  const client = new HumeClient({ apiKey: process.env.HUME_API_KEY });
  const allChatGroupEvents: ReturnChatEvent[] = [];

  // Retrieve an async iterator over all chat events
  const chatGroupEventsIterator = await client.empathicVoice.chats.listChatGroupEvents(chatId);

  // Collect all events from the iterator
  for await (const chatGroupEvent of chatGroupEventsIterator) {
    allChatGroupEvents.push(chatGroupEvent);
  }

  return allChatGroupEvents;
}
```

#### Python

```Python
from hume import HumeClient
from hume.empathic_voice.types import ReturnChatEvent

async def fetch_all_chat_group_events(chat_id: str) -> list[ReturnChatEvent]:
    client = AsyncHumeClient(api_key=os.environ.get("HUME_API_KEY"))

    all_chat_group_events: list[ReturnChatEvent] = []
    # The response is an iterator over chat events
    response = await client.empathic_voice.chats.list_chat_group_events(id=chat_id)

    async for event in response:
        all_chat_group_events.append(event)
    return all_chat_group_events
```

### Parsing Chat Events

Chat events provide a structured record of each conversation, capturing both transcribed messages and expression
measures over time. Use this data to generate readable transcripts, analyze sentiment, and build visualizations of
user–assistant interactions.

The following examples show how to work with chat event data using the Hume SDKs:

#### [TypeScript Example](https://github.com/HumeAI/hume-api-examples/tree/main/evi/evi-typescript-chat-history)

Parse chat events, transcripts, and emotions with the TypeScript SDK.

#### [Python Example](https://github.com/HumeAI/hume-api-examples/tree/main/evi/evi-python-chat-history)

Extract transcripts and emotion data using the Python SDK.

#### Chat transcription

Conversation transcripts can be reconstructed from `USER_MESSAGE` and `AGENT_MESSAGE` events. These events include the
speaker's role, timestamp, and message text, allowing you to format the dialogue into a readable script.

The following example extracts a chat transcript from a list of events and writes it to a text file:

#### TypeScript

```typescript maxLines=0
import fs from "fs"; 
import { ReturnChatEvent } from "hume/api/resources/empathicVoice";

function generateTranscript(chatEvents: ReturnChatEvent[]): void {
  // Filter events for user and assistant messages
  const relevantChatEvents = chatEvents.filter(
    (chatEvent) => chatEvent.type === "USER_MESSAGE" || chatEvent.type === "AGENT_MESSAGE"
  );

  // Map each relevant event to a formatted line
  const transcriptLines = relevantChatEvents.map((chatEvent) => {
    const role = chatEvent.role === "USER" ? "User" : "Assistant";
    const timestamp = new Date(chatEvent.timestamp).toLocaleString();
    return `[${timestamp}] ${role}: ${chatEvent.messageText}`;
  });

  // Join all lines into a single transcript string
  const transcript =  transcriptLines.join("\n");
  // Define the transcript file name
  const transcriptFileName = `transcript_${CHAT_ID}.txt`;
  // Write the transcript to a text file
  try {
    fs.writeFileSync(transcriptFileName, transcript, "utf8");
    console.log(`Transcript saved to ${transcriptFileName}`);
  } catch (fileError) {
    console.error(
      `Error writing to file ${transcriptFileName}:`,
      fileError
    );
  }
}
```

#### Python

```python maxLines=0
import os 
from hume.empathic_voice.types import ReturnChatEvent

def generate_transcript(chat_events: list[ReturnChatEvent]) -> None:
  # Filter for user and assistant messages
  relevant_events = [e for e in chat_events if e.type in ("USER_MESSAGE", "AGENT_MESSAGE")]

  lines: list[str] = []
  for event in relevant_events:
      role = "User" if event.role == "USER" else "Assistant"
      timestamp = event.timestamp
      dt = datetime.fromtimestamp(timestamp / 1000.0)
      readable_time = dt.strftime("%Y-%m-%d %H:%M:%S")
      lines.append(f"[{readable_time}] {role}: {event.message_text}")

  transcript = "\n".join(lines)

  # Write the transcript to a text file
  transcript_file_name = f"transcript_{CHAT_ID}.txt"
  with open(transcript_file_name, "w", encoding="utf-8") as f:
      f.write(transcript)
  print(f"Transcript saved to {transcript_file_name}")
```

#### Expression measurement

Expression measurement predictions are stored in the `USER_MESSAGE` events under the `emotion_features` property.
These predictions provide confidence levels for various emotions detected in the user's speech.

For example, you might want to gauge the emotional tone of a conversation to better understand user sentiment. This
information can guide customer support strategies or highlight trends in the expression measurement predictions over
time.

The following example calculates the top 3 emotions from the `USER_MESSAGE` events by averaging their emotion scores
across the Chat session:

#### TypeScript

```typescript maxLines=0
import { ReturnChatEvent, EmotionScores } from "hume/api/resources/empathicVoice";

function getTopEmotions(chatEvents: ReturnChatEvent[]): Partial<EmotionScores> {
  // Extract user messages that have emotion features
  const userMessages = chatEvents.filter(
    (event) => event.type === "USER_MESSAGE" && event.emotionFeatures
  );

  const totalMessages = userMessages.length;

  // Infer emotion keys from the first user message
  const firstMessageEmotions = JSON.parse(userMessages[0].emotionFeatures!) as EmotionScores;
  const emotionKeys = Object.keys(firstMessageEmotions) as (keyof EmotionScores)[];

  // Initialize sums for all emotions to 0 (no extra type assertions needed)
  const emotionSums: Record<keyof EmotionScores, number> = Object.fromEntries(
    emotionKeys.map((key) => [key, 0])
  ) as Record<keyof EmotionScores, number>;

  // Accumulate emotion scores from each user message
  for (const event of userMessages) {
    const emotions = JSON.parse(event.emotionFeatures!) as EmotionScores;
    for (const key of emotionKeys) {
      emotionSums[key] += emotions[key];
    }
  }

  // Compute average scores for each emotion
  const averageEmotions = emotionKeys.map((key) => ({
    emotion: key,
    score: emotionSums[key] / totalMessages,
  }));

  // Sort by average score (descending) and pick the top 3
  averageEmotions.sort((a, b) => b.score - a.score);
  const top3 = averageEmotions.slice(0, 3);

  // Build a Partial<EmotionScores> with only the top 3 emotions
  const result: Partial<EmotionScores> = {};
  for (const { emotion, score } of top3) {
    result[emotion] = score;
  }

  return result;
}
```

#### Python

```python maxLines=0
from hume.empathic_voice.types import ReturnChatEvent

def get_top_emotions(chat_events: list[ReturnChatEvent]) -> dict[str, float]:
    # Filter user messages that have emotion features
    user_messages = [e for e in chat_events if e.type == "USER_MESSAGE" and e.emotion_features]

    total_messages = len(user_messages)

    # Parse the emotion features of the first user message to determine emotion keys
    first_message_emotions = cast(dict[str, float], json.loads(cast(str, user_messages[0].emotion_features)))
    emotion_keys: list[str] = list(first_message_emotions.keys())

    # Initialize sums for all emotions to 0
    emotion_sums = {key: 0.0 for key in emotion_keys}

    # Accumulate emotion scores from each user message
    for event in user_messages:
        emotions = json.loads(cast(str, event.emotion_features))
        for key in emotion_keys:
            emotion_sums[key] += emotions[key]

    # Compute average scores for each emotion
    average_emotions: list[EmotionScore] = [{"emotion": key, "score": emotion_sums[key] / total_messages} for key in emotion_keys]

    # Sort by average score (descending) and return top 3
    average_emotions.sort(key=lambda x: x["score"], reverse=True)
    top_3 = average_emotions[:3]

    # Convert top 3 into a dictionary of { emotion: score }
    return {item["emotion"]: item["score"] for item in top_3}
```

---