{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "dce15696-5aeb-459c-a56d-c65cb4b7fe76",
      "name": "Main Sticky",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2320,
        16
      ],
      "parameters": {
        "color": 2,
        "width": 500,
        "height": 600,
        "content": "## Automate HeyGen Video Generation from Google Sheets\nEffortlessly convert text scripts in Google Sheets into AI-generated videos using HeyGen.\n\n\n### How it works\n1. Monitor Google Sheets for new row updates.\n2. Sanitize script text to prevent JSON errors.\n3. Send text to HeyGen API for processing.\n4. Poll for video completion status.\n5. Update Google Sheets with the final video URL.\n\n\n### Setup\n1. Connect your Google Sheets account.\n2. Provide your HeyGen API Key in the HTTP Request node.\n3. Configure Document ID and Sheet Name in Google nodes.\n4. Adjust the avatar and voice IDs as needed.\n\n\n### Customization\nConsolidate user-specific values (API Keys, IDs) in a Set node at the workflow start for easy configuration."
      },
      "typeVersion": 1
    },
    {
      "id": "bc6f1693-f349-435e-915a-35f402d47a72",
      "name": "Google Sheets Trigger",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "position": [
        -1696,
        160
      ],
      "parameters": {
        "event": "rowUpdate",
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_RESOURCE_ID_HERE"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_RESOURCE_ID_HERE"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "2dc1d0e8-1ab6-4879-90ad-3a6979b929b5",
      "name": "Has Script?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1472,
        160
      ],
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.Script }}",
              "operation": "isNotEmpty"
            },
            {
              "value1": "={{ $json.Status }}",
              "value2": "Completed",
              "operation": "notEqual"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "c046b6a7-18ce-4d14-ad89-6e1b44e46b54",
      "name": "Process One at a Time",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -1264,
        144
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "a4fe5b87-f7c3-42e6-940b-87fbfb1295d1",
      "name": "Sanitize Script",
      "type": "n8n-nodes-base.code",
      "position": [
        -1024,
        160
      ],
      "parameters": {
        "jsCode": "// Sanitize the Script field to make it safe for JSON injection\n// Removes newlines, carriage returns, tabs, and escapes quotes\nconst item = $input.item.json;\n\nconst rawScript = item.Script || '';\n\nconst cleanScript = rawScript\n  .replace(/\\r\\n/g, ' ')  // Windows line endings\n  .replace(/\\n/g, ' ')    // Unix line endings\n  .replace(/\\r/g, ' ')    // Old Mac line endings\n  .replace(/\\t/g, ' ')    // Tabs\n  .replace(/\\\\/g, '\\\\\\\\') // Escape backslashes first\n  .replace(/\"/g, '\\\\\"')   // Escape double quotes\n  .replace(/\\s+/g, ' ')   // Collapse multiple spaces\n  .trim();\n\nreturn [{\n  json: {\n    ...item,\n    CleanScript: cleanScript\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "eeabc558-954a-4f55-bfa3-a3793a9a1e7f",
      "name": "Generate Video",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -736,
        160
      ],
      "parameters": {
        "url": "YOUR_API_ENDPOINT_HERE",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"video_inputs\": [\n    {\n      \"character\": {\n        \"type\": \"avatar\",\n        \"avatar_id\": \"4185302635fa4de29a99ad8ca17475bc\"\n      },\n      \"voice\": {\n        \"type\": \"text\",\n        \"input_text\": \"{{ $json.CleanScript }}\",\n        \"voice_id\": \"d7f0f1713ccd458790d6cffb41ad27e2\"\n      }\n    }\n  ],\n  \"dimension\": {\n    \"width\": 720,\n    \"height\": 1280\n  }\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Api-Key",
              "value": "sk_V2_hgu_khdB00SgqzE_dDenmzsc8IVkvjmZh4pkrngPLd6Alx72"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "825db574-df10-42e7-9c5e-b92c9ffb53e3",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [
        -512,
        160
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "51d6ebcf-1b85-4752-a61f-a343c5d0e08c",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "=",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "3a1d1e70-1ecb-444e-a8f9-7d06661901e1",
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        -288,
        144
      ],
      "parameters": {
        "amount": 60
      },
      "typeVersion": 1.1
    },
    {
      "id": "bfef54cd-103f-4e31-b62e-40ac6438db8e",
      "name": "Video Status",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -64,
        144
      ],
      "parameters": {
        "url": "YOUR_API_ENDPOINT_HERE",
        "options": {},
        "sendQuery": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "queryParameters": {
          "parameters": [
            {
              "name": "video_id",
              "value": "={{ $('Generate Video').item.json.data.video_id }}"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "accept",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "945b5fee-2151-48f9-a16e-dc2fe46f3a9e",
      "name": "Is Complete?1",
      "type": "n8n-nodes-base.if",
      "position": [
        240,
        144
      ],
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.data.status }}",
              "value2": "completed"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "b02f439c-71cf-4718-a04e-795cbdc25592",
      "name": "Update Sheet with Video1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        480,
        128
      ],
      "parameters": {
        "columns": {
          "value": {
            "Row_ID": "={{ $('Process One at a Time').item.json.Row_ID }}",
            "Status": "Completed",
            "Video_URL": "={{ $json.data.video_url }}",
            "row_number": 0,
            "Completed_At": "={{ $now.toISO() }}"
          },
          "schema": [
            {
              "id": "Row_ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Row_ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Script",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Script",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Video_URL",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Video_URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Completed_At",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Completed_At",
              "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_ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_RESOURCE_ID_HERE"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_RESOURCE_ID_HERE"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "11b872c3-8ae5-465b-905a-9baccafa3c26",
      "name": "Wait Before Retry1",
      "type": "n8n-nodes-base.wait",
      "position": [
        480,
        320
      ],
      "parameters": {
        "amount": 60
      },
      "typeVersion": 1.1
    },
    {
      "id": "ac9ac3b8-3833-4b0d-a1b4-6c3b4f02f394",
      "name": "Section 1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1760,
        16
      ],
      "parameters": {
        "color": 7,
        "width": 892,
        "height": 312,
        "content": "## 1. Trigger & Data Processing\nWatches Google Sheets for pending scripts and cleans the text."
      },
      "typeVersion": 1
    },
    {
      "id": "04080163-8397-4d96-8818-99d90eb157ba",
      "name": "Section 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -800,
        16
      ],
      "parameters": {
        "color": 7,
        "width": 892,
        "height": 312,
        "content": "## 2. API Communication\nHandles HeyGen requests and polls for video generation status."
      },
      "typeVersion": 1
    },
    {
      "id": "89fc0f7f-d86d-477f-8745-e14cec4de985",
      "name": "Section 3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        176,
        16
      ],
      "parameters": {
        "color": 7,
        "width": 488,
        "height": 520,
        "content": "## 3. Sheet Update\nWrites the generated video URL back to the spreadsheet."
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "If": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait": {
      "main": [
        [
          {
            "node": "Video Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Script?": {
      "main": [
        [
          {
            "node": "Process One at a Time",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Video Status": {
      "main": [
        [
          {
            "node": "Is Complete?1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Complete?1": {
      "main": [
        [
          {
            "node": "Update Sheet with Video1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait Before Retry1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Video": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sanitize Script": {
      "main": [
        [
          {
            "node": "Generate Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait Before Retry1": {
      "main": [
        [
          {
            "node": "Video Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets Trigger": {
      "main": [
        [
          {
            "node": "Has Script?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process One at a Time": {
      "main": [
        [],
        [
          {
            "node": "Sanitize Script",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Sheet with Video1": {
      "main": [
        [
          {
            "node": "Process One at a Time",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}