Image & Video webhooks
Image & Video webhooks
How-to guide · Assumes you have completed the Image & Video quickstart.
Overview
Video generations can take several minutes, which makes polling expensive to hold open. Opt a
generation into webhook delivery and ElevenLabs sends a flows_generation event to your endpoint
once the generation reaches completed or failed.
The event payload is the terminal response of the corresponding GET endpoint, so a handler that already understands the polling response needs no separate parsing path.
Before you begin
Webhook delivery uses the webhooks your workspace has subscribed to generation events. Setting one up takes two steps: create the webhook, then subscribe it to the event.
Create a webhook
Go to Developers > Webhooks and create a webhook with a publicly reachable HTTPS callback URL. Keep the signing secret it returns; you need it to verify incoming events.
Subscribe it to generation events
Under Select events to listen to, tick Image & Video API generation completed. A webhook that exists but is not subscribed to this event is never called.
You can do the same through the API by passing the flows event to
Update workspace webhook:
Creating and subscribing webhooks requires the Webhooks Manage permission, or workspace admin. A
single event accepts up to 10 webhooks; beyond that the request fails with too_many_webhooks.
A generation that requests webhook delivery when no webhook is subscribed to generation events is rejected, so a result is never generated with nowhere to deliver it.
Request webhook delivery
Add a webhook object to the create request. Use {"type": "all"} to deliver to every webhook
subscribed to generation events, which keeps the request stable as webhooks are added or replaced.
To target specific webhooks instead, set the webhook field to a list of IDs. Each ID must be one
of the workspace’s webhooks subscribed to generation events.
The create request validates the target before starting the generation and returns an error when delivery would not be possible:
Webhook payload
A completed generation delivers the output URL and MIME type:
A failed generation delivers the failure category and message instead:
Branch on data.status to decide which fields are present. The two terminal statuses are the only
ones a webhook can carry, since delivery happens only when a generation finishes.
content_url is a signed URL that expires roughly an hour after the event is sent. Download the
media promptly, or fetch the generation again for a fresh URL.
Handle the event
A handler verifies the signature, checks the event type, then branches on data.status. This
example downloads the output of a completed generation and logs the reason for a failed one.
Both examples download inside the request for brevity. A large video takes long enough that this can outlast the delivery timeout, so in production hand the generation ID to a queue and return 2xx immediately. The signed URL is valid for about an hour, which is ample for a background worker.
To receive events on a local server during development, expose it with a tunnel such as ngrok and use the HTTPS URL it gives you as the webhook’s callback URL.
Verify the signature
The handler above calls construct_event / constructEvent, which verifies the
ElevenLabs-Signature header, validates the timestamp, and parses the payload in one step. Always
verify before trusting an event.
It is important for the listener to validate all incoming webhooks. Webhooks currently support authentication via HMAC signatures. Set up HMAC authentication by:
- Securely storing the shared secret generated upon creation of the webhook
- Verifying the ElevenLabs-Signature header in your endpoint using the SDK
The JavaScript SDK exposes constructEvent; the Python SDK exposes construct_event with rawBody, sig_header, and secret (these are not named payload / signature in Python). Both verify the signature, validate the timestamp, and parse the JSON payload.
Python
JavaScript
Example webhook handler using FastAPI:
Delivery behavior
Each generation delivers exactly one terminal event per targeted webhook. Delivery is independent of the generation itself: a webhook that fails or is unreachable does not affect the result, which stays available from the GET endpoint and in the list response.
Return a 2xx status promptly from your handler. Repeated failures auto-disable a webhook, and a
disabled webhook causes subsequent generations that target it to be rejected at create time. Design
the handler to be idempotent and use the generation id to deduplicate.
For workflows where a missed result is not acceptable, treat webhooks as the fast path and reconcile
periodically with flows.image.list or flows.video.list, filtering on status.