How to use Video2Prompt in n8n?
-
Make sure you have registered and have enough credits in your account (during the beta phase, admittance and granting credits are limited).
-
Once you have enough credits, on your Dashboard, click "Generate new API Key" and copy the key.
-
Copy the JSON at the bottom of this page and save it as a file in json format, to be imported into n8n.
-
Import the workflow template via n8n's "Import from file" option.
-
Enter your API key inside the "Config" node.
-
The template already has the manual trigger and webhook trigger ready. You can add new triggers (e.g. email, telegram, slack etc,) as you wish, make sure they are connected to the same "Config" node. Likewise, If you'd like to use the generated prompt (from the "Format Final" node) somewhere else, connect it to your own nodes. The format of the output is specified in the sticky notes of the template.
{
"name": "Video3Prompt Automation",
"nodes": [
{
"parameters": {
"content": "## 🚀 How to Use This Workflow\n\n1. **API Key**: Enter your API Key in the **Config** node code (`apiKey: \"...\"`) or send it via webhook.\n - [Get API Key Here](https://ggunay6.wixstudio.com/video2prompt)\n\n2. **Input**: This workflow accepts a video URL from:\n - **Webhook**: POST JSON `{\"url\": \"...\"}`\n - **Manual Trigger**: Input `url` field.\n - **Text**: Any trigger containing a URL.\n\n3. **Output**: Returns a JSON object with `success: true` and the extracted `result`.",
"height": 460,
"width": 300
},
"name": "Usage Instructions",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [ -550, 0 ]
},
{
"parameters": {},
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [ -200, 0 ]
},
{
"parameters": {
"path": "submit-video",
"responseMode": "lastNode",
"options": {}
},
"name": "Webhook Trigger",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [ -200, 200 ]
},
{
"parameters": {
"functionCode": "// Get URL from trigger input (query param, body, text, message, or default)\nconst item = items[0].json;\n\n// 1. Define possible fields where a URL might hide\nconst candidates = [\n item.url,\n item.link,\n item.text, // Telegram/Slack message text\n item.message, // Generic message\n item.body?.url, // Webhook nested\n item.query?.url, // Webhook query\n item.body // Raw body (if string)\n];\n\n// 2. Helper to find URL in a string\nfunction findUrl(text) {\n if (typeof text !== 'string') return null;\n // If exact match\n if (text.startsWith('http')) return text;\n // If embedded (e.g. \"Check this https://tiktok.com/...\")\n const match = text.match(/(https?:\\/\\/[^\\s]+)/);\n return match ? match[0] : null;\n}\n\n// 3. Iterate and find first valid URL\nlet finalUrl = \"https://youtu.be/example\"; // Default fallback\n\nfor (const candidate of candidates) {\n const found = findUrl(candidate);\n if (found) {\n finalUrl = found;\n break;\n }\n}\n\nreturn [\n {\n json: {\n apiKey: \"REPLACE_WITH_KEY\",\n videoUrl: finalUrl,\n apiBaseUrl: \"https://ggunay6.wixstudio.com/video2prompt/_functions\"\n }\n }\n];"
},
"name": "Config",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [ 0, 0 ]
},
{
"parameters": {
"method": "POST",
"url": "={{ $('Config').item.json.apiBaseUrl }}/submitAnalysis",
"authentication": "none",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{ "name": "x-api-key", "value": "={{ $json.apiKey }}" },
{ "name": "Content-Type", "value": "application/json" }
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{ "name": "url", "value": "={{ $json.videoUrl }}" }
]
},
"options": {
"retryOnFail": true,
"maxRetries": 3,
"retryDelay": 10000
}
},
"name": "Submit Analysis",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 3,
"position": [ 200, 0 ],
"continueOnFail": true
},
{
"parameters": {
"conditions": {
"boolean": [
{ "value1": "={{ $json.success }}", "value2": true }
]
}
},
"name": "Success?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [ 400, 0 ]
},
{
"parameters": {
"functionCode": "const errorMsg = items[0].json.error || 'Unknown Error';\nconst details = items[0].json.message || 'Check API Key/Credits';\nthrow new Error(`${errorMsg}: ${details}`);"
},
"name": "Error Output",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [ 600, 300 ]
},
{
"parameters": {
"conditions": {
"boolean": [
{ "value1": "={{ $json.cached }}", "value2": true }
]
}
},
"name": "Is Cached?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [ 600, -100 ]
},
{
"parameters": {
"amount": 10,
"unit": "seconds"
},
"name": "Wait 10s",
"type": "n8n-nodes-base.wait",
"typeVersion": 1,
"position": [ 800, 100 ]
},
{
"parameters": {
"method": "GET",
"url": "={{ $('Config').item.json.apiBaseUrl }}/status",
"authentication": "none",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{ "name": "x-api-key", "value": "={{ $('Config').item.json.apiKey }}" }
]
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{ "name": "jobId", "value": "={{ $('Submit Analysis').item.json.jobId }}" }
]
},
"options": {
"ignoreResponseCode": true
}
},
"name": "Check Status",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 3,
"position": [ 1000, 100 ]
},
{
"parameters": {
"conditions": {
"string": [
{ "value1": "={{ typeof $json.status === 'object' ? $json.status.status : $json.status }}", "value2": "completed" }
]
}
},
"name": "Is Completed?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [ 1200, 100 ]
},
{
"parameters": {
"conditions": {
"string": [
{ "value1": "={{ typeof $json.status === 'object' ? $json.status.status : $json.status }}", "value2": "failed" }
]
}
},
"name": "Is Failed?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [ 1200, 300 ]
},
{
"parameters": {
"functionCode": "const items = $items(\"Check Status\");\nconst attempt = (items[0].json.pollAttempt || 0) + 1;\n\nif (attempt > 180) {\n return [{ json: { error: \"Timeout\", success: false } }];\n}\n\nitems[0].json.pollAttempt = attempt;\nitems[0].json.timedOut = attempt > 180;\n\nreturn items;"
},
"name": "Increment Attempt",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [ 1400, 300 ]
},
{
"parameters": {
"conditions": {
"boolean": [
{ "value1": "={{ $json.timedOut }}", "value2": true }
]
}
},
"name": "Is Timeout?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [ 1600, 300 ]
},
{
"parameters": {
"functionCode": "throw new Error(\"Timeout after 30 minutes\");"
},
"name": "Timeout Output",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [ 1800, 300 ]
},
{
"parameters": {
"functionCode": "// Standardize output\nconst data = items[0].json;\n\n// Extract result from nested status object if present\nconst finalResult = data.result || (data.status && data.status.result) || data.prompt;\n\n// Success case (Cached or Processed)\nif (finalResult) {\n return [{\n json: {\n result: finalResult,\n success: true,\n source: data.cached ? \"cached\" : \"processed\"\n }\n }];\n}\n\n// Error case\nif (data.error) {\n return [{\n json: {\n success: false,\n error: data.error,\n details: data.details\n }\n }];\n}\n\n// Fallback\nreturn [{ json: { success: false, error: \"Unknown Error or No Result\" } }];"
},
"name": "Format Final",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [ 2000, 0 ]
},
{
"parameters": {
"content": "## 🔗 Connect Your Next Step\n\nThe **Format Final** node outputs a clean JSON object:\n```json\n{ \"success\": true, \"result\": \"...\" }\n```\n\n**Connect this node to:**\n- Google Sheets (to save row)\n- HTTP Request (to send to AI Video Service)\n- Slack/Telegram (to notify user)",
"height": 440,
"width": 300
},
"name": "Next Steps",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [ 2250, 0 ]
}
],
"connections": {
"Manual Trigger": { "main": [[{ "node": "Config", "type": "main", "index": 0 }]] },
"Webhook Trigger": { "main": [[{ "node": "Config", "type": "main", "index": 0 }]] },
"Config": { "main": [[{ "node": "Submit Analysis", "type": "main", "index": 0 }]] },
"Submit Analysis": { "main": [[{ "node": "Success?", "type": "main", "index": 0 }]] },
"Success?": { "main": [[{ "node": "Is Cached?", "type": "main", "index": 0 }], [{ "node": "Error Output", "type": "main", "index": 0 }]] },
"Error Output": { "main": [[]] },
"Is Cached?": { "main": [[{ "node": "Format Final", "type": "main", "index": 0 }], [{ "node": "Wait 10s", "type": "main", "index": 0 }]] },
"Wait 10s": { "main": [[{ "node": "Check Status", "type": "main", "index": 0 }]] },
"Check Status": { "main": [[{ "node": "Is Completed?", "type": "main", "index": 0 }]] },
"Is Completed?": { "main": [[{ "node": "Format Final", "type": "main", "index": 0 }], [{ "node": "Is Failed?", "type": "main", "index": 0 }]] },
"Is Failed?": { "main": [[{ "node": "Error Output", "type": "main", "index": 0 }], [{ "node": "Increment Attempt", "type": "main", "index": 0 }]] },
"Increment Attempt": { "main": [[{ "node": "Is Timeout?", "type": "main", "index": 0 }]] },
"Is Timeout?": { "main": [[{ "node": "Timeout Output", "type": "main", "index": 0 }], [{ "node": "Wait 10s", "type": "main", "index": 0 }]] },
"Timeout Output": { "main": [[]] }
}
}
