Developer Guides
Video Rendering Webhooks: API Status Guide
Learn how video rendering webhooks work with webhook endpoints, polling, callbacks, retries, and Zvid JSON workflows.
Published June 9, 2026
Video Rendering Webhooks: API Status Guide
Video rendering webhooks let an application react when an asynchronous video job changes state, usually when the render finishes or fails. A webhook endpoint receives an HTTP POST request with JSON data about the event, then returns a 2xx response after it accepts the notification. The important idea is simple: your app should not keep a user request open while a video renders. It should submit a render job, store the job ID, track status, and trigger the next workflow step when the final result is available.
Zvid's documented public API flow uses that same asynchronous model: submit a JSON video payload to POST https://api.zvid.io/api/render/api-key, save the returned job ID, then poll GET https://api.zvid.io/api/jobs/{id} until the job is completed or failed. If your product needs webhook behavior, build a small status worker that polls Zvid, then sends your own internal webhook, queue message, or database event when the render reaches a terminal state. Keep the Getting Started guide, Authentication guide, Submit render job reference, Get render job status reference, and JSON Structure overview open while wiring the flow.
If you are still designing the broader automation system, compare this guide with Zvid's JSON to Video API guide, the guide to bulk video generation with an API, and the build-vs-buy guide for an FFmpeg rendering pipeline.

A reliable render lifecycle separates job submission, status tracking, and downstream callbacks.
The basic video render lifecycle
A video rendering API usually works asynchronously because rendering can take longer than a normal HTTP request should stay open. The server accepts a render request, queues work, renders the video, stores the result, and exposes status while the job moves through the system.
The minimum lifecycle is:
- Build a render payload from source data, template rules, media URLs, and output settings.
- Submit the payload to the rendering API.
- Store the returned job ID with your source record.
- Check status until the job is completed or failed.
- Store the completed video URL or failure reason.
- Notify the rest of your product through a webhook, queue, email, CMS update, or review task.
For Zvid, the public API request starts like this:
curl -X POST https://api.zvid.io/api/render/api-key \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d @render-job.json
Then your worker checks status:
curl -X GET https://api.zvid.io/api/jobs/$JOB_ID \
-H "x-api-key: YOUR_API_KEY"
The first request creates the job. The second request tells your system whether the job is still active, completed, or failed. The webhook part is what your application does after that status change is known.
Webhook, callback, or polling: what changes?
Teams often use these words loosely, so define the boundary before coding:
- Polling means your worker asks the render API for status on a schedule.
- Webhook means one system sends an HTTP request to a webhook endpoint when an event happens.
- Callback is the broader idea of triggering a follow-up action after a job changes state.
- Queue event means your system publishes a message to a worker, event bus, or task processor.
Provider-sent webhooks are convenient when the rendering platform calls your URL directly. Polling is easier to reason about when your application wants full control over retry timing, concurrency, and state transitions. A polling-to-webhook pattern gives you both: your backend polls the render API, then emits a webhook request or internal event to the rest of your product.
For real-time automation, webhooks are automated notifications rather than a replacement for every API call. Your application still needs a way to retrieve the source record, inspect the render payload, and store the completed video URL. For high-volume VOD, streaming, or AI video workflows, the webhook should simplify status updates without hiding the rendering process from your own database.

A polling worker can turn render status changes into internal webhook events.
Copy-paste Zvid render payload for a status workflow demo
The payload below renders a simple status workflow scene. In a real product, the text and panels could come from a product feed, CRM record, course lesson, listing, or AI-approved script. Your backend would submit the payload, store the returned job ID, and trigger downstream work only after the status endpoint returns a final result.
{
"name": "video-rendering-webhooks-status-demo",
"resolution": "hd",
"duration": 9,
"frameRate": 30,
"outputFormat": "mp4",
"backgroundColor": "#07111F",
"visuals": [
{
"type": "SVG",
"width": 1280,
"height": 720,
"track": 1,
"svg": "<svg width='1280' height='720' viewBox='0 0 1280 720' xmlns='http://www.w3.org/2000/svg'><defs><linearGradient id='bg' x1='0' y1='0' x2='1' y2='1'><stop offset='0' stop-color='#07111F'/><stop offset='1' stop-color='#29184A'/></linearGradient><linearGradient id='accent' x1='0' y1='0' x2='1' y2='0'><stop offset='0' stop-color='#2DD4BF'/><stop offset='0.55' stop-color='#FADD46'/><stop offset='1' stop-color='#FB7185'/></linearGradient></defs><rect width='1280' height='720' fill='url(#bg)'/><rect x='58' y='54' width='1164' height='612' rx='30' fill='rgba(255,255,255,0.052)' stroke='rgba(255,255,255,0.15)'/><text x='640' y='120' text-anchor='middle' fill='#FFFFFF' font-family='Arial' font-size='40' font-weight='800'>Render Status Workflow</text><text x='640' y='160' text-anchor='middle' fill='#C8D2F1' font-family='Arial' font-size='20'>Submit, track, then trigger the next step</text><rect x='96' y='248' width='230' height='214' rx='24' fill='rgba(7,17,31,0.86)' stroke='rgba(45,212,191,0.42)'/><rect x='386' y='248' width='230' height='214' rx='24' fill='rgba(7,17,31,0.86)' stroke='rgba(250,221,70,0.42)'/><rect x='676' y='248' width='230' height='214' rx='24' fill='rgba(7,17,31,0.86)' stroke='rgba(251,113,133,0.42)'/><rect x='966' y='248' width='230' height='214' rx='24' fill='rgba(7,17,31,0.86)' stroke='rgba(255,255,255,0.24)'/><text x='211' y='314' text-anchor='middle' fill='#2DD4BF' font-family='Arial' font-size='25' font-weight='800'>Submit</text><text x='501' y='314' text-anchor='middle' fill='#FADD46' font-family='Arial' font-size='25' font-weight='800'>Poll</text><text x='791' y='314' text-anchor='middle' fill='#FB7185' font-family='Arial' font-size='25' font-weight='800'>Event</text><text x='1081' y='314' text-anchor='middle' fill='#FFFFFF' font-family='Arial' font-size='25' font-weight='800'>Deliver</text><rect x='132' y='360' width='158' height='18' rx='9' fill='rgba(255,255,255,0.22)'/><rect x='132' y='396' width='124' height='14' rx='7' fill='rgba(255,255,255,0.13)'/><rect x='422' y='360' width='158' height='18' rx='9' fill='rgba(255,255,255,0.22)'/><rect x='422' y='396' width='124' height='14' rx='7' fill='rgba(255,255,255,0.13)'/><rect x='712' y='360' width='158' height='18' rx='9' fill='rgba(255,255,255,0.22)'/><rect x='712' y='396' width='124' height='14' rx='7' fill='rgba(255,255,255,0.13)'/><rect x='1002' y='360' width='158' height='18' rx='9' fill='rgba(255,255,255,0.22)'/><rect x='1002' y='396' width='124' height='14' rx='7' fill='rgba(255,255,255,0.13)'/><path d='M326 355 H386' stroke='url(#accent)' stroke-width='8' stroke-linecap='round'/><path d='M616 355 H676' stroke='url(#accent)' stroke-width='8' stroke-linecap='round'/><path d='M906 355 H966' stroke='url(#accent)' stroke-width='8' stroke-linecap='round'/><rect x='250' y='548' width='780' height='50' rx='25' fill='rgba(255,255,255,0.09)'/><rect x='250' y='548' width='580' height='50' rx='25' fill='url(#accent)'/><text x='640' y='581' text-anchor='middle' fill='#07111F' font-family='Arial' font-size='21' font-weight='800'>Job ID links every callback to the source record</text></svg>"
}
]
}

This payload visual is generated from the same Zvid API payload shown above.
For the public render endpoint, wrap the project object in a top-level payload field:
{
"payload": {
"name": "video-rendering-webhooks-status-demo",
"resolution": "hd",
"duration": 9,
"frameRate": 30,
"outputFormat": "mp4",
"visuals": []
}
}
How to build a polling-to-webhook bridge
The bridge is a small worker that converts render job status into product events. It does not need to know how to design videos. It only needs to know which jobs are active, how often to check them, and what to do when the state changes.
A practical database row might store:
- Source record ID.
- Zvid job ID.
- Payload version.
- Current state.
- Last checked time.
- Attempt count.
- Completed video URL.
- Failure reason.
- Whether the downstream callback has been delivered.
The worker loop is straightforward:
- Select active jobs that are due for a status check.
- Call
GET https://api.zvid.io/api/jobs/{id}with the server-side API key. - Update the local job state.
- If the job is completed, store the result URL and publish a
video.render.completedevent. - If the job failed, store the failure reason and publish a
video.render.failedevent. - Mark callback delivery separately from render completion so delivery can be retried safely.

Keep render completion and webhook delivery as separate states so each can be retried cleanly.
Set up and secure the webhook endpoint
A webhook endpoint should be publicly accessible only if an external system must call it. For internal render automation, it can also be a private service, queue consumer, serverless function, or backend route. The endpoint should accept a POST request, parse a small JSON payload, verify that the event is valid, and return a 2xx status only after the event is safely stored.
Use a webhook secret to sign event bodies when requests cross a public network. Store the secret server-side, include a timestamp header, and reject requests with missing signatures, stale timestamps, or invalid hashes. This protects your endpoint from replayed notifications and random traffic from a browser, bot, or misconfigured integration.
If your product already uses Kafka, SQS, RabbitMQ, or another event bus, the webhook can be the edge of the system rather than the whole system. The HTTP receiver validates the request, then publishes a durable event such as video.render.completed for downstream workers, analytics, dashboard updates, user notifications, or CMS publishing.
Retry rules for automated rendering callbacks
Do not retry every failure the same way. A temporary network error while checking status is different from a render failure caused by bad input data.
Good retry rules usually include:
- Retry transient request failures with backoff.
- Stop polling when the render job reaches a terminal state.
- Do not keep sending the same webhook forever without a maximum attempt count.
- Store the exact callback payload you tried to deliver.
- Make downstream handlers idempotent by including the render job ID and event type.
- Treat completed render state and callback delivery state as separate columns.
- Treat any non-2xx response as undelivered unless the receiver has already stored the event.
This separation matters in bulk workflows. If a video completed successfully but your webhook receiver timed out, you should retry the notification without creating another render job. If a render failed because an image URL was unavailable, retrying the webhook will not help; someone needs to repair the source data or payload.
What should a render status webhook payload include?
Even when the webhook is internal to your own application, keep the event payload small and stable. The receiver should be able to look up richer state in your database if needed.
A useful event shape is:
{
"event": "video.render.completed",
"renderJobId": "zvid-job-id-from-your-record",
"sourceRecordId": "product-123",
"status": "completed",
"resultUrl": "https://example.com/final-video.mp4",
"payloadVersion": "product-promo-v4",
"occurredAt": "2026-05-24T06:00:00.000Z"
}
Keep API keys out of webhook payloads. Keep the Zvid request server-side. If your receiver is public, sign the event body, include a timestamp, and reject stale or replayed requests.
Useful events include video.render.queued, video.render.processing, video.render.completed, and video.render.failed. Many products only emit completion and failure events because those are the states that trigger external work, such as exporting a new video to a customer dashboard, updating analytics, or notifying a user.

The right status pattern depends on whether your product values provider callbacks, local state control, or both.
Common mistakes
The most common mistake is treating render submission as the end of the workflow. In video automation, submission is only the start. The useful product state comes later, when the output URL exists or the failure is clear enough to fix.
Other mistakes include:
- Keeping a frontend request open while waiting for a render.
- Not storing the job ID next to the source record.
- Polling forever after completion or failure.
- Retrying a failed render without understanding the failure.
- Sending webhook events before the result URL is stored.
- Letting webhook delivery failures create duplicate videos.
- Exposing API keys in client-side code.
- Forgetting to verify that webhook receivers are idempotent.
- Reusing one event type for both success and failure.
- Deleting the payload that produced the video.
- Accepting unsigned webhook requests on a public endpoint.
- Returning 2xx before the event has been stored.
The fix is a small amount of state discipline. Store job IDs, source record IDs, payload versions, current status, final result URLs, and callback delivery attempts. That gives developers and operators a clear audit trail.
When to use Zvid
Use Zvid when your application needs repeatable server-side video rendering from structured JSON, and your product can own the workflow around job tracking, status polling, and downstream events. Zvid is a strong fit for SaaS products, AI video apps, e-commerce catalogs, real estate listings, automotive inventory, EdTech lessons, marketing agencies, and internal automation tools.

Render status events are most useful when many source records need dependable follow-up actions.
Zvid is especially useful when you need:
- Backend-generated JSON payloads.
- Hosted render jobs with API submission and status polling.
- Programmatic control over text, media, timing, layout, captions, and output format.
- A durable relationship between source record, render job, payload version, and final video.
- A clean handoff from completed render to CMS publishing, customer notification, review queue, or another automation step.
Start with one render job and one callback path. Submit a real payload, store the job ID, poll until it finishes, then trigger one downstream action. After that loop is reliable, scale the worker across many records.
FAQs
What are video rendering webhooks?
Video rendering webhooks are HTTP callbacks or event notifications triggered when a video render job changes state, usually when it completes or fails. A webhook endpoint receives a POST request with JSON data about the event.
Does a video rendering API need webhooks?
Not always. A rendering API can expose status through polling, provider-sent webhooks, or both. The key requirement is that your application has a reliable way to learn when the job reaches a final state.
How does Zvid handle render status?
Zvid's documented public API flow returns a job ID after render submission and exposes a job status endpoint at GET https://api.zvid.io/api/jobs/{id}. Your backend can poll that endpoint and trigger its own callback, queue event, or webhook after completion or failure.
Should I use polling or webhooks for video rendering?
Use polling when your app needs direct control over status checks, retries, and state. Use webhooks when an event should push into another system. A polling-to-webhook bridge is often practical because it keeps render state in your database while still enabling event-driven workflows.
What should I store for each render job?
Store the source record ID, payload version, render job ID, current status, last checked time, result URL, failure reason, and downstream callback delivery attempts.
How do I avoid duplicate webhook actions?
Make webhook handlers idempotent. Include the render job ID, event type, and payload version, then ignore events that have already been processed.
What happens if the webhook receiver is down?
Do not create another render job just because the receiver failed. Store the completed render result and retry only the callback delivery with backoff and a maximum attempt count.
Can AI video apps use render status callbacks?
Yes. AI video apps can generate or approve scripts, scenes, captions, and media upstream, submit a structured Zvid payload for rendering, then use a callback event to publish the completed video or start review.
What is the safest first implementation?
Start with polling. Store every job ID and status transition, then add an internal webhook or queue event after completion. This keeps the first integration debuggable.
Is a webhook better than an API?
A webhook is not better than an API in every case. APIs are best for requesting or retrieving data, while webhooks are best for notifying another system after an event occurs.
What is the difference between a webhook and Kafka?
A webhook is usually an HTTP callback between systems. Kafka is an event streaming platform used inside larger event-driven architectures. A webhook receiver can publish validated render events into Kafka or another queue.