Submit a video job with a callback URL and verify OpenRouter webhook signatures
Use this guide when you need to add webhook-based video completion handling instead of polling from a client or worker.By the end, your implementation should submit a video job with callback_url and verify the webhook signature.
An OpenRouter API key available as OPENROUTER_API_KEY
Node.js 20 or newer
A public HTTPS endpoint for your webhook receiver
A webhook signing secret configured in your OpenRouter workspace settings
A video model slug for the job you submit with callback_url
If you haven’t chosen a model yet, read Choose a Video Generation Model first. It helps you pick one based on clip duration, output shape, input type, audio, provider controls, and cost.
Use the API reference pages as the source of truth for exact fields:
Add a webhook receiver that preserves the raw request body before parsing JSON. Signature verification must use the exact bytes OpenRouter sent, not a re-serialized payload.Example Express receiver:
Expose the receiver with a public HTTPS URL before using it as a real callback_url. A local tunnel or deployed preview URL works as long as OpenRouter can reach it over HTTPS.
Before spending credits on a real video job, test the receiver with a locally signed event. This verifies that raw-body handling, timestamp parsing, HMAC comparison, and idempotency headers are wired correctly.Example local sender:
A valid signed event should return 204. Change the secret or signature to confirm the receiver returns 401 for invalid requests.Actual local signature-test output:
204
You can also use a temporary Webhook.site URL as CALLBACK_URL to confirm OpenRouter delivers the webhook and includes the expected headers and envelope. Webhook.site doesn’t run your signature verifier; use your own public receiver with the workspace signing secret for end-to-end signature verification.Example Webhook.site delivery:
Once the receiver is reachable over public HTTPS, submit the video job with callback_url. You can set the callback URL per request, which is useful for preview environments or tenant-specific receivers.Example submit logic:
const apiKey = process.env.OPENROUTER_API_KEY;const callbackUrl = process.env.CALLBACK_URL;if (!apiKey) { throw new Error("Set OPENROUTER_API_KEY first.");}if (!callbackUrl) { throw new Error("Set CALLBACK_URL to your public HTTPS receiver URL.");}const response = await fetch("https://openrouter.ai/api/v1/videos", { method: "POST", headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", }, body: JSON.stringify({ model: "google/veo-3.1-lite", prompt: "A clean product reveal of a matte black desk lamp, slow camera slide, studio lighting", duration: 4, resolution: "720p", aspect_ratio: "16:9", generate_audio: false, callback_url: callbackUrl, }),});if (!response.ok) { throw new Error(await response.text());}console.log(await response.json());
The submit call returns the initial job fields. In a completed run, that job later completed and delivered a webhook with this final summary:
Handle webhook delivery as a terminal job update. The payload is an event envelope with the job fields inside data. Depending on the terminal state, data includes fields such as id, status, generation_id, model, unsigned_urls, usage, and error. Store the job state in your database, deduplicate retries with X-OpenRouter-Idempotency-Key, then download the video from the first unsigned_urls entry or from the content endpoint. If the URL points to the OpenRouter API, include the bearer token when downloading it.For a complete polling and download helper, see Generate and Download a Video from Text.Actual local receiver log shape from the signature test:
Video ready: { id: "job_test", idempotencyKey: "job_test-completed", url: "https://example.com/video.mp4"}
Your receiver should return 204 for a valid OpenRouter webhook and 401 for a request with a missing or invalid signature. A real callback delivery should produce a terminal job update that your app can store and use to download the generated video.
Was this page helpful?
Assistant
Responses are generated using AI and may contain mistakes.