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. JSON2Video waits at most 30 seconds; an answer that takes longer is recorded as a failed delivery and not sent again. If you need to do heavy work, queue it and return immediately.
  3. Be idempotent. The same payload arrives again when your endpoint answers with a 5xx status or drops the connection (up to 3 attempts in total). De-duplicate on project.
  4. Tolerate unknown fields. New fields may be added over time; your parser should not reject them.
  5. Ignore test requests. The Test button in Dashboard → Connections sends a payload with "test": true and fake values.

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. project, id and client-data are still sent.

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.

Checking deliveries

Every delivery attempt is recorded on the movie: open the render in the dashboard (Render logs → the render → Deliveries), or read destinations_result from GET /v2/movies?project={project}. A webhook saved in Connections → Output destinations also shows its Last delivery.

JSON2Video only calls public internet addresses: an endpoint that resolves to a private, loopback or link-local address is never called, so use a tunnel (below) to reach a local server.

Local development with webhooks

Public webhook receivers are hard during local development. Use:

See also