This workflow corresponds to n8n.io template #17137 — 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 →
{
"meta": {
"builderVariant": "mcp",
"aiBuilderAssisted": true,
"templateCredsSetupCompleted": false
},
"name": "Rotate YouTube channel banners on a schedule using Google Sheets and Drive",
"tags": [],
"nodes": [
{
"id": "a5d7e322-482c-44cf-baa1-f1d39bb22c96",
"name": "Run Daily at 8am",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
560,
352
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 8
}
]
}
},
"typeVersion": 1.3
},
{
"id": "44bfaa00-b9b6-41d0-ab38-672297fe9873",
"name": "Set Banner Config",
"type": "n8n-nodes-base.set",
"position": [
768,
352
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "channel-id",
"name": "channelId",
"type": "string",
"value": "YOUR_CHANNEL_ID"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "960e235f-b9ad-4816-a0dc-74d28d0d5865",
"name": "Read Banner Schedule",
"type": "n8n-nodes-base.googleSheets",
"position": [
1008,
352
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": 0,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "YOUR_GOOGLE_SHEET_ID",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit?usp=drivesdk",
"cachedResultName": "Your Spreadsheet"
}
},
"typeVersion": 4.7
},
{
"id": "1758639e-5c3f-432c-aee7-8cea25e4f95b",
"name": "Select Due Banners",
"type": "n8n-nodes-base.code",
"position": [
1232,
352
],
"parameters": {
"jsCode": "function toIsoDate(value) {\n const s = String(value ?? '').trim();\n if (!s) return null;\n if (/^\\d+(\\.\\d+)?$/.test(s)) {\n // Google Sheets date serial (days since 1899-12-30)\n const d = DateTime.fromISO('1899-12-30').plus({ days: Math.floor(Number(s)) });\n return d.isValid ? d.toFormat('yyyy-MM-dd') : null;\n }\n const d = DateTime.fromFormat(s, 'yyyy-MM-dd');\n return d.isValid ? d.toFormat('yyyy-MM-dd') : null;\n}\n\nconst today = $today.toFormat('yyyy-MM-dd');\nconst due = [];\nfor (const item of $input.all()) {\n const row = item.json;\n const swapDate = toIsoDate(row['Swap Date']);\n const fileId = String(row['Banner Drive File ID'] ?? '').trim();\n const status = String(row['Status'] ?? '').trim().toLowerCase();\n if (!swapDate || !fileId) continue;\n if (status === 'applied') continue;\n if (swapDate > today) continue;\n item.json.swapDateIso = swapDate;\n due.push(item);\n}\ndue.sort((a, b) => a.json.swapDateIso.localeCompare(b.json.swapDateIso));\nreturn due;"
},
"typeVersion": 2
},
{
"id": "2d51bc73-a256-459d-9afa-50cbf7eed480",
"name": "Download Banner Image",
"type": "n8n-nodes-base.googleDrive",
"position": [
1536,
352
],
"parameters": {
"fileId": {
"__rl": true,
"mode": "id",
"value": "={{ $json['Banner Drive File ID'] }}"
},
"options": {
"binaryPropertyName": "data"
},
"operation": "download"
},
"typeVersion": 3
},
{
"id": "6f1fe997-7a89-4b61-b4ee-53cb54d4115a",
"name": "Upload Banner Image",
"type": "n8n-nodes-base.youTube",
"position": [
1792,
352
],
"parameters": {
"channelId": "={{ $('Set Banner Config').first().json.channelId }}",
"operation": "uploadBanner"
},
"typeVersion": 1
},
{
"id": "87718eaf-4c32-4a4d-ab78-05210b8d007e",
"name": "Set New Banner as Active",
"type": "n8n-nodes-base.youTube",
"position": [
2336,
352
],
"parameters": {
"channelId": "={{ $('Set Banner Config').first().json.channelId }}",
"operation": "update",
"updateFields": {
"brandingSettingsUi": {
"imageSettingsValues": {
"image": {
"bannerExternalUrl": "={{ $('Upload Banner Image').item.json.url }}"
}
},
"channelSettingsValues": {
"channel": {
"country": "={{ $json.brandingSettings?.channel?.country ?? '' }}",
"keywords": "={{ $json.brandingSettings?.channel?.keywords ?? '' }}",
"description": "={{ $json.brandingSettings?.channel?.description ?? '' }}",
"unsubscribedTrailer": "={{ $json.brandingSettings?.channel?.unsubscribedTrailer ?? '' }}"
}
}
}
}
},
"typeVersion": 1
},
{
"id": "4c78ac56-08da-41e9-8287-a9e780f8aebc",
"name": "Mark Row as Applied",
"type": "n8n-nodes-base.googleSheets",
"position": [
2560,
352
],
"parameters": {
"columns": {
"value": {
"Status": "Applied",
"row_number": "={{ $('Select Due Banners').item.json.row_number }}"
},
"schema": [
{
"id": "row_number",
"type": "number",
"display": true,
"required": false,
"displayName": "row_number",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Status",
"type": "string",
"display": true,
"required": false,
"displayName": "Status",
"defaultMatch": false,
"canBeUsedToMatch": false
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"row_number"
]
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "list",
"value": 0,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "YOUR_GOOGLE_SHEET_ID",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit?usp=drivesdk",
"cachedResultName": "Your Spreadsheet"
}
},
"typeVersion": 4.7
},
{
"id": "2c88d015-34cd-4071-b0df-36a649e6cfc5",
"name": "Log the Banner Swap",
"type": "n8n-nodes-base.set",
"position": [
2784,
352
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "swapped-at",
"name": "swappedAt",
"type": "string",
"value": "={{ $now.toISO() }}"
},
{
"id": "channel-id",
"name": "channelId",
"type": "string",
"value": "={{ $('Set Banner Config').first().json.channelId }}"
},
{
"id": "banner-label",
"name": "bannerLabel",
"type": "string",
"value": "={{ $('Select Due Banners').item.json.Label }}"
},
{
"id": "swap-date",
"name": "swapDate",
"type": "string",
"value": "={{ $('Select Due Banners').item.json.swapDateIso }}"
},
{
"id": "banner-file-id",
"name": "bannerFileId",
"type": "string",
"value": "={{ $('Select Due Banners').item.json['Banner Drive File ID'] }}"
},
{
"id": "banner-url",
"name": "bannerUrl",
"type": "string",
"value": "={{ $('Upload Banner Image').item.json.url }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "48f6ff60-ad27-444b-b125-a96e0301d5c8",
"name": "Sticky Note f7779d20",
"type": "n8n-nodes-base.stickyNote",
"position": [
-128,
-80
],
"parameters": {
"width": 560,
"height": 928,
"content": "## Rotate YouTube channel banners on a schedule\n\n### How it works\n\n1. A Schedule Trigger runs the workflow every morning at 8am.\n2. Edit Fields holds your channel ID so you set it once.\n3. Google Sheets reads the banner schedule, one row per planned swap: a swap date, a Drive file ID, an optional label, and a Status column the workflow maintains.\n4. A Code node keeps rows due today or overdue that are not yet Applied, sorted oldest first, so a missed day catches up on the next run and the newest banner ends up live. Swap dates work as plain yyyy-MM-dd text or as real date cells.\n5. Google Drive downloads the banner art and YouTube uploads it with Channel: uploadBanner.\n6. The workflow reads your current channel branding, then Channel: update activates the new banner while re-sending your description, keywords, country, and trailer, because the YouTube API clears any branding field the update leaves out.\n7. Google Sheets marks the row Applied and a final step records what changed.\n\n### Setup steps\n\n- [ ] Connect a YouTube (Google) OAuth2 credential on the three YouTube nodes.\n- [ ] Connect your Google Sheets and Google Drive credentials.\n- [ ] In Set Banner Config, set channelId to your YouTube channel ID.\n- [ ] Point both Google Sheets nodes at your schedule spreadsheet and tab.\n- [ ] Add sheet columns Swap Date (yyyy-MM-dd), Banner Drive File ID, Label, and Status. Leave Status empty, the workflow fills it.\n\nBanner art: 2048x1152 px and under 6 MB. Adjust the run time in the Schedule Trigger."
},
"typeVersion": 1
},
{
"id": "8400d3ed-7de4-4291-a709-6123465f1702",
"name": "Sticky Note e998aea5",
"type": "n8n-nodes-base.stickyNote",
"position": [
496,
208
],
"parameters": {
"color": 7,
"width": 896,
"height": 320,
"content": "## Check the banner schedule\n\nRead the schedule and keep the rows due today or overdue that have not been applied yet, oldest first."
},
"typeVersion": 1
},
{
"id": "daad78ca-ec09-4983-9624-c97eabb199ea",
"name": "Sticky Note 190b6aa2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1456,
208
],
"parameters": {
"color": 7,
"width": 528,
"height": 320,
"content": "## Fetch and upload\n\nDownload the banner art from Drive and upload it to the channel."
},
"typeVersion": 1
},
{
"id": "374f2eda-a285-4228-9488-d15d070106ec",
"name": "Sticky Note 5fbbdf1f",
"type": "n8n-nodes-base.stickyNote",
"position": [
2048,
208
],
"parameters": {
"color": 7,
"width": 928,
"height": 320,
"content": "## Activate, mark, log\n\nRead the current branding, set the new banner live without clearing it, mark the row Applied, and record the swap."
},
"typeVersion": 1
},
{
"id": "a8656054-5e39-4573-8e0a-ff756a345dad",
"name": "Get Current Branding",
"type": "n8n-nodes-base.youTube",
"position": [
2112,
352
],
"parameters": {
"part": [
"brandingSettings"
],
"channelId": "={{ $('Set Banner Config').first().json.channelId }}",
"operation": "get"
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"availableInMCP": true,
"executionOrder": "v1"
},
"connections": {
"Run Daily at 8am": {
"main": [
[
{
"node": "Set Banner Config",
"type": "main",
"index": 0
}
]
]
},
"Set Banner Config": {
"main": [
[
{
"node": "Read Banner Schedule",
"type": "main",
"index": 0
}
]
]
},
"Select Due Banners": {
"main": [
[
{
"node": "Download Banner Image",
"type": "main",
"index": 0
}
]
]
},
"Mark Row as Applied": {
"main": [
[
{
"node": "Log the Banner Swap",
"type": "main",
"index": 0
}
]
]
},
"Upload Banner Image": {
"main": [
[
{
"node": "Get Current Branding",
"type": "main",
"index": 0
}
]
]
},
"Get Current Branding": {
"main": [
[
{
"node": "Set New Banner as Active",
"type": "main",
"index": 0
}
]
]
},
"Read Banner Schedule": {
"main": [
[
{
"node": "Select Due Banners",
"type": "main",
"index": 0
}
]
]
},
"Download Banner Image": {
"main": [
[
{
"node": "Upload Banner Image",
"type": "main",
"index": 0
}
]
]
},
"Set New Banner as Active": {
"main": [
[
{
"node": "Mark Row as Applied",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow runs daily at 8am to rotate a YouTube channel banner based on a schedule stored in Google Sheets, downloading the banner image from Google Drive, applying it via the YouTube API, and updating the sheet to mark the banner as applied. Runs every day at 8am on a…
Source: https://n8n.io/workflows/17137/ — 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.
Description:
Multi YT To TT. Uses googleSheets, httpRequest, youTube. Scheduled trigger; 30 nodes.
This automation runs daily to fetch the latest videos on Youtube from leading AI automators, such as: Nate Herk Nick Saraev Jack Roberts Cole Medin Nick Puru Ed Hill Jason Cooperson Manthan Patel Nick
Video Creation from Google Sheets and Upload to YouTube with VideoApiHub
How it works