Archived docs Get your API Key
Get started
Tutorials
Guides
Reference
Help for AI 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 the rendered movie object as the body.

What the payload looks like

The payload mirrors what the GET /v2/movies?project=... endpoint returns:

{
  "success": true,
  "movie": {
    "project": "WAEE8PohgVwv2teP",
    "status": "done",
    "url": "https://assets.json2video.com/clients/.../movie.mp4",
    "thumbnail": "https://assets.json2video.com/clients/.../thumbnail.jpg",
    "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 movie.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 { movie } = req.body;
  if (!movie?.project) return;

  // Lookup the order in your DB by movie.client-data.orderId
  await handleRender(movie);
});

Error handling

If the render fails, JSON2Video still calls your webhook. Check movie.status:

  • "done" — render succeeded; movie.url is set.
  • "error" — render failed; movie.message describes why.
  • "timeout" — render exceeded the maximum allowed time.

Handle the error states explicitly in your handler — don't assume success: true at the top level means the video is ready.

Local development with webhooks

Public webhook receivers are hard during local development. Use:

See also