AutomationFlowsAI & RAG › Bulk Auto-publish Videos to Social Networks with AI Copy and Client Approval

Bulk Auto-publish Videos to Social Networks with AI Copy and Client Approval

ByJuan Carlos Cavero Gracia @carlosgracia on n8n.io

This workflow automates batch video publishing prep from a Google Drive folder with AI-generated, platform-specific copy and a simple approval queue in Google Sheets. Perfect for Agencies, content creators or Teams Fetches videos from a Google Drive folder You provide a folder…

Event trigger★★★★★ complexityAI-powered34 nodesForm TriggerGoogle DriveGoogle GeminiAgentGoogle Gemini ChatOutput Parser StructuredGoogle SheetsN8N Nodes Upload Post
AI & RAG Trigger: Event Nodes: 34 Complexity: ★★★★★ AI nodes: yes Added:

This workflow corresponds to n8n.io template #11638 — we link there as the canonical source.

This workflow follows the Agent → Form Trigger 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 →

Download .json
{
  "nodes": [
    {
      "id": "412eca65-b548-4e92-a4d3-19e78c3aafa3",
      "name": "PUBLISHER NOTE",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        176,
        800
      ],
      "parameters": {
        "width": 468,
        "height": 180,
        "content": "## Flow 2: Approval & auto-publish\n\nEvery 15 minutes the workflow reads VideosTable, filters Status=approved, downloads the file and schedules it to the enabled platforms via Upload-Post. After a successful schedule, it updates the row to Status=scheduled."
      },
      "typeVersion": 1
    },
    {
      "id": "4b1448f5-3640-42d3-8453-f15be7f7d8e5",
      "name": "SAVE NOTE",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2672,
        144
      ],
      "parameters": {
        "width": 420,
        "height": 580,
        "content": "## Flow 1: Save drafts\n\nFormats the AI output plus schedule info and appends a new row in VideosTable with Status=draft. Change Status to approved when you want Flow 2 to publish it."
      },
      "typeVersion": 1
    },
    {
      "id": "7649ab2f-6975-466a-87d3-b8ba17bcd84b",
      "name": "AI NOTE",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1936,
        144
      ],
      "parameters": {
        "width": 712,
        "height": 584,
        "content": "## Flow 1: AI analysis & copy\n\nEach video is downloaded, analyzed with Gemini Flash, then Gemini Pro generates unique TikTok/Instagram/YouTube titles, descriptions and hashtags. The parser enforces valid JSON before saving."
      },
      "typeVersion": 1
    },
    {
      "id": "11ab6690-4752-41a5-aef8-f86944f18538",
      "name": "FETCH NOTE",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        736,
        208
      ],
      "parameters": {
        "width": 460,
        "height": 516,
        "content": "## Flow 1: Campaign intake & Drive fetch\n\nThe form collects campaign inputs. Google Drive lists files in the folder, then the filter keeps only video files before building the posting calendar."
      },
      "typeVersion": 1
    },
    {
      "id": "51f64105-513c-4ec7-a018-a8861f9b5f74",
      "name": "WORKFLOW INFO",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        96,
        -160
      ],
      "parameters": {
        "color": 4,
        "width": 560,
        "height": 456,
        "content": "## How it works\nThis template has two flows. Flow 1 starts with a Form, reads all videos from a Google Drive folder, filters to keep video files, and builds a posting calendar using the start date, cadence and publish hour. Each file is downloaded, analyzed with Gemini, and used to generate platform-specific titles, descriptions and hashtags for TikTok, Instagram Reels and YouTube Shorts. The workflow then formats the results and appends them to a Google Sheet as drafts.\n\n## Setup steps\n1) Prepare a Google Drive folder with your videos.\n2) Copy this template sheet in your drive: https://docs.google.com/spreadsheets/d/1cegJHxj7Kx4Tg8gMr3uixpzToNc62VEvuuz37iFvnRw/edit?usp=sharing\n3) Connect Google Drive, Google Sheets and Google Gemini credentials.\n4) Run the Form and provide the Drive Folder ID, Upload-Post profile username, platforms, timezone, start date, cadence, publish hour, and Sheet ID.\n5) Activate the workflow. Set any draft row Status to approved; Flow 2 checks every 15 minutes, schedules it via Upload-Post, and updates the row to scheduled."
      },
      "typeVersion": 1
    },
    {
      "id": "4bbab1e5-df87-450f-bc5d-daa110838cf4",
      "name": "Start Campaign",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        368,
        384
      ],
      "parameters": {
        "options": {},
        "formTitle": "Social Video Autopilot",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Google Drive Folder ID",
              "placeholder": "1jojIQ8TUjC_O-HfuhcjZfJSGSOl-h0Om",
              "requiredField": true
            },
            {
              "fieldLabel": "Profile Username (Upload-Post)",
              "placeholder": "mi_perfil_whitelabel",
              "requiredField": true
            },
            {
              "fieldType": "dropdown",
              "fieldLabel": "Platforms",
              "fieldOptions": {
                "values": [
                  {
                    "option": "TikTok, Instagram, YouTube"
                  },
                  {
                    "option": "TikTok, Instagram"
                  },
                  {
                    "option": "TikTok, YouTube"
                  },
                  {
                    "option": "Instagram, YouTube"
                  },
                  {
                    "option": "TikTok"
                  },
                  {
                    "option": "Instagram"
                  },
                  {
                    "option": "YouTube"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldLabel": "Start Date (YYYY-MM-DD)",
              "placeholder": "2025-12-09",
              "requiredField": true
            },
            {
              "fieldType": "dropdown",
              "fieldLabel": "Cadence",
              "fieldOptions": {
                "values": [
                  {
                    "option": "1_daily"
                  },
                  {
                    "option": "5_per_week"
                  },
                  {
                    "option": "3_per_week"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldLabel": "Publish Hour (HH:MM)",
              "placeholder": "09:00",
              "requiredField": true
            },
            {
              "fieldLabel": "Google Sheet ID (for tracking)",
              "placeholder": "1lg_YOUR_AWS_SECRET_KEY_HERE",
              "requiredField": true
            }
          ]
        },
        "formDescription": "Batch process and schedule your videos across TikTok, Instagram & YouTube with AI-powered optimization"
      },
      "typeVersion": 2.2
    },
    {
      "id": "4b849329-0771-4a37-9491-0cd2315fb10f",
      "name": "Campaign Settings",
      "type": "n8n-nodes-base.set",
      "position": [
        528,
        384
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "drive_folder_id",
              "name": "drive_folder_id",
              "type": "string",
              "value": "={{ $json['Google Drive Folder ID'] }}"
            },
            {
              "id": "profile_username",
              "name": "profile_username",
              "type": "string",
              "value": "={{ $json['Profile Username (Upload-Post)'] }}"
            },
            {
              "id": "platforms_raw",
              "name": "platforms_raw",
              "type": "string",
              "value": "={{ $json['Platforms'] }}"
            },
            {
              "id": "timezone",
              "name": "timezone",
              "type": "string",
              "value": "={{ $json['Timezone'] }}"
            },
            {
              "id": "start_date",
              "name": "start_date",
              "type": "string",
              "value": "={{ $json['Start Date (YYYY-MM-DD)'] }}"
            },
            {
              "id": "cadence",
              "name": "cadence",
              "type": "string",
              "value": "={{ $json['Cadence'] }}"
            },
            {
              "id": "publish_hour",
              "name": "publish_hour",
              "type": "string",
              "value": "={{ $json['Publish Hour (HH:MM)'] }}"
            },
            {
              "id": "sheet_id",
              "name": "sheet_id",
              "type": "string",
              "value": "={{ $json['Google Sheet ID (for tracking)'] }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "8d8a2adc-b83f-4e09-9fd4-520ad2cf6c00",
      "name": "Fetch Videos from Drive",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        800,
        384
      ],
      "parameters": {
        "filter": {},
        "options": {},
        "resource": "fileFolder",
        "returnAll": true,
        "queryString": "='{{ $json.drive_folder_id }}' in parents and trashed = false",
        "searchMethod": "query"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "bcc2ac88-8e63-4328-a060-575818f0f487",
      "name": "Video Files Only",
      "type": "n8n-nodes-base.filter",
      "position": [
        1040,
        384
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "video-mp4",
              "operator": {
                "type": "string",
                "operation": "contains"
              },
              "leftValue": "={{ $json.name }}",
              "rightValue": ".mp4"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "dfc0a1f7-5c8c-4b2a-964a-c546d7f008af",
      "name": "Build Schedule Calendar",
      "type": "n8n-nodes-base.code",
      "position": [
        1280,
        384
      ],
      "parameters": {
        "jsCode": "// Get configuration from Campaign Settings node\nconst config = $('Campaign Settings').first().json;\n\nconst items = $input.all();\nconst enrichedItems = [];\n\nconst startDate = new Date(config.start_date);\nlet cadenceDays = [];\n\nswitch(config.cadence) {\n  case '1_daily':\n    for(let i = 0; i < items.length; i++) cadenceDays.push(i);\n    break;\n  case '5_per_week':\n    let dayCounter5 = 0;\n    let weekDay5 = startDate.getDay();\n    for(let i = 0; i < items.length; i++) {\n      while(weekDay5 === 0 || weekDay5 === 6) {\n        dayCounter5++;\n        weekDay5 = (startDate.getDay() + dayCounter5) % 7;\n      }\n      cadenceDays.push(dayCounter5);\n      dayCounter5++;\n      weekDay5 = (startDate.getDay() + dayCounter5) % 7;\n    }\n    break;\n  case '3_per_week':\n    let dayCounter3 = 0;\n    for(let i = 0; i < items.length; i++) {\n      while(![1, 3, 5].includes((startDate.getDay() + dayCounter3) % 7)) dayCounter3++;\n      cadenceDays.push(dayCounter3);\n      dayCounter3++;\n    }\n    break;\n}\n\nfor (let i = 0; i < items.length; i++) {\n  const item = items[i];\n  const scheduleDaysOffset = cadenceDays[i] ?? i;\n\n  const scheduleDate = new Date(startDate);\n  scheduleDate.setDate(scheduleDate.getDate() + scheduleDaysOffset);\n  const scheduleDateStr = scheduleDate.toISOString().split('T')[0];\n  const scheduleDateTime = `${scheduleDateStr}T${config.publish_hour}:00`;\n\n  enrichedItems.push({\n    json: {\n      ...item.json,\n      video_index: i + 1,\n      total_videos: items.length,\n      config: {\n        drive_folder_id: config.drive_folder_id,\n        profile_username: config.profile_username,\n        platforms_raw: config.platforms_raw,\n        timezone: config.timezone,\n        start_date: config.start_date,\n        cadence: config.cadence,\n        publish_hour: config.publish_hour,\n        sheet_id: config.sheet_id\n      },\n      schedule: {\n        date: scheduleDateStr,\n        datetime: scheduleDateTime\n      }\n    }\n  });\n}\n\nreturn enrichedItems;"
      },
      "typeVersion": 2
    },
    {
      "id": "7f2ae67b-4dfc-4df8-a678-489a60fd260b",
      "name": "Process Each Video",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        1520,
        384
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "e5844599-a45e-41df-bc14-c9ee5314fd48",
      "name": "Download from Drive",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        1760,
        368
      ],
      "parameters": {
        "fileId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.id }}"
        },
        "options": {},
        "operation": "download"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "f69c1659-1e4e-4986-b870-5fda82c87f22",
      "name": "AI Video Analysis",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "position": [
        2000,
        368
      ],
      "parameters": {
        "text": "=Analyze the attached video and provide:\n\n1) DETAILED DESCRIPTION: A thorough, faithful narrative of what is seen and heard.\n2) FULL TRANSCRIPTION of the audio.\n3) KEYWORDS: 10-15 SEO-relevant keywords.\n4) KEY MOMENTS with approximate timestamps.\n5) CONTENT TYPE (tutorial, review, vlog, entertainment, educational, etc.).\n6) TONE (serious, funny, professional, casual, etc.).\n7) DETECTED PRIMARY LANGUAGE of the spoken content.\n\nImportant output rule:\nStart your answer with a single line exactly like:\nDetected language: <English|Spanish|Other>\n\nThen write the rest of the analysis in English.\nThis analysis will be used to generate social titles and descriptions in the same detected language.",
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "models/gemini-2.5-flash",
          "cachedResultName": "models/gemini-2.5-flash"
        },
        "options": {},
        "resource": "video",
        "inputType": "binary",
        "operation": "analyze"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ecb3ab65-a67e-47cc-8707-8d45af3608f4",
      "name": "Generate Social Copy",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        2192,
        368
      ],
      "parameters": {
        "text": "=[VIDEO ANALYSIS]\n{{ $json.content.parts[0].text }}\n\n[FILE INFO]\nName: {{ $('Download from Drive').item.json.name }}\n\n[CAMPAIGN SETTINGS]\nPlatforms: {{ $json.config.platforms_raw }}\n\n[GOAL]\nGenerate platform-specific titles, descriptions and hashtags for the selected platforms.\n\nLanguage rule:\n- Use the SAME language as the video.\n- The analysis starts with \"Detected language:\". Follow it strictly.\n- If it says English, write everything in English.\n- If it says Spanish, write everything in Spanish (Spain).\n\nOutput rules:\n- Each platform must be UNIQUE (do not copy/paste across platforms).\n- Return ONLY valid JSON matching this exact structure:\n{\n  \"tiktok\": {\n    \"title\": \"Optimized TikTok title (max 150 chars, strong hook)\",\n    \"description\": \"Short, engaging TikTok description\",\n    \"hashtags\": \"#hashtag1 #hashtag2 ...\"\n  },\n  \"instagram\": {\n    \"title\": \"Optimized Instagram Reels caption\",\n    \"description\": \"Value-driven description with CTA\",\n    \"hashtags\": \"#hashtag1 #hashtag2 ...\"\n  },\n  \"youtube\": {\n    \"title\": \"SEO-first YouTube Shorts title\",\n    \"description\": \"YouTube description with natural keywords and CTA\",\n    \"tags\": \"tag1, tag2, tag3\"\n  },\n  \"summary\": \"Very short internal summary\"\n}",
        "options": {
          "systemMessage": "You are a social media content marketing expert specialized in TikTok, Instagram Reels and YouTube Shorts.\n\nYour goals:\n- Maximize CTR\n- Improve retention\n- Increase engagement\n- Improve organic reach\n- Boost SEO on YouTube\n\nPlatform guidelines:\n\nTikTok:\n- Short, punchy titles with an emotional hook\n- Use emojis sparingly if the language/style fits\n- 10-15 hashtags mixing trending + niche\n- Casual, native tone\n\nInstagram Reels:\n- Strong first line hook\n- Slightly longer caption with light storytelling\n- Clear CTA (save, share, comment)\n- 20-30 hashtags mixing large/medium/niche\n\nYouTube Shorts:\n- SEO-first title, main keyword near the start\n- Ideal 50-60 characters\n- Description with natural keywords and CTA\n- 10-15 tags comma-separated\n\nCritical rules:\n- Each platform content must be DIFFERENT\n- Do not invent facts not present in the video\n- Output ONLY valid JSON, no markdown\n- Use the language detected in the analysis line: \"Detected language:\""
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "retryOnFail": true,
      "typeVersion": 3
    },
    {
      "id": "a4f34bd2-8f50-41e7-8db8-2681c1add61b",
      "name": "Gemini Pro",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        2192,
        592
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "47d7f3de-a93d-47cf-9dbe-1b9070ba8f71",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        2336,
        576
      ],
      "parameters": {
        "autoFix": true,
        "jsonSchemaExample": "{\n  \"tiktok\": {\n    \"title\": \"Optimized TikTok title (max 150 chars, strong hook)\",\n    \"description\": \"Short, engaging TikTok description\",\n    \"hashtags\": \"#hashtag1 #hashtag2 #hashtag3\"\n  },\n  \"instagram\": {\n    \"title\": \"Optimized Instagram Reels caption\",\n    \"description\": \"Value-driven description with CTA\",\n    \"hashtags\": \"#hashtag1 #hashtag2\"\n  },\n  \"youtube\": {\n    \"title\": \"SEO-first YouTube Shorts title\",\n    \"description\": \"YouTube description with natural keywords and CTA\",\n    \"tags\": \"tag1, tag2, tag3\"\n  },\n  \"summary\": \"Very short internal summary\"\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "0def5138-4385-45b0-96e0-cb334d208c8a",
      "name": "Format Content Data",
      "type": "n8n-nodes-base.code",
      "position": [
        2496,
        368
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst results = [];\n\nfor (const item of items) {\n  const calendarInfo = $('Build Schedule Calendar').item?.json ?? {};\n  const downloadInfo = $('Download from Drive').item?.json ?? {};\n\n  const makeBaseRow = () => ({\n    \"Video ID\": calendarInfo?.id || downloadInfo?.id || 'unknown',\n    \"Video Name\": downloadInfo?.name || calendarInfo?.name || 'unknown',\n    \"Index\": String(calendarInfo?.video_index ?? ''),\n    \"Status\": 'draft',\n    \"Schedule Date\": calendarInfo?.schedule?.date || '',\n    \"Schedule DateTime\": calendarInfo?.schedule?.datetime || '',\n\n    \"TikTok Title\": '',\n    \"TikTok Description\": '',\n    \"TikTok Hashtags\": '',\n\n    \"Instagram Title\": '',\n    \"Instagram Description\": '',\n    \"Instagram Hashtags\": '',\n\n    \"YouTube Title\": '',\n    \"YouTube Description\": '',\n    \"YouTube Tags\": '',\n\n    \"Summary\": '',\n    \"Profile\": calendarInfo?.config?.profile_username || '',\n    \"Platforms\": calendarInfo?.config?.platforms_raw || '',\n    \"Created At\": new Date().toISOString()\n  });\n\n  try {\n    const rawOutput = item.json.output ?? item.json.text ?? item.json;\n\n    let content;\n    if (rawOutput && typeof rawOutput === 'object' && !Array.isArray(rawOutput)) {\n      content = rawOutput;\n    } else {\n      let cleanedOutput = String(rawOutput)\n        .replace(/```json\\n?/g, '')\n        .replace(/```\\n?/g, '')\n        .trim();\n\n      const firstBrace = cleanedOutput.indexOf('{');\n      const lastBrace = cleanedOutput.lastIndexOf('}');\n      if (firstBrace !== -1 && lastBrace !== -1 && lastBrace > firstBrace) {\n        cleanedOutput = cleanedOutput.slice(firstBrace, lastBrace + 1);\n      }\n\n      content = JSON.parse(cleanedOutput);\n    }\n\n    const row = makeBaseRow();\n\n    row[\"TikTok Title\"] = content.tiktok?.title || '';\n    row[\"TikTok Description\"] = content.tiktok?.description || '';\n    row[\"TikTok Hashtags\"] = content.tiktok?.hashtags || '';\n\n    row[\"Instagram Title\"] = content.instagram?.title || '';\n    row[\"Instagram Description\"] = content.instagram?.description || '';\n    row[\"Instagram Hashtags\"] = content.instagram?.hashtags || '';\n\n    row[\"YouTube Title\"] = content.youtube?.title || '';\n    row[\"YouTube Description\"] = content.youtube?.description || '';\n    row[\"YouTube Tags\"] = content.youtube?.tags || '';\n\n    row[\"Summary\"] = content.summary || '';\n\n    results.push({ json: row });\n  } catch (error) {\n    const row = makeBaseRow();\n    row[\"Status\"] = 'error';\n    row[\"Summary\"] = `PARSE_ERROR: ${error.message}`;\n    results.push({ json: row });\n  }\n}\n\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "f58e58d3-88a4-4f92-9d06-c9bc829ef206",
      "name": "Save Draft to Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2736,
        368
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [
            {
              "id": "Video ID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Video ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Video Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Video Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Index",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Index",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Schedule Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Schedule Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Schedule DateTime",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Schedule DateTime",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "TikTok Title",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "TikTok Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "TikTok Description",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "TikTok Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "TikTok Hashtags",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "TikTok Hashtags",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Instagram Title",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Instagram Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Instagram Description",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Instagram Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Instagram Hashtags",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Instagram Hashtags",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "YouTube Title",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "YouTube Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "YouTube Description",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "YouTube Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "YouTube Tags",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "YouTube Tags",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Summary",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Summary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Profile",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Profile",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Platforms",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Platforms",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Created At",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Created At",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {
          "cellFormat": "USER_ENTERED"
        },
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "VideosTable"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Campaign Settings').first().json.sheet_id }}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "cb08748d-d99a-4743-a19a-9d94bd51c3ba",
      "name": "Publisher Config",
      "type": "n8n-nodes-base.set",
      "position": [
        560,
        1040
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "sheet_id_2",
              "name": "sheet_id",
              "type": "string",
              "value": "1cegJHxj7Kx4Tg8gMr3uixpzToNc62VEvuuz37iFvnRw"
            },
            {
              "id": "drive_id_2",
              "name": "drive_folder_id",
              "type": "string",
              "value": "1LB5YDDVnt0vLeC9qN1jc0UgLjMsnCRYn"
            },
            {
              "id": "profile_2",
              "name": "profile_username",
              "type": "string",
              "value": "automated-tests"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "8271992f-6325-401d-b3ba-cfcb5ee43121",
      "name": "Load Content Queue",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        800,
        1040
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "VideosTable"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.sheet_id }}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "2d6f6002-bb19-47c1-9bd3-4b023a5f42fa",
      "name": "Only Approved",
      "type": "n8n-nodes-base.filter",
      "position": [
        1040,
        1040
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "status-approved",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.Status }}",
              "rightValue": "approved"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "537be859-9d07-4492-835a-0958cc1702f8",
      "name": "Process Queue",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        1280,
        1040
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "fff9142c-bca7-4bb2-b44d-64924ac9b695",
      "name": "Get Video File",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        1520,
        1040
      ],
      "parameters": {
        "fileId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json['Video ID'] }}"
        },
        "options": {},
        "operation": "download"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "8fcd5a01-cce9-4657-93d6-65d8b54efb6b",
      "name": "Build Upload Payload",
      "type": "n8n-nodes-base.code",
      "position": [
        1760,
        1040
      ],
      "parameters": {
        "jsCode": "const item = $input.first();\nconst sheetData = item.json;\nconst config = $('Publisher Config').first().json;\n\nconst platforms = (sheetData.Platforms || 'tiktok,instagram,youtube').toLowerCase();\n\nreturn {\n  json: {\n    video_id: sheetData['Video ID'],\n    video_name: sheetData['Video Name'],\n    row_index: sheetData.row_number,\n\n    has_tiktok: platforms.includes('tiktok'),\n    has_instagram: platforms.includes('instagram'),\n    has_youtube: platforms.includes('youtube'),\n\n    tiktok_content: [sheetData['TikTok Title'], sheetData['TikTok Description'], sheetData['TikTok Hashtags']].filter(Boolean).join('\\n\\n'),\n    instagram_content: [sheetData['Instagram Title'], sheetData['Instagram Description'], sheetData['Instagram Hashtags']].filter(Boolean).join('\\n\\n'),\n\n    youtube_title: sheetData['YouTube Title'] || '',\n    youtube_description: sheetData['YouTube Description'] || '',\n\n    schedule_datetime: sheetData['Schedule DateTime'] || '',\n\n    profile_username: sheetData.Profile || config.profile_username,\n    sheet_id: config.sheet_id\n  },\n  binary: item.binary\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "32780527-174c-4926-b760-911556755750",
      "name": "TikTok Enabled?",
      "type": "n8n-nodes-base.if",
      "position": [
        2000,
        880
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "tiktok-check",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.has_tiktok }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "05d73820-46e5-4dff-b6e4-1fe20482639e",
      "name": "Instagram Enabled?",
      "type": "n8n-nodes-base.if",
      "position": [
        2000,
        1040
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "ig-check",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.has_instagram }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "8ba87910-da6f-4c0c-8d92-f0c4c30dee24",
      "name": "YouTube Enabled?",
      "type": "n8n-nodes-base.if",
      "position": [
        2000,
        1200
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "yt-check",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.has_youtube }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "81515599-ec20-4e67-8039-e57bbd1d0470",
      "name": "Schedule TikTok",
      "type": "n8n-nodes-upload-post.uploadPost",
      "position": [
        2240,
        864
      ],
      "parameters": {
        "user": "automated-tests",
        "title": "={{ $json.tiktok_content }}",
        "video": "data",
        "platform": [
          "tiktok"
        ],
        "operation": "uploadVideo",
        "scheduledDate": "={{ $json.schedule_datetime }}",
        "waitForCompletion": "="
      },
      "credentials": {
        "uploadPostApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "94ab80ea-33cf-4b4e-8b3f-30db536bd2e9",
      "name": "Schedule Instagram",
      "type": "n8n-nodes-upload-post.uploadPost",
      "position": [
        2240,
        1024
      ],
      "parameters": {
        "user": "automated-tests",
        "title": "={{ $json.instagram_content }}",
        "video": "data",
        "platform": [
          "instagram"
        ],
        "operation": "uploadVideo",
        "scheduledDate": "={{ $json.schedule_datetime }}",
        "waitForCompletion": "=",
        "instagramMediaType": "REELS"
      },
      "credentials": {
        "uploadPostApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "fe8db90a-f902-4c2e-b600-5b8583580b76",
      "name": "Schedule YouTube",
      "type": "n8n-nodes-upload-post.uploadPost",
      "position": [
        2240,
        1184
      ],
      "parameters": {
        "user": "automated-tests",
        "title": "={{ $json.youtube_title }}",
        "video": "data",
        "platform": [
          "youtube"
        ],
        "operation": "uploadVideo",
        "youtubeTitle": "={{ $json.youtube_title }}",
        "scheduledDate": "={{ $json.schedule_datetime }}",
        "waitForCompletion": "="
      },
      "credentials": {
        "uploadPostApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "3cd7eafd-2a62-4fa4-9599-99012580bb04",
      "name": "Mark as Scheduled",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2576,
        1024
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "scheduled",
            "Video ID": "={{ $('Get Video File').item.json[\"Video ID\"] }}"
          },
          "schema": [
            {
              "id": "Video ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Video ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Video Name",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Video Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Index",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Index",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Schedule Date",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Schedule Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Schedule DateTime",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Schedule DateTime",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "TikTok Title",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "TikTok Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "TikTok Description",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "TikTok Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "TikTok Hashtags",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "TikTok Hashtags",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Instagram Title",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Instagram Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Instagram Description",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Instagram Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Instagram Hashtags",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Instagram Hashtags",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "YouTube Title",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "YouTube Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "YouTube Description",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "YouTube Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "YouTube Tags",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "YouTube Tags",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Summary",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Summary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Profile",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Profile",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Platforms",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Platforms",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Created At",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Created At",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Video ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1275860196,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1cegJHxj7Kx4Tg8gMr3uixpzToNc62VEvuuz37iFvnRw/edit#gid=1275860196",
          "cachedResultName": "VideosTable"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Publisher Config').item.json.sheet_id }}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "cf513981-645e-4221-838f-68f75e9da08c",
      "name": "15 mins check",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        304,
        1040
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 15
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "eb774e71-1f51-4da3-931a-0ceffd6d502d",
      "name": "FETCH NOTE1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1920,
        736
      ],
      "parameters": {
        "width": 460,
        "height": 628,
        "content": "## Upload to social networks\n\nIs configured Tiktok Instagram and Youtube, but you can add all : TikTok, Instagram, LinkedIn, YouTube, Facebook, X, Threads, Pinterest, Reddit, Bluesky"
      },
      "typeVersion": 1
    },
    {
      "id": "9182d207-8e6c-47e5-b092-c86a06b5b8e2",
      "name": "SAVE NOTE1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2400,
        736
      ],
      "parameters": {
        "width": 420,
        "height": 628,
        "content": "## Flow 1: Save Schedule\n\nFormats the AI output plus schedule info and appends a new row in VideosTable with Status=approved. Change Status to scheduled when you want Flow 2 to publish it."
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Gemini Pro": {
      "ai_languageModel": [
        [
          {
            "node": "Generate Social Copy",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Structured Output Parser",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "15 mins check": {
      "main": [
        [
          {
            "node": "Publisher Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Only Approved": {
      "main": [
        [
          {
            "node": "Process Queue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Queue": {
      "main": [
        [],
        [
          {
            "node": "Get Video File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Video File": {
      "main": [
        [
          {
            "node": "Build Upload Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start Campaign": {
      "main": [
        [
          {
            "node": "Campaign Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule TikTok": {
      "main": [
        [
          {
            "node": "Mark as Scheduled",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "TikTok Enabled?": {
      "main": [
        [
          {
            "node": "Schedule TikTok",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Publisher Config": {
      "main": [
        [
          {
            "node": "Load Content Queue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule YouTube": {
      "main": [
        [
          {
            "node": "Mark as Scheduled",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Video Files Only": {
      "main": [
        [
          {
            "node": "Build Schedule Calendar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "YouTube Enabled?": {
      "main": [
        [
          {
            "node": "Schedule YouTube",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Video Analysis": {
      "main": [
        [
          {
            "node": "Generate Social Copy",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Campaign Settings": {
      "main": [
        [
          {
            "node": "Fetch Videos from Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark as Scheduled": {
      "main": [
        [
          {
            "node": "Process Queue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Instagram Enabled?": {
      "main": [
        [
          {
            "node": "Schedule Instagram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Load Content Queue": {
      "main": [
        [
          {
            "node": "Only Approved",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Each Video": {
      "main": [
        [],
        [
          {
            "node": "Download from Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Instagram": {
      "main": [
        [
          {
            "node": "Mark as Scheduled",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download from Drive": {
      "main": [
        [
          {
            "node": "AI Video Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Content Data": {
      "main": [
        [
          {
            "node": "Save Draft to Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Draft to Sheet": {
      "main": [
        [
          {
            "node": "Process Each Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Upload Payload": {
      "main": [
        [
          {
            "node": "TikTok Enabled?",
            "type": "main",
            "index": 0
          },
          {
            "node": "Instagram Enabled?",
            "type": "main",
            "index": 0
          },
          {
            "node": "YouTube Enabled?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Social Copy": {
      "main": [
        [
          {
            "node": "Format Content Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Schedule Calendar": {
      "main": [
        [
          {
            "node": "Process Each Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Videos from Drive": {
      "main": [
        [
          {
            "node": "Video Files Only",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Generate Social Copy",
            "type": "ai_outputParser",
            "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.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow automates batch video publishing prep from a Google Drive folder with AI-generated, platform-specific copy and a simple approval queue in Google Sheets. Perfect for Agencies, content creators or Teams Fetches videos from a Google Drive folder You provide a folder…

Source: https://n8n.io/workflows/11638/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Streamline your recruitment process with AI-powered resume analysis that goes beyond keyword matching.

Form Trigger, Google Drive, Google Gemini Chat +7
AI & RAG

This workflow automatically creates AI product review videos from a product image and short description using n8n and Veo 3.

Google Gemini Chat, Output Parser Structured, HTTP Request +5
AI & RAG

A fully automated Telegram-based personal finance tracker that: Accepts receipts as images, PDFs, or plain text Uses Google Gemini Vision for OCR & intelligent extraction Logs every expense into Googl

Telegram Trigger, Agent, Google Gemini Chat +8
AI & RAG

This workflow transforms any video you drop into a Google Drive folder into a ready-to-publish YouTube upload. It analyzes the video with AI to craft 3 high-CTR title ideas, 3 long SEO-friendly descri

Agent, Google Gemini Chat, Output Parser Structured +5
AI & RAG

This n8n workflow is a full inbound → outbound hybrid funnel designed for AI agencies. It captures warm leads through instant AI value, then automatically follows up with personalized, context-aware o

Form Trigger, Google Gemini, Microsoft Outlook +4