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

# Webhook di Immagini e Video

> **Note**
>
> **Guida pratica** · Presuppone che tu abbia completato la [guida rapida di Immagini e Video ](/docs/it/eleven-api/guides/cookbooks/image-and-video).

## Panoramica

La generazione di video può richiedere diversi minuti, rendendo costoso mantenere aperto il polling. Configura una
generazione per la consegna tramite webhook e ElevenLabs invierà un evento `flows_generation` al tuo endpoint
quando la generazione raggiunge lo stato `completed` o `failed`.

Il payload dell'evento corrisponde alla risposta finale del relativo endpoint GET, quindi un handler che
comprende già la risposta del polling non richiede un percorso di parsing separato.

## Prima di iniziare

La consegna tramite webhook usa i webhook del tuo workspace iscritti agli eventi di generazione. La configurazione
richiede due passaggi: crea il webhook, quindi iscrivilo all'evento.

#### Crea un webhook

Vai a [**Sviluppatori** > **Webhook**](https://elevenlabs.io/app/developers/webhooks) e crea un
webhook con un URL di callback HTTPS raggiungibile pubblicamente. Conserva il segreto di firma restituito:
ti serve per verificare gli eventi in arrivo.

#### Iscrivilo agli eventi di generazione

In **Seleziona gli eventi da ascoltare**, seleziona **Generazione Image & Video API completata**. Un webhook
esistente ma non iscritto a questo evento non verrà mai chiamato.

Puoi fare lo stesso tramite l'API passando l'evento `flows` a
[Aggiorna webhook del workspace](/docs/it/api-reference/webhooks/update):

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

Per creare e iscrivere webhook sono necessari l'autorizzazione Gestione webhook o il ruolo di amministratore del workspace. Un
singolo evento accetta fino a 10 webhook; oltre questo limite, la richiesta fallisce con `too_many_webhooks`.

Una generazione che richiede la consegna tramite webhook quando nessun webhook è iscritto agli eventi di generazione viene
rifiutata, così non viene mai generato un risultato senza una destinazione a cui consegnarlo.

## Richiedi la consegna tramite webhook

Aggiungi un oggetto `webhook` alla richiesta di creazione. Usa `{"type": "all"}` per consegnare a ogni webhook
iscritto agli eventi di generazione: in questo modo la richiesta resta invariata quando i webhook vengono aggiunti o sostituiti.

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

Per indirizzare webhook specifici, imposta invece il campo `webhook` su un elenco di ID. Ogni ID deve corrispondere a uno
dei webhook del workspace iscritti agli eventi di generazione.

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

La richiesta di creazione convalida la destinazione prima di avviare la generazione e restituisce un errore quando
la consegna non sarebbe possibile:

| Stato dell'errore        | Causa                                                                                 |
| ------------------------ | ------------------------------------------------------------------------------------- |
| `no_webhooks_configured` | È stata richiesta la consegna a tutti i webhook, ma il workspace non ne ha.           |
| `invalid_webhook_id`     | Un webhook elencato non è iscritto agli eventi di generazione o non esiste più.       |
| `webhook_disabled`       | Un webhook di destinazione è disabilitato, manualmente o automaticamente dopo errori. |

La consegna tramite webhook si abbina bene alle [generazioni concatenate](/docs/it/eleven-api/guides/how-to/image-and-video/references#chain-one-generation-into-the-next):
imposta `webhook` sulla generazione finale e l'intera catena viene eseguita lato server con un solo evento alla
fine. Vale anche quando la catena fallisce a metà: l'errore si propaga alla generazione
finale, che lo consegna come evento `failed` con motivo `dependency_failed`.

## Payload del webhook

Una generazione completata fornisce l'URL di output e il tipo 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"
  }
}
```

Una generazione non riuscita fornisce invece la categoria e il messaggio dell'errore:

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

Verifica `data.status` per stabilire quali campi sono presenti. I due stati finali sono gli unici
che un webhook può contenere, poiché la consegna avviene solo al termine di una generazione.

> **Warning**
>
> `content_url` è un URL firmato che scade circa un'ora dopo l'invio dell'evento. Scarica subito il
> file multimediale oppure recupera di nuovo la generazione per ottenere un URL aggiornato.

## Gestisci l'evento

Un handler verifica la firma, controlla il tipo di evento e poi dirama in base a `data.status`. Questo
esempio scarica l'output di una generazione completata e registra il motivo di una generazione non riuscita.

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

Entrambi gli esempi scaricano durante la richiesta per semplicità. Un video di grandi dimensioni richiede abbastanza tempo da
superare il timeout di consegna, quindi in produzione invia l'ID della generazione a una coda e restituisci subito 2xx.
L'URL firmato è valido per circa un'ora, un tempo più che sufficiente per un worker in background.

> **Tip**
>
> Per ricevere eventi su un server locale durante lo sviluppo, esponilo con un tunnel come
> [ngrok](https://ngrok.com/) e usa come URL di callback del webhook l'URL HTTPS che ti fornisce.

## Verifica la firma

L'handler sopra chiama `construct_event` / `constructEvent`, che verifica l'header
`ElevenLabs-Signature`, convalida il timestamp e analizza il payload in un solo passaggio. Verifica sempre un evento
prima di considerarlo attendibile.

È importante che il listener convalidi tutti i webhook in arrivo. I webhook supportano attualmente l'autenticazione tramite firme HMAC. Configura l'autenticazione HMAC:

* Archiviando in modo sicuro il segreto condiviso generato alla creazione del webhook
* Verificando l'header ElevenLabs-Signature nel tuo endpoint tramite l'SDK

L'SDK JavaScript espone `constructEvent`; l'SDK Python espone `construct_event` con **`rawBody`**, **`sig_header`** e **`secret`** (in Python non si chiamano `payload` / `signature`). Entrambi verificano la firma, convalidano il timestamp e analizzano il payload JSON.

#### Python

Esempio di gestore webhook con 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

Esempio di gestore webhook con 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

Esempio di gestore webhook con una route API di 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 });
}
```

## Comportamento di consegna

Ogni generazione consegna esattamente un evento finale per ogni webhook di destinazione. La consegna è indipendente dalla
generazione: un webhook che fallisce o non è raggiungibile non influisce sul risultato, che
resta disponibile dall'endpoint GET e nella risposta dell'elenco.

Restituisci tempestivamente uno stato 2xx dal tuo handler. Errori ripetuti disabilitano automaticamente un webhook e un
webhook disabilitato fa sì che le generazioni successive che lo usano come destinazione vengano rifiutate al momento della creazione. Progetta
l'handler in modo che sia idempotente e usa l'`id` della generazione per eliminare i duplicati.

Per workflow in cui non è accettabile perdere un risultato, considera i webhook come percorso rapido e riconcilia
periodicamente con `flows.image.list` o `flows.video.list`, filtrando in base a `status`.

## Passaggi successivi

#### [Riferimenti e asset](/docs/it/eleven-api/guides/how-to/image-and-video/references)

Guida una generazione con una generazione precedente, un asset caricato o contenuti multimediali inline.

#### [Configurazione dei webhook](/docs/it/eleven-api/resources/webhooks)

Crea, proteggi e gestisci i webhook del tuo workspace.

#### [Riferimento API](/docs/it/api-reference/flows/image/create)

Esplora gli endpoint per immagini, video e asset.