Jump to content

Connect SuperML | Leeroopedia MCP: Equip your AI agents with best practices, code verification, and debugging knowledge. Powered by Leeroo — building Organizational Superintelligence. Contact us at founders@leeroo.com.

Implementation:Elevenlabs Elevenlabs python WebhookSignatureVerification

From Leeroopedia
Field Value
sources src/elevenlabs/webhooks_custom.py
domains Webhook Security, HMAC-SHA256 Signature Verification, Event Construction
last_updated 2026-02-15

Overview

The WebhookSignatureVerification module provides custom webhook clients that extend the auto-generated ElevenLabs webhook clients. The primary purpose is to verify incoming webhook payloads using HMAC-SHA256 signature verification, ensuring that events originate from ElevenLabs and have not been tampered with.

Both synchronous (WebhooksClient) and asynchronous (AsyncWebhooksClient) variants are provided. Each exposes a single construct_event method that:

  1. Validates the presence of a signature header and webhook secret.
  2. Parses the t= (timestamp) and v0= (signature) components from the header.
  3. Rejects requests with timestamps older than 30 minutes (replay attack protection).
  4. Computes the expected HMAC-SHA256 digest of {timestamp}.{rawBody} using the webhook secret.
  5. Compares the computed digest against the provided signature.
  6. Returns the parsed JSON event payload if verification succeeds.

Typical usage:

from elevenlabs import ElevenLabs

client = ElevenLabs(api_key="your_api_key")
event = client.webhooks.construct_event(
    rawBody=request.body,
    sig_header=request.headers["elevenlabs-signature"],
    secret="your_webhook_secret",
)

Code Reference

Source file: src/elevenlabs/webhooks_custom.py

WebhooksClient (Synchronous)

class WebhooksClient(AutogeneratedWebhooksClient):
    def construct_event(self, rawBody: str, sig_header: str, secret: str) -> Dict: ...

AsyncWebhooksClient (Asynchronous)

class AsyncWebhooksClient(AutogeneratedAsyncWebhooksClient):
    def construct_event(self, rawBody: str, sig_header: str, secret: str) -> Dict: ...

Note: The AsyncWebhooksClient.construct_event method is not declared as async because all operations (HMAC computation, string parsing, JSON decoding) are CPU-bound and do not require awaiting I/O.

Import statement:

from elevenlabs.webhooks_custom import WebhooksClient, AsyncWebhooksClient

Dependencies

Module Usage
hmac HMAC computation for signature verification
hashlib SHA-256 hash algorithm
json Parsing the raw body into a Python dictionary
time Current time for timestamp tolerance check
elevenlabs.errors.BadRequestError Error raised on verification failure
elevenlabs.types.bad_request_error_body.BadRequestErrorBody Error body structure

I/O Contract

construct_event

Input Parameters:

Parameter Type Required Description
rawBody str Yes The raw webhook request body as a string. Must be the raw body, not a parsed JSON object.
sig_header str Yes The signature header from the webhook request (e.g., t=1234567890,v0=abcdef...).
secret str Yes Your webhook secret provided by ElevenLabs for HMAC verification.

Return Value:

Type Description
Dict The verified webhook event payload parsed from the raw body JSON string.

Raises:

Exception Condition Error Message
BadRequestError sig_header is empty or falsy "Missing signature header"
BadRequestError secret is empty or falsy "Webhook secret not configured"
BadRequestError No t= or v0= components found in header "No signature hash found with expected scheme v0"
BadRequestError Timestamp is older than 30 minutes "Timestamp outside the tolerance zone"
BadRequestError Computed HMAC does not match provided signature "Signature hash does not match the expected signature hash for payload"

Signature Format

The signature header follows the format:

t=<unix_timestamp_seconds>,v0=<hex_encoded_hmac_sha256>

The HMAC message is constructed as:

{timestamp}.{rawBody}

The digest is computed using:

"v0=" + hmac.new(secret.encode("utf-8"), message.encode("utf-8"), hashlib.sha256).hexdigest()

Timestamp Tolerance

The verification enforces a 30-minute tolerance window. The timestamp from the header (in seconds) is converted to milliseconds and compared against the current time minus 30 minutes:

req_timestamp = int(timestamp) * 1000
tolerance = int(time.time() * 1000) - 30 * 60 * 1000
if req_timestamp < tolerance:
    raise BadRequestError(...)

Usage Examples

Flask Webhook Endpoint

from flask import Flask, request, jsonify
from elevenlabs import ElevenLabs
from elevenlabs.errors import BadRequestError

app = Flask(__name__)
client = ElevenLabs(api_key="your_api_key")

WEBHOOK_SECRET = "whsec_your_secret_here"

@app.route("/webhook", methods=["POST"])
def handle_webhook():
    try:
        event = client.webhooks.construct_event(
            rawBody=request.get_data(as_text=True),
            sig_header=request.headers.get("elevenlabs-signature", ""),
            secret=WEBHOOK_SECRET,
        )
    except BadRequestError as e:
        return jsonify({"error": "Invalid signature"}), 400

    # Process the verified event
    event_type = event.get("type")
    print(f"Received event: {event_type}")
    return jsonify({"status": "ok"}), 200

FastAPI Webhook Endpoint

from fastapi import FastAPI, Request, HTTPException
from elevenlabs import ElevenLabs
from elevenlabs.errors import BadRequestError

app = FastAPI()
client = ElevenLabs(api_key="your_api_key")

WEBHOOK_SECRET = "whsec_your_secret_here"

@app.post("/webhook")
async def handle_webhook(request: Request):
    body = await request.body()
    sig_header = request.headers.get("elevenlabs-signature", "")

    try:
        event = client.webhooks.construct_event(
            rawBody=body.decode("utf-8"),
            sig_header=sig_header,
            secret=WEBHOOK_SECRET,
        )
    except BadRequestError as e:
        raise HTTPException(status_code=400, detail="Invalid webhook signature")

    # Process the verified event
    return {"status": "received", "event_type": event.get("type")}

Manual Verification for Testing

import hmac
import hashlib
import time
from elevenlabs import ElevenLabs

client = ElevenLabs(api_key="your_api_key")

# Simulate a webhook payload for testing
secret = "test_secret"
body = '{"type": "tts.completed", "data": {"id": "abc123"}}'
timestamp = str(int(time.time()))
message = f"{timestamp}.{body}"
signature = "v0=" + hmac.new(
    secret.encode("utf-8"),
    message.encode("utf-8"),
    hashlib.sha256,
).hexdigest()
sig_header = f"t={timestamp},{signature}"

# Verify it
event = client.webhooks.construct_event(
    rawBody=body,
    sig_header=sig_header,
    secret=secret,
)
print(event)  # {"type": "tts.completed", "data": {"id": "abc123"}}

Related Pages

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment