{
  "name": "MCP Veo Video",
  "active": true,
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "veo-video",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "webhook-trigger",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        240,
        304
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "condition-input",
              "leftValue": "={{ $json.body.prompt || $json.body.sourceVideo }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "exists"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "check-input",
      "name": "Has required input?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        464,
        304
      ]
    },
    {
      "parameters": {
        "errorMessage": "={{ \"Missing required input. Provide 'prompt' for most operations or 'sourceVideo' for extendVideo\" }}"
      },
      "id": "error-no-input",
      "name": "Error: Missing Input",
      "type": "n8n-nodes-base.stopAndError",
      "typeVersion": 1,
      "position": [
        688,
        500
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "condition-callback",
              "leftValue": "={{ $json.body.callback_url }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "exists"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "check-callback",
      "name": "Has Callback URL?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        688,
        304
      ]
    },
    {
      "parameters": {
        "jsCode": "// Store callback info for async processing\nconst crypto = require('crypto');\nconst input = $('Webhook').first().json;\nconst body = input.body || input;\n\n// Generate or use provided job_id\nconst job_id = body.job_id || crypto.randomUUID();\nconst callback_url = body.callback_url;\n\nreturn [{\n  json: {\n    ...input,\n    _callback: {\n      job_id,\n      callback_url\n    }\n  }\n}];"
      },
      "id": "store-callback",
      "name": "Store Callback Info",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        912,
        192
      ]
    },
    {
      "parameters": {
        "jsCode": "// Prepare data for Veo Video node (sync mode)\nconst input = $('Webhook').first().json;\nconst body = input.body || input;\n\n// Get operation from request (default: generateFromText)\nconst operation = body.operation || 'generateFromText';\n\n// Video parameters\nconst model = body.model || 'veo-3.1-generate-001';\nconst durationSeconds = body.durationSeconds || 6;\nconst aspectRatio = body.aspectRatio || '16:9';\nconst resolution = body.resolution || '1080p';\nconst generateAudio = body.generateAudio !== false;\nconst enhancePrompt = body.enhancePrompt !== false;\nconst personGeneration = body.personGeneration || 'allow_adult';\nconst preset = body.preset || 'none';\n\n// Build result\nconst result = {\n  operation,\n  prompt: body.prompt || '',\n  model,\n  durationSeconds,\n  aspectRatio,\n  resolution,\n  generateAudio,\n  enhancePrompt,\n  personGeneration,\n  preset,\n  originalRequest: { ...body }\n};\n\n// For generateFromImage\nif (operation === 'generateFromImage') {\n  result.sourceImage = body.sourceImage || '';\n  result.sourceImageMimeType = body.sourceImageMimeType || 'image/png';\n}\n\n// For generateLongVideo\nif (operation === 'generateLongVideo') {\n  result.targetDuration = body.targetDuration || 30;\n}\n\n// For extendVideo\nif (operation === 'extendVideo') {\n  result.sourceVideo = body.sourceVideo || '';\n  result.extensionPrompt = body.extensionPrompt || '';\n}\n\nreturn [{ json: result }];"
      },
      "id": "prepare-config-sync",
      "name": "Prepare Config (Sync)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        912,
        416
      ]
    },
    {
      "parameters": {
        "jsCode": "// Prepare data for Veo Video node (async mode)\nconst input = $input.first().json;\nconst body = input.body || input;\nconst _callback = input._callback;\n\n// Get operation from request (default: generateFromText)\nconst operation = body.operation || 'generateFromText';\n\n// Video parameters\nconst model = body.model || 'veo-3.1-generate-001';\nconst durationSeconds = body.durationSeconds || 6;\nconst aspectRatio = body.aspectRatio || '16:9';\nconst resolution = body.resolution || '1080p';\nconst generateAudio = body.generateAudio !== false;\nconst enhancePrompt = body.enhancePrompt !== false;\nconst personGeneration = body.personGeneration || 'allow_adult';\nconst preset = body.preset || 'none';\n\n// Build result\nconst result = {\n  operation,\n  prompt: body.prompt || '',\n  model,\n  durationSeconds,\n  aspectRatio,\n  resolution,\n  generateAudio,\n  enhancePrompt,\n  personGeneration,\n  preset,\n  originalRequest: { ...body },\n  _callback\n};\n\n// For generateFromImage\nif (operation === 'generateFromImage') {\n  result.sourceImage = body.sourceImage || '';\n  result.sourceImageMimeType = body.sourceImageMimeType || 'image/png';\n}\n\n// For generateLongVideo\nif (operation === 'generateLongVideo') {\n  result.targetDuration = body.targetDuration || 30;\n}\n\n// For extendVideo\nif (operation === 'extendVideo') {\n  result.sourceVideo = body.sourceVideo || '';\n  result.extensionPrompt = body.extensionPrompt || '';\n}\n\nreturn [{ json: result }];"
      },
      "id": "prepare-config-async",
      "name": "Prepare Config (Async)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1136,
        192
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "condition-optimize-sync",
              "leftValue": "={{ $json.operation }}",
              "rightValue": "optimizePrompt",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "route-operation-sync",
      "name": "Optimize Prompt? (Sync)",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1136,
        416
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "condition-optimize-async",
              "leftValue": "={{ $json.operation }}",
              "rightValue": "optimizePrompt",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "route-operation-async",
      "name": "Optimize Prompt? (Async)",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1360,
        192
      ]
    },
    {
      "parameters": {
        "operation": "optimizePrompt",
        "prompt": "={{ $json.prompt }}",
        "preset": "={{ $json.preset }}"
      },
      "id": "veo-optimize-sync",
      "name": "Optimize Prompt (Sync)",
      "type": "n8n-nodes-veo-video.veoVideo",
      "typeVersion": 1,
      "position": [
        1360,
        304
      ],
      "credentials": {
        "googleVertexAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "={{ $json.operation }}",
        "prompt": "={{ $json.prompt }}",
        "extensionPrompt": "={{ $json.extensionPrompt }}",
        "sourceVideo": "={{ $json.sourceVideo }}",
        "sourceImage": "={{ $json.sourceImage }}",
        "sourceImageMimeType": "={{ $json.sourceImageMimeType }}",
        "preset": "={{ $json.preset }}",
        "model": "={{ $json.model }}",
        "targetDuration": "={{ $json.targetDuration }}",
        "durationSeconds": "={{ $json.durationSeconds }}",
        "aspectRatio": "={{ $json.aspectRatio }}",
        "resolution": "={{ $json.resolution }}",
        "generateAudio": "={{ $json.generateAudio }}",
        "enhancePrompt": "={{ $json.enhancePrompt }}",
        "options": {
          "personGeneration": "={{ $json.personGeneration }}"
        }
      },
      "id": "veo-video-sync",
      "name": "Veo Video (Sync)",
      "type": "n8n-nodes-veo-video.veoVideo",
      "typeVersion": 1,
      "position": [
        1360,
        528
      ],
      "credentials": {
        "googleVertexAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Format response for optimizePrompt (sync)\nconst input = $input.first().json;\n\nconst response = {\n  success: true,\n  execution_mode: 'sync',\n  operation: 'optimizePrompt',\n  originalPrompt: input.originalPrompt,\n  optimizedPrompt: input.optimizedPrompt,\n  keywordsAdded: input.keywordsAdded,\n  preset: input.preset,\n  metadata: input.metadata\n};\n\nreturn [{ json: response }];"
      },
      "id": "format-optimize-response-sync",
      "name": "Format Optimize Response (Sync)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1584,
        304
      ]
    },
    {
      "parameters": {
        "jsCode": "// Format response for video operations (sync)\nconst input = $input.first().json;\n\nconst response = {\n  success: true,\n  execution_mode: 'sync',\n  operation: input.operation,\n  video: input.video,\n  videoBase64: input.videoBase64,\n  model: input.model,\n  generationTimeSeconds: input.generationTimeSeconds,\n  preset: input.preset,\n  metadata: input.metadata\n};\n\nreturn [{ json: response }];"
      },
      "id": "format-response-sync",
      "name": "Format Response (Sync)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1584,
        528
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ $json }}",
        "options": {}
      },
      "id": "respond-success-sync",
      "name": "Respond Success (Sync)",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        1808,
        416
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ { success: true, job_id: $json._callback.job_id, status: 'processing', message: 'Video generation started' } }}",
        "options": {
          "responseCode": 202
        }
      },
      "id": "respond-immediate",
      "name": "Respond Immediate",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        1584,
        80
      ]
    },
    {
      "parameters": {
        "operation": "optimizePrompt",
        "prompt": "={{ $json.prompt }}",
        "preset": "={{ $json.preset }}"
      },
      "id": "veo-optimize-async",
      "name": "Optimize Prompt (Async)",
      "type": "n8n-nodes-veo-video.veoVideo",
      "typeVersion": 1,
      "position": [
        1808,
        80
      ],
      "credentials": {
        "googleVertexAiApi": {
          "name": "<your credential>"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "operation": "={{ $json.operation }}",
        "prompt": "={{ $json.prompt }}",
        "extensionPrompt": "={{ $json.extensionPrompt }}",
        "sourceVideo": "={{ $json.sourceVideo }}",
        "sourceImage": "={{ $json.sourceImage }}",
        "sourceImageMimeType": "={{ $json.sourceImageMimeType }}",
        "preset": "={{ $json.preset }}",
        "model": "={{ $json.model }}",
        "targetDuration": "={{ $json.targetDuration }}",
        "durationSeconds": "={{ $json.durationSeconds }}",
        "aspectRatio": "={{ $json.aspectRatio }}",
        "resolution": "={{ $json.resolution }}",
        "generateAudio": "={{ $json.generateAudio }}",
        "enhancePrompt": "={{ $json.enhancePrompt }}",
        "options": {
          "personGeneration": "={{ $json.personGeneration }}"
        }
      },
      "id": "veo-video-async",
      "name": "Veo Video (Async)",
      "type": "n8n-nodes-veo-video.veoVideo",
      "typeVersion": 1,
      "position": [
        1808,
        192
      ],
      "credentials": {
        "googleVertexAiApi": {
          "name": "<your credential>"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "jsCode": "// Format response for optimizePrompt (async)\nconst input = $input.first().json;\nconst _callback = $('Prepare Config (Async)').first().json._callback;\n\nconst response = {\n  success: true,\n  execution_mode: 'async',\n  job_id: _callback.job_id,\n  operation: 'optimizePrompt',\n  originalPrompt: input.originalPrompt,\n  optimizedPrompt: input.optimizedPrompt,\n  keywordsAdded: input.keywordsAdded,\n  preset: input.preset,\n  metadata: input.metadata,\n  _callback\n};\n\nreturn [{ json: response }];"
      },
      "id": "format-optimize-response-async",
      "name": "Format Optimize Response (Async)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2032,
        80
      ]
    },
    {
      "parameters": {
        "jsCode": "// Format response for video operations (async)\nconst input = $input.first().json;\nconst _callback = $('Prepare Config (Async)').first().json._callback;\n\nconst response = {\n  success: true,\n  execution_mode: 'async',\n  job_id: _callback.job_id,\n  operation: input.operation,\n  video: input.video,\n  videoBase64: input.videoBase64,\n  model: input.model,\n  generationTimeSeconds: input.generationTimeSeconds,\n  preset: input.preset,\n  metadata: input.metadata,\n  _callback\n};\n\nreturn [{ json: response }];"
      },
      "id": "format-response-async",
      "name": "Format Response (Async)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2032,
        192
      ]
    },
    {
      "parameters": {
        "jsCode": "// Prepare callback payload for success\nconst input = $input.first().json;\nconst _callback = input._callback;\n\n// Remove internal _callback from the payload\nconst { _callback: _, ...data } = input;\n\nconst body = {\n  success: true,\n  job_id: _callback.job_id,\n  status: 'completed',\n  data\n};\n\nreturn [{\n  json: {\n    body,\n    callback_url: _callback.callback_url\n  }\n}];"
      },
      "id": "prepare-callback-payload",
      "name": "Prepare Callback Payload",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2256,
        136
      ]
    },
    {
      "parameters": {
        "jsCode": "// Compute HMAC signature (RFC-040 format)\nconst crypto = require('crypto');\nconst input = $input.first().json;\n\nconst secret = $env.N8N_WEBHOOK_SECRET || 'default-secret';\nconst payload = JSON.stringify(input.body);\n\n// Sign body only, output hex WITHOUT prefix\nconst signature = crypto.createHmac('sha256', secret).update(payload).digest('hex');\n\nreturn [{\n  json: {\n    ...input,\n    signature\n  }\n}];"
      },
      "id": "compute-hmac",
      "name": "Compute HMAC Signature",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2480,
        136
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $json.callback_url }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "X-N8N-Signature",
              "value": "={{ $json.signature }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json.body }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "callback-success",
      "name": "Callback Success",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2704,
        136
      ]
    },
    {
      "parameters": {
        "jsCode": "// Prepare error callback payload\nconst _callback = $('Prepare Config (Async)').first().json._callback;\n\n// Get error information\nlet errorMessage = 'Unknown error occurred';\nlet errorDetails = {};\n\ntry {\n  const input = $input.first().json;\n  if (input.error) {\n    errorMessage = input.error.message || input.error;\n    errorDetails = input.error;\n  } else if (input.message) {\n    errorMessage = input.message;\n  }\n} catch (e) {\n  errorMessage = 'Video generation failed';\n}\n\nconst body = {\n  success: false,\n  job_id: _callback.job_id,\n  status: 'failed',\n  error: {\n    message: errorMessage,\n    details: errorDetails\n  }\n};\n\nreturn [{\n  json: {\n    body,\n    callback_url: _callback.callback_url\n  }\n}];"
      },
      "id": "prepare-error-callback",
      "name": "Prepare Error Callback",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2032,
        360
      ]
    },
    {
      "parameters": {
        "jsCode": "// Compute HMAC signature for error callback (RFC-040 format)\nconst crypto = require('crypto');\nconst input = $input.first().json;\n\nconst secret = $env.N8N_WEBHOOK_SECRET || 'default-secret';\nconst payload = JSON.stringify(input.body);\n\n// Sign body only, output hex WITHOUT prefix\nconst signature = crypto.createHmac('sha256', secret).update(payload).digest('hex');\n\nreturn [{\n  json: {\n    ...input,\n    signature\n  }\n}];"
      },
      "id": "compute-hmac-error",
      "name": "Compute HMAC Error",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2256,
        360
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $json.callback_url }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "X-N8N-Signature",
              "value": "={{ $json.signature }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json.body }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "callback-error",
      "name": "Callback Error",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2480,
        360
      ]
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Has required input?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has required input?": {
      "main": [
        [
          {
            "node": "Has Callback URL?",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error: Missing Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Callback URL?": {
      "main": [
        [
          {
            "node": "Store Callback Info",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Prepare Config (Sync)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store Callback Info": {
      "main": [
        [
          {
            "node": "Prepare Config (Async)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Config (Sync)": {
      "main": [
        [
          {
            "node": "Optimize Prompt? (Sync)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Config (Async)": {
      "main": [
        [
          {
            "node": "Optimize Prompt? (Async)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Optimize Prompt? (Sync)": {
      "main": [
        [
          {
            "node": "Optimize Prompt (Sync)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Veo Video (Sync)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Optimize Prompt? (Async)": {
      "main": [
        [
          {
            "node": "Respond Immediate",
            "type": "main",
            "index": 0
          },
          {
            "node": "Optimize Prompt (Async)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond Immediate",
            "type": "main",
            "index": 0
          },
          {
            "node": "Veo Video (Async)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Optimize Prompt (Sync)": {
      "main": [
        [
          {
            "node": "Format Optimize Response (Sync)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Veo Video (Sync)": {
      "main": [
        [
          {
            "node": "Format Response (Sync)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Optimize Response (Sync)": {
      "main": [
        [
          {
            "node": "Respond Success (Sync)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Response (Sync)": {
      "main": [
        [
          {
            "node": "Respond Success (Sync)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Respond Immediate": {
      "main": [
        []
      ]
    },
    "Optimize Prompt (Async)": {
      "main": [
        [
          {
            "node": "Format Optimize Response (Async)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Prepare Error Callback",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Veo Video (Async)": {
      "main": [
        [
          {
            "node": "Format Response (Async)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Prepare Error Callback",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Optimize Response (Async)": {
      "main": [
        [
          {
            "node": "Prepare Callback Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Response (Async)": {
      "main": [
        [
          {
            "node": "Prepare Callback Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Callback Payload": {
      "main": [
        [
          {
            "node": "Compute HMAC Signature",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compute HMAC Signature": {
      "main": [
        [
          {
            "node": "Callback Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Error Callback": {
      "main": [
        [
          {
            "node": "Compute HMAC Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compute HMAC Error": {
      "main": [
        [
          {
            "node": "Callback Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "callerPolicy": "workflowsFromSameOwner",
    "availableInMCP": false
  },
  "tags": [],
  "activeVersion": {
    "updatedAt": "2026-03-16T10:47:24.351Z",
    "createdAt": "2026-03-16T10:47:24.351Z",
    "versionId": "a61bb1df-69de-4b92-bdca-ba3d580f5ec5",
    "workflowId": "wlWgNRY2QBOzWXUY",
    "nodes": [
      {
        "parameters": {
          "httpMethod": "POST",
          "path": "veo-video",
          "responseMode": "responseNode",
          "options": {}
        },
        "id": "webhook-trigger",
        "name": "Webhook",
        "type": "n8n-nodes-base.webhook",
        "typeVersion": 2,
        "position": [
          240,
          304
        ],
        "webhookId": "veo-video-webhook"
      },
      {
        "parameters": {
          "conditions": {
            "options": {
              "caseSensitive": true,
              "leftValue": "",
              "typeValidation": "strict"
            },
            "conditions": [
              {
                "id": "condition-input",
                "leftValue": "={{ $json.body.prompt || $json.body.sourceVideo }}",
                "rightValue": "",
                "operator": {
                  "type": "string",
                  "operation": "exists"
                }
              }
            ],
            "combinator": "and"
          },
          "options": {}
        },
        "id": "check-input",
        "name": "Has required input?",
        "type": "n8n-nodes-base.if",
        "typeVersion": 2,
        "position": [
          464,
          304
        ]
      },
      {
        "parameters": {
          "errorMessage": "={{ \"Missing required input. Provide 'prompt' for most operations or 'sourceVideo' for extendVideo\" }}"
        },
        "id": "error-no-input",
        "name": "Error: Missing Input",
        "type": "n8n-nodes-base.stopAndError",
        "typeVersion": 1,
        "position": [
          688,
          500
        ]
      },
      {
        "parameters": {
          "conditions": {
            "options": {
              "caseSensitive": true,
              "leftValue": "",
              "typeValidation": "strict"
            },
            "conditions": [
              {
                "id": "condition-callback",
                "leftValue": "={{ $json.body.callback_url }}",
                "rightValue": "",
                "operator": {
                  "type": "string",
                  "operation": "exists"
                }
              }
            ],
            "combinator": "and"
          },
          "options": {}
        },
        "id": "check-callback",
        "name": "Has Callback URL?",
        "type": "n8n-nodes-base.if",
        "typeVersion": 2,
        "position": [
          688,
          304
        ]
      },
      {
        "parameters": {
          "jsCode": "// Store callback info for async processing\nconst crypto = require('crypto');\nconst input = $('Webhook').first().json;\nconst body = input.body || input;\n\n// Generate or use provided job_id\nconst job_id = body.job_id || crypto.randomUUID();\nconst callback_url = body.callback_url;\n\nreturn [{\n  json: {\n    ...input,\n    _callback: {\n      job_id,\n      callback_url\n    }\n  }\n}];"
        },
        "id": "store-callback",
        "name": "Store Callback Info",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          912,
          192
        ]
      },
      {
        "parameters": {
          "jsCode": "// Prepare data for Veo Video node (sync mode)\nconst input = $('Webhook').first().json;\nconst body = input.body || input;\n\n// Get operation from request (default: generateFromText)\nconst operation = body.operation || 'generateFromText';\n\n// Video parameters\nconst model = body.model || 'veo-3.1-generate-001';\nconst durationSeconds = body.durationSeconds || 6;\nconst aspectRatio = body.aspectRatio || '16:9';\nconst resolution = body.resolution || '1080p';\nconst generateAudio = body.generateAudio !== false;\nconst enhancePrompt = body.enhancePrompt !== false;\nconst personGeneration = body.personGeneration || 'allow_adult';\nconst preset = body.preset || 'none';\n\n// Build result\nconst result = {\n  operation,\n  prompt: body.prompt || '',\n  model,\n  durationSeconds,\n  aspectRatio,\n  resolution,\n  generateAudio,\n  enhancePrompt,\n  personGeneration,\n  preset,\n  originalRequest: { ...body }\n};\n\n// For generateFromImage\nif (operation === 'generateFromImage') {\n  result.sourceImage = body.sourceImage || '';\n  result.sourceImageMimeType = body.sourceImageMimeType || 'image/png';\n}\n\n// For generateLongVideo\nif (operation === 'generateLongVideo') {\n  result.targetDuration = body.targetDuration || 30;\n}\n\n// For extendVideo\nif (operation === 'extendVideo') {\n  result.sourceVideo = body.sourceVideo || '';\n  result.extensionPrompt = body.extensionPrompt || '';\n}\n\nreturn [{ json: result }];"
        },
        "id": "prepare-config-sync",
        "name": "Prepare Config (Sync)",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          912,
          416
        ]
      },
      {
        "parameters": {
          "jsCode": "// Prepare data for Veo Video node (async mode)\nconst input = $input.first().json;\nconst body = input.body || input;\nconst _callback = input._callback;\n\n// Get operation from request (default: generateFromText)\nconst operation = body.operation || 'generateFromText';\n\n// Video parameters\nconst model = body.model || 'veo-3.1-generate-001';\nconst durationSeconds = body.durationSeconds || 6;\nconst aspectRatio = body.aspectRatio || '16:9';\nconst resolution = body.resolution || '1080p';\nconst generateAudio = body.generateAudio !== false;\nconst enhancePrompt = body.enhancePrompt !== false;\nconst personGeneration = body.personGeneration || 'allow_adult';\nconst preset = body.preset || 'none';\n\n// Build result\nconst result = {\n  operation,\n  prompt: body.prompt || '',\n  model,\n  durationSeconds,\n  aspectRatio,\n  resolution,\n  generateAudio,\n  enhancePrompt,\n  personGeneration,\n  preset,\n  originalRequest: { ...body },\n  _callback\n};\n\n// For generateFromImage\nif (operation === 'generateFromImage') {\n  result.sourceImage = body.sourceImage || '';\n  result.sourceImageMimeType = body.sourceImageMimeType || 'image/png';\n}\n\n// For generateLongVideo\nif (operation === 'generateLongVideo') {\n  result.targetDuration = body.targetDuration || 30;\n}\n\n// For extendVideo\nif (operation === 'extendVideo') {\n  result.sourceVideo = body.sourceVideo || '';\n  result.extensionPrompt = body.extensionPrompt || '';\n}\n\nreturn [{ json: result }];"
        },
        "id": "prepare-config-async",
        "name": "Prepare Config (Async)",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          1136,
          192
        ]
      },
      {
        "parameters": {
          "conditions": {
            "options": {
              "caseSensitive": true,
              "leftValue": "",
              "typeValidation": "strict"
            },
            "conditions": [
              {
                "id": "condition-optimize-sync",
                "leftValue": "={{ $json.operation }}",
                "rightValue": "optimizePrompt",
                "operator": {
                  "type": "string",
                  "operation": "equals"
                }
              }
            ],
            "combinator": "and"
          },
          "options": {}
        },
        "id": "route-operation-sync",
        "name": "Optimize Prompt? (Sync)",
        "type": "n8n-nodes-base.if",
        "typeVersion": 2,
        "position": [
          1136,
          416
        ]
      },
      {
        "parameters": {
          "conditions": {
            "options": {
              "caseSensitive": true,
              "leftValue": "",
              "typeValidation": "strict"
            },
            "conditions": [
              {
                "id": "condition-optimize-async",
                "leftValue": "={{ $json.operation }}",
                "rightValue": "optimizePrompt",
                "operator": {
                  "type": "string",
                  "operation": "equals"
                }
              }
            ],
            "combinator": "and"
          },
          "options": {}
        },
        "id": "route-operation-async",
        "name": "Optimize Prompt? (Async)",
        "type": "n8n-nodes-base.if",
        "typeVersion": 2,
        "position": [
          1360,
          192
        ]
      },
      {
        "parameters": {
          "operation": "optimizePrompt",
          "prompt": "={{ $json.prompt }}",
          "preset": "={{ $json.preset }}"
        },
        "id": "veo-optimize-sync",
        "name": "Optimize Prompt (Sync)",
        "type": "n8n-nodes-veo-video.veoVideo",
        "typeVersion": 1,
        "position": [
          1360,
          304
        ],
        "credentials": {
          "googleVertexAiApi": {
            "id": "Yis7YQJ8FMrvHGzl",
            "name": "Google Vertex AI account"
          }
        }
      },
      {
        "parameters": {
          "operation": "={{ $json.operation }}",
          "prompt": "={{ $json.prompt }}",
          "extensionPrompt": "={{ $json.extensionPrompt }}",
          "sourceVideo": "={{ $json.sourceVideo }}",
          "sourceImage": "={{ $json.sourceImage }}",
          "sourceImageMimeType": "={{ $json.sourceImageMimeType }}",
          "preset": "={{ $json.preset }}",
          "model": "={{ $json.model }}",
          "targetDuration": "={{ $json.targetDuration }}",
          "durationSeconds": "={{ $json.durationSeconds }}",
          "aspectRatio": "={{ $json.aspectRatio }}",
          "resolution": "={{ $json.resolution }}",
          "generateAudio": "={{ $json.generateAudio }}",
          "enhancePrompt": "={{ $json.enhancePrompt }}",
          "options": {
            "personGeneration": "={{ $json.personGeneration }}"
          }
        },
        "id": "veo-video-sync",
        "name": "Veo Video (Sync)",
        "type": "n8n-nodes-veo-video.veoVideo",
        "typeVersion": 1,
        "position": [
          1360,
          528
        ],
        "credentials": {
          "googleVertexAiApi": {
            "id": "Yis7YQJ8FMrvHGzl",
            "name": "Google Vertex AI account"
          }
        }
      },
      {
        "parameters": {
          "jsCode": "// Format response for optimizePrompt (sync)\nconst input = $input.first().json;\n\nconst response = {\n  success: true,\n  execution_mode: 'sync',\n  operation: 'optimizePrompt',\n  originalPrompt: input.originalPrompt,\n  optimizedPrompt: input.optimizedPrompt,\n  keywordsAdded: input.keywordsAdded,\n  preset: input.preset,\n  metadata: input.metadata\n};\n\nreturn [{ json: response }];"
        },
        "id": "format-optimize-response-sync",
        "name": "Format Optimize Response (Sync)",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          1584,
          304
        ]
      },
      {
        "parameters": {
          "jsCode": "// Format response for video operations (sync)\nconst input = $input.first().json;\n\nconst response = {\n  success: true,\n  execution_mode: 'sync',\n  operation: input.operation,\n  video: input.video,\n  videoBase64: input.videoBase64,\n  model: input.model,\n  generationTimeSeconds: input.generationTimeSeconds,\n  preset: input.preset,\n  metadata: input.metadata\n};\n\nreturn [{ json: response }];"
        },
        "id": "format-response-sync",
        "name": "Format Response (Sync)",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          1584,
          528
        ]
      },
      {
        "parameters": {
          "respondWith": "json",
          "responseBody": "={{ $json }}",
          "options": {}
        },
        "id": "respond-success-sync",
        "name": "Respond Success (Sync)",
        "type": "n8n-nodes-base.respondToWebhook",
        "typeVersion": 1.1,
        "position": [
          1808,
          416
        ]
      },
      {
        "parameters": {
          "respondWith": "json",
          "responseBody": "={{ { success: true, job_id: $json._callback.job_id, status: 'processing', message: 'Video generation started' } }}",
          "options": {
            "responseCode": 202
          }
        },
        "id": "respond-immediate",
        "name": "Respond Immediate",
        "type": "n8n-nodes-base.respondToWebhook",
        "typeVersion": 1.1,
        "position": [
          1584,
          80
        ]
      },
      {
        "parameters": {
          "operation": "optimizePrompt",
          "prompt": "={{ $json.prompt }}",
          "preset": "={{ $json.preset }}"
        },
        "id": "veo-optimize-async",
        "name": "Optimize Prompt (Async)",
        "type": "n8n-nodes-veo-video.veoVideo",
        "typeVersion": 1,
        "position": [
          1808,
          80
        ],
        "credentials": {
          "googleVertexAiApi": {
            "id": "Yis7YQJ8FMrvHGzl",
            "name": "Google Vertex AI account"
          }
        },
        "onError": "continueErrorOutput"
      },
      {
        "parameters": {
          "operation": "={{ $json.operation }}",
          "prompt": "={{ $json.prompt }}",
          "extensionPrompt": "={{ $json.extensionPrompt }}",
          "sourceVideo": "={{ $json.sourceVideo }}",
          "sourceImage": "={{ $json.sourceImage }}",
          "sourceImageMimeType": "={{ $json.sourceImageMimeType }}",
          "preset": "={{ $json.preset }}",
          "model": "={{ $json.model }}",
          "targetDuration": "={{ $json.targetDuration }}",
          "durationSeconds": "={{ $json.durationSeconds }}",
          "aspectRatio": "={{ $json.aspectRatio }}",
          "resolution": "={{ $json.resolution }}",
          "generateAudio": "={{ $json.generateAudio }}",
          "enhancePrompt": "={{ $json.enhancePrompt }}",
          "options": {
            "personGeneration": "={{ $json.personGeneration }}"
          }
        },
        "id": "veo-video-async",
        "name": "Veo Video (Async)",
        "type": "n8n-nodes-veo-video.veoVideo",
        "typeVersion": 1,
        "position": [
          1808,
          192
        ],
        "credentials": {
          "googleVertexAiApi": {
            "id": "Yis7YQJ8FMrvHGzl",
            "name": "Google Vertex AI account"
          }
        },
        "onError": "continueErrorOutput"
      },
      {
        "parameters": {
          "jsCode": "// Format response for optimizePrompt (async)\nconst input = $input.first().json;\nconst _callback = $('Prepare Config (Async)').first().json._callback;\n\nconst response = {\n  success: true,\n  execution_mode: 'async',\n  job_id: _callback.job_id,\n  operation: 'optimizePrompt',\n  originalPrompt: input.originalPrompt,\n  optimizedPrompt: input.optimizedPrompt,\n  keywordsAdded: input.keywordsAdded,\n  preset: input.preset,\n  metadata: input.metadata,\n  _callback\n};\n\nreturn [{ json: response }];"
        },
        "id": "format-optimize-response-async",
        "name": "Format Optimize Response (Async)",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          2032,
          80
        ]
      },
      {
        "parameters": {
          "jsCode": "// Format response for video operations (async)\nconst input = $input.first().json;\nconst _callback = $('Prepare Config (Async)').first().json._callback;\n\nconst response = {\n  success: true,\n  execution_mode: 'async',\n  job_id: _callback.job_id,\n  operation: input.operation,\n  video: input.video,\n  videoBase64: input.videoBase64,\n  model: input.model,\n  generationTimeSeconds: input.generationTimeSeconds,\n  preset: input.preset,\n  metadata: input.metadata,\n  _callback\n};\n\nreturn [{ json: response }];"
        },
        "id": "format-response-async",
        "name": "Format Response (Async)",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          2032,
          192
        ]
      },
      {
        "parameters": {
          "jsCode": "// Prepare callback payload for success\nconst input = $input.first().json;\nconst _callback = input._callback;\n\n// Remove internal _callback from the payload\nconst { _callback: _, ...data } = input;\n\nconst body = {\n  success: true,\n  job_id: _callback.job_id,\n  status: 'completed',\n  data\n};\n\nreturn [{\n  json: {\n    body,\n    callback_url: _callback.callback_url\n  }\n}];"
        },
        "id": "prepare-callback-payload",
        "name": "Prepare Callback Payload",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          2256,
          136
        ]
      },
      {
        "parameters": {
          "jsCode": "// Compute HMAC signature (RFC-040 format)\nconst crypto = require('crypto');\nconst input = $input.first().json;\n\nconst secret = $env.N8N_WEBHOOK_SECRET || 'default-secret';\nconst payload = JSON.stringify(input.body);\n\n// Sign body only, output hex WITHOUT prefix\nconst signature = crypto.createHmac('sha256', secret).update(payload).digest('hex');\n\nreturn [{\n  json: {\n    ...input,\n    signature\n  }\n}];"
        },
        "id": "compute-hmac",
        "name": "Compute HMAC Signature",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          2480,
          136
        ]
      },
      {
        "parameters": {
          "method": "POST",
          "url": "={{ $json.callback_url }}",
          "sendHeaders": true,
          "headerParameters": {
            "parameters": [
              {
                "name": "Content-Type",
                "value": "application/json"
              },
              {
                "name": "X-N8N-Signature",
                "value": "={{ $json.signature }}"
              }
            ]
          },
          "sendBody": true,
          "specifyBody": "json",
          "jsonBody": "={{ $json.body }}",
          "options": {
            "timeout": 30000
          }
        },
        "id": "callback-success",
        "name": "Callback Success",
        "type": "n8n-nodes-base.httpRequest",
        "typeVersion": 4.2,
        "position": [
          2704,
          136
        ]
      },
      {
        "parameters": {
          "jsCode": "// Prepare error callback payload\nconst _callback = $('Prepare Config (Async)').first().json._callback;\n\n// Get error information\nlet errorMessage = 'Unknown error occurred';\nlet errorDetails = {};\n\ntry {\n  const input = $input.first().json;\n  if (input.error) {\n    errorMessage = input.error.message || input.error;\n    errorDetails = input.error;\n  } else if (input.message) {\n    errorMessage = input.message;\n  }\n} catch (e) {\n  errorMessage = 'Video generation failed';\n}\n\nconst body = {\n  success: false,\n  job_id: _callback.job_id,\n  status: 'failed',\n  error: {\n    message: errorMessage,\n    details: errorDetails\n  }\n};\n\nreturn [{\n  json: {\n    body,\n    callback_url: _callback.callback_url\n  }\n}];"
        },
        "id": "prepare-error-callback",
        "name": "Prepare Error Callback",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          2032,
          360
        ]
      },
      {
        "parameters": {
          "jsCode": "// Compute HMAC signature for error callback (RFC-040 format)\nconst crypto = require('crypto');\nconst input = $input.first().json;\n\nconst secret = $env.N8N_WEBHOOK_SECRET || 'default-secret';\nconst payload = JSON.stringify(input.body);\n\n// Sign body only, output hex WITHOUT prefix\nconst signature = crypto.createHmac('sha256', secret).update(payload).digest('hex');\n\nreturn [{\n  json: {\n    ...input,\n    signature\n  }\n}];"
        },
        "id": "compute-hmac-error",
        "name": "Compute HMAC Error",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          2256,
          360
        ]
      },
      {
        "parameters": {
          "method": "POST",
          "url": "={{ $json.callback_url }}",
          "sendHeaders": true,
          "headerParameters": {
            "parameters": [
              {
                "name": "Content-Type",
                "value": "application/json"
              },
              {
                "name": "X-N8N-Signature",
                "value": "={{ $json.signature }}"
              }
            ]
          },
          "sendBody": true,
          "specifyBody": "json",
          "jsonBody": "={{ $json.body }}",
          "options": {
            "timeout": 30000
          }
        },
        "id": "callback-error",
        "name": "Callback Error",
        "type": "n8n-nodes-base.httpRequest",
        "typeVersion": 4.2,
        "position": [
          2480,
          360
        ]
      }
    ],
    "connections": {
      "Webhook": {
        "main": [
          [
            {
              "node": "Has required input?",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Has required input?": {
        "main": [
          [
            {
              "node": "Has Callback URL?",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Error: Missing Input",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Has Callback URL?": {
        "main": [
          [
            {
              "node": "Store Callback Info",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Prepare Config (Sync)",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Store Callback Info": {
        "main": [
          [
            {
              "node": "Prepare Config (Async)",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Prepare Config (Sync)": {
        "main": [
          [
            {
              "node": "Optimize Prompt? (Sync)",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Prepare Config (Async)": {
        "main": [
          [
            {
              "node": "Optimize Prompt? (Async)",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Optimize Prompt? (Sync)": {
        "main": [
          [
            {
              "node": "Optimize Prompt (Sync)",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Veo Video (Sync)",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Optimize Prompt? (Async)": {
        "main": [
          [
            {
              "node": "Respond Immediate",
              "type": "main",
              "index": 0
            },
            {
              "node": "Optimize Prompt (Async)",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Respond Immediate",
              "type": "main",
              "index": 0
            },
            {
              "node": "Veo Video (Async)",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Optimize Prompt (Sync)": {
        "main": [
          [
            {
              "node": "Format Optimize Response (Sync)",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Veo Video (Sync)": {
        "main": [
          [
            {
              "node": "Format Response (Sync)",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Format Optimize Response (Sync)": {
        "main": [
          [
            {
              "node": "Respond Success (Sync)",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Format Response (Sync)": {
        "main": [
          [
            {
              "node": "Respond Success (Sync)",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Respond Immediate": {
        "main": [
          []
        ]
      },
      "Optimize Prompt (Async)": {
        "main": [
          [
            {
              "node": "Format Optimize Response (Async)",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Prepare Error Callback",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Veo Video (Async)": {
        "main": [
          [
            {
              "node": "Format Response (Async)",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Prepare Error Callback",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Format Optimize Response (Async)": {
        "main": [
          [
            {
              "node": "Prepare Callback Payload",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Format Response (Async)": {
        "main": [
          [
            {
              "node": "Prepare Callback Payload",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Prepare Callback Payload": {
        "main": [
          [
            {
              "node": "Compute HMAC Signature",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Compute HMAC Signature": {
        "main": [
          [
            {
              "node": "Callback Success",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Prepare Error Callback": {
        "main": [
          [
            {
              "node": "Compute HMAC Error",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Compute HMAC Error": {
        "main": [
          [
            {
              "node": "Callback Error",
              "type": "main",
              "index": 0
            }
          ]
        ]
      }
    },
    "authors": "Sebbah fsebbah@azy.solutions",
    "name": null,
    "description": null,
    "autosaved": false,
    "workflowPublishHistory": []
  }
}