Archived docs Get your API Key
Get started
Tutorials
Guides
Reference
Help for coding agents
🤖 AI Assistant

Webhooks (production patterns)

Webhooks are the recommended way to learn when a render finishes — they replace polling and let your backend react the moment a video is ready. This guide covers the production-grade patterns that go beyond the basic webhooks reference.

When to use webhooks vs polling

Situation Recommended
Backend service with a public HTTPS endpoint Webhooks
Local script or interactive CLI tool Polling
Long-running batch job (hundreds of renders / minute) Webhooks
Render duration < 30 seconds and you can afford a sync wait Polling (simpler)

Configure a webhook destination

Add a webhook entry to the exports[].destinations array:

{
  "resolution": "full-hd",
  "scenes": [ /* ... */ ],
  "exports": [{
    "destinations": [{
      "type": "webhook",
      "endpoint": "https://api.example.com/json2video/done"
    }]
  }]
}

When the movie finishes (or fails), JSON2Video sends an HTTP POST with a flat payload describing the render outcome.

What the payload looks like

The payload is a flat JSON object — it does not mirror GET /v2/movies and there is no nested movie wrapper (see the webhooks reference for the field-by-field contract):

{
  "success": true,
  "project": "WAEE8PohgVwv2teP",
  "url": "https://assets.json2video.com/clients/.../movie.mp4",
  "id": "your-movie-id",
  "width": "1920",
  "height": "1080",
  "duration": "12.5",
  "size": "2451234",
  "client-data": { /* anything you sent in the original request */ }
}

The client-data field is the most important field for production. Set it on the original POST /v2/movies to anything that helps your backend identify which business object this render belongs to (an order ID, a user ID, a campaign slug, a row ID in your database). It is echoed back verbatim.

Receiving webhooks safely

Your endpoint must:

  1. Be publicly reachable over HTTPS. Self-signed certs are not accepted; use a real CA (Let's Encrypt, Cloudflare, etc.).
  2. Respond quickly. Aim for < 5 seconds. If you need to do heavy work, queue it and return immediately.
  3. Be idempotent. The same movie payload may arrive more than once if your endpoint times out. De-duplicate on project.
  4. Tolerate unknown fields. New fields may be added over time; your parser should not reject them.

A minimal Node/Express handler:

import express from "express";
const app = express();
app.use(express.json({ limit: "1mb" }));

app.post("/json2video/done", async (req, res) => {
  res.status(200).end(); // ack first, work after

  const payload = req.body;
  if (!payload?.project) return;

  // Lookup the order in your DB by payload["client-data"].orderId
  await handleRender(payload);
});

Error handling

If the render fails, JSON2Video still calls your webhook. Check the flat payload:

  • success: true — render succeeded; url is set.
  • success: false — render failed; the error field describes why and url is empty or absent.

There is no status field in the webhook payload. To get the full status object (done / error / timeout), call GET /v2/movies?project={project} — cross-checking there is also the recommended way to verify an unauthenticated webhook.

Local development with webhooks

Public webhook receivers are hard during local development. Use:

See also