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

# इमेज और वीडियो वेबहुक्स

> **Note**
>
> **कैसे करें गाइड** · यह मानकर चलता है कि आपने [इमेज और वीडियो क्विकस्टार्ट](/docs/hi/eleven-api/guides/cookbooks/image-and-video) पूरा कर लिया है।

## ओवरव्यू

वीडियो जनरेशन में कई मिनट लग सकते हैं, जिससे पोलिंग कनेक्शन खुला रखना महंगा हो जाता है। किसी
generation के लिए webhook डिलीवरी चुनें और जनरेशन के `completed` या `failed` होने पर ElevenLabs आपके endpoint पर `flows_generation` इवेंट भेजता है।

इवेंट payload संबंधित GET endpoint का अंतिम response होता है, इसलिए जो handler पहले से
polling response को समझता है, उसे अलग parsing path की जरूरत नहीं होती।

## शुरू करने से पहले

Webhook डिलीवरी के लिए आपके workspace में generation events के लिए subscribed webhooks इस्तेमाल होते हैं। इसे
सेट अप करने के दो चरण हैं: webhook बनाएं, फिर उसे इवेंट के लिए subscribe करें।

#### वेबहुक बनाएं

[**डेवलपर्स** > **वेबहुक्स**](https://elevenlabs.io/app/developers/webhooks) पर जाएं और सार्वजनिक रूप से उपलब्ध HTTPS callback URL के साथ एक
webhook बनाएं। मिलने वाला signing secret संभालकर रखें; incoming events को verify करने के लिए इसकी जरूरत होगी।

#### इसे जनरेशन इवेंट्स के लिए सब्सक्राइब करें

**सुनने के लिए इवेंट चुनें** में **इमेज और वीडियो API जनरेशन या टेम्पलेट रन
पूरा हुआ** को चुनें। यही इवेंट पूरे हुए [टेम्पलेट रन](/docs/hi/eleven-api/guides/how-to/templates/run-a-template) भी भेजता है।
मौजूद होने पर भी, अगर webhook इस इवेंट के लिए subscribed नहीं है, तो उसे कभी कॉल नहीं किया जाता।

API से भी ऐसा किया जा सकता है—इसके लिए `flows` इवेंट को
[वर्कस्पेस वेबहुक अपडेट करें](/docs/hi/api-reference/webhooks/update) में पास करें:

```json
{
  "events": ["flows"]
}
```

Webhook बनाने और subscribe करने के लिए Webhooks Manage permission या workspace admin होना जरूरी है। एक
इवेंट के लिए अधिकतम 10 webhooks स्वीकार किए जाते हैं; इसके बाद request `too_many_webhooks` के साथ fail हो जाती है।

अगर किसी generation में webhook डिलीवरी मांगी जाती है, लेकिन generation events के लिए कोई webhook subscribed नहीं है, तो उसे
reject कर दिया जाता है। इस तरह नतीजा कभी ऐसी जगह के लिए generate नहीं होता जहां उसे भेजा न जा सके।

## वेबहुक डिलीवरी का अनुरोध करें

Create request में एक `webhook` object जोड़ें। generation events के लिए subscribed हर webhook को डिलीवर करने के लिए `{"type": "all"}` इस्तेमाल करें। इससे webhooks जोड़े या बदले जाने पर भी request स्थिर रहती है।

```python
from elevenlabs import VideoGenerationRequest_Veo31FastGenerate001, WebhookTarget_All

generation = elevenlabs.flows.video.create(
    request=VideoGenerationRequest_Veo31FastGenerate001(
        prompt="A corgi rides a tiny surfboard across a sunlit wave at golden hour, cinematic",
        duration_secs=8,
        webhook=WebhookTarget_All(),
    )
)
```

```typescript
const generation = await elevenlabs.flows.video.create({
  modelId: "veo-3.1-fast-generate-001",
  prompt: "A corgi rides a tiny surfboard across a sunlit wave at golden hour, cinematic",
  durationSecs: 8,
  webhook: { type: "all" },
});
```

इसके बजाय खास webhooks को target करने के लिए `webhook` field में IDs की सूची सेट करें। हर ID,
workspace के ऐसे webhook की होनी चाहिए जो generation events के लिए subscribed हो।

```json
{
  "webhook": {
    "type": "ids",
    "ids": ["Q8mVr2LpXcT4nB6yJdKw"]
  }
}
```

Create request generation शुरू करने से पहले target को validate करती है और जब डिलीवरी संभव न हो,
tो error लौटाती है:

| Error status             | कारण                                                                                    |
| ------------------------ | --------------------------------------------------------------------------------------- |
| `no_webhooks_configured` | सभी webhooks को डिलीवरी का अनुरोध किया गया था, लेकिन workspace में कोई नहीं है।         |
| `invalid_webhook_id`     | सूची में दिया webhook generation events के लिए subscribed नहीं है, या अब मौजूद नहीं है। |
| `webhook_disabled`       | Target किया गया webhook बंद है, मैन्युअल रूप से या failures के बाद अपने-आप।             |

Webhook डिलीवरी [चेन किए गए generations](/docs/hi/eleven-api/guides/how-to/image-and-video/references#chain-one-generation-into-the-next) के साथ अच्छी तरह काम करती है:
अंतिम generation पर `webhook` सेट करें और पूरी chain server-side चलेगी, जिसके अंत में एक ही event मिलेगा। अगर chain बीच में fail हो जाए तब भी यह लागू होता है — failure अंतिम generation तक पहुंचता है,
जो इसे `dependency_failed` कारण वाले `failed` event के रूप में डिलीवर करता है।

## वेबहुक पेलोड

पूरा हुआ generation output URL और MIME type डिलीवर करता है:

```json
{
  "type": "flows_generation",
  "event_timestamp": 1739721600,
  "data": {
    "id": "JWr5N6X9ZTqf8jD2LmQb",
    "status": "completed",
    "content_url": "https://storage.googleapis.com/generations/JWr5N6X9ZTqf8jD2LmQb",
    "content_mime_type": "video/mp4"
  }
}
```

Failed generation इसके बजाय failure category और message डिलीवर करता है:

```json
{
  "type": "flows_generation",
  "event_timestamp": 1739721600,
  "data": {
    "id": "JWr5N6X9ZTqf8jD2LmQb",
    "status": "failed",
    "failure_reason": "timeout",
    "error_message": "Timed out while processing. You were not charged for this generation."
  }
}
```

यह तय करने के लिए कि कौन-से fields मौजूद हैं, `data.status` पर branch करें। ये दोनों terminal statuses ही
वे एकमात्र statuses हैं जो webhook में आ सकते हैं, क्योंकि डिलीवरी केवल generation पूरा होने पर होती है।

> **Warning**
>
> `content_url` एक signed URL है, जो event भेजे जाने के लगभग एक घंटे बाद expire हो जाता है। Media को
> तुरंत डाउनलोड करें या नया URL पाने के लिए generation को फिर से fetch करें।

## इवेंट हैंडल करें

Handler signature को verify करता है, event type जांचता है, फिर `data.status` पर branch करता है। यह
उदाहरण पूरे हुए generation का output डाउनलोड करता है और failed generation का कारण log करता है।

```python maxLines=0
# server.py
import os

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

load_dotenv()

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


@app.post("/webhook/flows")
async def receive_generation(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:
        return JSONResponse(content={"error": "Invalid signature"}, status_code=401)

    # construct_event returns a parsed dict, not an object with attributes.
    if event.get("type") != "flows_generation":
        return {"status": "ignored"}

    generation = event["data"]
    if generation["status"] == "completed":
        media = requests.get(generation["content_url"]).content
        with open(f"{generation['id']}.mp4", "wb") as f:
            f.write(media)
    else:
        print(f"Generation {generation['id']} failed: {generation['failure_reason']}")

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

```typescript maxLines=0
// server.mts
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import "dotenv/config";
import express from "express";
import { writeFile } from "fs/promises";

const elevenlabs = new ElevenLabsClient();
const app = express();

const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;

// The raw body is required: verification runs over the exact bytes sent.
app.post("/webhook/flows", express.raw({ type: "application/json" }), async (req, res) => {
  const signature = req.headers["elevenlabs-signature"] as string;

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

  if (event.type !== "flows_generation") {
    return res.status(200).json({ received: true });
  }

  const generation = event.data;
  if (generation.status === "completed") {
    const response = await fetch(generation.content_url);
    await writeFile(`${generation.id}.mp4`, Buffer.from(await response.arrayBuffer()));
  } else {
    console.error(`Generation ${generation.id} failed: ${generation.failure_reason}`);
  }

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

app.listen(3000);
```

संक्षिप्तता के लिए, दोनों उदाहरण request के दौरान ही डाउनलोड करते हैं। बड़े वीडियो में इतना समय लग सकता है कि
डिलीवरी timeout पार हो जाए, इसलिए production में generation ID को queue में दें और तुरंत 2xx लौटाएं। Signed URL लगभग एक घंटे तक valid रहता है, जो background worker के लिए पर्याप्त है।

> **Tip**
>
> डेवलपमेंट के दौरान local server पर events पाने के लिए, उसे [ngrok](https://ngrok.com/) जैसे tunnel से
> expose करें और उससे मिले HTTPS URL को webhook के callback URL के रूप में इस्तेमाल करें।

## सिग्नेचर सत्यापित करें

ऊपर दिया handler `construct_event` / `constructEvent` को कॉल करता है, जो
`ElevenLabs-Signature` header को verify करता है, timestamp को validate करता है और एक ही चरण में payload parse करता है। किसी event पर भरोसा करने से पहले हमेशा verify करें।

लिस्नर के लिए सभी इनकमिंग वेबहुक को वैलिडेट करना ज़रूरी है। वेबहुक फ़िलहाल HMAC सिग्नेचर के ज़रिए ऑथेंटिकेशन सपोर्ट करते हैं। HMAC ऑथेंटिकेशन सेट अप करने के लिए:

* वेबहुक बनाते समय जनरेट हुए शेयर किए गए सीक्रेट को सुरक्षित रूप से स्टोर करें
* SDK का इस्तेमाल करके अपने एंडपॉइंट में ElevenLabs-Signature हेडर को वेरिफ़ाई करें

JavaScript SDK में `constructEvent` और Python SDK में **`rawBody`**, **`sig_header`**, और **`secret`** के साथ `construct_event` उपलब्ध है (Python में इनके नाम `payload` / `signature` नहीं हैं)। दोनों सिग्नेचर वेरिफ़ाई करते हैं, टाइमस्टैम्प वैलिडेट करते हैं और JSON पेलोड पार्स करते हैं।

#### Python

FastAPI का इस्तेमाल करने वाले वेबहुक हैंडलर का उदाहरण:

```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 का इस्तेमाल करने वाले वेबहुक हैंडलर का उदाहरण:

```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 रूट का इस्तेमाल करने वाले वेबहुक हैंडलर का उदाहरण:

**`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 });
}
```

## डिलीवरी व्यवहार

हर generation, target किए गए हर webhook पर ठीक एक terminal event डिलीवर करता है। डिलीवरी generation से स्वतंत्र है: जो webhook fail हो या पहुंच से बाहर हो, वह नतीजे को प्रभावित नहीं करता। नतीजा GET endpoint और list response में उपलब्ध रहता है।

अपने handler से तुरंत 2xx status लौटाएं। बार-बार failures होने पर webhook अपने-आप बंद हो जाता है, और बंद webhook को target करने वाले बाद के generations को create समय पर reject कर दिया जाता है। Handler को idempotent बनाएं और duplicate हटाने के लिए generation `id` इस्तेमाल करें।

जिन workflows में कोई नतीजा छूटना स्वीकार्य नहीं है, उनमें webhooks को fast path मानें और `status` पर filter करते हुए समय-समय पर `flows.image.list` या `flows.video.list` से reconcile करें।

## अगले चरण

#### [रेफरेंसेज़ और एसेट्स](/docs/hi/eleven-api/guides/how-to/image-and-video/references)

पिछले generation, uploaded asset या inline media से किसी generation को गाइड करें।

#### [वेबहुक कॉन्फ़िगरेशन](/docs/hi/eleven-api/resources/webhooks)

अपने workspace के लिए webhooks बनाएं, सुरक्षित करें और मैनेज करें।

#### [API रेफरेंस](/docs/hi/api-reference/flows/image/create)

इमेज, वीडियो और asset endpoints देखें।