Webhooks

Guide to EVI's webhooks configuration option.

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.

Looking for example code? See example projects in TypeScript and Python on GitHub:

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.

Chat started

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_namestring

Always "chat_started".

chat_group_idstring

Unique ID of the Chat Group associated with the Chat session.

chat_idstring

Unique ID of the Chat session.

config_idstring

Unique 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_timeinteger

Numeric Unix timestamp (in milliseconds) indicating when the session started.

chat_start_typestring

Indicates 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}

Chat ended

Trigger

The chat_ended event is triggered when a Chat session is ended.

Use cases

  • Analytics: Measure session durations and analyze reasons for chat termination to improve performance or user experience.
  • Workflow automations: Automatically process transcripts or save session data to external systems for further analysis or reporting.
  • Error monitoring: Track sessions that terminate with an error or timeout to identify and address recurring issues.

Payload structure

FieldTypeDescription
event_namestring

Always "chat_ended".

chat_group_idstring

Unique ID of the Chat Group associated with the Chat session.

chat_idstring

Unique ID of the Chat session.

config_idstring

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

end_timeinteger

Numeric Unix timestamp (in milliseconds) indicating when the session ended.

duration_secondsinteger

Total duration of the session in seconds.

end_reasonstring

Reason for the session’s termination (e.g., USER_ENDED, USER_TIMEOUT, MAX_DURATION_TIMEOUT, INACTIVITY_TIMEOUT, or ERROR.).

Sample payload

Sample payload
1{
2 "event_name": "chat_ended",
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 "end_time": 1716244958546,
9 "duration_seconds": 180,
10 "end_reason": "USER_ENDED"
11}

Tool call

Trigger

The tool_call event is triggered when a tool call is made during a Chat session.

Use cases

  • Server-side tool use: Invoke your tools server-side and send tool responses to EVI via the Control Plane API.
  • Analytics: Track tool calls to measure usage patterns and identify popular tools.
  • Error monitoring: Track tool calls that fail to complete to identify and address recurring issues.

Payload structure

FieldTypeDescription
event_namestring

Always "tool_call".

chat_group_idstring

Unique ID of the Chat Group associated with the Chat session.

chat_idstring

Unique ID of the Chat session.

config_idstring

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

timestampinteger

Numeric Unix timestamp (in milliseconds) indicating when the tool call message was sent.

tool_call_message[name]string

Name of the tool call’s corresponding tool.

tool_call_message[parameters]string

Parameters of the tool call. Is a stringified JSON schema.

tool_call_message[response_required]boolean

Indicates whether a response to the tool call is required from the developer, either in the form of a Tool Response message or a Tool Error message.

tool_call_message[tool_call_id]string

The unique identifier for the specific tool call instance.

tool_call_message[tool_type]enum

Type of tool called. Either builtin for natively implemented tools, like web search, or function for user-defined tools.

Sample payload

Sample payload
1{
2 "event_name": "tool_call",
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": "string",
8 "timestamp": 1716244958546,
9 "tool_call_message": {
10 "name": "get_current_weather",
11 "parameters": "{\"format\": \"fahrenheit\", \"location\": \"San Francisco, CA\"}",
12 "response_required": true,
13 "tool_call_id": "d20827af-5d8d-4f66-b6b9-ce2e3e1ea2b2",
14 "tool_type": "function"
15 }
16}

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 https://api.hume.ai/v0/evi/configs \
2 -H "X-Hume-Api-Key: <YOUR_API_KEY>" \
3 --json '{
4 "evi_version": "3",
5 "name": "Sample Webhook Config",
6 "webhooks": [{
7 "url": <YOUR_WEBHOOK_URL>,
8 "events": ["chat_started", "chat_ended", "tool_call"]
9 }]
10 }'

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.

1import type { 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 case 'tool_call':
22 console.info("Processing tool_call event:", event);
23 // Add additional tool_call processing logic here
24 break;
25
26 default:
27 res.status(400).json({
28 error: `Unsupported event type: '${event.eventName}'`
29 });
30 return;
31 }
32
33 res.json({
34 status: "success",
35 message: `${event.event_name} processed`
36 });
37 } catch (error) {
38 console.error("Error processing event:", error);
39 res.status(500).json({ error: "Internal server error" });
40 }
41});

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.

Webhook signing key

Each webhook payload is signed with a dedicated webhook signing key. The signing key is a stable, per-account secret that does not change when you rotate your API key or switch authentication methods.

To provision your webhook signing key:

  1. Navigate to the Developers page on the Hume platform.
  2. Click Generate signing key.
  3. Copy the key and store it securely. You will use this key to verify incoming webhook payloads.

If you generate a new signing key, the previous key is immediately invalidated. Any webhook payloads signed with the old key will fail verification. For organizations, only admins can generate and rotate the webhook signing key.

Verifying authenticity

Each webhook request contains the following headers:

  • X-Hume-AI-Webhook-Signature: HMAC-SHA256 signature of the payload and timestamp, signed using your webhook signing key.
  • 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 signing key.
  3. Compare the computed hash with the provided signature using a timing-safe comparison.

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.

Example

The following example combines both signature verification and timestamp validation into a single function:

1import * as crypto from 'crypto';
2import { IncomingHttpHeaders } from 'http';
3
4export function validateWebhookHeaders(
5 payload: string,
6 headers: IncomingHttpHeaders,
7): void {
8 // Extract required headers
9 const timestamp = headers['x-hume-ai-webhook-timestamp'] as string;
10 if (!timestamp) {
11 throw new Error('Missing timestamp header');
12 }
13
14 const signature = headers['x-hume-ai-webhook-signature'] as string;
15 if (!signature) {
16 throw new Error('Missing signature header');
17 }
18
19 // Validate HMAC signature using the webhook signing key
20 const signingKey = process.env.HUME_WEBHOOK_SIGNING_KEY;
21 if (!signingKey) {
22 throw new Error('HUME_WEBHOOK_SIGNING_KEY is not set in environment variables');
23 }
24
25 const message = `${payload}.${timestamp}`;
26 const expectedSig = crypto
27 .createHmac('sha256', signingKey)
28 .update(message)
29 .digest('hex');
30
31 const signatureBuffer = Buffer.from(signature, 'utf8');
32 const expectedSigBuffer = Buffer.from(expectedSig, 'utf8');
33 const validSignature =
34 signatureBuffer.length === expectedSigBuffer.length &&
35 crypto.timingSafeEqual(signatureBuffer, expectedSigBuffer);
36
37 if (!validSignature) {
38 throw new Error('Invalid HMAC signature');
39 }
40
41 // Validate timestamp to prevent replay attacks
42 const timestampInt = parseInt(timestamp, 10);
43 if (isNaN(timestampInt)) {
44 throw new Error('Invalid timestamp format');
45 }
46
47 const currentTime = Math.floor(Date.now() / 1000);
48 const TIMESTAMP_VALIDATION_WINDOW = 180;
49 if (currentTime - timestampInt > TIMESTAMP_VALIDATION_WINDOW) {
50 throw new Error('The timestamp on the request is too old');
51 }
52}