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

Webhooks

Webhooks deliver render completion events to a URL of your choice. They are the recommended alternative to polling GET /v2/movies.

Configuration

Webhooks are declared in the movie's exports array as a destinations[].type = "webhook" entry, or by referencing a Dashboard connection ID.

Inline endpoint

{
    "resolution": "full-hd",
    "scenes": [],
    "exports": [
        {
            "destinations": [
                {
                    "type": "webhook",
                    "endpoint": "https://example.com/webhook",
                    "content-type": "json"
                }
            ]
        }
    ]
}
Field Required Description
type yes "webhook".
endpoint yes Publicly reachable HTTPS URL.
content-type no json (default) or urlencoded.

Connection reference

For security, store the endpoint as a Dashboard connection and reference it by ID. This keeps secrets out of the Movie JSON.

{
    "exports": [
        { "destinations": [ { "id": "your-webhook-connection-id" } ] }
    ]
}

Event types

The webhook fires once per export pipeline, after rendering completes (or, when configured, after another destination has finished). There is one event today:

Event Fired when
movie.completed The render finished — successfully or with an error. Inspect success and status in the payload to distinguish.

The event name is not currently transmitted in the payload; consumers should treat any inbound POST to their webhook URL as a movie.completed event.

Payload

The HTTP request is POST with the body shape below. Default content-type is application/json. When the destination declares content-type: urlencoded the same fields are sent as application/x-www-form-urlencoded.

{
    "width": "1920",
    "height": "1080",
    "duration": "10.5",
    "size": "4567890",
    "url": "https://assets.json2video.com/clients/xxxxxxxx/renders/yourmovie.mp4",
    "project": "JkGxEoPRF9EgRb32",
    "id": "your-movie-id",
    "client-data": {
        "order_id": "ord_42"
    }
}
Field Type Description
width string Output width in pixels.
height string Output height in pixels.
duration string Output duration in seconds.
size string Output file size in bytes.
url string Public URL of the rendered MP4.
project string The 16-character project ID.
id string The id from the submitted Movie JSON, if any.
client-data object The client-data from the submitted Movie JSON, if any.

Field types are reported as strings in the current payload format. Validate accordingly.

Failure cases

When the render fails, the webhook is still fired (assuming an export pipeline ran). The payload may have an empty or partial url field. Consumers should always cross-check the final status by calling GET /v2/movies?project={id} when handling a webhook.

Retries

The exact retry behaviour is subject to change. Confirm with support if your workflow depends on guaranteed delivery. The handler treats the destination as a fire-and-forget HTTP POST: it is sent once, with a short request timeout, and any non-2xx response is recorded but not retried automatically by the public API today.

For workflows that require strong delivery guarantees, configure your endpoint behind a queue you control (e.g. Make.com, n8n, or your own job runner) and reconcile against GET /v2/movies on a schedule.

Verification

Webhooks are not currently signed by JSON2Video. To verify authenticity:

  1. Configure a long, unguessable path in your endpoint URL (e.g. https://your.app/webhooks/json2video/abc123…).
  2. Optionally include a secret token in the URL query string or as part of the path, and check it server-side.
  3. Always cross-check the payload by calling GET /v2/movies?project={id} with your API key.

Multiple destinations

Destinations inside an exports[].destinations array run sequentially. A common pattern is to upload the video to an FTP server first, then notify your backend with a webhook:

{
    "exports": [
        {
            "destinations": [
                { "id": "your-ftp-connection-id" },
                { "type": "webhook", "endpoint": "https://example.com/webhook" }
            ]
        }
    ]
}

Building the receiver

The endpoint must be publicly reachable over HTTPS with a valid certificate. Minimal receivers:

PHP

<?php
$payload = json_decode(file_get_contents("php://input"), true);
$videoUrl  = $payload["url"] ?? null;
$projectId = $payload["project"] ?? null;

// Your business logic here.

http_response_code(200);
echo "ok";

Node.js (Express)

const express = require("express");
const app = express();
app.use(express.json());

app.post("/webhook", (req, res) => {
    const { url, project } = req.body;
    // Your business logic here.
    res.status(200).send("ok");
});

app.listen(3000);

Always respond with a 2xx status code as soon as the payload is durably captured. Heavy work should happen out of band.