> This is a page from the ElevenLabs documentation. For a complete page index, fetch https://elevenlabs.io/docs/llms.txt. For the full documentation in a single file, fetch https://elevenlabs.io/docs/llms-full.txt.

# Webhooks

## 概述

ElevenLabs 中的某些事件可配置为触发 webhook，让外部应用和系统能在事件发生时接收和处理它们。目前支持的事件类型包括：

| 事件类型                             | 说明                                                                                      |
| -------------------------------- | --------------------------------------------------------------------------------------- |
| `post_call_transcription`        | Agents Platform 通话已结束，且分析已完成                                                            |
| `voice_removal_notice`           | 已共享音色计划被移除                                                                              |
| `voice_removal_notice_withdrawn` | 已共享音色不再计划被移除                                                                            |
| `voice_removed`                  | 已共享音色已被移除，无法再使用                                                                         |
| `flows_generation`               | [Image & Video 生成](/docs/zh/eleven-api/guides/how-to/image-and-video/webhooks)已完成或失败    |
| `flows_template_run`             | [模板运行](/docs/zh/eleven-api/guides/how-to/templates/run-a-template#get-the-result)已完成或失败 |

## 配置

可在常规设置页面创建、禁用和删除 webhook。对于[工作区](/docs/zh/overview/administration/workspaces/overview)中的用户，只有工作区管理员可以配置该工作区的 webhook。

![HMAC webhook 配置](/docs/_fern-img/9ea298daac1c64eb43c802a12f7824e83accca44ba2edd1d01a39bcd62c0b9d6.webp)

创建后，可在 [Agents Platform](/docs/zh/eleven-agents/workflows/post-call-webhooks) 等产品设置中选择该 webhook 来监听事件。

`flows_generation` 和 `flows_template_run` 事件均由一个订阅启用：在 **Select events to listen to** 下，勾选 **Image & Video API generation or template run completed**，或将 `flows` 事件传给[更新工作区 webhook](/docs/zh/api-reference/webhooks/update)。只有在创建请求中包含 `webhook` 字段时，生成或运行才会发送其事件。

可随时从常规设置页面禁用 webhook。如果连续失败次数达到 10 次或以上，且最近一次成功传送超过 7 天前，或从未成功传送过，反复失败的 webhook 会被自动禁用。自动禁用的 webhook 需要在设置页面重新启用。未被任何产品使用的 webhook 可以删除。

## 重试

可为每个 webhook 启用重试，以便在请求失败时自动重新尝试传送。默认禁用重试。可通过 [API](/docs/zh/api-reference/webhooks/create) 创建或更新 webhook 时，或在 webhook 设置中启用重试。

> **Note**
>
> ElevenAgents [通话后 webhook](/docs/zh/eleven-agents/workflows/post-call-webhooks)支持重试，包括转写
> （`post_call_transcription`）、音频（`post_call_audio`）和通话发起失败
> （`call_initiation_failure`）事件。

### 重试计划

当传送尝试因可重试错误而失败时，系统最多重试 5 次，各次尝试之间的延迟逐渐增加：

| 尝试次数 | 延迟    |
| ---- | ----- |
| 1    | 立即    |
| 2    | 30 秒  |
| 3    | 2 分钟  |
| 4    | 8 分钟  |
| 5    | 30 分钟 |

每次重试都会加入少量随机抖动（最多为延迟的 10%），以分散负载并避免惊群问题。

### 可重试错误

并非所有失败都会触发重试。只有以下失败会被视为可重试：

* `5xx` 状态码（服务器错误，如 500、502、503、504）。
* `429`（请求过多）。
* `408`（请求超时）。
* 连接错误和请求超时。

`4xx` 范围内的请求错误（如 400、401、403、404）**不会**重试，因为它们通常表示需要手动修正的配置问题。

### 每个 webhook 的队列限制

每个 webhook 最多可有 100 个待处理的重试任务。如果某个 webhook 累积超过 100 个排队重试任务，在处理完现有重试前，额外任务会被丢弃。这可防止配置错误的单个 webhook 消耗过多资源。

音频 webhook 还有两个额外限制。大于 50 MiB 的音频负载仅传送一次，不会重试；单个 webhook 排队的音频重试总量不得超过 400 MiB。

### 自动禁用行为

系统会跟踪每个 webhook 的连续传送失败次数。满足以下两个条件时，webhook 会被自动禁用：

* 已发生 10 次或以上连续传送失败。
* webhook 从未成功传送过，或最近一次成功传送已超过 7 天。

webhook 被自动禁用时，工作区管理员会收到邮件通知。必须在设置页面手动重新启用该 webhook 后，才会恢复传送。

## 集成

要集成 webhook，请创建一个端点处理程序，以 POST 请求接收 webhook 事件数据。验证签名后，处理程序应及时返回 HTTP 200，表示已成功接收。反复未能返回成功响应可能导致 webhook 被自动禁用。

重试负载与原始传送尝试完全相同。webhook 消费者无法仅通过负载区分初始传送和重试，因此请将处理程序设计为**幂等**——多次处理同一事件应产生相同结果。必要时可使用 `event_timestamp` 和事件特定标识符（如 `conversation_id`）去重。

### 顶层字段

| 字段                | 类型     | 说明       |
| ----------------- | ------ | -------- |
| `type`            | string | 事件类型     |
| `data`            | object | 事件数据     |
| `event_timestamp` | string | 此事件发生的时间 |

## webhook 负载示例

```json
{
  "type": "post_call_transcription",
  "event_timestamp": 1739537297,
  "data": {
    "agent_id": "xyz",
    "conversation_id": "abc",
    "status": "done",
    "transcript": [
      {
        "role": "agent",
        "message": "Hey there angelo. How are you?",
        "tool_calls": null,
        "tool_results": null,
        "feedback": null,
        "time_in_call_secs": 0,
        "conversation_turn_metrics": null
      },
      {
        "role": "user",
        "message": "Hey, can you tell me, like, a fun fact about 11 Labs?",
        "tool_calls": null,
        "tool_results": null,
        "feedback": null,
        "time_in_call_secs": 2,
        "conversation_turn_metrics": null
      },
      {
        "role": "agent",
        "message": "I do not have access to fun facts about Eleven Labs. However, I can share some general information about the company. Eleven Labs is an AI voice technology platform that specializes in voice cloning and text-to-speech...",
        "tool_calls": null,
        "tool_results": null,
        "feedback": null,
        "time_in_call_secs": 9,
        "conversation_turn_metrics": {
          "convai_llm_service_ttfb": {
            "elapsed_time": 0.3704247010173276
          },
          "convai_llm_service_ttf_sentence": {
            "elapsed_time": 0.5551181449554861
          }
        }
      }
    ],
    "metadata": {
      "start_time_unix_secs": 1739537297,
      "call_duration_secs": 22,
      "cost": 296,
      "deletion_settings": {
        "deletion_time_unix_secs": 1802609320,
        "deleted_logs_at_time_unix_secs": null,
        "deleted_audio_at_time_unix_secs": null,
        "deleted_transcript_at_time_unix_secs": null,
        "delete_transcript_and_pii": true,
        "delete_audio": true
      },
      "feedback": {
        "overall_score": null,
        "likes": 0,
        "dislikes": 0
      },
      "authorization_method": "authorization_header",
      "charging": {
        "dev_discount": true
      },
      "termination_reason": ""
    },
    "analysis": {
      "evaluation_criteria_results": {},
      "data_collection_results": {},
      "call_successful": "success",
      "transcript_summary": "The conversation begins with the agent asking how Angelo is, but Angelo redirects the conversation by requesting a fun fact about 11 Labs. The agent acknowledges they don't have specific fun facts about Eleven Labs but offers to provide general information about the company. They briefly describe Eleven Labs as an AI voice technology platform specializing in voice cloning and text-to-speech technology. The conversation is brief and informational, with the agent adapting to the user's request despite not having the exact information asked for."
    },
    "conversation_initiation_client_data": {
      "conversation_config_override": {
        "agent": {
          "prompt": null,
          "first_message": null,
          "language": "en"
        },
        "tts": {
          "voice_id": null
        }
      },
      "custom_llm_extra_body": {},
      "dynamic_variables": {
        "user_name": "angelo"
      }
    }
  }
}
```

## 身份验证

监听器必须验证所有传入的 webhook。Webhook 目前支持通过 HMAC 签名进行身份验证。可按以下方式设置 HMAC 身份验证：

* 安全存储创建 webhook 时生成的共享密钥
* 使用 SDK 在端点中验证 ElevenLabs-Signature 请求头

JavaScript SDK 提供 `constructEvent`；Python SDK 提供 `construct_event`，并使用 **`rawBody`**、**`sig_header`** 和 **`secret`**（在 Python 中，它们不叫 `payload` / `signature`）。两者都会验证签名、验证时间戳，并解析 JSON 负载。

#### Python

使用 FastAPI 的 webhook 处理程序示例：

```python
from dotenv import load_dotenv
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from elevenlabs.client import ElevenLabs
from elevenlabs.errors import BadRequestError
import os

load_dotenv()

app = FastAPI()
elevenlabs = ElevenLabs(
    api_key=os.getenv("ELEVENLABS_API_KEY"),
)

WEBHOOK_SECRET = os.getenv("WEBHOOK_SECRET")

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

    try:
        event = elevenlabs.webhooks.construct_event(
            rawBody=payload.decode("utf-8"),
            sig_header=signature,
            secret=WEBHOOK_SECRET,
        )
    except BadRequestError as e:
        return JSONResponse(content={"error": "Invalid signature"}, status_code=401)

    # construct_event returns a dict (parsed JSON), not an object with attributes
    if event.get("type") == "post_call_transcription":
        print(f"Received transcription: {event.get('data')}")

    return {"status": "received"}
```

#### JavaScript

#### Express

使用 Express 的 webhook 处理程序示例：

```javascript
import { ElevenLabsClient } from '@elevenlabs/elevenlabs-js';
import express from 'express';

const app = express();

const elevenlabs = new ElevenLabsClient();
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;

// Use express.text() to preserve raw body for signature verification
app.post('/webhook', express.text({ type: 'application/json' }), async (req, res) => {
  const signature = req.headers['elevenlabs-signature'];
  const payload = req.body; // Raw string body

  let event;
  try {
    event = await elevenlabs.webhooks.constructEvent(payload, signature, WEBHOOK_SECRET);
  } catch (error) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Process the webhook event
  if (event.type === 'post_call_transcription') {
    console.log('Received transcription:', event.data);
  }

  res.status(200).json({ received: true });
});
```

#### Next.js

使用 Next.js API 路由的 webhook 处理程序示例：

**`app/api/webhook/route.ts`**

```typescript app/api/webhook/route.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { ElevenLabsClient } from '@elevenlabs/elevenlabs-js';

const elevenlabs = new ElevenLabsClient();
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;

export async function POST(req: NextRequest) {
  const body = await req.text();
  const signature = req.headers.get('elevenlabs-signature');

  let event;
  try {
    event = await elevenlabs.webhooks.constructEvent(body, signature, WEBHOOK_SECRET);
  } catch (error) {
    return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
  }

  // Process the webhook event
  if (event.type === 'post_call_transcription') {
    console.log('Received transcription:', event.data);
  }

  return NextResponse.json({ received: true }, { status: 200 });
}
```