n8n Automation with ViralSync
Automate video generation workflows in n8n by connecting ViralSync's Render API to your existing data sources, AI tools, and notification systems.
Prerequisites
- n8n instance (self-hosted or cloud at app.n8n.cloud)
- ViralSync API key (Pro plan or higher) — create in Dashboard → Settings → API Keys
- Your ViralSync project ID — visible in Dashboard → Projects
Pattern 1: Submit and poll (beginner)
This simple workflow submits a render job and polls until it completes. Good for workflows where n8n isn't publicly reachable.
Step 1 — HTTP Request: Submit render job
Configure an HTTP Request node:
| Setting | Value |
|---|---|
| Method | POST |
| URL | https://api.viralsync.io/v1/render/jobs |
| Authentication | Header Auth |
| Header Name | x-api-key |
| Header Value | {{ $vars.VIRALSYNC_API_KEY }} (store in n8n Variables) |
| Body | JSON |
Body content:
{
"kind": "movie",
"projectId": "proj_abc123",
"movie": {
"width": 1920,
"height": 1080,
"fps": 30,
"scenes": [
{
"id": "scene_1",
"duration": 5,
"bgColor": "#1a1a2e",
"layers": [
{
"id": "title",
"type": "text",
"text": "{{ $json.title }}",
"startTime": 0,
"duration": 5,
"zIndex": 1,
"position": { "x": 360, "y": 460 },
"size": { "width": 1200, "height": 160 },
"fontSize": 72,
"fontWeight": "bold",
"color": "#ffffff",
"textAlign": "center"
}
]
}
]
}
}
This returns { "jobId": "job_..." }. Save the jobId for subsequent nodes.
Step 2 — Wait node
Add a Wait node set to 30 seconds. This gives the render job time to process before the first status check.
Step 3 — HTTP Request: Check status
| Setting | Value |
|---|---|
| Method | GET |
| URL | https://api.viralsync.io/v1/jobs/{{ $('Submit Render Job').item.json.jobId }} |
| Header Name | x-api-key |
| Header Value | {{ $vars.VIRALSYNC_API_KEY }} |
Step 4 — If node: check status
Add an If node with two conditions:
Branch 1 (done): {{ $json.status }} equals done → proceed to your success handler (download video, send Slack message, etc.)
Branch 2 (failed): {{ $json.status }} equals failed → proceed to your error handler
Branch 3 (otherwise): Loop back to the Wait node for another 30 seconds, then re-check
Step 5 — Use the output URL
In the success branch, access the video URL:
{{ $json.outputUrl }}
Pattern 2: Webhook-driven (recommended for production)
Instead of polling, receive a push notification when the job completes. Requires n8n to have a public URL.
Step 1 — Webhook node
Add a Webhook node (trigger type: POST). Copy the webhook URL — it will look like:
https://your-n8n.com/webhook/viralsync-render
Step 2 — Submit render job with callbackUrl
In your HTTP Request node, add callbackUrl to the JSON body:
{
"kind": "movie",
"projectId": "proj_abc123",
"callbackUrl": "https://your-n8n.com/webhook/viralsync-render",
"movie": { ... }
}
Step 3 — Process the webhook event
When the job completes, ViralSync sends:
{
"jobId": "job_a1b2c3d4e5f6",
"status": "done",
"outputUrl": "https://r2.viralsync.io/outputs/job_a1b2c3d4e5f6/output.mp4",
"mediaData": { "duration": 10.0, "width": 1920, "height": 1080 }
}
Access values in downstream nodes:
- Output URL:
{{ $json.outputUrl }} - Duration:
{{ $json.mediaData.duration }} - Status:
{{ $json.status }}
Step 4 — Handle different event types
Add an If node or Switch node on {{ $json.status }}:
done→ success branch (save URL, send notification)failed→ error branch (log error, alert)running/progress→ optional: update a progress indicator
Building dynamic movie JSON
Use n8n expressions to inject data from upstream nodes into the movie JSON:
Example: product video with dynamic title and image
Assume an upstream node returns { "productName": "Widget Pro", "imageUrl": "https://cdn...." }:
{
"kind": "movie",
"projectId": "proj_abc123",
"movie": {
"width": 1920,
"height": 1080,
"fps": 30,
"scenes": [
{
"id": "scene_product",
"duration": 6,
"bgColor": "#0f172a",
"layers": [
{
"id": "product_image",
"type": "image",
"source": "{{ $json.imageUrl }}",
"startTime": 0,
"duration": 6,
"zIndex": 0,
"position": { "x": 0, "y": 0 },
"size": { "width": 960, "height": 1080 }
},
{
"id": "product_name",
"type": "text",
"text": "{{ $json.productName }}",
"startTime": 0,
"duration": 6,
"zIndex": 1,
"position": { "x": 980, "y": 400 },
"size": { "width": 880, "height": 120 },
"fontSize": 56,
"fontWeight": "bold",
"color": "#ffffff",
"textAlign": "left"
}
]
}
]
}
}
Handling errors in n8n
ViralSync error responses include a code field. Use this in an If node to handle specific errors:
| Error code | n8n action |
|---|---|
CONCURRENCY_LIMIT_EXCEEDED | Add a 30-second Wait, then retry the submission |
RATE_LIMITED | Read Retry-After from headers; add a Wait node for that duration |
RENDER_QUOTA_EXCEEDED | Send a notification and stop the workflow |
ASSET_NOT_FOUND | Log the failed URL, alert the team |
In the HTTP Request node, enable "Continue on Fail" and check {{ $json.code }} in a downstream If node.
Tips
Store your API key in n8n Variables, not hardcoded in nodes: go to Settings → Variables → Add Variable (VIRALSYNC_API_KEY). Then use {{ $vars.VIRALSYNC_API_KEY }} in all HTTP Request headers.
Use n8n's built-in retry: In the HTTP Request node's Options, set Retry on Fail: 3 and Retry Interval: 2000ms to automatically handle transient 5xx errors.
For multi-scene videos from a list: Use n8n's Loop Over Items node to build a scenes array from a list of data items, then pass it as the movie.scenes field.
Webhook tunnel for local development: If running n8n locally, use ngrok http 5678 to expose it and use the ngrok URL as your callbackUrl.