Guide to configuring the Empathic Voice Interface (EVI).

The Empathic Voice Interface (EVI) is designed to be highly configurable, allowing developers to customize the interface to align with their specific requirements. Configuration of EVI can be managed through two primary methods: an EVI configuration and session settings.

Configuration options

EVI configuration options affect the behavior and capabilities of the interface, and include the following configuration options:

OptionDescription
VoiceSelect a voice from a list of 8 preset options or create a custom voice. For further details, see our guide on creating custom voices.
EVI versionSelect the version of EVI you would like to use. For details on similarities and differences between EVI versions 1 and 2, refer to our feature comparison.
System promptProvide a system prompt to guide how EVI should respond. For details on expressive prompt engineering, refer to our prompting guide.
Language modelSelect a language model that best fits your application’s needs. For details on selecting a supplementary language model to meet your needs, such as optimizing for lowest latency, refer to our EVI FAQ. To incorporate your own language model, refer to our guide on using your own language model.
ToolsChoose user-created or built-in tools for EVI to use during conversations. For details on creating tools and adding them to your configuration, see our tool use guide.
Event messagesConfigure messages that EVI will send in specific situations. For details on configuring event messages, see our API Reference.
TimeoutsDefine limits on a chat with EVI to manage conversation flow. For details on specifying timeouts, see our API Reference.
WebhooksSpecify a webhook URL and subscribe to events, such as when a Chat session is started or ended. For details on subscribing to events, see our webhooks guide.

Configs, as well as system prompts and tools, are versioned. This versioning system supports iterative development, allowing you to progressively refine configurations and revert to previous versions if needed.

Default configuration options

EVI is pre-configured with a set of default values, which are automatically applied if you do not specify a configuration. The default configuration includes a preset voice and language model, but does not include a system prompt or tools. To customize these options, you will need to create and specify your own EVI configuration.

The default configuration settings are as follows:

EVI 1EVI 2
Language model: Claude 3.5 SonnetLanguage model: hume-evi-2
Voice: ItoVoice: Ito
System prompt: Hume defaultSystem prompt: Hume default
Tools: NoneTools: None

Default configuration settings are subject to change. To ensure your setup remains consistent should changes occur, we recommend choosing explicit options when defining your EVI configuration.

Create a configuration

See instructions below for creating an EVI configuration through the Portal. In the Portal, navigate to the EVI Configurations page. Click the Create configuration button to begin.

EVI configurations page without existing configs
1

Choose EVI version

To learn more about the differences between EVI versions 1 and 2, please see the feature comparison guide.

Version selection, the first step of EVI configuration
2

Choose voice

Select a voice from Hume’s 8 presets, or create your own custom voice. To learn more about voice customization options on the Hume Platform, please visit the Voices page.

Voice selection, the second step of EVI configuration
3

Set up the LLM

Select a supported language model and specify a system prompt.

Supplemental LLM setup, the third step of EVI configuration
4

Add tools

Equip EVI with built-in tools, like web search, or custom user-defined tools. Click the + Add button to select an existing tool or create a new one.

Tool use addition, the fourth step of EVI configuration
5

Name config

Name your EVI configuration and add an optional description.

Providing a name and description, the fifth and final step of EVI configuration
6

Test the configuration

The newly created configuration can now be tested. From the EVI Config details page, click Run in playground to test it out.

The page shown after a successful EVI configuration; the ID and name are displayed, and two buttons appear ("Run in playground" and "Edit configuration")
Successful EVI config creation

Once in the EVI Playground, click Start call to connect to EVI with your configuration.

EVI playground
Using an EVI configuration in the Playground

The event message and timeout configuration options are not part of the initial config creation flow. However, you can set these options at any time in the playground or from the configuration’s edit page after your configuration has been created.

Event message and timeout options in playground
Event message and timeout options in the Playground
Event message and timeout options on edit page
Event message and timeout options in the Config edit page
7

Apply the configuration

After creating an EVI configuration, you can use it in your conversations with EVI by including the config_id in the query parameters of your connection request. You can find the config_id on the configuration’s edit page. To access this page, first navigate to the configurations page and then click the Edit button for the desired configuration.

Configuration ID

See the sample code below which showcases how to apply your configuration:

1import { Hume, HumeClient } from 'hume';
2// instantiate the HumeClient with credentials
3// avoid hard coding your API key, retrieve from environment variables
4const client = new HumeClient({
5 apiKey: <YOUR_API_KEY>,
6 secretKey: <YOUR_SECRET_KEY>,
7});
8// instantiate WebSocket connection with specified EVI config
9const socket = await client.empathicVoice.chat.connect({
10 configId: <YOUR_CONFIG_ID> // specify config ID here
11});

Session settings

EVI configurations are persistent and version-controlled. In contrast, session settings are temporary and apply only to the current session, such as microphone settings. These parameters can be adjusted dynamically based on the requirements of each session to ensure optimal performance and user experience.

Refer to the API reference for detailed descriptions of the various session settings options.

Updating the session settings is only a requirement when the audio input is encoded in PCM Linear 16. If this is the case, be sure to send the following Session Settings message prior to sending an audio input:

SessionSettings
1{
2 "type": "session_settings",
3 "audio": {
4 "channels": 1,
5 "encoding": "linear16",
6 "sample_rate": 48000
7 }
8}

Webhooks

EVI webhooks send structured payloads to your specified URL in real time, allowing your application to respond to key events during EVI Chat sessions. They enable you to connect EVI with your systems to monitor events, automate workflows, and gain valuable insights into user interactions.

For more information on EVI Chat sessions, check out our Chat history guide.

Supported events

The following section details each supported event, including what triggers the event, the structure of its payload, and practical use cases to help you integrate it into your workflows.

Trigger: The chat_started event is triggered when a new Chat session is started. This includes both new and resumed sessions.

Use cases

  • Workflow initiation: Use this event to trigger workflows such as starting a logging session, updating a dashboard, or notifying a team.
  • Activity monitoring: Track when new or resumed sessions occur to measure usage trends or generate real-time analytics.
  • Custom integrations: Push session start data to third-party systems (e.g., Zapier) to automate downstream actions like data collection or tracking.

Payload structure

FieldTypeDescription
event_namestringAlways "chat_started".
chat_group_idstringUnique ID of the Chat Group associated with the Chat session.
chat_idstringUnique ID of the Chat session.
config_idstringUnique ID of the EVI Config used for the session.
caller_numberstring(Optional) Phone number of the caller in E.164 format (e.g., +12223333333). This field is included only if the Chat was created via the Twilio phone calling integration.
custom_session_idstring(Optional) User-defined session ID. Relevant only when employing a custom language model in the EVI Config.
start_timeintegerNumeric Unix timestamp (in milliseconds) indicating when the session started.
chat_start_typestringIndicates if the session is new ("new_chat_group") or resumed ("resumed_chat_group").

Sample payload

Sample payload
1{
2 "event_name": "chat_started",
3 "chat_group_id": "9fc18597-3567-42d5-94d6-935bde84bf2f",
4 "chat_id": "470a49f6-1dec-4afe-8b61-035d3b2d63b0",
5 "config_id": "1b60e1a0-cc59-424a-8d2c-189d354db3f3",
6 "caller_number": null,
7 "custom_session_id": null,
8 "start_time": 1716244940648,
9 "chat_start_type": "new_chat_group"
10}

Subscribing to events

To receive event notifications, define your webhook URL and specify the events you want to subscribe to within your EVI Config. The example below demonstrates how to configure a webhook URL for the chat_started and chat_ended events:

1curl -X POST https://api.hume.ai/v0/evi/configs \
2 -H "X-Hume-Api-Key: <YOUR_API_KEY>" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "evi_version": "2",
6 "name": "Sample Webhook Config",
7 "webhooks": [{
8 "url": <YOUR_WEBHOOK_URL>,
9 "events": ["chat_started", "chat_ended"]
10 }]
11 }'

Handling events

When EVI sends event payloads to your webhook URL, your application can process them by implementing a handler. Below are simplified example implementations in TypeScript and Python for handling chat_started and chat_ended events.

For complete implementations, check out our TypeScript Example Project and Python Example Project.

1import { WebhookEvent } from "hume/serialization/resources/empathicVoice/types/WebhookEvent";
2
3// Route to handle webhook events
4app.post("/hume-webhook", (req: Request, res: Response) => {
5 // Validate and parse using WebhookEvent
6 const event = WebhookEvent.parseOrThrow(JSON.parse(req.body));
7
8 try {
9 // Handle the specific event type
10 switch (event.eventName) {
11 case 'chat_started':
12 console.info('Processing chat_started event:', event);
13 // Add additional chat_started processing logic here
14 break;
15
16 case 'chat_ended':
17 console.info("Processing chat_ended event:", event);
18 // Add additional chat_ended processing logic here
19 break;
20
21 default:
22 console.warn(`[Event Handling] Unsupported event type: '${event.eventName}'`);
23 res.status(400).json({ error: `Unsupported event type: '${event.eventName}'` });
24 return;
25 }
26
27 res.json({ status: "success", message: `${event.event_name} processed` });
28 } catch (error) {
29 console.error("Error processing event:", error);
30 res.status(500).json({ error: "Internal server error" });
31 }
32});

Security

To ensure the authenticity and integrity of webhook payloads, EVI includes an HMAC signature and a timestamp in each request. Implementing verification safeguards your application from tampering and replay attacks.

Verifying Authenticity

Each webhook request contains the following headers:

  • X-Hume-AI-Webhook-Signature: HMAC-SHA256 signature of the payload and timestamp, signed with your Webhook Secret.
  • X-Hume-AI-Webhook-Timestamp: Unix timestamp indicating when the request was sent.

To verify authenticity:

  1. Retrieve the X-Hume-AI-Webhook-Signature and X-Hume-AI-Webhook-Timestamp headers.
  2. Concatenate the payload and timestamp, then compute the HMAC-SHA256 hash using your Webhook Secret.
  3. Compare the computed hash with the provided signature using a timing-safe comparison.
1import * as crypto from 'crypto';
2
3export function validateHmacSignature(payload: string, headers: IncomingHttpHeaders): void {
4 // Retrieve the timestamp and signature from headers
5 const timestamp = headers['x-hume-ai-webhook-timestamp'];
6 if (!timestamp) {
7 console.error('Error: Missing timestamp in the request headers.');
8 throw new Error('Missing timestamp header');
9 }
10
11 const signature = headers['x-hume-ai-webhook-signature'] as string;
12 if (!signature) {
13 console.error('Error: Missing signature in the request headers.');
14 throw new Error('Missing signature header');
15 }
16
17 // 2. Retrieve the API key from environment variables
18 const apiKey = process.env.HUME_API_KEY;
19 if (!apiKey) {
20 console.error('Error: HUME_API_KEY is not set in environment variables.');
21 throw new Error('Missing API key');
22 }
23
24 // 3. Construct the message to be hashed by concatenating the payload and the timestamp
25 const message = `${payload}.${timestamp}`;
26 const expectedSig = crypto
27 .createHmac('sha256', apiKey)
28 .update(message)
29 .digest('hex');
30
31 // 4. Compare the provided signature with the expected one using timing-safe comparison
32 const signatureBuffer = Buffer.from(signature, 'utf8');
33 const expectedSigBuffer = Buffer.from(expectedSig, 'utf8');
34 const validSignature =
35 signatureBuffer.length === expectedSigBuffer.length &&
36 crypto.timingSafeEqual(signatureBuffer, expectedSigBuffer);
37
38 // 5. If the signatures do not match, throw an error
39 if (!validSignature) {
40 console.error(`Error: Invalid HMAC signature. Expected: ${expectedSig}, Received: ${signature}`);
41 throw new Error('Invalid HMAC signature');
42 }
43
44 console.info('HMAC validation successful!');
45}
Preventing Replay Attacks

Validate the X-Hume-AI-Webhook-Timestamp header to ensure the request is recent:

  1. Check if the timestamp is within a predefined range (e.g., 3 minutes from the current time).
  2. Reject requests with timestamps outside this range.
1export function validateTimestamp(headers: IncomingHttpHeaders): void {
2 // 1. Retrieve the timestamp from the headers
3 const timestamp = headers['x-hume-ai-webhook-timestamp'] as string;
4 if (!timestamp) {
5 console.error('Error: Missing timestamp.');
6 throw new Error('Missing timestamp');
7 }
8
9 // 2. Attempt to parse the timestamp to a number
10 let timestampInt: number;
11 try {
12 timestampInt = parseInt(timestamp, 10);
13 if (isNaN(timestampInt)) {
14 // parseInt can return NaN if the string isn't a valid integer
15 throw new Error();
16 }
17 } catch (err) {
18 console.error(`Error: Invalid timestamp format: ${timestamp}`);
19 throw new Error('Invalid timestamp format');
20 }
21
22 // 3. Get the current time in seconds
23 const currentTime = Math.floor(Date.now() / 1000);
24
25 // 4. Check if the timestamp is more than 180 seconds behind the current time
26 const TIMESTAMP_VALIDATION_WINDOW = 180;
27 if (currentTime - timestampInt > TIMESTAMP_VALIDATION_WINDOW) {
28 console.error(`Error: The timestamp on the request is too old. Current time: ${currentTime}, Timestamp: ${timestamp}`);
29 throw new Error('The timestamp on the request is too old');
30 }
31
32 console.info('Timestamp validation successful!');
33}

Built with