{
  "name": "UGC Automation v2",
  "nodes": [
    {
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "type": "n8n-nodes-base.telegramTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        800
      ],
      "id": "0a3028aa-bcc7-4ce7-be8b-cb53745cbad8",
      "name": "Telegram Trigger",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "c1a1",
              "name": "telegram_bot_token",
              "value": "={{ $env.TELEGRAM_BOT_TOKEN }}",
              "type": "string"
            },
            {
              "id": "c1a2",
              "name": "describe_model",
              "value": "gpt-4.1-mini",
              "type": "string"
            },
            {
              "id": "c1a3",
              "name": "director_model",
              "value": "gpt-5-mini",
              "type": "string"
            },
            {
              "id": "c1a4",
              "name": "image_endpoint",
              "value": "https://queue.fal.run/fal-ai/nano-banana-pro/edit",
              "type": "string"
            },
            {
              "id": "c1a5",
              "name": "video_endpoint",
              "value": "https://api.kie.ai/api/v1/veo/generate",
              "type": "string"
            },
            {
              "id": "c1a6",
              "name": "video_status_endpoint",
              "value": "https://api.kie.ai/api/v1/veo/record-info",
              "type": "string"
            },
            {
              "id": "c1a7",
              "name": "video_model_budget",
              "value": "veo3_lite",
              "type": "string"
            },
            {
              "id": "c1a8",
              "name": "video_model_standard",
              "value": "veo3_fast",
              "type": "string"
            },
            {
              "id": "c1a9",
              "name": "video_model_premium",
              "value": "veo3",
              "type": "string"
            },
            {
              "id": "c1a10",
              "name": "model_note",
              "value": "Verified against kie.ai Veo API docs (docs.kie.ai) on 2026-07-23: the model param only accepts veo3 (quality/premium), veo3_fast (default/standard), veo3_lite (budget). There is no veo3_1 or veo3_1_fast identifier in the current API -- the spec's assumed names were wrong and have been corrected here.",
              "type": "string"
            },
            {
              "id": "c1a11",
              "name": "merge_endpoint",
              "value": "https://queue.fal.run/fal-ai/ffmpeg-api/merge-videos",
              "type": "string"
            },
            {
              "id": "c1a12",
              "name": "poll_interval_sec",
              "value": 20,
              "type": "number"
            },
            {
              "id": "c1a13",
              "name": "max_poll_attempts",
              "value": 30,
              "type": "number"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        220,
        800
      ],
      "id": "141177de-d244-4b85-8ca4-dfa7710488a8",
      "name": "Config"
    },
    {
      "parameters": {
        "url": "=https://api.telegram.org/bot{{ $('Config').first().json.telegram_bot_token }}/getFile?file_id={{ $('Telegram Trigger').first().json.message.photo.last().file_id }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        440,
        800
      ],
      "id": "6657d021-a1d0-4a13-99be-23495276cbab",
      "name": "Get Img Path"
    },
    {
      "parameters": {
        "resource": "image",
        "operation": "analyze",
        "modelId": {
          "__rl": true,
          "value": "gpt-4.1-mini",
          "mode": "list",
          "cachedResultName": "gpt-4.1-mini"
        },
        "text": "# Describe Image \u2014 Extraction Prompt\n\nAnalyze the given image and determine whether it primarily depicts a **product**, a **character**, or **BOTH**.\n\n- If the image is of a **product**, return the analysis in YAML format with the following fields:\n\n```yaml\nbrand_name: # Name of the brand shown in the image, if visible or inferable\ncolor_scheme:\n  - hex: # Hex code of each prominent color used\n    name: # Descriptive name of the color\nfont_style: # Describe the font family or style used: serif/sans-serif, bold/thin, etc.\nvisual_description: # A full sentence or two summarizing what is seen in the image, ignoring the background\nvisible_text: # ALL text visible on packaging/labels, transcribed EXACTLY, character for character\n```\n\n- If the image is of a **character**, return the analysis in YAML format with the following fields:\n\n```yaml\ncharacter_name: # Name of the character if visible or inferable\ncolor_scheme:\n  - hex: # Hex code of each prominent color used on the character\n    name: # Descriptive name of the color\noutfit_style: # Description of clothing style, accessories, or notable features\nvisual_description: # A full sentence or two summarizing what the character looks like, ignoring the background\n```\n\n- If it is **BOTH**, return both blocks as guided above in YAML format.\n\n## Critical rule: text transcription\n\nAny text visible on packaging, labels, or the product itself must be transcribed **exactly** \u2014 character for character, including punctuation, casing, and line breaks \u2014 into the `visible_text` field. Downstream image generation depends on this field to reproduce the product's real text accurately. Never paraphrase, summarize, or correct it.\n\nOnly return the YAML. Do not explain or add any other comments.\n",
        "imageUrls": "=https://api.telegram.org/file/bot{{ $('Config').first().json.telegram_bot_token }}/{{ $json.result.file_path }}",
        "simplify": false,
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1.8,
      "position": [
        660,
        800
      ],
      "id": "f11a955f-6b6c-43e9-934e-ef09aa642ee2",
      "name": "Describe Image",
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "chatId": "={{ $('Telegram Trigger').first().json.message.chat.id }}",
        "text": "Got it! Writing your ad script...",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        880,
        800
      ],
      "id": "c3315feb-5ed4-451d-bc88-67f453b88f4a",
      "name": "Progress 1",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=Your task: Create the start-frame image prompt and every scene's video prompt as guided by your system guidelines.\n\nMake sure that the reference image is depicted as ACCURATELY as possible in the resulting prompts, especially all visible text.\n\n***\n\nThese are the user's instructions\n{{ $('Telegram Trigger').first().json.message.caption }}\n\n***\n\nDescription of the reference image:\n{{ $('Describe Image').first().json.choices[0].message.content }}\n\n***\n\nUse the Think tool to double check your output\n",
        "hasOutputParser": true,
        "options": {
          "systemMessage": "=# Creative Director \u2014 Combined Image + Video Prompt Generator\n\nYou are a UGC (User-Generated Content) creative director AI. Your task: take the reference image (and its\nextracted description) plus the user's request, and produce **one structured JSON object** containing the\nstart-frame image prompt and every scene's video prompt for an 8-seconds-per-scene UGC-style ad.\n\nMake sure the reference image/product is depicted as ACCURATELY as possible in the resulting prompts,\nespecially all visible text.\n\n## Output schema\n\nReturn exactly this JSON shape \u2014 nothing else, no commentary:\n\n```json\n{\n  \"image_prompt\": \"stringified YAML: action, character, product, setting, camera, style, text_accuracy\",\n  \"aspect_ratio_image\": \"2:3 or 3:2 (default 2:3 vertical)\",\n  \"tier\": \"budget | standard | premium\",\n  \"scenes\": [\n    {\n      \"video_prompt\": \"stringified YAML: dialogue, emotion, voice_type, action, character, setting, camera\",\n      \"aspect_ratio_video\": \"9:16 or 16:9 (default 9:16)\"\n    }\n  ]\n}\n```\n\nThe `scenes` array must contain **exactly** `scene_count` objects \u2014 never more, never fewer.\n\n## Style: UGC casual realism (default)\n\nUnless the user asks for a polished/professional ad style, all outputs must feel **natural, candid, and\nunpolished** \u2014 avoiding professional or overly staged looks. This means:\n\n- Everyday realism with authentic, relatable settings\n- Amateur-quality iPhone photo/video style\n- Slightly imperfect framing and lighting\n- Candid poses and genuine expressions\n- Visible imperfections (blemishes, messy hair, uneven skin, texture flaws)\n- Real-world environments left as-is (clutter, busy backgrounds)\n\nThe `camera` field in both `image_prompt` and every `video_prompt` must always use keywords like these:\nunremarkable amateur iPhone photo, reddit image, snapchat video, casual iPhone selfie, slightly uneven\nframing, authentic share, slightly blurry.\n\nIf the user explicitly asks for a polished/professional ad style instead (clean studio look), honor it for\nthe visual style. Style and tier are independent \u2014 see the tier logic below.\n\n## Tier logic\n\n- If the user's message contains \"premium\" or \"polished\" \u2192 `\"tier\": \"premium\"`.\n- If the user's message contains \"budget\" or \"cheap\" \u2192 `\"tier\": \"budget\"`.\n- Otherwise \u2192 `\"tier\": \"standard\"`.\n\n## Text and claims accuracy\n\nAlways preserve all visible product text **exactly** as given in the image description's `visible_text`\nfield (logos, slogans, packaging claims). Never invent extra claims or numbers. The `text_accuracy` field\nin `image_prompt` must state that visible text is preserved exactly as sourced.\n\n## Dialogue rules\n\n- Casual, conversational tone, under 150 characters per scene \u2014 like talking to a friend. Avoid\n  overly formal or sales-like language.\n- Use `...` to indicate pauses.\n- Never use em dashes, hyphens, or double quotes inside dialogue or any prompt field's text content (this does not apply to the JSON syntax itself, which must still use standard double-quoted keys and string values).\n- Dialogue must run **continuously** across scenes: each scene picks up exactly where the previous one\n  left off \u2014 no restarts, no repeated greetings.\n- The character talks about the product's actual benefits based on what kind of product it is: a drink \u2192\n  talk about the taste; a bag \u2192 talk about the design; tech \u2192 talk about its features; and so on.\n- If the character mentions the brand name, only do so in the **FIRST scene**.\n- Unless the user explicitly asks otherwise, the character only shows the product to camera \u2014 never\n  opens, eats, or uses it.\n\n## Scene count\n\n- Read the user's requested total video duration. Each scene is 8 seconds long.\n- `scene_count = ceil(total_requested_duration_seconds / 8)`.\n- If the user does not specify a duration, default to **3 scenes**.\n- Output exactly `scene_count` scene objects \u2014 never more, never fewer.\n\n## Character and diversity\n\n- Ensure diversity in gender, ethnicity, and hair when applicable.\n- Default actor age range: 21\u201338, unless the user specifies otherwise.\n- Avoid mentioning the name of any copyrighted characters in the prompt.\n\n## Aspect ratios\n\nInfer aspect ratio from the user's message (vertical/horizontal cues). Default to vertical if unspecified:\n`aspect_ratio_image` defaults to `2:3`, `aspect_ratio_video` defaults to `9:16`.\n\n## Continuity and shot variety across scenes\n\nThe generated start-frame image becomes scene 1's first frame only. Every video generation call restarts\nfrom that same original still frame, not from where the previous scene's clip ended. Because of this, do\nnot write scene prompts as if they are one continuous unbroken shot. Instead, treat each scene as a\ndeliberate new camera angle within the same UGC video, the way a real person editing a phone video would\ncut between takes.\n\nRules for each scene's camera and action:\n\n- Scene 1 must match `image_prompt`'s character, product, and setting exactly, since it inherits that\n  frame directly.\n- Every scene after scene 1 must use a visibly different framing or angle than the scene before it. Rotate\n  through: extreme close-up on face, mid-shot with product held up, slightly wider shot showing more of\n  the room, product close-up with face partially out of frame, over-the-shoulder or slight side angle.\n  Never repeat the same framing distance and angle back to back.\n- Each new scene's `action` field must describe the character already mid-motion or mid-expression in the\n  new angle, not starting from a neutral resting pose. This sells the cut as intentional rather than a\n  reset.\n- Keep wardrobe, hair, setting, and lighting direction consistent across all scenes even though the angle\n  changes, so it still reads as the same person in the same room, just filmed from a different spot.\n- Dialogue continues to flow as one uninterrupted line across scenes per the dialogue rules above. The cut\n  in footage should feel like a jump cut in editing, not a jump in the conversation.\n\n## Tools\n\nUse the Think tool to double-check your output before returning it: completeness, exact scene count,\ndialogue continuity, text accuracy, tier and aspect ratio inference, and adherence to the JSON schema\nabove.\n"
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2,
      "position": [
        1100,
        800
      ],
      "id": "bf06163f-d95f-4356-a81a-6c3ec710edaf",
      "name": "Creative Director"
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "gpt-5-mini",
          "mode": "list",
          "cachedResultName": "gpt-5-mini"
        },
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.2,
      "position": [
        1020,
        960
      ],
      "id": "9598b5b0-5502-4db7-8502-0aebdb29e42c",
      "name": "Creative Director Model",
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsonSchemaExample": "{\n  \"image_prompt\": \"stringified YAML: action, character, product, setting, camera, style, text_accuracy\",\n  \"aspect_ratio_image\": \"2:3\",\n  \"tier\": \"standard\",\n  \"scenes\": [\n    {\n      \"video_prompt\": \"stringified YAML: dialogue, emotion, voice_type, action, character, setting, camera\",\n      \"aspect_ratio_video\": \"9:16\"\n    }\n  ]\n}\n",
        "autoFix": true
      },
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "typeVersion": 1.3,
      "position": [
        1200,
        960
      ],
      "id": "e4d9437e-21cd-41cd-bf60-96780b230f51",
      "name": "Creative Director Output Parser"
    },
    {
      "parameters": {},
      "type": "@n8n/n8n-nodes-langchain.toolThink",
      "typeVersion": 1,
      "position": [
        1140,
        1080
      ],
      "id": "efc10adc-7682-4392-b7fe-db6a5bf5a023",
      "name": "Think"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $('Config').first().json.image_endpoint }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"prompt\": \"{{ $('Creative Director').first().json.output.image_prompt.replace(/\\\"/g, '\\\\\\\"').replace(/\\n/g, '\\\\n') }}\",\n  \"image_urls\": [\n    \"https://api.telegram.org/file/bot{{ $('Config').first().json.telegram_bot_token }}/{{ $('Get Img Path').first().json.result.file_path }}\"\n  ]\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1320,
        500
      ],
      "id": "e59eb2cb-67a3-464f-b2e6-f0524c1ee2cf",
      "name": "Create Image",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "amount": "={{ $('Config').first().json.poll_interval_sec }}"
      },
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        1540,
        500
      ],
      "id": "7caf7d63-c6b1-4319-9489-585d2b2b5bae",
      "name": "Wait Image"
    },
    {
      "parameters": {
        "url": "={{ $('Create Image').item.json.response_url }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1760,
        500
      ],
      "id": "4099149b-4852-4238-972f-6c7baee83ba2",
      "name": "Get Image",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "img-ready-cond-1",
              "leftValue": "={{ $json.images[0].url }}",
              "rightValue": 1,
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1980,
        500
      ],
      "id": "91a4c3e2-5262-4d8a-82f1-59e42e2c3079",
      "name": "If Image Ready"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "img-attempts-cond-1",
              "leftValue": "={{ $runIndex }}",
              "rightValue": "={{ $('Config').first().json.max_poll_attempts }}",
              "operator": {
                "type": "number",
                "operation": "lt"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        2200,
        620
      ],
      "id": "2ad61ba6-851e-4389-9fef-263ac876b336",
      "name": "If Image Attempts Left"
    },
    {
      "parameters": {
        "chatId": "={{ $('Telegram Trigger').first().json.message.chat.id }}",
        "text": "=Image generation failed or timed out. fal request: {{ $('Create Image').first().json.response_url }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        2420,
        720
      ],
      "id": "ac1cd2db-eeb2-4361-b9ea-5d986fc5f2b6",
      "name": "Fail Image Timeout",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "chatId": "={{ $('Telegram Trigger').first().json.message.chat.id }}",
        "text": "Start frame ready. Generating video clips (takes a few minutes)...",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        2200,
        380
      ],
      "id": "83a25ad0-1e47-40cd-a60b-e7785b5dfecf",
      "name": "Progress 2",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "prep-scenes-1",
              "name": "output",
              "value": "={{ $('Creative Director').first().json.output }}",
              "type": "object"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        2420,
        380
      ],
      "id": "b2e9b6a1-4e1c-4a3f-9e01-1d6c6f0e2a11",
      "name": "Prepare Scenes"
    },
    {
      "parameters": {
        "fieldToSplitOut": "output.scenes",
        "options": {}
      },
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        2640,
        380
      ],
      "id": "d668e35a-a986-4565-b6b3-f3425f53575e",
      "name": "Split Out"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $('Config').first().json.video_endpoint }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={\n  \"prompt\":\"{{ $('Split Out').item.json.video_prompt\n    .replace(/\\n/g, '\\\\n')\n    .replace(/\\\"/g, '\\\\\\\"') }}\",\n  \"model\": \"{{ $('Config').first().json['video_model_' + $('Creative Director').first().json.output.tier] }}\",\n  \"aspectRatio\": \"{{ $('Split Out').item.json.aspect_ratio_video }}\",\n  \"imageUrls\": \"{{ $('Get Image').first().json.images[0].url }}\"\n}\n",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1,
              "batchInterval": 3000
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2860,
        250
      ],
      "id": "e8ee19bc-d7bc-4115-b745-2baca1a07cbf",
      "name": "Create Video",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "amount": "={{ $('Config').first().json.poll_interval_sec }}"
      },
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        3080,
        250
      ],
      "id": "a45aab2d-3d9f-48bb-ab88-37d165cb6fb0",
      "name": "Wait Video"
    },
    {
      "parameters": {
        "url": "={{ $('Config').first().json.video_status_endpoint }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "taskId",
              "value": "={{ $('Create Video').item.json.data.taskId }}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3300,
        250
      ],
      "id": "d13ed3d8-2757-4b53-b75f-ec9e4c529b0b",
      "name": "Get Video",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "vid-success-cond-1",
              "leftValue": "={{ $json._allReady }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        3520,
        250
      ],
      "id": "c0f787cf-2294-4c62-8b30-2d13e78b8939",
      "name": "If Video Success"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "vid-failed-cond-1",
              "leftValue": "={{ $json._anyFailed }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        3740,
        130
      ],
      "id": "a23519ad-696b-4257-a476-0d7e26243727",
      "name": "If Video Failed"
    },
    {
      "parameters": {
        "chatId": "={{ $('Telegram Trigger').first().json.message.chat.id }}",
        "text": "=Video generation failed for task {{ $('Create Video').item.json.data.taskId }}. Status code: {{ $json.data.successFlag }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        3960,
        30
      ],
      "id": "f4730ce3-1e44-4224-814c-3c8883191bd0",
      "name": "Fail Video Failed",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "vid-attempts-cond-1",
              "leftValue": "={{ $runIndex }}",
              "rightValue": "={{ $('Config').first().json.max_poll_attempts }}",
              "operator": {
                "type": "number",
                "operation": "lt"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        3740,
        370
      ],
      "id": "1727bea6-f772-4220-8576-ea57dd811713",
      "name": "If Video Attempts Left"
    },
    {
      "parameters": {
        "chatId": "={{ $('Telegram Trigger').first().json.message.chat.id }}",
        "text": "=Video generation timed out for task {{ $('Create Video').item.json.data.taskId }}.",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        3960,
        470
      ],
      "id": "816599b8-2d6f-4886-b936-a4f0af3948e8",
      "name": "Fail Video Timeout",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "fieldsToAggregate": {
          "fieldToAggregate": [
            {
              "fieldToAggregate": "data.response.resultUrls[0]",
              "renameField": true,
              "outputFieldName": "resultUrls"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.aggregate",
      "typeVersion": 1,
      "position": [
        3520,
        550
      ],
      "id": "baabfcc3-3a80-4e17-b574-e52b173d5a2c",
      "name": "Aggregate"
    },
    {
      "parameters": {
        "chatId": "={{ $('Telegram Trigger').first().json.message.chat.id }}",
        "text": "Clips done. Stitching final ad...",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        3740,
        550
      ],
      "id": "2f249d6f-07db-4c7a-8685-7ea56c6b9d71",
      "name": "Progress 3",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $('Config').first().json.merge_endpoint }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"video_urls\": [{{ $('Aggregate').first().json.resultUrls.map(url => `\\\"${url}\\\"`) }}]\n}\n",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3960,
        550
      ],
      "id": "001b84bc-5a98-4aaa-ba43-9104db9b0ccc",
      "name": "Combine Clips",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "amount": "={{ $('Config').first().json.poll_interval_sec }}"
      },
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        4180,
        550
      ],
      "id": "bda78dea-ee81-43e0-8574-23d819aaf480",
      "name": "Wait Merge"
    },
    {
      "parameters": {
        "url": "={{ $('Combine Clips').first().json.status_url }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        4400,
        550
      ],
      "id": "557ae870-c162-4ecf-bee7-fbfe5af426e0",
      "name": "Get Final Video",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "merge-ready-cond-1",
              "leftValue": "={{ $json.status }}",
              "rightValue": "COMPLETED",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        4620,
        550
      ],
      "id": "623d0195-d810-4594-9839-c8b5a5cdf3a1",
      "name": "If Merge Ready"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "merge-attempts-cond-1",
              "leftValue": "={{ $runIndex }}",
              "rightValue": "={{ $('Config').first().json.max_poll_attempts }}",
              "operator": {
                "type": "number",
                "operation": "lt"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        4840,
        650
      ],
      "id": "85a76cd0-280b-49ac-b749-7e4f5ce7fdec",
      "name": "If Merge Attempts Left"
    },
    {
      "parameters": {
        "chatId": "={{ $('Telegram Trigger').first().json.message.chat.id }}",
        "text": "=Video merge failed or timed out. fal request: {{ $('Combine Clips').first().json.response_url }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        5060,
        750
      ],
      "id": "2684e3c2-72f6-44c4-a740-b2c3506cea40",
      "name": "Fail Merge Timeout",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "sendVideo",
        "chatId": "={{ $('Telegram Trigger').first().json.message.chat.id }}",
        "file": "={{ $json.video.url }}",
        "additionalFields": {}
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        4840,
        450
      ],
      "id": "b39a35b1-e021-49ee-9d73-8b847a3d7a62",
      "name": "Send Video",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "id": "b3f2a9e1-6d4c-4a2b-9f1e-2c7d8a5b6e10",
      "name": "Get Merge Result",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        4600,
        460
      ],
      "parameters": {
        "url": "={{ $('Combine Clips').first().json.response_url }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {}
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "id": "c7a1d4e2-9f3b-4e6a-8c2d-1b5f7a9e3d40",
      "name": "Check Video Batch Status",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3450,
        250
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const items = $input.all();\nconst anyFailed = items.some(item => item.json.data.successFlag === 2 || item.json.data.successFlag === 3);\nconst allReady = items.every(item => item.json.data.successFlag === 1 && item.json.data.response && item.json.data.response.resultUrls && item.json.data.response.resultUrls[0]);\nreturn items.map((item, i) => ({\n  json: { ...item.json, _anyFailed: anyFailed, _allReady: allReady },\n  pairedItem: item.pairedItem !== undefined ? item.pairedItem : i,\n}));"
      }
    }
  ],
  "connections": {
    "Telegram Trigger": {
      "main": [
        [
          {
            "node": "Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Config": {
      "main": [
        [
          {
            "node": "Get Img Path",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Img Path": {
      "main": [
        [
          {
            "node": "Describe Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Describe Image": {
      "main": [
        [
          {
            "node": "Progress 1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Progress 1": {
      "main": [
        [
          {
            "node": "Creative Director",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Creative Director": {
      "main": [
        [
          {
            "node": "Create Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Creative Director Model": {
      "ai_languageModel": [
        [
          {
            "node": "Creative Director",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Creative Director Output Parser",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Creative Director Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Creative Director",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Think": {
      "ai_tool": [
        [
          {
            "node": "Creative Director",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Create Image": {
      "main": [
        [
          {
            "node": "Wait Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait Image": {
      "main": [
        [
          {
            "node": "Get Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Image": {
      "main": [
        [
          {
            "node": "If Image Ready",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Image Ready": {
      "main": [
        [
          {
            "node": "Progress 2",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "If Image Attempts Left",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Image Attempts Left": {
      "main": [
        [
          {
            "node": "Wait Image",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Fail Image Timeout",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Progress 2": {
      "main": [
        [
          {
            "node": "Prepare Scenes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Scenes": {
      "main": [
        [
          {
            "node": "Split Out",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out": {
      "main": [
        [
          {
            "node": "Create Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Video": {
      "main": [
        [
          {
            "node": "Wait Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait Video": {
      "main": [
        [
          {
            "node": "Get Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Video": {
      "main": [
        [
          {
            "node": "Check Video Batch Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Video Success": {
      "main": [
        [
          {
            "node": "Aggregate",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "If Video Attempts Left",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Video Failed": {
      "main": [
        [
          {
            "node": "Fail Video Failed",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "If Video Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Video Attempts Left": {
      "main": [
        [
          {
            "node": "Wait Video",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Fail Video Timeout",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate": {
      "main": [
        [
          {
            "node": "Progress 3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Progress 3": {
      "main": [
        [
          {
            "node": "Combine Clips",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine Clips": {
      "main": [
        [
          {
            "node": "Wait Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait Merge": {
      "main": [
        [
          {
            "node": "Get Final Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Final Video": {
      "main": [
        [
          {
            "node": "If Merge Ready",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Merge Ready": {
      "main": [
        [
          {
            "node": "Get Merge Result",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "If Merge Attempts Left",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Merge Attempts Left": {
      "main": [
        [
          {
            "node": "Wait Merge",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Fail Merge Timeout",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Merge Result": {
      "main": [
        [
          {
            "node": "Send Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Video Batch Status": {
      "main": [
        [
          {
            "node": "If Video Failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate"
  },
  "versionId": "d5c9c3d0-1a2b-4c3d-9e4f-5a6b7c8d9e0f",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodeGroups": [],
  "id": "ugcAutomationV2Build001",
  "tags": []
}