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

# Webhooki Image & Video

> **Note**
>
> **Poradnik** · Zakłada ukończenie [szybkiego startu Image & Video](/docs/pl/eleven-api/guides/cookbooks/image-and-video).

## Przegląd

Generowanie filmów może trwać kilka minut, więc ciągłe odpytywanie jest kosztowne. Włącz dostarczanie
przez webhook dla generowania, a ElevenLabs wyśle zdarzenie `flows_generation` na twój endpoint,
gdy generowanie uzyska status `completed` lub `failed`.

Ładunek zdarzenia to końcowa odpowiedź odpowiedniego endpointu GET, więc handler, który już obsługuje
odpowiedź z odpytywania, nie potrzebuje osobnej ścieżki parsowania.

## Zanim zaczniesz

Dostarczanie przez webhook korzysta z webhooków w twoim workspace subskrybowanych do zdarzeń generowania.
Konfiguracja wymaga dwóch kroków: utwórz webhook, a potem zasubskrybuj go do zdarzenia.

#### Utwórz webhook

Przejdź do [**Developers** > **Webhooks**](https://elevenlabs.io/app/developers/webhooks) i utwórz
webhook z publicznie dostępnym adresem URL wywołania zwrotnego HTTPS. Zachowaj zwrócony sekret
podpisu — będzie potrzebny do weryfikacji przychodzących zdarzeń.

#### Zasubskrybuj go do zdarzeń generowania

W sekcji **Select events to listen to** zaznacz \*\*Image & Video API generation or template run
completed \*\*. To samo zdarzenie dostarcza ukończone [uruchomienia szablonów](/docs/pl/eleven-api/guides/how-to/templates/run-a-template).
Webhook, który istnieje, ale nie subskrybuje tego zdarzenia, nigdy nie zostanie wywołany.

Możesz zrobić to samo przez API, przekazując zdarzenie `flows` do
[Update workspace webhook](/docs/pl/api-reference/webhooks/update):

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

Tworzenie i subskrybowanie webhooków wymaga uprawnienia Webhooks Manage lub roli administratora workspace.
Jedno zdarzenie obsługuje maksymalnie 10 webhooków; powyżej tego limitu żądanie kończy się błędem `too_many_webhooks`.

Generowanie żądające dostarczenia przez webhook, gdy żaden webhook nie subskrybuje zdarzeń generowania,
zostanie odrzucone, więc wynik nigdy nie powstanie bez miejsca, do którego można go dostarczyć.

## Poproś o dostarczenie przez webhook

Dodaj obiekt `webhook` do żądania utworzenia. Użyj `{"type": "all"}`, aby dostarczać do każdego webhooka
subskrybowanego do zdarzeń generowania — żądanie pozostanie stabilne, gdy webhooki będą dodawane lub zastępowane.

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

Aby wskazać konkretne webhooki, ustaw pole `webhook` jako listę identyfikatorów. Każdy identyfikator
musi należeć do webhooka workspace subskrybowanego do zdarzeń generowania.

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

Żądanie utworzenia sprawdza cel przed rozpoczęciem generowania i zwraca błąd, gdy dostarczenie
nie byłoby możliwe:

| Status błędu             | Przyczyna                                                                    |
| ------------------------ | ---------------------------------------------------------------------------- |
| `no_webhooks_configured` | Zażądano dostarczenia do wszystkich webhooków, ale workspace nie ma żadnego. |
| `invalid_webhook_id`     | Wskazany webhook nie subskrybuje zdarzeń generowania lub już nie istnieje.   |
| `webhook_disabled`       | Wskazany webhook jest wyłączony ręcznie lub automatycznie po błędach.        |

Dostarczanie przez webhook dobrze sprawdza się przy [połączonych generowaniach](/docs/pl/eleven-api/guides/how-to/image-and-video/references#chain-one-generation-into-the-next):
ustaw `webhook` w końcowym generowaniu, a cały łańcuch uruchomi się po stronie serwera i wyśle jedno
zdarzenie na końcu. Dotyczy to także błędu w trakcie łańcucha — błąd przechodzi do końcowego
generowania, które dostarcza go jako zdarzenie `failed` z przyczyną `dependency_failed`.

## Ładunek webhooka

Ukończone generowanie dostarcza URL wyniku i typ MIME:

```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"
  }
}
```

Nieudane generowanie dostarcza za to kategorię błędu i komunikat:

```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."
  }
}
```

Sprawdzaj `data.status`, aby ustalić, które pola są dostępne. Te dwa statusy końcowe są jedynymi,
które może zawierać webhook, ponieważ dostarczenie następuje dopiero po zakończeniu generowania.

> **Warning**
>
> `content_url` to podpisany URL, który wygasa mniej więcej godzinę po wysłaniu zdarzenia. Pobierz
> multimedia od razu albo ponownie pobierz generowanie, aby uzyskać nowy URL.

## Obsłuż zdarzenie

Handler weryfikuje podpis, sprawdza typ zdarzenia, a potem analizuje `data.status`. Ten przykład
pobiera wynik ukończonego generowania i loguje przyczynę nieudanego.

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

Dla zwięzłości oba przykłady pobierają dane w ramach żądania. Duży film może być pobierany na tyle długo,
że przekroczy limit czasu dostarczenia, więc na produkcji przekaż identyfikator generowania do kolejki i od razu
zwróć 2xx. Podpisany URL jest ważny przez około godzinę, co wystarcza workerowi działającemu w tle.

> **Tip**
>
> Aby odbierać zdarzenia na lokalnym serwerze podczas pracy deweloperskiej, udostępnij go przez tunel,
> np. [ngrok](https://ngrok.com/), i użyj podanego przez niego adresu HTTPS jako adresu URL wywołania zwrotnego webhooka.

## Zweryfikuj podpis

Powyższy handler wywołuje `construct_event` / `constructEvent`, co w jednym kroku weryfikuje nagłówek
`ElevenLabs-Signature`, sprawdza znacznik czasu i parsuje ładunek. Zawsze weryfikuj zdarzenie, zanim mu zaufasz.

Ważne, by odbiornik weryfikował wszystkie przychodzące webhooki. Webhooki obecnie obsługują uwierzytelnianie za pomocą podpisów HMAC. Aby skonfigurować uwierzytelnianie HMAC:

* Bezpiecznie przechowuj współdzielony sekret wygenerowany podczas tworzenia webhooka
* Zweryfikuj nagłówek ElevenLabs-Signature w swoim endpointzie za pomocą SDK

SDK JavaScript udostępnia `constructEvent`, a SDK Python `construct_event` z parametrami **`rawBody`**, **`sig_header`** i **`secret`** (w Pythonie nie nazywają się one `payload` / `signature`). Oba weryfikują podpis, sprawdzają znacznik czasu i parsują dane JSON.

#### Python

Przykładowy handler webhooka z użyciem 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

Przykładowy handler webhooka z użyciem 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

Przykładowy handler webhooka z użyciem trasy API Next.js:

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

## Zachowanie dostarczania

Każde generowanie dostarcza dokładnie jedno zdarzenie końcowe na każdy wskazany webhook. Dostarczanie nie zależy
od samego generowania: webhook, który zawiedzie lub jest niedostępny, nie wpływa na wynik, który nadal jest dostępny
z endpointu GET i w odpowiedzi listy.

Szybko zwracaj status 2xx z handlera. Powtarzające się błędy automatycznie wyłączają webhook, a wyłączony webhook
sprawia, że kolejne generowania go wskazujące są odrzucane podczas tworzenia. Zaprojektuj handler tak, by był idempotentny,
i używaj `id` generowania do usuwania duplikatów.

W workflow, w których pominięty wynik jest niedopuszczalny, traktuj webhooki jako szybką ścieżkę i okresowo uzgadniaj
wyniki za pomocą `flows.image.list` lub `flows.video.list`, filtrując według `status`.

## Kolejne kroki

#### [Referencje i zasoby](/docs/pl/eleven-api/guides/how-to/image-and-video/references)

Kieruj generowaniem za pomocą wcześniejszego generowania, przesłanego zasobu lub multimediów inline.

#### [Konfiguracja webhooka](/docs/pl/eleven-api/resources/webhooks)

Twórz, zabezpieczaj i zarządzaj webhookami swojego workspace.

#### [Dokumentacja API](/docs/pl/api-reference/flows/image/create)

Poznaj endpointy obrazów, filmów i zasobów.