> 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 de Imagem e Vídeo

> **Note**
>
> **Guia prático** · Pressupõe que você concluiu o [início rápido de Imagem e Vídeo](/docs/pt/eleven-api/guides/cookbooks/image-and-video).

## Visão geral

A geração de vídeos pode levar vários minutos, o que torna caro manter consultas periódicas abertas. Ative
a entrega por webhook para uma geração, e a ElevenLabs enviará um evento `flows_generation` ao seu endpoint
quando a geração chegar a `completed` ou `failed`.

A carga útil do evento é a resposta final do endpoint GET correspondente, portanto, um manipulador que
já entende a resposta da consulta periódica não precisa de um caminho de análise separado.

## Antes de começar

A entrega por webhook usa os webhooks que seu espaço de trabalho assinou para eventos de geração. A configuração
tem duas etapas: criar o webhook e depois assiná-lo para o evento.

#### Criar um webhook

Acesse [**Desenvolvedores** > **Webhooks**](https://elevenlabs.io/app/developers/webhooks) e crie um
webhook com uma URL de callback HTTPS acessível publicamente. Guarde o segredo de assinatura retornado;
você precisará dele para verificar os eventos recebidos.

#### Assiná-lo para eventos de geração

Em **Selecionar eventos para ouvir**, marque \*\*Geração da API de Imagem e Vídeo ou execução de modelo
concluída \*\*. O mesmo evento entrega [execuções de modelos](/docs/pt/eleven-api/guides/how-to/templates/run-a-template) concluídas.
Um webhook que existe, mas não está assinado para esse evento, nunca é chamado.

Você pode fazer o mesmo pela API passando o evento `flows` para
[Atualizar webhook do espaço de trabalho](/docs/pt/api-reference/webhooks/update):

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

Criar e assinar webhooks exige a permissão Gerenciar Webhooks ou ser administrador do espaço de trabalho. Um
único evento aceita até 10 webhooks; acima disso, a solicitação falha com `too_many_webhooks`.

Uma geração que solicita entrega por webhook quando nenhum webhook está assinado para eventos de geração é
rejeitada, para que um resultado nunca seja gerado sem um destino de entrega.

## Solicitar entrega por webhook

Adicione um objeto `webhook` à solicitação de criação. Use `{"type": "all"}` para entregar a todos os webhooks
assinados para eventos de geração, o que mantém a solicitação estável conforme webhooks são adicionados ou substituídos.

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

Para direcionar a webhooks específicos, defina o campo `webhook` como uma lista de IDs. Cada ID deve ser de um
dos webhooks do espaço de trabalho assinados para eventos de geração.

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

A solicitação de criação valida o destino antes de iniciar a geração e retorna um erro quando
a entrega não for possível:

| Status do erro           | Causa                                                                                  |
| ------------------------ | -------------------------------------------------------------------------------------- |
| `no_webhooks_configured` | Foi solicitada a entrega a todos os webhooks, mas o espaço de trabalho não tem nenhum. |
| `invalid_webhook_id`     | Um webhook listado não está assinado para eventos de geração ou não existe mais.       |
| `webhook_disabled`       | Um webhook direcionado está desativado, manualmente ou automaticamente após falhas.    |

A entrega por webhook funciona bem com [gerações encadeadas](/docs/pt/eleven-api/guides/how-to/image-and-video/references#chain-one-generation-into-the-next):
defina `webhook` na geração final, e toda a cadeia será executada no servidor com um único evento ao
final. Isso também vale quando a cadeia falha no meio do processo — a falha é propagada para a
geração final, que a entrega como um evento `failed` com o motivo `dependency_failed`.

## Carga útil do webhook

Uma geração concluída entrega a URL de saída e o 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"
  }
}
```

Uma geração com falha entrega a categoria e a mensagem de falha:

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

Use `data.status` para decidir quais campos estão presentes. Os dois status finais são os únicos
que um webhook pode conter, já que a entrega ocorre apenas quando uma geração termina.

> **Warning**
>
> `content_url` é uma URL assinada que expira aproximadamente uma hora após o envio do evento. Baixe a
> mídia rapidamente ou busque a geração novamente para obter uma URL nova.

## Processar o evento

Um manipulador verifica a assinatura, confere o tipo de evento e, então, ramifica com base em `data.status`. Este
exemplo baixa a saída de uma geração concluída e registra o motivo de uma geração que falhou.

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

Ambos os exemplos fazem o download dentro da solicitação por brevidade. Um vídeo grande pode levar tempo suficiente
para exceder o tempo limite de entrega, então, em produção, envie o ID da geração a uma fila e retorne 2xx
imediatamente. A URL assinada é válida por cerca de uma hora, o que é suficiente para um worker em segundo plano.

> **Tip**
>
> Para receber eventos em um servidor local durante o desenvolvimento, exponha-o com um túnel como o
> [ngrok](https://ngrok.com/) e use a URL HTTPS fornecida como URL de callback do webhook.

## Verificar a assinatura

O manipulador acima chama `construct_event` / `constructEvent`, que verifica o cabeçalho
`ElevenLabs-Signature`, valida o registro de data e hora e analisa a carga útil em uma única etapa. Sempre
verifique antes de confiar em um evento.

É importante que o listener valide todos os webhooks recebidos. Atualmente, os webhooks oferecem suporte à autenticação por assinaturas HMAC. Configure a autenticação HMAC:

* Armazenando com segurança o segredo compartilhado gerado na criação do webhook
* Verificando o cabeçalho ElevenLabs-Signature no seu endpoint usando o SDK

O SDK JavaScript disponibiliza `constructEvent`; o SDK Python disponibiliza `construct_event` com **`rawBody`**, **`sig_header`** e **`secret`** (em Python, eles não se chamam `payload` / `signature`). Ambos verificam a assinatura, validam o carimbo de data e hora e analisam o payload JSON.

#### Python

Exemplo de manipulador de webhook usando 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

Exemplo de manipulador de webhook usando 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

Exemplo de manipulador de webhook usando uma rota de API do 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 de entrega

Cada geração entrega exatamente um evento final por webhook direcionado. A entrega é independente da
própria geração: um webhook que falha ou está inacessível não afeta o resultado, que
continua disponível no endpoint GET e na resposta da lista.

Retorne prontamente um status 2xx do seu manipulador. Falhas repetidas desativam um webhook automaticamente, e um
webhook desativado faz com que gerações subsequentes que o direcionam sejam rejeitadas no momento da criação. Projete
o manipulador para ser idempotente e use o `id` da geração para eliminar duplicatas.

Para fluxos de trabalho em que não é aceitável perder um resultado, trate os webhooks como o caminho rápido e reconcilie
periodicamente com `flows.image.list` ou `flows.video.list`, filtrando por `status`.

## Próximas etapas

#### [Referências e recursos](/docs/pt/eleven-api/guides/how-to/image-and-video/references)

Oriente uma geração com uma geração anterior, um recurso enviado ou mídia inline.

#### [Configuração de webhook](/docs/pt/eleven-api/resources/webhooks)

Crie, proteja e gerencie webhooks para seu espaço de trabalho.

#### [Referência da API](/docs/pt/api-reference/flows/image/create)

Explore os endpoints de imagem, vídeo e recursos.