This workflow follows the Agent → Form recipe pattern — see all workflows that pair these two integrations.
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 →
{
"name": "video-stages",
"nodes": [
{
"parameters": {
"method": "POST",
"url": "https://us-central1-poetic-analog-460707-i9.cloudfunctions.net/process-video-workflow",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "video_gcs_uri",
"value": "={{ $('Upload Video').item.json.URL }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
820,
-210
],
"id": "e9f3b393-bbe4-4c51-815d-90692ef0b40e",
"name": "HTTP Request"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "40f10007-6309-46e1-9662-1d9db5bee855",
"leftValue": "={{ $json.result }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
600,
-110
],
"id": "7e246d8b-6d31-4bb9-aae3-ef3532c83a10",
"name": "If"
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [
820,
-10
],
"id": "33923bf3-a4de-4538-bb50-743718685c72",
"name": "No Operation, do nothing"
},
{
"parameters": {
"content": "**What this code does:**\n\n* It looks at the video input URL provided.\n* It checks if the web address starts with \"gs://.\n* Then, it tells you \"TRUE\" or \"FALSE\" for each URL's validation.",
"height": 340,
"width": 300,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
280,
-290
],
"id": "4939f6f9-72aa-4b5d-afff-dacfe6ca2802",
"name": "Sticky Note",
"disabled": true
},
{
"parameters": {
"formTitle": "Provide GSUTIL URL",
"formDescription": "Provide the GSUTIL URL of the video file stored in Google Cloud Bucket.",
"formFields": {
"values": [
{
"fieldLabel": "URL",
"requiredField": true
}
]
},
"options": {}
},
"type": "n8n-nodes-base.formTrigger",
"typeVersion": 2.2,
"position": [
160,
-110
],
"id": "1fb764c6-c56d-4478-8f3d-42094d5ea9e6",
"name": "Upload Video"
},
{
"parameters": {
"content": "---\n### **Video Process Analysis (Cloud Function)**\n\nThis is a **Google Cloud Function** that automatically analyzes videos. It uses advanced **AI** to break down complex processes into easy-to-understand stages and steps.\n\n* **Cloud Function:** It's an automated program running in Google Cloud. Just give it a video link, and it gets to work.\n* **AI Power (Vertex AI):** It uses **Gemini 2.0 Flash**, a powerful AI model from Google's Vertex AI platform, to \"watch\" and understand the video's content.\n* **Video Input:** You provide the function with a link to your video (typically stored in Google Cloud Storage).\n* **Process Breakdown:** The AI identifies the main **stages** of the activity and lists all the individual **steps** within each stage, then provides this analysis in a structured format.\n\n---",
"height": 400,
"width": 520
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
660,
-620
],
"id": "94f28a5b-12cf-4cda-acee-178deebce190",
"name": "Sticky Note1"
},
{
"parameters": {
"jsCode": "const items = $input.all();\nconst cleanedItems = items.map((item) => {\n let jsonString = item?.json?.data;\n jsonString = jsonString.replace(/json|\\\\n|```/g, \"\");\n try {\n const parsedJson = JSON.parse(jsonString);\n return { json: parsedJson };\n } catch (error) {\n console.log(\"Error parsing JSON string:\", error);\n return null;\n }\n});\nreturn cleanedItems.filter((item) => item !== null);\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1040,
-210
],
"id": "eb89e1af-c3b8-4c84-b2ee-1f0d55c46436",
"name": "Structured Output JSON Parser"
},
{
"parameters": {
"jsCode": "const items = $input.all();\nconst result = items.map((item) => {\n return { result: item?.json?.URL.startsWith(\"gs://\") };\n});\nreturn result;\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
380,
-110
],
"id": "6a0f3121-a6d0-4af1-a380-02ea8cee4411",
"name": "Validate URL"
},
{
"parameters": {
"promptType": "define",
"text": "=You are an expert in process visualization and Mermaid.js syntax. Your task is to convert the provided JSON process analysis into a Mermaid.js flowchart.\n\nInstructions:\nAnalyze the JSON: Carefully read the stages and steps within each stage.\nGenerate a Flowchart: Create a Mermaid.js flowchart using graph TD (top-down) for clear sequential flow.\nRepresent Stages as Nodes:\nEach stage should have a short, simple, alphanumeric ID (e.g., S1, S2, S3).\nThe visible label for each node must be enclosed in square brackets and double quotes, allowing for multi-line content (e.g., NodeID[\"Visible Name<br>Key Step 1<br>Key Step 2\"]).\nRepresent Flow with Arrows: Use --> to show the sequential flow from one node to the next.\nInclude Key Steps in Node Descriptions: For clarity, include the stage_name on the first line, followed by the first 1-2 key steps from each stage's steps list on subsequent lines within the node's label. Use <br> for line breaks within the quoted node label.\nStart and End Nodes: Add a clear \"Start\" and \"End\" node to delineate the process boundaries. These should also follow the NodeID[\"Label\"] format (e.g., Start[\"Start\"]).\nOutput Format: Provide only the Mermaid.js code block, enclosed in markdown code fences (```mermaid ... ```). Do not include any additional text or explanations outside of the Mermaid code block.\n\nJSON is : {{$json.stages.map(stage => `${stage.stage_name}:\\n - ${stage.steps.slice(0, 2).join('\\n - ')}${stage.steps.length > 2 ? '\\n ...' : ''}`).join('\\n\\n') }}",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2,
"position": [
1340,
-60
],
"id": "14011a89-dbfb-481b-b341-51ab74de3e95",
"name": "AI Agent"
},
{
"parameters": {
"modelName": "models/gemini-1.5-flash",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"typeVersion": 1,
"position": [
1340,
160
],
"id": "4498b48c-92b9-447e-80ef-9af2ae35dc54",
"name": "Google Gemini Chat Model",
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const items = $input.all();\nconst cleanedItems = items.map((item) => {\n let output = item?.json?.output;\n output = output.replace(/```/g, \"\");\n output = output.replace(/mermaid/g, \"\");\n return { ...item.json, output };\n});\nreturn cleanedItems;\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1720,
-60
],
"id": "80f60d60-ea60-4766-9a8d-b07912749714",
"name": "Output Parser"
},
{
"parameters": {
"operation": "completion",
"respondWith": "showText",
"responseText": "={{ $json.output }}"
},
"type": "n8n-nodes-base.form",
"typeVersion": 1,
"position": [
1980,
-60
],
"id": "a9f511cd-1b58-4939-a183-abda6c78cf23",
"name": "Display Mermaid JS Code"
},
{
"parameters": {
"content": "## Mermaid code Generation\n\n* For the generated stages from the cloud function, the LLM here converts the data to mermaid JS code.\n* Next it parses the output into a clean code.\n* It then display the Mermaid code on a display page.",
"height": 540,
"width": 560,
"color": 6
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1300,
-240
],
"id": "33fa398c-72c6-4b24-b841-01b050a7657d",
"name": "Sticky Note2"
},
{
"parameters": {
"jsCode": "const items = $input.all();\n\nconst formattedOutput = items.map(item => {\n // --- Adjust this line based on your actual input JSON path ---\n // Example for Scenario A: If your JSON is directly in item.json\n const fullProcessData = item.json; \n\n // Example for Scenario B: If your JSON is in item.json.myProcessData\n // const fullProcessData = item.json.myProcessData; \n\n // Example if your full JSON is an array (like the one you pasted)\n // and the main process object is the first element\n // const fullProcessData = item.json; // Assuming the array is directly in item.json\n // const processData = fullProcessData[0]; // Then access the first element\n // Replace 'fullProcessData' with 'processData' in the rest of the code.\n\n\n if (!fullProcessData || fullProcessData.length === 0) { // Check if fullProcessData is valid\n return { json: { formattedStagesMarkdown: \"Error: Input data is empty or invalid.\" } };\n }\n\n // This line assumes fullProcessData is an array and your main process object is the first element.\n // If fullProcessData is *already* the main process object, remove '[0]'\n const processData = Array.isArray(fullProcessData) ? fullProcessData[0] : fullProcessData; \n\n if (!processData || !Array.isArray(processData.stages)) {\n return { json: { formattedStagesMarkdown: \"Error: 'stages' array not found in process data or processData is not an object.\" } };\n }\n\n let markdownOutput = `# Process: ${processData.process_title || 'Untitled Process'}\\n\\n`;\n markdownOutput += \"## Stages and Steps Overview\\n\\n\";\n\n processData.stages.forEach(stage => {\n markdownOutput += `### ${stage.stage_number}. **${stage.stage_name}**\\n`;\n if (stage.stage_description) {\n markdownOutput += `*Description*: ${stage.stage_description}\\n`;\n }\n if (Array.isArray(stage.steps) && stage.steps.length > 0) {\n stage.steps.forEach(step => {\n markdownOutput += `* ${step}\\n`;\n });\n } else {\n markdownOutput += \"*No steps listed for this stage.*\\n\";\n }\n markdownOutput += \"\\n\"; \n });\n\n if (processData.completeness_check) {\n markdownOutput += `---\\n## Completeness Check\\n${processData.completeness_check}\\n`;\n }\n\n item.json.formattedStagesMarkdown = markdownOutput;\n\n return item;\n});\n\nreturn formattedOutput;"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1360,
-560
],
"id": "d4c95ef4-2900-44df-b259-20bd7ee9f9cd",
"name": "Code"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "958f91b1-57b7-4aea-8405-879a920bf4cb",
"name": "formattedStagesMarkdown",
"value": "={{ $json.formattedStagesMarkdown }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1620,
-560
],
"id": "324b73bf-ae79-49c5-924f-83fb3701a550",
"name": "Edit Fields"
},
{
"parameters": {
"mode": "markdownToHtml",
"markdown": "={{ $json.formattedStagesMarkdown }}",
"options": {}
},
"type": "n8n-nodes-base.markdown",
"typeVersion": 1,
"position": [
1860,
-560
],
"id": "72d5fb60-fc1c-4635-b56e-24e261d83dc9",
"name": "Markdown"
},
{
"parameters": {
"content": "## Markdown Code Generation\n\n* For the generated stages from the cloud function, the code here converts the json into a markdown code.\n* Then convert the markdown into html to display stages on screen.",
"height": 300,
"width": 760,
"color": 6
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1260,
-700
],
"id": "6fa530fc-17f4-4724-8c36-036f641a2ca7",
"name": "Sticky Note3"
},
{
"parameters": {
"operation": "completion",
"respondWith": "showText",
"responseText": "={{ $json.data }}"
},
"type": "n8n-nodes-base.form",
"typeVersion": 1,
"position": [
2100,
-560
],
"id": "add13c3f-7ea6-4db3-9aee-04253c412f8b",
"name": "Display Markdown Code"
}
],
"connections": {
"If": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
],
[
{
"node": "No Operation, do nothing",
"type": "main",
"index": 0
}
]
]
},
"Upload Video": {
"main": [
[
{
"node": "Validate URL",
"type": "main",
"index": 0
}
]
]
},
"HTTP Request": {
"main": [
[
{
"node": "Structured Output JSON Parser",
"type": "main",
"index": 0
}
]
]
},
"Validate URL": {
"main": [
[
{
"node": "If",
"type": "main",
"index": 0
}
]
]
},
"Structured Output JSON Parser": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
},
{
"node": "Code",
"type": "main",
"index": 0
}
]
]
},
"Google Gemini Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"AI Agent": {
"main": [
[
{
"node": "Output Parser",
"type": "main",
"index": 0
}
]
]
},
"Output Parser": {
"main": [
[
{
"node": "Display Mermaid JS Code",
"type": "main",
"index": 0
}
]
]
},
"Display Mermaid JS Code": {
"main": [
[]
]
},
"Code": {
"main": [
[
{
"node": "Edit Fields",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields": {
"main": [
[
{
"node": "Markdown",
"type": "main",
"index": 0
}
]
]
},
"Markdown": {
"main": [
[
{
"node": "Display Markdown Code",
"type": "main",
"index": 0
}
]
]
},
"Display Markdown Code": {
"main": [
[]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "d012ba69-15c4-408f-8b6d-3affe16d0480",
"meta": {
"templateCredsSetupCompleted": true
},
"id": "vUJCUfGpdbuznn9a",
"tags": []
}
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.
googlePalmApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
video-stages. Uses httpRequest, formTrigger, agent, lmChatGoogleGemini. Event-driven trigger; 18 nodes.
Source: https://github.com/Varun2480/Process_Mining/blob/be88ace69f488e96981094dc3b0e65013e0c8cf6/n8n/video_stages.json — 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.
Automatically generate SEO-optimized YouTube Titles, Descriptions, Tags & Hashtags – enriched with blog articles, affiliate links, and product recommendations!
Summary
This workflow contains community nodes that are only compatible with the self-hosted version of n8n.
Transform training prescriptions into perfectly formatted Intervals.icu workouts using AI. This workflow automatically converts free-text workout descriptions into structured interval training session
How it works: This end-to-end workflow automates your personal or brand content strategy by: 🧠 Using Google Gemini or OpenAI to generate engaging LinkedIn/X content from a title or trending posts. 🗓️