This workflow corresponds to n8n.io template #13752 — we link there as the canonical source.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"meta": {
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "a05ea6b6-03cd-4915-b716-2dd0b0b54e95",
"name": "\ud83d\udccb Flow Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
-672,
-416
],
"parameters": {
"width": 740,
"height": 580,
"content": "## \ud83c\udfac OpenAI Sora \u2013 Video Generation \u2192 Upload to URL\n\n**How it works:**\n1. Webhook receives a POST with `jobType`: **ecommerce** or **remix**\n2. **Route by Job Type** switch sends it down the correct pipeline\n3. A code node crafts the **Sora prompt** and submits the job via `POST /v1/video/generations`\n4. Sora is **async** \u2014 a **Wait \u2192 Poll \u2192 IF** loop checks status every 20 seconds\n5. When `status = succeeded`, an HTTP GET node **downloads the MP4 as binary**\n6. The binary is passed to the **n8n Upload to URL node** which PUTs the MP4 to your CDN\n7. A code node builds the **JSON response** with the public CDN URL\n\n**Credential needed:**\n`OpenAI Header Auth` \u2014 HTTP Header Auth\nHeader name: `Authorization` \u00b7 Value: `Bearer YOUR_OPENAI_KEY`\nSora access required: platform.openai.com (Tier 4/5 or enterprise)\n\n**Sora API endpoints used:**\n- Submit: `POST https://api.openai.com/v1/video/generations`\n- Poll: `GET https://api.openai.com/v1/video/generations/{id}`\n- Download: video URL from completed response\n\n"
},
"typeVersion": 1
},
{
"id": "d3d90ba8-0ba0-41d0-941b-379b5034f085",
"name": "Sticky \u2013 Entry & Routing",
"type": "n8n-nodes-base.stickyNote",
"position": [
-32,
192
],
"parameters": {
"color": 7,
"width": 560,
"height": 924,
"content": "### 1\ufe0f\u20e3 Webhook Entry & Job Router\n**Webhook \u2013 Receive Video Job** accepts a POST at `/sora-video-job` with `jobType` and generation parameters in the request body.\n**Route by Job Type Switch** reads `$json.body.jobType` and routes:\n- Output 0 `ecommerce` \u2192 Cinematic E-commerce pipeline\n- Output 1 `remix` \u2192 Social Media Remix pipeline\n- Fallback \u2192 **Respond \u2013 Error** returns a 400 JSON error"
},
"typeVersion": 1
},
{
"id": "e1b88a43-f58c-4dfb-acf0-6355204a40b3",
"name": "Sticky \u2013 E-commerce Pipeline",
"type": "n8n-nodes-base.stickyNote",
"position": [
592,
176
],
"parameters": {
"color": 7,
"width": 2480,
"height": 500,
"content": "### 2\ufe0f\u20e3 Cinematic E-commerce Walkthrough\n**Build E-commerce Prompt** constructs the Sora prompt: cinematic product reveal, 360\u00b0 rotation, studio lighting, close-ups on key features, premium brand aesthetic. Injects `productName`, `duration` (10\u201320 s), `style`, and stamps a `jobId`.\n**Sora \u2013 Submit E-commerce Job** POSTs to `/v1/video/generations` with the prompt, `input_image` URL, duration and resolution `1080p`. Returns a `generation_id` immediately \u2014 Sora renders async.\n**Store E-commerce Job ID** code node carries `generationId` and all metadata forward.\n**Wait 20s** pauses execution while Sora renders.\n**Sora \u2013 Poll E-commerce Status** GETs `/v1/video/generations/{generationId}` to read the current status.\n**Check E-commerce Done IF** evaluates `$json.status`: `succeeded` \u2192 true branch (continue); anything else \u2192 false branch (loop back to Wait 20s).\n**Fetch E-commerce Video** HTTP GET downloads the MP4 binary from `$json.video_url`.\n**Upload to URL \u2013 E-commerce** is the **n8n built-in Upload to URL node** \u2014 PUTs the MP4 binary to your CDN presigned URL.\n**Build E-commerce Response** returns `{ publicUrl, productName, duration, jobId, generatedAt }`.\n**Respond to Webhook \u2013 E-commerce** sends JSON to caller."
},
"typeVersion": 1
},
{
"id": "3daf779d-1dd3-430c-b015-b361b60d3fbd",
"name": "Sticky \u2013 Remix Pipeline",
"type": "n8n-nodes-base.stickyNote",
"position": [
592,
704
],
"parameters": {
"color": 7,
"width": 2480,
"height": 480,
"content": "### 3\ufe0f\u20e3 Dynamic Social Media Remix\n**Build Remix Prompt** constructs the Sora prompt: transforms the source image into a `remixStyle` visual (e.g. cyberpunk, anime, golden hour), adds cinematic camera movement and `addEffect`. Sets `aspectRatio: 9:16` for Reels/TikTok, `1:1` for Feed. Duration fixed at 8 s. Stamps a `jobId`.\n**Sora \u2013 Submit Remix Job** POSTs to `/v1/video/generations` with `input_image`, prompt, duration 8 and platform-appropriate aspect ratio.\n**Store Remix Job ID** carries `generationId` and metadata forward.\n**Wait 20s** pauses while Sora renders.\n**Sora \u2013 Poll Remix Status** GETs the generation status endpoint.\n**Check Remix Done IF** evaluates status: `succeeded` \u2192 true; else \u2192 false loops back to Wait 20s.\n**Fetch Remix Video** downloads the MP4 binary from the completed response.\n**Upload to URL \u2013 Remix** is the **n8n built-in Upload to URL node** \u2014 PUTs the MP4 to your CDN.\n**Build Remix Response** returns `{ publicUrl, remixStyle, platform, aspectRatio, jobId, generatedAt }`.\n**Respond to Webhook \u2013 Remix** sends JSON to caller."
},
"typeVersion": 1
},
{
"id": "19282007-01ef-4021-9702-4a6f521ea0da",
"name": "Webhook \u2013 Receive Video Job",
"type": "n8n-nodes-base.webhook",
"position": [
0,
608
],
"parameters": {
"path": "sora-video-job",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "aa7080f8-4aae-4f67-ba08-562f3d7e4180",
"name": "Route by Job Type",
"type": "n8n-nodes-base.switch",
"position": [
320,
608
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "E-commerce",
"conditions": {
"options": {
"caseSensitive": false,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.body.jobType.toLowerCase() }}",
"rightValue": "ecommerce"
}
]
},
"renameOutput": true
},
{
"outputKey": "Remix",
"conditions": {
"options": {
"caseSensitive": false,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.body.jobType.toLowerCase() }}",
"rightValue": "remix"
}
]
},
"renameOutput": true
}
]
},
"options": {
"fallbackOutput": "extra"
}
},
"typeVersion": 3
},
{
"id": "a66d6c39-f952-4303-b966-a7e7627e4a97",
"name": "Respond \u2013 Error",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
336,
928
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json) }}"
},
"typeVersion": 1
},
{
"id": "d1aaf53c-fa42-4eb4-bb77-42d3612824da",
"name": "Build E-commerce Prompt",
"type": "n8n-nodes-base.code",
"position": [
624,
448
],
"parameters": {
"jsCode": "const b = $json.body;\nconst product = b.productName || 'Product';\nconst dur = parseInt(b.duration || 15);\nconst style = b.style || 'premium product reveal';\nconst imgUrl = b.productImageUrl || '';\nconst jobId = `ECOM-${Date.now()}`;\nconst prompt = `Cinematic ${dur}-second product video for ${product}. ` +\n `Style: ${style}. Start with a dramatic reveal from black, ` +\n `smoothly rotate the product 360 degrees under professional studio lighting, ` +\n `close-up on key features with shallow depth of field, ` +\n `end with the product centred on a clean white background. ` +\n `4K quality, no text, premium commercial aesthetic.`;\nreturn [{ json: { prompt, product, duration: dur, style, productImageUrl: imgUrl, jobId } }];\n"
},
"typeVersion": 2
},
{
"id": "70b20b5c-dd27-44e7-92ae-278c26bd2553",
"name": "Sora \u2013 Submit E-commerce Job",
"type": "n8n-nodes-base.httpRequest",
"position": [
880,
448
],
"parameters": {
"url": "https://api.openai.com/v1/video/generations",
"method": "POST",
"options": {},
"sendHeaders": true,
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "e305f297-4187-4df1-a6c7-85f8d60e01a5",
"name": "Store E-commerce Job ID",
"type": "n8n-nodes-base.code",
"position": [
1152,
448
],
"parameters": {
"jsCode": "const prev = $('Build E-commerce Prompt').item.json;\nconst genId = $json.id || $json.generation_id;\nif (!genId) throw new Error('No generation_id returned from Sora');\nreturn [{ json: { ...prev, generationId: genId } }];\n"
},
"typeVersion": 2
},
{
"id": "a7a3bb94-30d6-4da2-932e-0f7c96f990cc",
"name": "Wait 20s \u2013 E-commerce",
"type": "n8n-nodes-base.wait",
"position": [
1392,
448
],
"parameters": {
"amount": 20
},
"typeVersion": 1.1
},
{
"id": "6e696d0f-5dba-4c9d-ba98-7a0bb6ed5eea",
"name": "Sora \u2013 Poll E-commerce Status",
"type": "n8n-nodes-base.httpRequest",
"position": [
1632,
448
],
"parameters": {
"url": "=https://api.openai.com/v1/video/generations/{{ $json.generationId }}",
"options": {}
},
"typeVersion": 4.2
},
{
"id": "e13f76b5-6b3b-4386-b6cc-13c7bebeb947",
"name": "Check E-commerce Done",
"type": "n8n-nodes-base.if",
"position": [
1872,
448
],
"parameters": {
"options": {},
"conditions": {
"options": {
"caseSensitive": false,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.status }}",
"rightValue": "succeeded"
}
]
}
},
"typeVersion": 2.1
},
{
"id": "afab4d94-8cc3-479c-8ff8-1f3ff6263bf6",
"name": "Fetch E-commerce Video",
"type": "n8n-nodes-base.httpRequest",
"position": [
2112,
368
],
"parameters": {
"url": "={{ $json.video_url || $json.output?.url || $json.generations?.[0]?.url }}",
"options": {}
},
"typeVersion": 4.2
},
{
"id": "1ba3c0f3-a981-4bec-b377-5b4af8a1a192",
"name": "Build E-commerce Response",
"type": "n8n-nodes-base.code",
"position": [
2608,
368
],
"parameters": {
"jsCode": "const p = $('Build E-commerce Prompt').item.json;\nreturn [{ json: {\n success: true, jobId: p.jobId, jobType: 'ecommerce',\n productName: p.product, duration: p.duration, style: p.style,\n publicUrl: `https://YOUR_CDN_DOMAIN/${p.jobId}.mp4`,\n generatedAt: new Date().toISOString(),\n note: 'Embed this MP4 URL directly in your Shopify/Meesho product listing.'\n}}];\n"
},
"typeVersion": 2
},
{
"id": "ce0c666a-fde5-4e5a-b65f-b3932eb9e564",
"name": "Respond to Webhook \u2013 E-commerce",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
2848,
368
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json) }}"
},
"typeVersion": 1
},
{
"id": "f371d4b1-a647-42b6-a007-756f60c4ca30",
"name": "Build Remix Prompt",
"type": "n8n-nodes-base.code",
"position": [
624,
960
],
"parameters": {
"jsCode": "const b = $json.body;\nconst style = b.remixStyle || 'cyberpunk';\nconst effect = b.addEffect || 'golden hour lighting';\nconst platform = (b.platform || 'reels').toLowerCase();\nconst srcImg = b.sourceImageUrl || '';\nconst jobId = `REMIX-${Date.now()}`;\nconst aspect = ['reels','tiktok','story'].includes(platform) ? '9:16' : '1:1';\nconst dur = 8;\nconst prompt = `Transform this image into a ${dur}-second cinematic ${style} style video. ` +\n `Apply ${effect} with smooth cinematic camera movement and dramatic transitions. ` +\n `Vibrant colour grading, professional motion blur on cuts, ` +\n `optimised for ${platform} vertical short-form format. No text overlays.`;\nreturn [{ json: { prompt, style, effect, platform, aspect, duration: dur, sourceImageUrl: srcImg, jobId } }];\n"
},
"typeVersion": 2
},
{
"id": "2549243b-f77b-4f20-bc13-8cffa7c586d5",
"name": "Sora \u2013 Submit Remix Job",
"type": "n8n-nodes-base.httpRequest",
"position": [
848,
960
],
"parameters": {
"url": "https://api.openai.com/v1/video/generations",
"method": "POST",
"options": {},
"sendHeaders": true,
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "434029b0-9ca9-4081-9760-5929a899632e",
"name": "Store Remix Job ID",
"type": "n8n-nodes-base.code",
"position": [
1152,
960
],
"parameters": {
"jsCode": "const prev = $('Build Remix Prompt').item.json;\nconst genId = $json.id || $json.generation_id;\nif (!genId) throw new Error('No generation_id returned from Sora');\nreturn [{ json: { ...prev, generationId: genId } }];\n"
},
"typeVersion": 2
},
{
"id": "6cea91b0-5583-4062-8e74-e0de974d57af",
"name": "Wait 20s \u2013 Remix",
"type": "n8n-nodes-base.wait",
"position": [
1376,
960
],
"parameters": {
"amount": 20
},
"typeVersion": 1.1
},
{
"id": "ccc8ec96-c821-46e7-a38e-58b2f4585e40",
"name": "Sora \u2013 Poll Remix Status",
"type": "n8n-nodes-base.httpRequest",
"position": [
1648,
960
],
"parameters": {
"url": "=https://api.openai.com/v1/video/generations/{{ $json.generationId }}",
"options": {}
},
"typeVersion": 4.2
},
{
"id": "7665ced7-b14a-4e64-a607-31b48ae3ab92",
"name": "Check Remix Done",
"type": "n8n-nodes-base.if",
"position": [
1888,
960
],
"parameters": {
"options": {},
"conditions": {
"options": {
"caseSensitive": false,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.status }}",
"rightValue": "succeeded"
}
]
}
},
"typeVersion": 2.1
},
{
"id": "7beeb3d4-c4d7-474f-bfbc-5123c3d66cc7",
"name": "Fetch Remix Video",
"type": "n8n-nodes-base.httpRequest",
"position": [
2128,
944
],
"parameters": {
"url": "={{ $json.video_url || $json.output?.url || $json.generations?.[0]?.url }}",
"options": {}
},
"typeVersion": 4.2
},
{
"id": "9bddd891-bcc3-4321-8f84-43c087f514fc",
"name": "Build Remix Response",
"type": "n8n-nodes-base.code",
"position": [
2576,
944
],
"parameters": {
"jsCode": "const p = $('Build Remix Prompt').item.json;\nreturn [{ json: {\n success: true, jobId: p.jobId, jobType: 'remix',\n remixStyle: p.style, effect: p.effect,\n platform: p.platform, aspectRatio: p.aspect,\n publicUrl: `https://YOUR_CDN_DOMAIN/${p.jobId}.mp4`,\n generatedAt: new Date().toISOString(),\n note: 'Post this URL directly to TikTok/Reels via their upload API.'\n}}];\n"
},
"typeVersion": 2
},
{
"id": "a8fcc0ba-1093-4fe8-8718-98947247d385",
"name": "Respond to Webhook \u2013 Remix",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
2832,
944
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json) }}"
},
"typeVersion": 1
},
{
"id": "23ee271e-8b4f-46a1-875a-81d18e259f21",
"name": "Upload to URL",
"type": "n8n-nodes-uploadtourl.uploadToUrl",
"position": [
2368,
368
],
"parameters": {},
"credentials": {
"uploadToUrlApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "41c3168b-2bb4-4e41-a8d2-bb7fce5f45b0",
"name": "Upload to URL1",
"type": "n8n-nodes-uploadtourl.uploadToUrl",
"position": [
2352,
944
],
"parameters": {},
"credentials": {
"uploadToUrlApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
}
],
"connections": {
"Upload to URL": {
"main": [
[
{
"node": "Build E-commerce Response",
"type": "main",
"index": 0
}
]
]
},
"Upload to URL1": {
"main": [
[
{
"node": "Build Remix Response",
"type": "main",
"index": 0
}
]
]
},
"Check Remix Done": {
"main": [
[
{
"node": "Fetch Remix Video",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait 20s \u2013 Remix",
"type": "main",
"index": 0
}
]
]
},
"Fetch Remix Video": {
"main": [
[
{
"node": "Upload to URL1",
"type": "main",
"index": 0
}
]
]
},
"Route by Job Type": {
"main": [
[
{
"node": "Build E-commerce Prompt",
"type": "main",
"index": 0
}
],
[
{
"node": "Build Remix Prompt",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond \u2013 Error",
"type": "main",
"index": 0
}
]
]
},
"Build Remix Prompt": {
"main": [
[
{
"node": "Sora \u2013 Submit Remix Job",
"type": "main",
"index": 0
}
]
]
},
"Store Remix Job ID": {
"main": [
[
{
"node": "Wait 20s \u2013 Remix",
"type": "main",
"index": 0
}
]
]
},
"Wait 20s \u2013 Remix": {
"main": [
[
{
"node": "Sora \u2013 Poll Remix Status",
"type": "main",
"index": 0
}
]
]
},
"Build Remix Response": {
"main": [
[
{
"node": "Respond to Webhook \u2013 Remix",
"type": "main",
"index": 0
}
]
]
},
"Check E-commerce Done": {
"main": [
[
{
"node": "Fetch E-commerce Video",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait 20s \u2013 E-commerce",
"type": "main",
"index": 0
}
]
]
},
"Fetch E-commerce Video": {
"main": [
[
{
"node": "Upload to URL",
"type": "main",
"index": 0
}
]
]
},
"Build E-commerce Prompt": {
"main": [
[
{
"node": "Sora \u2013 Submit E-commerce Job",
"type": "main",
"index": 0
}
]
]
},
"Store E-commerce Job ID": {
"main": [
[
{
"node": "Wait 20s \u2013 E-commerce",
"type": "main",
"index": 0
}
]
]
},
"Wait 20s \u2013 E-commerce": {
"main": [
[
{
"node": "Sora \u2013 Poll E-commerce Status",
"type": "main",
"index": 0
}
]
]
},
"Build E-commerce Response": {
"main": [
[
{
"node": "Respond to Webhook \u2013 E-commerce",
"type": "main",
"index": 0
}
]
]
},
"Sora \u2013 Submit Remix Job": {
"main": [
[
{
"node": "Store Remix Job ID",
"type": "main",
"index": 0
}
]
]
},
"Sora \u2013 Poll Remix Status": {
"main": [
[
{
"node": "Check Remix Done",
"type": "main",
"index": 0
}
]
]
},
"Webhook \u2013 Receive Video Job": {
"main": [
[
{
"node": "Route by Job Type",
"type": "main",
"index": 0
}
]
]
},
"Sora \u2013 Submit E-commerce Job": {
"main": [
[
{
"node": "Store E-commerce Job ID",
"type": "main",
"index": 0
}
]
]
},
"Sora \u2013 Poll E-commerce Status": {
"main": [
[
{
"node": "Check E-commerce Done",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
uploadToUrlApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Elevate your digital presence with high-fidelity cinematic video automation. This workflow orchestrates the complex, asynchronous rendering process of OpenAI Sora—transforming static product images or creative concepts into hosted MP4 assets ready for immediate deployment to…
Source: https://n8n.io/workflows/13752/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Streamline your creative production with this high-performance image generation and hosting pipeline. This workflow automates the transition from raw creative prompts to hosted assets, leveraging Gemi
Transform raw product images into fully-optimized e-commerce listings in seconds. This workflow automates the bridge between a photo upload and a live product page by combining UploadToURL for hosting
Schedule social media posts from local files using UploadToURL, OpenAI, and Buffer
Accelerate your real estate marketing by moving from "photo capture" to "published listing" in seconds. This workflow automates the entire listing process by hosting property photos via UploadToURL, u
This workflow automates end-to-end social media publishing powered by Late API. It generates text content with Google Gemini, creates branded visuals with Kie.ai, uploads media to Late, and publishes