n8n end-to-end
n8n is a fair-code workflow automation tool. It is the most common self-hostable alternative to Make.com / Zapier and pairs well with JSON2Video for production-grade pipelines you want to run on your own infrastructure.
JSON2Video publishes an official n8n node, n8n-nodes-json2video, built and maintained by our team. It is a verified community node, so it is available on n8n Cloud and self-hosted alike. It gives you 22 operations across movies, templates and Drive storage, with the template's variables rendered as real input fields — no hand-built HTTP requests, no JSON string wrangling.
If you are on an old self-hosted version, or your instance has community nodes disabled, skip to Appendix: the HTTP Request fallback.
1. Prerequisites
- An n8n instance (self-hosted, n8n Cloud, or local).
- A JSON2Video account with an API key. Get one at json2video.com/dashboard/apikeys.
- Familiarity with n8n's expression editor (
{{ }}syntax) and credentials.
2. Install the node
n8n Cloud: search for JSON2Video in the nodes panel and drag it onto the canvas. Verified nodes install without leaving the editor.
Self-hosted:
- Go to Settings → Community Nodes.
- Select Install.
- Enter
n8n-nodes-json2video. - Agree to the risks and select Install.
The package is published from GitHub Actions with an npm provenance attestation, so the tarball you install is cryptographically traceable to the public source commit it was built from — this is what n8n's verification process requires.
3. Create the credential
- In n8n, add a JSON2Video node to a workflow.
- Under Credential to connect with, select Create new credential.
- Paste your API key and save.
Best practice is a dedicated key per integration rather than reusing one key everywhere — if the n8n side is ever compromised you revoke just this key. The Render role is enough to render movies and use the Drive; Editor is needed to create, update or delete templates. See API keys for what each role can do.
The key is sent as the x-api-key header on every request, stored encrypted by n8n, and never echoed back in error messages.
4. Pick the right operation
| Resource | Operation | Use it for |
|---|---|---|
| Movie | Create | Submit a render and return immediately with a project ID |
| Render and Wait | Submit a render and poll until it finishes, then return the video URL | |
| Get Status | Check one movie by project ID | |
| Get Many | List the account's renders within a date range | |
| Delete | Delete a rendered file before its 7-day expiry | |
| Template | Get Many / Get Library | List your templates, or the public gallery |
| Get Variables | Discover a template's {{placeholder}} inputs at runtime |
|
| Create / Update / Duplicate / Delete | Manage templates from a workflow | |
| Storage | Upload File | Push binary data from a previous node and get a public URL |
| List Folder / Get File / Move / Delete | Manage the JSON2Video Drive | |
| Get Storage Usage | Bytes used, free allowance, upload-blocked flag |
The full list of all 22 operations is in the node's README.
For most workflows the pattern is:
- Trigger (e.g. Google Sheets — Watch new rows).
- JSON2Video → Movie → Render and Wait.
- Downstream action (upload to YouTube, post to Slack, store the URL).
5. Render a template
The common case is a saved template whose placeholders come from workflow data.
-
Add a JSON2Video node, resource Movie, operation Render and Wait.
-
Set Source to Template.
-
Pick the template from the Template dropdown — it lists the templates in your account.
-
The Variables section then fills in with that template's own variables, one labelled field each. Type a literal value, or drop in an expression:
Headline {{ $json.property_title }} Image URL {{ $json.photo_url }} Price {{ $json.price }}
Because the fields are generated from the template itself, renaming a variable in the template surfaces here rather than silently rendering an empty video.
To build the movie JSON directly instead, set Source to Movie JSON and paste any movie document. Both sources accept overrides for resolution, quality, frame rate, cache, client data and the webhook destination.
6. Wait for the render
A render typically takes 10–90 seconds. Two options:
Option A: Render and Wait
Movie → Render and Wait polls for you and emits one item when the render reaches a terminal state. Configure the poll timeout to sit comfortably under your n8n workflow timeout — if the workflow times out first, the render still completes on our side, but the workflow loses the result.
This is the simplest correct choice for renders that finish in a couple of minutes.
Option B: Webhook callback
For long renders, or to avoid holding an execution open:
- Use Movie → Create and set the Webhook URL field to an n8n Webhook trigger URL.
- In n8n, create a second workflow starting with a Webhook trigger node. n8n shows you the public URL to paste in step 1.
- The trigger fires with the full movie object as the body when the render finishes.
Webhook-based flows scale better and don't burn n8n executions on polling. See Webhooks.
7. Use the rendered video
With Simplify on (the default), the node drops the response envelope and emits the movie object as the item itself:
{{ $json.url }}— direct MP4 URL on the JSON2Video CDN.nulluntil the render isdone, andnullagain once the file expires after 7 days.{{ $json.duration }}— duration in seconds.{{ $json.size }}— file size in bytes.{{ $json.width }}/{{ $json.height }}— output dimensions in pixels.{{ $json.rendering_time }}— how long the render took, in seconds.{{ $json.status }}—pending|running|done|error|timeout.{{ $json.message }}— the failure reason whenstatusiserror.{{ $json.project }}— the 16-character project ID.{{ $json.ass }}— the subtitles file, when the movie generated one.
Turn Simplify off to get the raw envelope, where the same fields sit under movie and the response also carries remaining_quota.
Connect downstream nodes:
- YouTube → upload the rendered MP4.
- Slack / Discord → notify a channel with the URL.
- HTTP Request → send the URL to your own backend.
- Postgres / MySQL → store the URL in your database.
Rendered files are deleted 7 days after the render. Copy the MP4 to your own storage if you need it longer — see File retention.
8. Error handling
In production, wire up an error branch:
- A render that fails comes back with
status: "error"and a human-readablemessage— surface that string, it is the one that tells you what went wrong. status: "timeout"means the render went 15 minutes without a heartbeat.- Network failures should retry with exponential backoff (n8n has a built-in Retry on Fail option).
See Error handling for the full pattern.
9. Use the node as an AI Agent tool
The node is marked usableAsTool, so an n8n AI Agent can call it directly. Pair Template → Get Variables with Movie → Render and Wait and the agent can discover a template's inputs at runtime and fill them itself, instead of having the variable names hard-coded in the prompt.
Appendix: the HTTP Request fallback
If community nodes are disabled on your instance, you can still call the REST API directly with the built-in HTTP Request node.
Store the API key as a credential: open Credentials → New, choose Header Auth, set Name = x-api-key and Value = your API key.
Submit a render:
| Field | Value |
|---|---|
| Method | POST |
| URL | https://api.json2video.com/v2/movies |
| Authentication | Generic Credential Type → Header Auth → JSON2Video API key |
| Send Body | enabled, Body Content Type: JSON, Specify Body: Using JSON |
| Body | the movie JSON |
A minimal body that produces a 5-second video:
{
"comment": "Generated by n8n",
"resolution": "full-hd",
"scenes": [
{
"elements": [
{ "type": "text", "text": "Hello from n8n!", "duration": 5, "style": "002" }
]
}
]
}
The response carries the project ID:
{
"success": true,
"project": "WAEE8PohgVwv2teP",
"timestamp": "2025-05-28T14:57:34.393Z"
}
Poll for the result:
- Add a Wait node (e.g. 5 seconds).
- Add a second HTTP Request node, method
GET, URLhttps://api.json2video.com/v2/movies?project={{ $node["Create movie"].json.project }}, same credential. - Add an IF node on
{{ $json.movie.status }}:done→ continue,errorortimeout→ error handler, anything else → loop back to the Wait node.
Cap the maximum number of loop iterations so a stuck render can't loop forever. On this path the movie fields are nested under movie — {{ $json.movie.url }}, {{ $json.movie.duration }}, and so on.