or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

audio

audio-processing.mdrealtime-transcription.mdspeech-to-speech.mdspeech-to-text.mdtext-to-speech.md
index.md
tile.json

webhooks.mddocs/integration/

Webhooks

Manage workspace webhooks with HMAC-SHA256 signature verification for secure event notifications. Receive real-time updates about generation completions, errors, and other events.

Quick Reference

import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";

const client = new ElevenLabsClient({ apiKey: "your-api-key" });
// Access this API via: client.webhooks

Capabilities

Create Webhook

Create a new webhook for workspace events.

/**
 * @param request - Webhook URL and configuration
 * @param requestOptions - Optional request configuration
 * @returns Created webhook with secret
 * @throws UnprocessableEntityError if request fails
 */
client.webhooks.create(
  request: BodyCreateWorkspaceWebhookV1WorkspaceWebhooksPost,
  requestOptions?: RequestOptions
): HttpResponsePromise<WorkspaceCreateWebhookResponseModel>;

interface BodyCreateWorkspaceWebhookV1WorkspaceWebhooksPost {
  /** Webhook URL to receive events */
  url: string;
  /** Array of event types to subscribe to */
  events?: string[];
}

interface WorkspaceCreateWebhookResponseModel {
  /** Webhook ID */
  webhook_id: string;
  /** Webhook URL */
  url: string;
  /** Webhook secret for signature verification */
  secret: string;
  /** Subscribed event types */
  events: string[];
}

List Webhooks

List all workspace webhooks.

/**
 * @param request - Optional parameters
 * @param requestOptions - Optional request configuration
 * @returns List of webhooks
 * @throws UnprocessableEntityError if request fails
 */
client.webhooks.list(
  request?: WebhooksListRequest,
  requestOptions?: RequestOptions
): HttpResponsePromise<WorkspaceWebhookListResponseModel>;

interface WebhooksListRequest {
  /** Include usage statistics */
  includeUsages?: boolean;
}

interface WorkspaceWebhookListResponseModel {
  webhooks: WorkspaceWebhook[];
}

interface WorkspaceWebhook {
  webhook_id: string;
  url: string;
  events: string[];
  created_at: number;
  usage_count?: number;
  last_used_at?: number;
}

Update Webhook

Update webhook configuration.

/**
 * @param webhook_id - Webhook ID
 * @param request - Updated webhook configuration
 * @param requestOptions - Optional request configuration
 * @returns Updated webhook metadata
 * @throws UnprocessableEntityError if request fails
 */
client.webhooks.update(
  webhook_id: string,
  request: BodyUpdateWorkspaceWebhookV1WorkspaceWebhooksWebhookIdPatch,
  requestOptions?: RequestOptions
): HttpResponsePromise<PatchWorkspaceWebhookResponseModel>;

interface BodyUpdateWorkspaceWebhookV1WorkspaceWebhooksWebhookIdPatch {
  /** New webhook URL */
  url?: string;
  /** Updated event types */
  events?: string[];
}

interface PatchWorkspaceWebhookResponseModel {
  webhook_id: string;
  url: string;
  events: string[];
}

Delete Webhook

Delete a webhook.

/**
 * @param webhook_id - Webhook ID
 * @param requestOptions - Optional request configuration
 * @returns Deletion confirmation
 * @throws UnprocessableEntityError if request fails
 */
client.webhooks.delete(
  webhook_id: string,
  requestOptions?: RequestOptions
): HttpResponsePromise<DeleteWorkspaceWebhookResponseModel>;

interface DeleteWorkspaceWebhookResponseModel {
  success: boolean;
}

Verify Webhook Signature

Verify webhook signature and construct event.

/**
 * Verify webhook signature using HMAC-SHA256
 * @param rawBody - Raw request body as string
 * @param sigHeader - Signature header value
 * @param secret - Webhook secret
 * @returns Parsed and verified event
 * @throws Error if signature is invalid or timestamp is too old
 */
client.webhooks.constructEvent(
  rawBody: string,
  sigHeader: string,
  secret: string
): Promise<WebhookEvent>;

/**
 * Webhook event structure
 */
interface WebhookEvent {
  /** Event type identifier */
  event_type: string;
  /** Event data payload */
  data: Record<string, any>;
  /** Timestamp when event was created */
  created_at: string;
}

Webhook Event Types

Common webhook event types:

  • generation.completed - Audio generation completed successfully
  • generation.failed - Audio generation failed
  • dubbing.completed - Dubbing job completed
  • dubbing.failed - Dubbing job failed
  • voice.created - Voice was created
  • voice.deleted - Voice was deleted

Usage Examples

Create Webhook

import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";

const client = new ElevenLabsClient({ apiKey: "your-api-key" });

// Create webhook for generation events
const webhook = await client.webhooks.create({
  url: "https://your-server.com/webhook",
  events: ["generation.completed", "generation.failed"],
});

console.log("Webhook ID:", webhook.webhook_id);
console.log("Secret:", webhook.secret); // Store securely!

List Webhooks

// Get all webhooks with usage stats
const webhooks = await client.webhooks.list({
  includeUsages: true,
});

for (const webhook of webhooks.webhooks) {
  console.log(`${webhook.webhook_id}: ${webhook.url}`);
  console.log(`  Events: ${webhook.events.join(", ")}`);
  if (webhook.usage_count !== undefined) {
    console.log(`  Calls: ${webhook.usage_count}`);
  }
}

Update Webhook

// Update webhook URL or events
await client.webhooks.update("webhook-id", {
  url: "https://new-server.com/webhook",
  events: ["generation.completed", "dubbing.completed"],
});

Delete Webhook

// Remove webhook
await client.webhooks.delete("webhook-id");

Verify Webhook in Express

import express from "express";

const app = express();

// Important: Use express.raw() to get raw body for signature verification
app.post(
  "/webhook",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    const signature = req.headers["elevenlabs-signature"] as string;
    const webhookSecret = process.env.WEBHOOK_SECRET!;

    try {
      // Verify signature and parse event
      const event = await client.webhooks.constructEvent(
        req.body.toString(),
        signature,
        webhookSecret
      );

      console.log("Verified event:", event);

      // Handle event
      switch (event.type) {
        case "generation.completed":
          console.log("Generation completed:", event.data);
          break;
        case "generation.failed":
          console.error("Generation failed:", event.data);
          break;
        default:
          console.log("Unhandled event type:", event.type);
      }

      res.json({ received: true });
    } catch (error) {
      console.error("Webhook verification failed:", error);
      res.status(400).send("Webhook verification failed");
    }
  }
);

app.listen(3000);

Verify Webhook in Next.js

// pages/api/webhook.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";

const client = new ElevenLabsClient({ apiKey: process.env.ELEVENLABS_API_KEY! });

// Disable Next.js body parsing to get raw body
export const config = {
  api: {
    bodyParser: false,
  },
};

async function getRawBody(req: NextApiRequest): Promise<string> {
  const chunks: Buffer[] = [];
  for await (const chunk of req) {
    chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
  }
  return Buffer.concat(chunks).toString("utf8");
}

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method !== "POST") {
    return res.status(405).send("Method not allowed");
  }

  const signature = req.headers["elevenlabs-signature"] as string;
  const rawBody = await getRawBody(req);

  try {
    const event = await client.webhooks.constructEvent(
      rawBody,
      signature,
      process.env.WEBHOOK_SECRET!
    );

    // Handle event
    console.log("Webhook event:", event);

    res.status(200).json({ received: true });
  } catch (error) {
    console.error("Webhook error:", error);
    res.status(400).send("Webhook verification failed");
  }
}

Handle Generation Events

async function handleWebhookEvent(event: any): Promise<void> {
  switch (event.type) {
    case "generation.completed":
      const { history_item_id, voice_id, text } = event.data;
      console.log("Generation completed:", history_item_id);

      // Download the audio
      const audio = await client.history.getAudio(history_item_id);

      // Process audio...
      break;

    case "generation.failed":
      console.error("Generation failed:", event.data.error);
      // Handle failure...
      break;

    case "dubbing.completed":
      const { dubbing_id, target_languages } = event.data;
      console.log("Dubbing completed:", dubbing_id);

      // Download dubbed audio for each language
      for (const lang of target_languages) {
        const audio = await client.dubbing.audio.get(dubbing_id, lang);
        // Save audio...
      }
      break;
  }
}

Webhook with Retry Logic

// Server-side: Implement retry logic for webhook delivery
async function sendWebhook(
  url: string,
  event: any,
  secret: string,
  maxRetries = 3
): Promise<void> {
  const timestamp = Math.floor(Date.now() / 1000);
  const payload = JSON.stringify(event);

  // Create signature: HMAC-SHA256(timestamp + "." + payload)
  const crypto = require("crypto");
  const signature = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${payload}`)
    .digest("hex");

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "elevenlabs-signature": `t=${timestamp},v1=${signature}`,
        },
        body: payload,
      });

      if (response.ok) {
        console.log("Webhook delivered successfully");
        return;
      }

      console.log(`Webhook delivery failed (attempt ${attempt + 1}/${maxRetries})`);
    } catch (error) {
      console.error(`Webhook error (attempt ${attempt + 1}/${maxRetries}):`, error);
    }

    // Wait before retry (exponential backoff)
    if (attempt < maxRetries - 1) {
      await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, attempt)));
    }
  }

  throw new Error("Webhook delivery failed after all retries");
}

Log Webhook Events

// Log all webhook events to database
interface WebhookLog {
  id: string;
  webhook_id: string;
  event_type: string;
  payload: any;
  timestamp: Date;
  verified: boolean;
}

async function logWebhookEvent(
  webhookId: string,
  event: any,
  verified: boolean
): Promise<void> {
  const log: WebhookLog = {
    id: generateId(),
    webhook_id: webhookId,
    event_type: event.type,
    payload: event,
    timestamp: new Date(),
    verified,
  };

  // Save to database
  await saveToDatabase(log);
}

// In webhook handler
app.post("/webhook", async (req, res) => {
  const signature = req.headers["elevenlabs-signature"] as string;
  let verified = false;

  try {
    const event = await client.webhooks.constructEvent(
      req.body.toString(),
      signature,
      webhookSecret
    );

    verified = true;
    await logWebhookEvent("webhook-id", event, verified);

    // Handle event...

    res.json({ received: true });
  } catch (error) {
    await logWebhookEvent("webhook-id", req.body, verified);
    res.status(400).send("Verification failed");
  }
});

Subscribe to Multiple Events

// Create webhook for all major events
const webhook = await client.webhooks.create({
  url: "https://your-server.com/elevenlabs-webhook",
  events: [
    "generation.completed",
    "generation.failed",
    "dubbing.completed",
    "dubbing.failed",
    "voice.created",
    "voice.deleted",
  ],
});

Test Webhook

// Send test event to webhook
async function testWebhook(webhookUrl: string): Promise<void> {
  const testEvent = {
    type: "test",
    data: {
      message: "This is a test webhook event",
      timestamp: Date.now(),
    },
  };

  const response = await fetch(webhookUrl, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(testEvent),
  });

  if (response.ok) {
    console.log("Test webhook delivered successfully");
  } else {
    console.error("Test webhook failed:", await response.text());
  }
}

await testWebhook("https://your-server.com/webhook");