This workflow corresponds to n8n.io template #16050 — we link there as the canonical source.
This workflow follows the Google Drive → Google Sheets 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 →
{
"id": "dfik1DBSb5Xzsgdq",
"meta": {
"builderVariant": "mcp",
"aiBuilderAssisted": true
},
"name": "KB Sync \u2014 OpenAI Vector Store Manager",
"tags": [],
"nodes": [
{
"id": "46edab24-9e69-48bb-aab3-193ec9d37281",
"name": "Sticky Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1024,
336
],
"parameters": {
"color": 7,
"width": 520,
"height": 500,
"content": "## KB Sync \u2014 OpenAI Vector Store Manager\n\nKeeps your OpenAI Vector Store in sync with files from Google Drive, AWS S3, or custom URLs. Runs every 2 minutes.\n\n### How it works\n1. Schedule trigger reads all rows from the Google Sheet queue\n2. Rows with status `outdated` \u2014 old file deleted from Vector Store first\n3. New/unembedded rows filtered and looped through one by one\n4. Each file routed to correct download method by URL pattern\n5. File uploaded to OpenAI, added to Vector Store, polled until completed\n6. Sheet row updated to `active` (or `error` on failure)\n\n### Setup \u2014 replace ALL placeholders\n- [ ] YOUR_GOOGLE_SHEET_ID in all Google Sheets nodes (3 places)\n- [ ] YOUR_VECTOR_STORE_ID in Delete, Add, and Poll HTTP nodes\n- [ ] YOUR_S3_BUCKET_NAME in the AWS S3 download node\n- [ ] YOUR_ARTICLES_DOMAIN in the Switch route condition\n- [ ] Add credentials: Google Sheets OAuth2, OpenAI API, AWS S3, Google Drive OAuth2"
},
"typeVersion": 1
},
{
"id": "a7d4df0f-069e-4663-a73d-b0dd043e1e1e",
"name": "Sticky Trigger",
"type": "n8n-nodes-base.stickyNote",
"position": [
-336,
400
],
"parameters": {
"color": 7,
"width": 412,
"height": 368,
"content": "## Trigger & Read Sheet\n\nRuns every **2 minutes**. Reads ALL rows from the Google Sheet queue.\n\n\ud83d\udcdd Change: YOUR_GOOGLE_SHEET_ID in \"Get Row(s) From Sheet\""
},
"typeVersion": 1
},
{
"id": "be64ce89-89c7-48de-ab5a-8fe4e44d3181",
"name": "Sticky Delete",
"type": "n8n-nodes-base.stickyNote",
"position": [
176,
-160
],
"parameters": {
"color": 7,
"width": 440,
"height": 408,
"content": "## Delete Outdated Files\n\nIf status = outdated, deletes file from Vector Store via OpenAI API DELETE, then marks the row as deleted in the sheet.\n\n\ud83d\udcdd Change:\n- YOUR_VECTOR_STORE_ID in the DELETE HTTP node\n- YOUR_GOOGLE_SHEET_ID in \"Mark Row as Deleted\""
},
"typeVersion": 1
},
{
"id": "4d4bb550-9dcf-4125-953b-f24073c93686",
"name": "Sticky Filter",
"type": "n8n-nodes-base.stickyNote",
"position": [
272,
432
],
"parameters": {
"color": 7,
"width": 332,
"height": 292,
"content": "## Filter \u2014 New Files Only\n\nPasses rows where:\n- status is empty\n- file_url is not empty\n\nAlready-processed rows are skipped. Files processed one by one in a loop."
},
"typeVersion": 1
},
{
"id": "76a61a0a-dcd1-4d02-9b21-cd9f1dd471a9",
"name": "Sticky Download",
"type": "n8n-nodes-base.stickyNote",
"position": [
656,
0
],
"parameters": {
"color": 7,
"width": 644,
"height": 744,
"content": "## Route & Download\n\nSwitch routes by URL pattern:\n- URL contains google \u2192 Google Drive download\n- URL contains /care/ \u2192 AWS S3 (YOUR_S3_BUCKET_NAME)\n- URL contains YOUR_ARTICLES_DOMAIN \u2192 HTTP download\n\n\ud83d\udcdd Change: YOUR_S3_BUCKET_NAME and YOUR_ARTICLES_DOMAIN in Switch node"
},
"typeVersion": 1
},
{
"id": "fe895c1c-4e21-463a-a8bd-3e32cd94138e",
"name": "Sticky Upload",
"type": "n8n-nodes-base.stickyNote",
"position": [
1328,
0
],
"parameters": {
"color": 7,
"width": 1300,
"height": 1004,
"content": "## Upload & Embed to Vector Store\n\n1. File uploaded to OpenAI Files API (purpose: assistants)\n2. File ID added to Vector Store via POST\n3. Wait 30s then poll file status\n4. completed \u2014 mark row active, loop to next\n5. in_progress \u2014 wait again (retry loop)\n6. Other status \u2014 mark row error, loop to next\n\n\ud83d\udcdd Change: YOUR_VECTOR_STORE_ID in Add and Poll HTTP nodes"
},
"typeVersion": 1
},
{
"id": "35dbbca9-6e99-479a-a37e-500fc7e575f6",
"name": "Every 2 Minutes",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-304,
592
],
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 2
}
]
}
},
"typeVersion": 1.3
},
{
"id": "a1043ba2-7285-4dc4-9c5b-caf6527ed847",
"name": "Get Row(s) From Sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
-80,
592
],
"parameters": {
"options": {},
"filtersUI": {
"values": []
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "YOUR_GOOGLE_SHEET_ID"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.5
},
{
"id": "e8598156-f546-45ac-bd39-2e3126b307e0",
"name": "If Outdated",
"type": "n8n-nodes-base.if",
"position": [
128,
592
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "check-outdated",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.status }}",
"rightValue": "outdated"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "e4a61833-0db5-44f6-9fa6-ec6bd22293e8",
"name": "Delete File From Vector Store",
"type": "n8n-nodes-base.httpRequest",
"position": [
240,
64
],
"parameters": {
"url": "=https://api.openai.com/v1/vector_stores/YOUR_VECTOR_STORE_ID/files/{{ $json.openai_file_id }}",
"method": "DELETE",
"options": {},
"authentication": "predefinedCredentialType",
"nodeCredentialType": "openAiApi"
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.4
},
{
"id": "6463b506-524f-47f1-8158-1e71e80cf4b5",
"name": "Mark Row as Deleted",
"type": "n8n-nodes-base.googleSheets",
"position": [
448,
64
],
"parameters": {
"columns": {
"value": {
"status": "deleted",
"row_number": "={{ $('If Outdated').item.json.row_number }}",
"last_updated": "={{ $now.format('dd-MM-yyyy') }}",
"openai_file_id": "={{ $json.id }}"
},
"schema": [
{
"id": "openai_file_id",
"type": "string",
"display": true,
"required": false,
"displayName": "openai_file_id",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "status",
"type": "string",
"display": true,
"required": false,
"displayName": "status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "last_updated",
"type": "string",
"display": true,
"required": false,
"displayName": "last_updated",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "row_number",
"type": "number",
"display": true,
"removed": false,
"readOnly": true,
"required": false,
"displayName": "row_number",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"row_number"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "YOUR_GOOGLE_SHEET_ID"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.5
},
{
"id": "c4f1b37e-057c-441d-b351-2d3852401987",
"name": "Filter New Files",
"type": "n8n-nodes-base.filter",
"position": [
400,
608
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "not-embedded",
"operator": {
"type": "string",
"operation": "empty",
"singleValue": true
},
"leftValue": "={{ $json.status }}",
"rightValue": "Embedded"
},
{
"id": "has-url",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $json.file_url }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "ab2f9b83-f9ec-4075-8fe2-847ab1947266",
"name": "All Files Done",
"type": "n8n-nodes-base.noOp",
"position": [
896,
192
],
"parameters": {},
"typeVersion": 1
},
{
"id": "3734e904-2be2-4d46-b02d-48baeb88f58f",
"name": "Route by File Source",
"type": "n8n-nodes-base.switch",
"position": [
896,
384
],
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "is-gdrive",
"operator": {
"type": "string",
"operation": "contains"
},
"leftValue": "={{ $json.file_url }}",
"rightValue": "google"
}
]
}
},
{
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "is-s3",
"operator": {
"type": "string",
"operation": "contains"
},
"leftValue": "={{ $json.file_url }}",
"rightValue": "/care/"
}
]
}
},
{
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "is-article",
"operator": {
"type": "string",
"operation": "contains"
},
"leftValue": "={{ $json.file_url }}",
"rightValue": "YOUR_ARTICLES_DOMAIN"
}
]
}
}
]
},
"options": {}
},
"typeVersion": 3.2
},
{
"id": "41931eb9-8319-45ba-8c44-4b503c52a4a5",
"name": "Download from Google Drive",
"type": "n8n-nodes-base.googleDrive",
"position": [
1120,
208
],
"parameters": {
"fileId": {
"__rl": true,
"mode": "url",
"value": "={{ $json.file_url }}"
},
"options": {},
"operation": "download"
},
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 3
},
{
"id": "c6ca0624-41cf-4c1f-92b1-5c81d2cb4499",
"name": "Upload File to OpenAI",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
1344,
400
],
"parameters": {
"options": {
"purpose": "assistants"
},
"resource": "file"
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.3
},
{
"id": "44c9b2f0-d0ba-43c8-bce9-f4b995f5a5d0",
"name": "Download from S3",
"type": "n8n-nodes-base.awsS3",
"position": [
1120,
400
],
"parameters": {
"fileKey": "={{ $json.file_url.split('.amazonaws.com/')[1] }}",
"bucketName": "YOUR_S3_BUCKET_NAME"
},
"credentials": {
"aws": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "700df8e6-aa75-4df6-a0b2-426351a2c67f",
"name": "Download via HTTP",
"type": "n8n-nodes-base.httpRequest",
"position": [
1120,
592
],
"parameters": {
"url": "={{ $json.file_url }}",
"options": {
"response": {
"response": {
"responseFormat": "file"
}
}
}
},
"typeVersion": 4.4
},
{
"id": "601d9c89-ca72-4994-bd20-959e75249c2f",
"name": "Loop Over Items",
"type": "n8n-nodes-base.splitInBatches",
"position": [
672,
576
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "a34aaca5-a8b2-422d-9ad6-6c816df87a27",
"name": "Add File to Vector Store",
"type": "n8n-nodes-base.httpRequest",
"position": [
1568,
400
],
"parameters": {
"url": "https://api.openai.com/v1/vector_stores/YOUR_VECTOR_STORE_ID/files",
"method": "POST",
"options": {},
"sendBody": true,
"authentication": "predefinedCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "file_id",
"value": "={{ $json.id }}"
}
]
},
"nodeCredentialType": "openAiApi"
},
"typeVersion": 4.4
},
{
"id": "a729e952-cec0-48c3-969a-9939efbb2d9f",
"name": "Wait 30 Seconds",
"type": "n8n-nodes-base.wait",
"position": [
1792,
400
],
"parameters": {
"amount": 30
},
"typeVersion": 1.1
},
{
"id": "bcb1a6f8-59b0-419d-80dc-c0c8246d6ad2",
"name": "Poll Vector Store File Status",
"type": "n8n-nodes-base.httpRequest",
"position": [
2016,
336
],
"parameters": {
"url": "=https://api.openai.com/v1/vector_stores/YOUR_VECTOR_STORE_ID/files/{{ $json.id }}",
"options": {},
"authentication": "predefinedCredentialType",
"nodeCredentialType": "openAiApi"
},
"typeVersion": 4.4
},
{
"id": "cb89cb57-d803-4eed-b3b5-71c278418f91",
"name": "Route by Embedding Status",
"type": "n8n-nodes-base.switch",
"position": [
2240,
384
],
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "is-completed",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.status }}",
"rightValue": "completed"
}
]
}
},
{
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "is-in-progress",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.status }}",
"rightValue": "in_progress"
}
]
}
}
]
},
"options": {
"fallbackOutput": "extra",
"renameFallbackOutput": "Error"
}
},
"typeVersion": 3.2
},
{
"id": "08378f4a-5bfc-4e52-af13-2c0582b478aa",
"name": "Mark Row as Active",
"type": "n8n-nodes-base.googleSheets",
"position": [
2464,
576
],
"parameters": {
"columns": {
"value": {
"status": "active",
"row_number": "={{ $('Loop Over Items').item.json.row_number }}",
"last_updated": "={{ $now.format('dd-MM-yyyy') }}",
"openai_file_id": "={{ $json.id }}"
},
"schema": [
{
"id": "openai_file_id",
"type": "string",
"display": true,
"required": false,
"displayName": "openai_file_id",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "status",
"type": "string",
"display": true,
"required": false,
"displayName": "status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "last_updated",
"type": "string",
"display": true,
"required": false,
"displayName": "last_updated",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "row_number",
"type": "number",
"display": true,
"removed": false,
"readOnly": true,
"required": false,
"displayName": "row_number",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"row_number"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "YOUR_GOOGLE_SHEET_ID"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.5
},
{
"id": "92baeb25-bdcb-4701-b4e9-f1fa3f6d4f47",
"name": "Mark Row as Error",
"type": "n8n-nodes-base.googleSheets",
"position": [
2464,
800
],
"parameters": {
"columns": {
"value": {
"status": "error",
"row_number": "={{ $('Loop Over Items').item.json.row_number }}",
"last_updated": "={{ $now.format('dd-MM-yyyy') }}",
"openai_file_id": "={{ $json.id }}"
},
"schema": [
{
"id": "openai_file_id",
"type": "string",
"display": true,
"required": false,
"displayName": "openai_file_id",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "status",
"type": "string",
"display": true,
"required": false,
"displayName": "status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "last_updated",
"type": "string",
"display": true,
"required": false,
"displayName": "last_updated",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "row_number",
"type": "number",
"display": true,
"removed": false,
"readOnly": true,
"required": false,
"displayName": "row_number",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"row_number"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "YOUR_GOOGLE_SHEET_ID"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.5
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"availableInMCP": true,
"executionOrder": "v1"
},
"versionId": "f7fd5740-19c3-4600-a322-89a39ec8fe3c",
"connections": {
"If Outdated": {
"main": [
[
{
"node": "Delete File From Vector Store",
"type": "main",
"index": 0
}
],
[
{
"node": "Filter New Files",
"type": "main",
"index": 0
}
]
]
},
"Every 2 Minutes": {
"main": [
[
{
"node": "Get Row(s) From Sheet",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Items": {
"main": [
[
{
"node": "All Files Done",
"type": "main",
"index": 0
}
],
[
{
"node": "Route by File Source",
"type": "main",
"index": 0
}
]
]
},
"Wait 30 Seconds": {
"main": [
[
{
"node": "Poll Vector Store File Status",
"type": "main",
"index": 0
}
]
]
},
"Download from S3": {
"main": [
[
{
"node": "Upload File to OpenAI",
"type": "main",
"index": 0
}
]
]
},
"Filter New Files": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Download via HTTP": {
"main": [
[
{
"node": "Upload File to OpenAI",
"type": "main",
"index": 0
}
]
]
},
"Mark Row as Error": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Mark Row as Active": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Route by File Source": {
"main": [
[
{
"node": "Download from Google Drive",
"type": "main",
"index": 0
}
],
[
{
"node": "Download from S3",
"type": "main",
"index": 0
}
],
[
{
"node": "Download via HTTP",
"type": "main",
"index": 0
}
]
]
},
"Get Row(s) From Sheet": {
"main": [
[
{
"node": "If Outdated",
"type": "main",
"index": 0
}
]
]
},
"Upload File to OpenAI": {
"main": [
[
{
"node": "Add File to Vector Store",
"type": "main",
"index": 0
}
]
]
},
"Add File to Vector Store": {
"main": [
[
{
"node": "Wait 30 Seconds",
"type": "main",
"index": 0
}
]
]
},
"Route by Embedding Status": {
"main": [
[
{
"node": "Mark Row as Active",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait 30 Seconds",
"type": "main",
"index": 0
}
],
[
{
"node": "Mark Row as Error",
"type": "main",
"index": 0
}
]
]
},
"Download from Google Drive": {
"main": [
[
{
"node": "Upload File to OpenAI",
"type": "main",
"index": 0
}
]
]
},
"Delete File From Vector Store": {
"main": [
[
{
"node": "Mark Row as Deleted",
"type": "main",
"index": 0
}
]
]
},
"Poll Vector Store File Status": {
"main": [
[
{
"node": "Route by Embedding Status",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
awsgoogleDriveOAuth2ApigoogleSheetsOAuth2ApiopenAiApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow runs every two minutes to sync a Google Sheets file queue with an OpenAI Vector Store by deleting outdated entries, downloading new files from Google Drive, AWS S3, or a URL, uploading them to OpenAI, and updating each row’s processing status. Runs every two…
Source: https://n8n.io/workflows/16050/ — 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.
This comprehensive n8n automation template orchestrates a complete end-to-end workflow for generating engaging short-form Point-of-View (POV) style videos using multiple AI services and automatically
AI-Powered Short-Form Video Generator with OpenAI, Flux, Kling, and ElevenLabs and upload to all social networks. Uses httpRequest, openAi, googleDrive, discord. Scheduled trigger; 51 nodes.
23-fully-automated-ai-video-generation-&-multi-platform-publishing. Uses httpRequest, openAi, googleDrive, discord. Scheduled trigger; 51 nodes.
Relatorio de custos AWS FinOps. Uses awsS3, googleSheets, emailSend, openAi. Scheduled trigger; 29 nodes.
This workflow automates the generation of ad-ready product images by combining product and influencer photos with AI styling. It runs on a scheduled trigger, fetches data from Google Sheets, and retri