> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.videogen.io/handling-async-tasks/webhooks/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.videogen.io/_mcp/server. # Webhooks > Register webhook endpoints to receive tool_execution.*, workflow_run.*, project_export.*, file.*, and assistant_message.* events instead of polling. Includes setup, signature verification, and example payloads. For production systems, webhooks are the better choice. Instead of polling, you register a URL and VideoGen sends you an event when work completes. For the full list of event types and payload schemas, see the [Webhook events reference](/webhook-events). ## 1. Register a webhook endpoint ```bash curl -X POST https://api.videogen.io/v1/webhooks/endpoints \ -H "Authorization: Bearer sk_videogen_live_..." \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-server.com/webhooks/videogen", "events": ["workflow_run.succeeded", "workflow_run.failed", "workflow_run.cancelled", "project_export.succeeded", "project_export.failed", "project_export.cancelled", "tool_execution.succeeded", "tool_execution.failed", "tool_execution.cancelled", "assistant_message.succeeded", "assistant_message.failed", "assistant_message.cancelled"] }' ``` The response includes a `signingSecret`. Save it; you'll need it to verify incoming requests. ## 2. Handle workflow run events Subscribe to `workflow_run.succeeded`, `workflow_run.failed`, and `workflow_run.cancelled` to track end-to-end video workflows started via `POST /v1/workflows/*`. When a run reaches a terminal status, VideoGen sends a POST request to your URL: ```json { "event": "workflow_run.succeeded", "workflowRunId": "vg_work_...", "occurredAt": 1745409600, "workflowType": "SCRIPT_TO_VIDEO", "projectId": "vg_proj_...", "projectUrl": "https://app.videogen.io/project/..." } ``` Payloads include `workflowRunId`, `workflowType`, `projectId`, and `projectUrl`. Failed events also include an `error.message` field. Use `projectId` with the Projects API to export or remix. `projectUrl` is an optional deep link for opening the project in the VideoGen editor (team members and project collaborators only); you can ignore it in a fully automated integration. ## 3. Handle project export events Subscribe to `project_export.succeeded`, `project_export.failed`, and `project_export.cancelled` to track MP4 exports started via `POST /v1/projects/{projectId}/export`. These fire only for exports started through the API. ```json { "event": "project_export.succeeded", "exportId": "vg_expo_...", "projectId": "vg_proj_...", "occurredAt": 1745409600, "exportFileId": "vg_file_..." } ``` Use `exportId` with `GET /v1/projects/{projectId}/exports/{exportId}` for signed download URLs. On success, `exportFileId` is the rendered MP4; pass it to `POST /v1/files/{fileId}/hydrate` if those URLs have expired. Failed events include an `error.message` field. `exportFileId` is `null` on failed and cancelled events. ## 4. Handle tool execution events Standalone media tools emit `tool_execution.succeeded`, `tool_execution.failed`, and `tool_execution.cancelled`. The success payload carries the generated files inline: ```json { "event": "tool_execution.succeeded", "toolExecutionId": "vg_tool_...", "toolType": "GENERATE_IMAGE", "occurredAt": 1745409600, "results": [ { "fileId": "vg_file_...", "type": "IMAGE", "file": { "fileId": "vg_file_...", "type": "IMAGE", "scope": "GLOBAL", "displayName": "A mountain at sunrise", "thumbnailSource": { "status": "ready", "url": "https://...", "expiresAt": 1745413200 }, "previewSource": { "status": "ready", "url": "https://...", "expiresAt": 1745413200 }, "downloadSource": { "status": "ready", "url": "https://...", "expiresAt": 1745413200 } } } ] } ``` Each entry in `results` contains the file id, type, and a hydrated `file` object with signed download URLs, so no extra API call is needed. For `tool_execution.failed` and `tool_execution.cancelled` events, `results` is absent. ## 5. Handle assistant message events Assistant chat POSTs (`POST /v1/assistants`, `POST /v1/assistants/{assistantId}/messages`, `POST /v1/assistants/{assistantId}/actions/{actionId}`) emit `assistant_message.succeeded`, `assistant_message.failed`, and `assistant_message.cancelled`. The payload mirrors `GET /v1/assistant-messages/{messageId}`: poll that endpoint if you need the full message after receiving the event. ## 6. Verify signatures Webhook deliveries follow the [Standard Webhooks](https://www.standardwebhooks.com/) spec. Both SDKs ship a `verifyWebhookSignature` helper so you can confirm a request is authentic without pulling in the `standardwebhooks` library yourself: **TypeScript:** ```typescript import { verifyWebhookSignature } from "@videogen/sdk"; // In your webhook handler (e.g. Express, Next.js API route): const payload = verifyWebhookSignature( rawBody, // the raw request body string (not parsed JSON) request.headers, // must include webhook-id, webhook-timestamp, webhook-signature signingSecret, // the secret returned when you created the endpoint ); console.log(payload.event); // "tool_execution.succeeded" console.log(payload.toolExecutionId); // "vg_tool_..." ``` **Python:** ```python from videogen import verify_webhook_signature # In your webhook handler (e.g. Flask, FastAPI): payload = verify_webhook_signature( raw_body=request.data.decode(), # the raw request body string (not parsed JSON) headers=dict(request.headers), # must include webhook-id, webhook-timestamp, webhook-signature signing_secret=signing_secret, # the secret returned when you created the endpoint ) print(payload["event"]) # "tool_execution.succeeded" print(payload["toolExecutionId"]) # "vg_tool_..." ``` **Manual verification:** If you'd rather verify manually, use the `standardwebhooks` library directly: ```bash npm install standardwebhooks # TypeScript pip install standardwebhooks # Python ``` ```typescript import { Webhook } from "standardwebhooks"; const wh = new Webhook(signingSecret); const payload = wh.verify(rawBody, headers); ``` The helpers throw if the signature is invalid or the timestamp is too old, so catch the error to reject the request. ## File upload webhooks In addition to tool execution events, you can subscribe to file upload lifecycle events. These are only fired for files uploaded via the API (not the VideoGen UI). | Event | When it fires | | ------------------------- | --------------------------------------------------------------------------------------------------- | | `file.upload.completed` | File bytes received and stored successfully. | | `file.upload.failed` | Upload or initial processing failed. | | `file.playback_ready` | HLS streaming is available. | | `file.download_ready` | At least one static rendition is available for download. | | `file.analysis_completed` | Description, transcript, and vector embedding are ready. The file is now searchable. | | `file.analysis_failed` | Automated analysis failed. The file is still usable, but description and transcript may be missing. | Register for file events the same way as tool events: ```bash curl -X POST https://api.videogen.io/v1/webhooks/endpoints \ -H "Authorization: Bearer sk_videogen_live_..." \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-server.com/webhooks/videogen", "events": ["file.upload.completed", "file.playback_ready", "file.download_ready", "file.analysis_completed"] }' ``` ## Next steps * [Webhook events reference](/webhook-events) — full list of event types and payload schemas * [Webhook management API](/rest-api-reference/webhooks/list-webhook-endpoints) — create, list, and delete webhook endpoints programmatically > HTTP API with TypeScript and Python SDKs for generating image, video, and audio assets with VideoGen.