> 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 Image & Vidéo

> **Note**
>
> **Guide pratique** · suppose que vous avez suivi le [guide de démarrage rapide Image & Video ](/docs/fr/eleven-api/guides/cookbooks/image-and-video).

## Présentation

Les générations de vidéos peuvent prendre plusieurs minutes, ce qui rend leur interrogation régulière coûteuse. Activez la livraison par webhook pour une
génération, et ElevenLabs envoie un événement `flows_generation` à votre endpoint
lorsque la génération atteint l'état `completed` ou `failed`.

La charge utile de l'événement est la réponse finale de l'endpoint GET correspondant. Un gestionnaire qui
comprend déjà la réponse d'interrogation n'a donc besoin d'aucune logique d'analyse distincte.

## Avant de commencer

La livraison par webhook utilise les webhooks de votre Workspace abonnés aux événements de génération. La configuration
se fait en deux étapes : créez le webhook, puis abonnez-le à l'événement.

#### Créer un webhook

Accédez à [**Développeurs** > **Webhooks**](https://elevenlabs.io/app/developers/webhooks) et créez un
webhook avec une URL de rappel HTTPS accessible publiquement. Conservez le secret de signature renvoyé : vous en
aurez besoin pour vérifier les événements entrants.

#### L'abonner aux événements de génération

Sous **Sélectionnez les événements à écouter**, cochez \*\*Génération ou exécution de modèle Image & Vidéo API
terminée \*\*. Le même événement transmet les [exécutions de modèle](/docs/fr/eleven-api/guides/how-to/templates/run-a-template)
terminées. Un webhook existant mais non abonné à cet événement n'est jamais appelé.

Vous pouvez procéder de la même façon via l'API en transmettant l'événement `flows` à
[Mettre à jour un webhook de Workspace](/docs/fr/api-reference/webhooks/update) :

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

La création et l'abonnement de webhooks nécessitent l'autorisation Gérer les webhooks, ou le rôle d'administrateur du Workspace. Un
même événement accepte jusqu'à 10 webhooks ; au-delà, la requête échoue avec `too_many_webhooks`.

Une génération qui demande une livraison par webhook alors qu'aucun webhook n'est abonné aux événements de génération est
rejetée. Un résultat n'est donc jamais généré sans destination de livraison.

## Demander la livraison par webhook

Ajoutez un objet `webhook` à la requête de création. Utilisez `{"type": "all"}` pour livrer le résultat à chaque webhook
abonné aux événements de génération. La requête reste ainsi stable lorsque des webhooks sont ajoutés ou remplacés.

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

Pour cibler des webhooks spécifiques, définissez plutôt le champ `webhook` comme une liste d'ID. Chaque ID doit correspondre à
l'un des webhooks du Workspace abonnés aux événements de génération.

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

La requête de création valide la cible avant de lancer la génération et renvoie une erreur lorsque
la livraison est impossible :

| État d'erreur            | Cause                                                                                  |
| ------------------------ | -------------------------------------------------------------------------------------- |
| `no_webhooks_configured` | La livraison à tous les webhooks a été demandée, mais le Workspace n'en possède aucun. |
| `invalid_webhook_id`     | Un webhook répertorié n'est pas abonné aux événements de génération, ou n'existe plus. |
| `webhook_disabled`       | Un webhook ciblé est désactivé, manuellement ou automatiquement après des échecs.      |

La livraison par webhook s'associe bien aux [générations enchaînées](/docs/fr/eleven-api/guides/how-to/image-and-video/references#chain-one-generation-into-the-next) :
définissez `webhook` sur la génération finale, et toute la chaîne s'exécute côté serveur avec un seul événement à
la fin. Cela reste vrai si la chaîne échoue en cours de route : l'échec se propage jusqu'à la
génération finale, qui le transmet en tant qu'événement `failed` avec le motif `dependency_failed`.

## Charge utile du webhook

Une génération terminée fournit l'URL de sortie et le type 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"
  }
}
```

Une génération échouée fournit à la place la catégorie et le message d'échec :

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

Utilisez `data.status` pour déterminer quels champs sont présents. Les deux états finaux sont les seuls
qu'un webhook peut contenir, car la livraison a lieu uniquement lorsqu'une génération se termine.

> **Warning**
>
> `content_url` est une URL signée qui expire environ une heure après l'envoi de l'événement. Téléchargez le
> média rapidement ou récupérez à nouveau la génération pour obtenir une nouvelle URL.

## Traiter l'événement

Un gestionnaire vérifie la signature, contrôle le type d'événement, puis utilise `data.status`. Cet
exemple télécharge la sortie d'une génération terminée et journalise le motif d'une génération échouée.

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

Les deux exemples effectuent le téléchargement dans la requête par souci de concision. Une vidéo volumineuse peut prendre suffisamment longtemps pour
dépasser le délai de livraison. En production, transmettez donc l'ID de génération à une file d'attente et renvoyez immédiatement un code 2xx.
L'URL signée reste valide environ une heure, ce qui laisse largement le temps à un processus d'arrière-plan.

> **Tip**
>
> Pour recevoir des événements sur un serveur local pendant le développement, exposez-le à l'aide d'un tunnel tel que
> [ngrok](https://ngrok.com/) et utilisez l'URL HTTPS fournie comme URL de rappel du webhook.

## Vérifier la signature

Le gestionnaire ci-dessus appelle `construct_event` / `constructEvent`, qui vérifie l'en-tête
`ElevenLabs-Signature`, valide l'horodatage et analyse la charge utile en une étape. Vérifiez toujours
un événement avant de lui faire confiance.

Il est important que le récepteur valide tous les webhooks entrants. Les webhooks prennent actuellement en charge l’authentification par signatures HMAC. Configurez l’authentification HMAC en :

* Stockant de manière sécurisée le secret partagé généré lors de la création du webhook
* Vérifiant l’en-tête ElevenLabs-Signature dans votre endpoint à l’aide du SDK

Le SDK JavaScript expose `constructEvent` ; le SDK Python expose `construct_event` avec **`rawBody`**, **`sig_header`** et **`secret`** (ils ne s’appellent pas `payload` / `signature` en Python). Les deux vérifient la signature, valident l’horodatage et analysent la charge utile JSON.

#### Python

Exemple de gestionnaire de webhook utilisant 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

Exemple de gestionnaire de webhook utilisant 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

Exemple de gestionnaire de webhook utilisant une route 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 });
}
```

## Comportement de livraison

Chaque génération fournit exactement un événement final par webhook ciblé. La livraison est indépendante de
la génération elle-même : un webhook qui échoue ou est inaccessible n'affecte pas le résultat, qui
reste disponible depuis l'endpoint GET et dans la réponse de liste.

Renvoyez rapidement un état 2xx depuis votre gestionnaire. Des échecs répétés désactivent automatiquement un webhook, et un
webhook désactivé entraîne le rejet, lors de leur création, des générations suivantes qui le ciblent. Concevez
le gestionnaire de manière idempotente et utilisez l'`id` de la génération pour dédupliquer.

Pour les workflows où un résultat manqué n'est pas acceptable, considérez les webhooks comme le chemin rapide et réconciliez
périodiquement avec `flows.image.list` ou `flows.video.list`, en filtrant selon `status`.

## Étapes suivantes

#### [Références et ressources](/docs/fr/eleven-api/guides/how-to/image-and-video/references)

Guidez une génération avec une génération précédente, une ressource importée ou un média intégré.

#### [Configuration des webhooks](/docs/fr/eleven-api/resources/webhooks)

Créez, sécurisez et gérez les webhooks de votre Workspace.

#### [Guide de l'API](/docs/fr/api-reference/flows/image/create)

Découvrez les endpoints d'images, de vidéos et de ressources.