AutomationFlowsAI & RAG › Gemini File Search - Drive Watcher

Gemini File Search - Drive Watcher

Gemini File Search - Drive Watcher. Uses googleDriveTrigger, googleSheets, googleDrive, httpRequest. Event-driven trigger; 16 nodes.

Event trigger★★★★☆ complexity16 nodesGoogle Drive TriggerGoogle SheetsGoogle DriveHTTP Request
AI & RAG Trigger: Event Nodes: 16 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #gemini-drive-watcher-v1 — we link there as the canonical source.

This workflow follows the Google Drive → Google Drive 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
{
  "name": "Gemini File Search - Drive Watcher",
  "nodes": [
    {
      "parameters": {
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "triggerOn": "specificFolder",
        "folderToWatch": {
          "__rl": true,
          "value": "={{ $json.watch_folder_id }}",
          "mode": "id"
        },
        "event": "fileCreated",
        "options": {}
      },
      "id": "trigger-drive",
      "name": "Watch KB-Uploads Folder",
      "type": "n8n-nodes-base.googleDriveTrigger",
      "typeVersion": 1,
      "position": [
        240,
        300
      ],
      "notes": "Polls your KB-Uploads folder every 60 seconds for new files.",
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "api-key",
              "name": "api_key",
              "value": "YOUR_GEMINI_API_KEY_HERE",
              "type": "string"
            },
            {
              "id": "store-id",
              "name": "store_id",
              "value": "fileSearchStores/YOUR_STORE_ID_HERE",
              "type": "string"
            },
            {
              "id": "sheet-id",
              "name": "sheet_id",
              "value": "YOUR_GOOGLE_SHEET_ID_HERE",
              "type": "string"
            },
            {
              "id": "watch-folder",
              "name": "watch_folder_id",
              "value": "YOUR_KB_UPLOADS_FOLDER_ID",
              "type": "string"
            },
            {
              "id": "processed-folder",
              "name": "processed_folder_id",
              "value": "YOUR_PROCESSED_FOLDER_ID",
              "type": "string"
            },
            {
              "id": "failed-folder",
              "name": "failed_folder_id",
              "value": "YOUR_FAILED_FOLDER_ID",
              "type": "string"
            },
            {
              "id": "ingestion-webhook",
              "name": "ingestion_webhook_url",
              "value": "YOUR_N8N_WEBHOOK_URL/webhook/gemini-ingestion",
              "type": "string"
            }
          ]
        }
      },
      "id": "set-config",
      "name": "Your Settings",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        460,
        300
      ],
      "notes": "EDIT THIS NODE:\n\n1. api_key: Your Gemini API key\n2. store_id: Your File Search Store ID\n3. sheet_id: Your Dashboard Google Sheet ID\n4. watch_folder_id: KB-Uploads folder ID\n5. processed_folder_id: Processed subfolder ID\n6. failed_folder_id: Failed subfolder ID\n7. ingestion_webhook_url: URL from your Ingestion Engine v2 workflow"
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// Extract file metadata from Drive trigger\nconst config = $('Your Settings').first().json;\nconst driveFile = $input.first().json;\n\n// Extract file info\nconst fileId = driveFile.id;\nconst fileName = driveFile.name;\nconst mimeType = driveFile.mimeType;\nconst fileSize = parseInt(driveFile.size || 0);\nconst modifiedTime = driveFile.modifiedTime;\n\n// Create idempotency key for duplicate detection\nconst idempotencyKey = `${fileId}_${modifiedTime}`;\n\n// Validate file type\nconst validTypes = {\n  'application/pdf': 'PDF',\n  'text/plain': 'TXT',\n  'text/markdown': 'Markdown',\n  'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'DOCX'\n};\n\nconst extension = fileName.split('.').pop().toLowerCase();\nconst isValid = validTypes[mimeType] || ['pdf', 'txt', 'md', 'docx'].includes(extension);\n\nif (!isValid) {\n  return [{\n    json: {\n      status: 'INVALID_TYPE',\n      file_id: fileId,\n      file_name: fileName,\n      mime_type: mimeType,\n      error_message: `File type not supported: ${extension}. Use PDF, DOCX, TXT, or MD.`,\n      config: config\n    }\n  }];\n}\n\n// Check file size (100MB limit)\nconst maxSize = 100 * 1024 * 1024;\nif (fileSize > maxSize) {\n  return [{\n    json: {\n      status: 'TOO_LARGE',\n      file_id: fileId,\n      file_name: fileName,\n      file_size_mb: (fileSize / 1024 / 1024).toFixed(2),\n      error_message: `File is ${(fileSize / 1024 / 1024).toFixed(2)}MB. Maximum is 100MB.`,\n      config: config\n    }\n  }];\n}\n\n// Determine doc_type from file name or default\nlet docType = 'general';\nconst lowerName = fileName.toLowerCase();\nif (lowerName.includes('policy') || lowerName.includes('terms') || lowerName.includes('privacy')) {\n  docType = 'policy';\n} else if (lowerName.includes('faq') || lowerName.includes('question')) {\n  docType = 'faq';\n} else if (lowerName.includes('how') || lowerName.includes('guide') || lowerName.includes('procedure')) {\n  docType = 'procedure';\n} else if (lowerName.includes('product') || lowerName.includes('catalog') || lowerName.includes('spec')) {\n  docType = 'product';\n}\n\nreturn [{\n  json: {\n    status: 'VALID',\n    file_id: fileId,\n    file_name: fileName,\n    mime_type: mimeType,\n    file_size: fileSize,\n    doc_type: docType,\n    idempotency_key: idempotencyKey,\n    detected_at: new Date().toISOString(),\n    config: config\n  }\n}];"
      },
      "id": "code-extract",
      "name": "Extract File Info",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        680,
        300
      ],
      "notes": "Extracts file metadata and validates file type/size."
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "condition-valid",
              "leftValue": "={{ $json.status }}",
              "rightValue": "VALID",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ]
        }
      },
      "id": "if-valid",
      "name": "File Valid?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        900,
        300
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "={{ $json.config.sheet_id }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Upload Queue",
          "mode": "list",
          "cachedResultName": "Upload Queue"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "File Name": "={{ $json.file_name }}",
            "Status": "Uploading",
            "Detected": "={{ $json.detected_at }}",
            "Processed": "",
            "Error Message": ""
          }
        },
        "options": {}
      },
      "id": "sheets-queue-start",
      "name": "Add to Upload Queue",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1120,
        200
      ],
      "notes": "Logs the file to Upload Queue as 'Uploading'.",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "download",
        "fileId": {
          "__rl": true,
          "value": "={{ $('Extract File Info').first().json.file_id }}",
          "mode": "id"
        },
        "options": {}
      },
      "id": "drive-download",
      "name": "Download File",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        1340,
        200
      ],
      "notes": "Downloads the file from Google Drive.",
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $('Extract File Info').first().json.config.ingestion_webhook_url }}",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"api_key\": \"{{ $('Extract File Info').first().json.config.api_key }}\",\n  \"store_id\": \"{{ $('Extract File Info').first().json.config.store_id }}\",\n  \"file_name\": \"{{ $('Extract File Info').first().json.file_name }}\",\n  \"doc_type\": \"{{ $('Extract File Info').first().json.doc_type }}\",\n  \"mime_type\": \"{{ $('Extract File Info').first().json.mime_type }}\",\n  \"file_binary\": \"{{ $binary.data.data }}\"\n}",
        "options": {
          "response": {
            "response": {
              "fullResponse": true
            }
          },
          "timeout": 120000
        }
      },
      "id": "http-ingest",
      "name": "Call Ingestion Workflow",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1560,
        200
      ],
      "notes": "Calls the Ingestion Engine v2 webhook to upload to Gemini."
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// Process ingestion response\nconst response = $input.first().json;\nconst fileInfo = $('Extract File Info').first().json;\n\n// Check response\nlet status = 'UNKNOWN';\nlet geminiFileId = null;\nlet errorMessage = null;\n\nif (response.body) {\n  // Parse response body\n  const body = typeof response.body === 'string' ? JSON.parse(response.body) : response.body;\n  \n  if (body.status === 'SUCCESS') {\n    status = 'SUCCESS';\n    geminiFileId = body.file_id;\n  } else {\n    status = 'FAILED';\n    errorMessage = body.message || body.error_code || 'Unknown error';\n  }\n} else if (response.status === 'SUCCESS') {\n  status = 'SUCCESS';\n  geminiFileId = response.file_id;\n} else {\n  status = 'FAILED';\n  errorMessage = response.message || response.error_code || 'Ingestion workflow error';\n}\n\nreturn [{\n  json: {\n    ingestion_status: status,\n    gemini_file_id: geminiFileId,\n    error_message: errorMessage,\n    file_info: fileInfo,\n    config: fileInfo.config\n  }\n}];"
      },
      "id": "code-process-response",
      "name": "Process Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1780,
        200
      ],
      "notes": "Parses the ingestion workflow response."
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "condition-success",
              "leftValue": "={{ $json.ingestion_status }}",
              "rightValue": "SUCCESS",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ]
        }
      },
      "id": "if-success",
      "name": "Ingestion Success?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        2000,
        200
      ]
    },
    {
      "parameters": {
        "operation": "move",
        "fileId": {
          "__rl": true,
          "value": "={{ $json.file_info.file_id }}",
          "mode": "id"
        },
        "folderId": {
          "__rl": true,
          "value": "={{ $json.config.processed_folder_id }}",
          "mode": "id"
        }
      },
      "id": "drive-move-success",
      "name": "Move to Processed",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        2220,
        100
      ],
      "notes": "Moves successfully uploaded file to Processed folder.",
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "={{ $('Process Response').first().json.config.sheet_id }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Dashboard",
          "mode": "list",
          "cachedResultName": "Dashboard"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "doc_name": "={{ $('Process Response').first().json.file_info.file_name }}",
            "doc_type": "={{ $('Process Response').first().json.file_info.doc_type }}",
            "status": "Active",
            "uploaded": "={{ $now.toFormat('yyyy-MM-dd') }}",
            "last_verified": "={{ $now.toLocaleString() }}",
            "file_id": "={{ $('Process Response').first().json.gemini_file_id }}",
            "notes": "Auto-uploaded via Drive"
          }
        },
        "options": {}
      },
      "id": "sheets-dashboard",
      "name": "Add to Dashboard",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        2440,
        100
      ],
      "notes": "Adds the new document to Dashboard.",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "={{ $('Process Response').first().json.config.sheet_id }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Activity Log",
          "mode": "list",
          "cachedResultName": "Activity Log"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Timestamp": "={{ $now.toISO() }}",
            "Action": "UPLOAD",
            "Document": "={{ $('Process Response').first().json.file_info.file_name }}",
            "Result": "Success",
            "Details": "Auto-uploaded via Drive Watcher"
          }
        },
        "options": {}
      },
      "id": "sheets-log-success",
      "name": "Log Success",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        2660,
        100
      ],
      "notes": "Logs successful upload to Activity Log.",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "move",
        "fileId": {
          "__rl": true,
          "value": "={{ $json.file_info.file_id }}",
          "mode": "id"
        },
        "folderId": {
          "__rl": true,
          "value": "={{ $json.config.failed_folder_id }}",
          "mode": "id"
        }
      },
      "id": "drive-move-failed",
      "name": "Move to Failed",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        2220,
        300
      ],
      "notes": "Moves failed file to Failed folder.",
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "={{ $('Process Response').first().json.config.sheet_id }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Activity Log",
          "mode": "list",
          "cachedResultName": "Activity Log"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Timestamp": "={{ $now.toISO() }}",
            "Action": "UPLOAD",
            "Document": "={{ $('Process Response').first().json.file_info.file_name }}",
            "Result": "Failed",
            "Details": "={{ $('Process Response').first().json.error_message }}"
          }
        },
        "options": {}
      },
      "id": "sheets-log-failed",
      "name": "Log Failed",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        2440,
        300
      ],
      "notes": "Logs failed upload to Activity Log.",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "move",
        "fileId": {
          "__rl": true,
          "value": "={{ $json.file_id }}",
          "mode": "id"
        },
        "folderId": {
          "__rl": true,
          "value": "={{ $json.config.failed_folder_id }}",
          "mode": "id"
        }
      },
      "id": "drive-move-invalid",
      "name": "Move Invalid to Failed",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        1120,
        400
      ],
      "notes": "Moves invalid file type to Failed folder.",
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "={{ $json.config.sheet_id }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Activity Log",
          "mode": "list",
          "cachedResultName": "Activity Log"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Timestamp": "={{ $now.toISO() }}",
            "Action": "UPLOAD",
            "Document": "={{ $json.file_name }}",
            "Result": "Skipped",
            "Details": "={{ $json.error_message }}"
          }
        },
        "options": {}
      },
      "id": "sheets-log-invalid",
      "name": "Log Skipped",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1340,
        400
      ],
      "notes": "Logs skipped file to Activity Log.",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Watch KB-Uploads Folder": {
      "main": [
        [
          {
            "node": "Your Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Your Settings": {
      "main": [
        [
          {
            "node": "Extract File Info",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract File Info": {
      "main": [
        [
          {
            "node": "File Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "File Valid?": {
      "main": [
        [
          {
            "node": "Add to Upload Queue",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Move Invalid to Failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add to Upload Queue": {
      "main": [
        [
          {
            "node": "Download File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download File": {
      "main": [
        [
          {
            "node": "Call Ingestion Workflow",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Call Ingestion Workflow": {
      "main": [
        [
          {
            "node": "Process Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Response": {
      "main": [
        [
          {
            "node": "Ingestion Success?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ingestion Success?": {
      "main": [
        [
          {
            "node": "Move to Processed",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Move to Failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Move to Processed": {
      "main": [
        [
          {
            "node": "Add to Dashboard",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add to Dashboard": {
      "main": [
        [
          {
            "node": "Log Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Move to Failed": {
      "main": [
        [
          {
            "node": "Log Failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Move Invalid to Failed": {
      "main": [
        [
          {
            "node": "Log Skipped",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "meta": {
    "templateId": "gemini-drive-watcher-v1"
  },
  "versionId": "v1-2025-11-27",
  "tags": [
    {
      "name": "Gemini",
      "createdAt": "2025-11-27T00:00:00.000Z",
      "updatedAt": "2025-11-27T00:00:00.000Z"
    },
    {
      "name": "Automation",
      "createdAt": "2025-11-27T00:00:00.000Z",
      "updatedAt": "2025-11-27T00:00:00.000Z"
    },
    {
      "name": "Week-3",
      "createdAt": "2025-11-27T00:00:00.000Z",
      "updatedAt": "2025-11-27T00:00:00.000Z"
    }
  ]
}

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

Gemini File Search - Drive Watcher. Uses googleDriveTrigger, googleSheets, googleDrive, httpRequest. Event-driven trigger; 16 nodes.

Source: https://github.com/8Dvibes/mindvalley-ai-mastery-students/blob/main/workflows/gemini-drive-watcher-v1-2025-11-27.json — 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

Monitor Google Drive folder, parsing PDF, DOCX and image file into a destination folder, ready for further processing (e.g. RAG ingestion, translation, etc.) Keep processing log in Google Sheet and se

Google Drive Trigger, Google Drive, HTTP Request +2
AI & RAG

Freelancers, small business owners, and accountants who want to automate receipt management without manual data entry.

Google Drive Trigger, Google Drive, HTTP Request +1
AI & RAG

The Problem That it Solves

Google Drive Trigger, OpenAI, Google Drive +5
AI & RAG

Content creators, YouTubers, and social media managers who want to repurpose long form videos into short clips without doing it manually. Works on self hosted n8n instances.

Google Drive Trigger, Google Drive, N8N Nodes Renderio +3
AI & RAG

Monitor a Google Drive folder, process each image based on the prompt defined in and save the new image to the specified output Google Drive folder. Maintain a processing log in Google Sheets.

Google Drive Trigger, Google Drive, HTTP Request +2