{
  "name": "Google Sheets \u2192 personalized videos (render, deliver, write back)",
  "nodes": [
    {
      "id": "c5d7f436-f695-4c0c-8fc2-6e167407cc78",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        272,
        208
      ],
      "parameters": {
        "width": 480,
        "height": 896,
        "content": "## Google Sheets \u2192 personalized videos (render, deliver, write back)\n\n### How it works\n\nThis workflow turns new Google Sheets rows into personalized video deliveries. When a row is added, it validates required data, starts a render job, polls the video editor until completion, then downloads and stores the MP4 in Google Drive. It writes the final video URL back to the sheet, emails the recipient, and sends failure alerts if rendering fails or exceeds the retry limit.\n\n### Setup steps\n\n- Connect Google Sheets credentials for the trigger and write-back node, and configure the target spreadsheet, worksheet, and row fields such as title, recipient email, attempts, and video URL.\n- Configure the video editor nodes with the required API credentials, render template, personalization inputs, and status/job ID mappings.\n- Connect Google Drive credentials and choose the destination folder and sharing settings for uploaded MP4 files.\n- Connect Gmail credentials and configure both the success email template and the failure alert recipient/message.\n- Verify the retry counter field and polling logic are initialized correctly so the workflow can loop until the maximum of 20 attempts.\n\n### Customization\n\nAdjust the 60-second wait time, the 20-attempt retry limit, the Drive folder or sharing permissions, and the email templates to match the expected render duration and delivery style."
      },
      "typeVersion": 1
    },
    {
      "id": "41dcd01d-58cf-49fb-a0b0-1f8e7d62eb7d",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        832,
        288
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 320,
        "content": "## Sheet trigger validation\n\nWatches Google Sheets for newly added rows and checks that the row contains the required title before starting video creation."
      },
      "typeVersion": 1
    },
    {
      "id": "e14b33b4-7595-4fb6-b362-67931e3b9513",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1280,
        224
      ],
      "parameters": {
        "color": 7,
        "width": 240,
        "height": 384,
        "content": "## Start video render\n\nSubmits the personalized video render job to the video editor service, with a direct failure path if the render request cannot be created."
      },
      "typeVersion": 1
    },
    {
      "id": "46c5b118-028f-4289-9a49-54da1928d0eb",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1552,
        304
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 304,
        "content": "## Poll render status\n\nWaits between checks, queries the render job status, and branches based on whether the video has completed or needs further handling."
      },
      "typeVersion": 1
    },
    {
      "id": "a537235e-05b8-47e2-8bb4-4091d728bf26",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2224,
        208
      ],
      "parameters": {
        "color": 7,
        "width": 848,
        "height": 304,
        "content": "## Deliver finished video\n\nDownloads the completed MP4, uploads it to Google Drive, writes the resulting video URL back to Google Sheets, and emails the link to the recipient."
      },
      "typeVersion": 1
    },
    {
      "id": "68f32e23-2bee-498c-8347-1bc6b46b0343",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1936,
        640
      ],
      "parameters": {
        "color": 7,
        "width": 896,
        "height": 304,
        "content": "## Retry or alert failure\n\nHandles unsuccessful or unfinished renders by detecting failure states, incrementing the attempt counter, looping back while under the retry limit, or sending a failure alert email."
      },
      "typeVersion": 1
    },
    {
      "id": "a1b2c3d4-1111-4aaa-8bbb-000000000021",
      "name": "When Row Added to Sheets",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "position": [
        880,
        440
      ],
      "parameters": {
        "event": "rowAdded",
        "pollTimes": {
          "item": [
            {
              "mode": "everyHour"
            }
          ]
        },
        "sheetName": "REPLACE_WITH_SHEET_NAME",
        "documentId": "REPLACE_WITH_GOOGLE_SHEET_DOCUMENT_ID"
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "a1b2c3d4-1111-4aaa-8bbb-000000000022",
      "name": "If Title Exists",
      "type": "n8n-nodes-base.if",
      "position": [
        1100,
        440
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "c0nd1t10-1111-4aaa-8bbb-+1234567890",
              "operator": {
                "name": "filter.operator.notEmpty",
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.Title }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "a1b2c3d4-1111-4aaa-8bbb-000000000023",
      "name": "Render Personalized Video",
      "type": "n8n-nodes-ilovevideoeditor.iloveVideoEditor",
      "onError": "continueErrorOutput",
      "position": [
        1320,
        440
      ],
      "parameters": {
        "resource": "render",
        "operation": "createFromTemplate",
        "variables": "={ \"title\": \"{{ $json.Title }}\", \"subtitle\": \"{{ $json.Subtitle }}\" }",
        "templateId": "cinematic-title"
      },
      "credentials": {
        "iloveVideoEditorApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "a1b2c3d4-1111-4aaa-8bbb-000000000024",
      "name": "Wait 60 Seconds",
      "type": "n8n-nodes-base.wait",
      "position": [
        1600,
        448
      ],
      "parameters": {
        "unit": "seconds",
        "amount": 60
      },
      "typeVersion": 1.1
    },
    {
      "id": "a1b2c3d4-1111-4aaa-8bbb-000000000025",
      "name": "Check Video Render Status",
      "type": "n8n-nodes-ilovevideoeditor.iloveVideoEditor",
      "position": [
        1824,
        448
      ],
      "parameters": {
        "renderId": "={{ $('Render Personalized Video').item.json.jobId }}",
        "resource": "render",
        "operation": "get"
      },
      "credentials": {
        "iloveVideoEditorApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "a1b2c3d4-1111-4aaa-8bbb-000000000026",
      "name": "If Render Complete",
      "type": "n8n-nodes-base.if",
      "position": [
        2048,
        448
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "c0nd1t10-1111-4aaa-8bbb-+1234567890",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.status }}",
              "rightValue": "completed"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "a1b2c3d4-1111-4aaa-8bbb-000000000027",
      "name": "If Render Failed",
      "type": "n8n-nodes-base.if",
      "position": [
        1984,
        768
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "c0nd1t10-1111-4aaa-8bbb-+1234567890",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.status }}",
              "rightValue": "failed"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "a1b2c3d4-1111-4aaa-8bbb-000000000028",
      "name": "Download Video File",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2272,
        336
      ],
      "parameters": {
        "url": "={{ $json.url }}",
        "method": "GET",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "a1b2c3d4-1111-4aaa-8bbb-000000000029",
      "name": "Upload Video to Drive",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        2496,
        336
      ],
      "parameters": {
        "name": "={{ $('When Row Added to Sheets').item.json.Title }}.mp4",
        "options": {},
        "parents": [
          "REPLACE_WITH_GOOGLE_DRIVE_FOLDER_ID"
        ],
        "resource": "file",
        "operation": "upload"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "a1b2c3d4-1111-4aaa-8bbb-000000000030",
      "name": "Update Video URL in Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2720,
        336
      ],
      "parameters": {
        "columns": {
          "value": {
            "ID": "={{ $('When Row Added to Sheets').item.json.ID }}",
            "Video URL": "={{ $json.webViewLink || ('https://drive.google.com/file/d/' + $json.id + '/view') }}"
          },
          "schema": [
            {
              "id": "ID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "ID",
              "defaultMatch": true,
              "canBeUsedToMatch": true
            },
            {
              "id": "Title",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Subtitle",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Subtitle",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Email",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Video URL",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Video URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "ID"
          ]
        },
        "options": {},
        "resource": "sheetWithinDocument",
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "REPLACE_WITH_SHEET_NAME"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "REPLACE_WITH_GOOGLE_SHEET_DOCUMENT_ID"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "a1b2c3d4-1111-4aaa-8bbb-000000000031",
      "name": "Send Video Link Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2928,
        336
      ],
      "parameters": {
        "message": "=Hi,\n\nYour personalized video is ready:\n{{ $('Upload Video to Drive').item.json.webViewLink || ('https://drive.google.com/file/d/' + $('Upload Video to Drive').item.json.id + '/view') }}\n\nEnjoy!",
        "options": {
          "sendTo": "={{ $('When Row Added to Sheets').item.json.Email }}"
        },
        "subject": "=Your video \"{{ $('When Row Added to Sheets').item.json.Title }}\" is ready",
        "resource": "message",
        "emailType": "text",
        "operation": "send"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "a1b2c3d4-1111-4aaa-8bbb-000000000032",
      "name": "Increment Attempt Count",
      "type": "n8n-nodes-base.set",
      "position": [
        2240,
        768
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "a5519nme-1111-4aaa-8bbb-+1234567890",
              "name": "attempts",
              "type": "number",
              "value": "={{ ($json.attempts ?? 0) + 1 }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "a1b2c3d4-1111-4aaa-8bbb-000000000033",
      "name": "If Under 20 Attempts",
      "type": "n8n-nodes-base.if",
      "position": [
        2464,
        768
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "c0nd1t10-1111-4aaa-8bbb-+1234567890",
              "operator": {
                "name": "filter.operator.lt",
                "type": "number",
                "operation": "lt"
              },
              "leftValue": "={{ $json.attempts }}",
              "rightValue": 20
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "a1b2c3d4-1111-4aaa-8bbb-000000000034",
      "name": "Send Failure Alert Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2688,
        768
      ],
      "parameters": {
        "message": "=A video render failed or timed out.\n\nSheet row ID: {{ $('When Row Added to Sheets').item.json.ID }}\nTitle: {{ $('When Row Added to Sheets').item.json.Title }}\n\nCheck the n8n execution log for details.",
        "options": {
          "sendTo": "REPLACE_WITH_YOUR_EMAIL"
        },
        "subject": "=Video render failed for \"{{ $('When Row Added to Sheets').item.json.Title }}\"",
        "resource": "message",
        "emailType": "text",
        "operation": "send"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    }
  ],
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "If Title Exists": {
      "main": [
        [
          {
            "node": "Render Personalized Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 60 Seconds": {
      "main": [
        [
          {
            "node": "Check Video Render Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Render Failed": {
      "main": [
        [
          {
            "node": "Send Failure Alert Email",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Increment Attempt Count",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Render Complete": {
      "main": [
        [
          {
            "node": "Download Video File",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "If Render Failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Video File": {
      "main": [
        [
          {
            "node": "Upload Video to Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Under 20 Attempts": {
      "main": [
        [
          {
            "node": "Wait 60 Seconds",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Failure Alert Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Video to Drive": {
      "main": [
        [
          {
            "node": "Update Video URL in Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Increment Attempt Count": {
      "main": [
        [
          {
            "node": "If Under 20 Attempts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Row Added to Sheets": {
      "main": [
        [
          {
            "node": "If Title Exists",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Video Render Status": {
      "main": [
        [
          {
            "node": "If Render Complete",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Render Personalized Video": {
      "main": [
        [
          {
            "node": "Wait 60 Seconds",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Failure Alert Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Video URL in Sheets": {
      "main": [
        [
          {
            "node": "Send Video Link Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}