Webhooks | Hume API

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:



TypeScript Example

See EVI WebHooks implemented in TypeScript.](https://github.com/HumeAI/hume-api-examples/blob/main/evi/evi-typescript-webhooks/README.md)

Python Example

See EVI WebHooks implemented in Python.](https://github.com/HumeAI/hume-api-examples/blob/main/evi/evi-python-webhooks/README.md)

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

Payload structure

Field Type Description
event_name string Always "chat_started".
chat_group_id string Unique ID of the Chat Group associated with the Chat session.
chat_id string Unique ID of the Chat session.
config_id string Unique ID of the EVI Config used for the session.
caller_number string (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_id string (Optional) User-defined session ID. Relevant only when employing a custom language model in the EVI Config.
start_time integer Numeric Unix timestamp (in milliseconds) indicating when the session started.
chat_start_type string Indicates if the session is new ("new_chat_group") or resumed ("resumed_chat_group").

Sample payload

Sample payload

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

Chat ended

Trigger

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

Use cases

Payload structure

Field Type Description
event_name string Always "chat_ended".
chat_group_id string Unique ID of the Chat Group associated with the Chat session.
chat_id string Unique ID of the Chat session.
config_id string Unique ID of the EVI Config used for the session.
caller_number string (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_id string (Optional) User-defined session ID. Relevant only when employing a custom language model in the EVI Config.
end_time integer Numeric Unix timestamp (in milliseconds) indicating when the session ended.
duration_seconds integer Total duration of the session in seconds.
end_reason string Reason for the session’s termination (e.g., USER_ENDED, USER_TIMEOUT, MAX_DURATION_TIMEOUT, INACTIVITY_TIMEOUT, or ERROR).

Sample payload

Sample payload

{
    "event_name": "chat_ended",
    "chat_group_id": "9fc18597-3567-42d5-94d6-935bde84bf2f",
    "chat_id": "470a49f6-1dec-4afe-8b61-035d3b2d63b0",
    "config_id": "1b60e1a0-cc59-424a-8d2c-189d354db3f3",
    "caller_number": null,
    "custom_session_id": null,
    "end_time": 1716244958546,
    "duration_seconds": 180,
    "end_reason": "USER_ENDED"
}

Tool call

Trigger

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

Use cases

Payload structure

Field Type Description
event_name string Always "tool_call".
chat_group_id string Unique ID of the Chat Group associated with the Chat session.
chat_id string Unique ID of the Chat session.
config_id string Unique ID of the EVI Config used for the session.
caller_number string (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_id string (Optional) User-defined session ID. Relevant only when employing a custom language model in the EVI Config.
timestamp integer 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

{
  "event_name": "tool_call",
  "chat_group_id": "9fc18597-3567-42d5-94d6-935bde84bf2f",
  "chat_id": "470a49f6-1dec-4afe-8b61-035d3b2d63b0",
  "config_id": "1b60e1a0-cc59-424a-8d2c-189d354db3f3",
  "caller_number": null,
  "custom_session_id": "string",
  "timestamp": 1716244958546,
  "tool_call_message": {
    "name": "get_current_weather",
    "parameters": "{\"format\": \"fahrenheit\", \"location\": \"San Francisco, CA\"}",
    "response_required": true,
    "tool_call_id": "d20827af-5d8d-4f66-b6b9-ce2e3e1ea2b2",
    "tool_type": "function"
  }
}

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:

curl https://api.hume.ai/v0/evi/configs \
  -H "X-Hume-Api-Key: <YOUR_API_KEY>" \
  --json '{\
    "evi_version": "3",\
    "name": "Sample Webhook Config",\
    "webhooks": [{\
      "url": <YOUR_WEBHOOK_URL>,\
      "events": ["chat_started", "chat_ended", "tool_call"]\
    }]\
  }'

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.

import type { WebhookEvent } from "hume/serialization/resources/empathicVoice/types/WebhookEvent";

// Route to handle webhook events
app.post("/hume-webhook", (req: Request, res: Response) => {
  // Validate and parse using WebhookEvent
  const event = WebhookEvent.parseOrThrow(JSON.parse(req.body));

try {
    // Handle the specific event type
    switch (event.eventName) {
      case 'chat_started':
        console.info('Processing chat_started event:', event);
        // Add additional chat_started processing logic here
        break;

case 'chat_ended':
        console.info("Processing chat_ended event:", event);
        // Add additional chat_ended processing logic here
        break;

case 'tool_call':
        console.info("Processing tool_call event:", event);
        // Add additional tool_call processing logic here
        break;

default:
        res.status(400).json({
          error: `Unsupported event type: '${event.eventName}'`
        });
        return;
    }

res.json({
      status: "success",
      message: `${event.event_name} processed`
    });
  } catch (error) {
    console.error("Error processing event:", error);
    res.status(500).json({ error: "Internal server error" });
  }
});

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:

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:

import * as crypto from 'crypto';
import { IncomingHttpHeaders } from 'http';

export function validateWebhookHeaders(
  payload: string,
  headers: IncomingHttpHeaders,
): void {
  // Extract required headers
  const timestamp = headers['x-hume-ai-webhook-timestamp'] as string;
  if (!timestamp) {
    throw new Error('Missing timestamp header');
  }

const signature = headers['x-hume-ai-webhook-signature'] as string;
  if (!signature) {
    throw new Error('Missing signature header');
  }

// Validate HMAC signature using the webhook signing key
  const signingKey = process.env.HUME_WEBHOOK_SIGNING_KEY;
  if (!signingKey) {
    throw new Error('HUME_WEBHOOK_SIGNING_KEY is not set in environment variables');
  }

const message = `${payload}.${timestamp}`;
  const expectedSig = crypto
    .createHmac('sha256', signingKey)
    .update(message)
    .digest('hex');

const signatureBuffer = Buffer.from(signature, 'utf8');
  const expectedSigBuffer = Buffer.from(expectedSig, 'utf8');
  const validSignature =
    signatureBuffer.length === expectedSigBuffer.length &&
    crypto.timingSafeEqual(signatureBuffer, expectedSigBuffer);

if (!validSignature) {
    throw new Error('Invalid HMAC signature');
  }

// Validate timestamp to prevent replay attacks
  const timestampInt = parseInt(timestamp, 10);
  if (isNaN(timestampInt)) {
    throw new Error('Invalid timestamp format');
  }

const currentTime = Math.floor(Date.now() / 1000);
  const TIMESTAMP_VALIDATION_WINDOW = 180;
  if (currentTime - timestampInt > TIMESTAMP_VALIDATION_WINDOW) {
    throw new Error('The timestamp on the request is too old');
  }
}