{
  "id": "ESU60gYy8ZZPGGo6",
  "meta": {
    "instanceId": "0000000000000000000000000000000000000000000000000000000000000000",
    "templateCredsSetupCompleted": true
  },
  "name": "Generate text-to-video content with Telegram, OpenAI and KIE.ai",
  "nodes": [
    {
      "id": "e7bfb21b-965b-4614-b17a-f389b0b1be5c",
      "name": "Main",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4672,
        -2016
      ],
      "parameters": {
        "color": 2,
        "width": 400,
        "height": 632,
        "content": "## How it works\n\n1. User sends a text or image prompt to your Telegram bot\n2. OpenAI enhances the prompt and generates video metadata\n3. User selects an AI model (Veo 3.1, Sora 2, or Seedance)\n4. Video is generated via KIE.ai and stored in S3\n5. User receives the video with one-tap publish buttons\n6. Publish to YouTube Shorts, TikTok, or Instagram Reels\n\nSupports slash commands: /general, /lost, /3d, /story\n\n## Setup steps\n\n1. Create a Telegram bot via @BotFather\n2. Add OpenAI API credentials (OpenAI)\n3. Get KIE.ai API key from kie.ai\n4. Configure Redis for session storage\n5. Set up S3 bucket for video storage\n6. Add YouTube OAuth2 credentials\n7. Add Late.dev API key for TikTok/Instagram\n8. Update webhook URLs to your n8n instance\n9. Deploy the companion polling workflow"
      },
      "typeVersion": 1
    },
    {
      "id": "4ec4a93e-1f93-4266-9ef1-f8f69f2f8df3",
      "name": "Telegram Input",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4960,
        -1120
      ],
      "parameters": {
        "color": 7,
        "width": 196,
        "height": 96,
        "content": "**Telegram Trigger** — Receives messages and button callbacks, routes to handlers"
      },
      "typeVersion": 1
    },
    {
      "id": "f1e20d54-12ea-466d-aa43-66fa9c818479",
      "name": "Prompt Generation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4080,
        -2576
      ],
      "parameters": {
        "color": 6,
        "width": 2240,
        "height": 352,
        "content": "**Prompt Generation** — Extracts input, calls OpenAI, stores session in Redis"
      },
      "typeVersion": 1
    },
    {
      "id": "9998e5e0-4779-48b3-8843-934a74dd4b03",
      "name": "Video Generation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4080,
        -2112
      ],
      "parameters": {
        "color": 7,
        "width": 2688,
        "height": 208,
        "content": "**Video Generation** — Checks balance, starts KIE.ai job, triggers polling webhook"
      },
      "typeVersion": 1
    },
    {
      "id": "723f5a09-a1fb-4266-ba46-b22805f51380",
      "name": "YouTube Publishing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4080,
        -1744
      ],
      "parameters": {
        "color": 7,
        "width": 1936,
        "height": 208,
        "content": "**YouTube Shorts** — Downloads from S3, uploads to YouTube with metadata"
      },
      "typeVersion": 1
    },
    {
      "id": "e67cac4c-0238-4d47-9c56-68ffe28ad428",
      "name": "Social Publishing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4048,
        -320
      ],
      "parameters": {
        "color": 7,
        "width": 2000,
        "height": 592,
        "content": "**TikTok & Instagram** — Publishes via Late.dev API with auto-generated captions"
      },
      "typeVersion": 1
    },
    {
      "id": "db0aba18-f06a-4570-b299-c73e4aa78430",
      "name": "Video Extension",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2528,
        -1360
      ],
      "parameters": {
        "color": 7,
        "width": 1552,
        "height": 208,
        "content": "**Extend Video** — Adds 8 seconds using OpenAI continuation prompt (Veo only)"
      },
      "typeVersion": 1
    },
    {
      "id": "7907fc5f-68a6-4287-af9c-85bede1be4a8",
      "name": "Video Merge",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3888,
        -864
      ],
      "parameters": {
        "color": 7,
        "width": 224,
        "height": 80,
        "content": "**Auto-Merge** — Concatenates original + extended video via Transloadit"
      },
      "typeVersion": 1
    },
    {
      "id": "b6ef9103-09a4-4893-bc62-928801ff6c01",
      "name": "Telegram Trigger",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        -4704,
        -1120
      ],
      "webhookId": "video-bot-trigger",
      "parameters": {
        "updates": [
          "message",
          "callback_query"
        ],
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "632b0e0a-d6fe-4d0e-abb0-e5779d98cc23",
      "name": "Determine Route",
      "type": "n8n-nodes-base.code",
      "position": [
        -4480,
        -1120
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json;\n\nlet routeType = 'unknown';\nlet template = 'general';\nlet userPrompt = '';\n\nif (data.message) {\n  const text = data.message.text || data.message.caption || '';\n  userPrompt = text;\n  \n  // Check for slash commands\n  if (text.startsWith('/')) {\n    const parts = text.split(' ');\n    const command = parts[0].toLowerCase();\n    userPrompt = parts.slice(1).join(' ');\n    \n    const templateMap = {\n      '/lost': 'lost', '/abandoned': 'lost', '/found': 'lost',\n      '/general': 'general',\n      '/3d': '3d', '/talking': '3d', '/object': '3d', '/edu': '3d',\n      '/story': 'story', '/cat': 'story', '/character': 'story', '/tale': 'story',\n      '/help': 'help'\n    };\n    \n    if (templateMap[command]) {\n      if (command === '/help') {\n        routeType = 'help';\n      } else {\n        template = templateMap[command];\n        routeType = 'new_message';\n      }\n    } else {\n      routeType = 'new_message';\n    }\n  } else {\n    routeType = 'new_message';\n  }\n} else if (data.callback_query) {\n  const cbData = data.callback_query.data || '';\n  if (cbData.startsWith('model:')) {\n    routeType = 'generate_with_model';\n  } else if (cbData.startsWith('generate:')) {\n    routeType = 'generate';\n  } else if (cbData.startsWith('publish:')) {\n    routeType = 'publish';\n  } else if (cbData.startsWith('tiktok:')) {\n    routeType = 'tiktok';\n  } else if (cbData.startsWith('instagram:')) {\n    routeType = 'instagram';\n  } else if (cbData.startsWith('publishall:')) {\n    routeType = 'publishall';\n  } else if (cbData.startsWith('extend:')) {\n    routeType = 'extend';\n  } else if (cbData.startsWith('cancel:')) {\n    routeType = 'cancel';\n  } else if (cbData.startsWith('am:')) {\n    routeType = 'automerge';\n  } else if (cbData.startsWith('mm:')) {\n    routeType = 'manualmerge';\n  }\n}\n\nreturn {\n  json: {\n    ...data,\n    routeType,\n    template,\n    userPrompt\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "1111d077-c462-46fa-b3a7-a67cf4ea197a",
      "name": "Route Input",
      "type": "n8n-nodes-base.switch",
      "position": [
        -4256,
        -1264
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "NewMessage",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.routeType }}",
                    "rightValue": "new_message"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "GenerateWithModel",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.routeType }}",
                    "rightValue": "generate_with_model"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Publish",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.routeType }}",
                    "rightValue": "publish"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Cancel",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.routeType }}",
                    "rightValue": "cancel"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Help",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.routeType }}",
                    "rightValue": "help"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Extend",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.routeType }}",
                    "rightValue": "extend"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "TikTok",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.routeType }}",
                    "rightValue": "tiktok"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Instagram",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.routeType }}",
                    "rightValue": "instagram"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "PublishAll",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.routeType }}",
                    "rightValue": "publishall"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "AutoMerge",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.routeType }}",
                    "rightValue": "automerge"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "ManualMerge",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.routeType }}",
                    "rightValue": "manualmerge"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "fallbackOutput": "none"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "b08060df-93d9-4b29-8f5a-4c8920b682a3",
      "name": "Extract Input",
      "type": "n8n-nodes-base.code",
      "position": [
        -4048,
        -2464
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json;\nconst message = data.message;\nconst chatId = message.chat.id;\nconst messageId = message.message_id;\nconst sessionId = `${chatId}_${messageId}_${Date.now()}`;\n\n// Get template and userPrompt from Determine Route\nconst template = data.template || 'general';\nconst userPrompt = data.userPrompt || message.text || message.caption || '';\n\nlet textInput = userPrompt;\nlet photoFileId = null;\n\nif (message.photo && message.photo.length > 0) {\n  const photo = message.photo[message.photo.length - 1];\n  photoFileId = photo.file_id;\n}\n\nreturn {\n  json: {\n    sessionId,\n    chatId,\n    messageId,\n    textInput,\n    photoFileId,\n    hasPhoto: !!photoFileId,\n    template,\n    timestamp: new Date().toISOString()\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "3214d6ca-74e5-4a9a-9ff0-1a20da9b0992",
      "name": "Send Initial Status",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -3808,
        -2528
      ],
      "webhookId": "18c96219-00b5-4a15-9ca5-5245348f4db7",
      "parameters": {
        "text": "🎬 *Working on it!*\n\nPreparing your video prompt...",
        "chatId": "={{ $json.chatId }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "2199dcf2-2d5d-4f92-ad97-7614e7cfde01",
      "name": "Has Photo?",
      "type": "n8n-nodes-base.if",
      "position": [
        -3808,
        -2368
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.hasPhoto }}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "928e6c0c-d468-4d6f-88bb-49b3dca85c15",
      "name": "Get Photo URL",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -3600,
        -2528
      ],
      "webhookId": "a8a9b6d6-54b8-402e-82a6-099f30674f2d",
      "parameters": {
        "fileId": "={{ $json.photoFileId }}",
        "resource": "file"
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "7929e961-fbef-4134-a560-b5c4dfc27093",
      "name": "Build Photo URL",
      "type": "n8n-nodes-base.code",
      "position": [
        -3392,
        -2528
      ],
      "parameters": {
        "jsCode": "const prevData = $('Extract Input').first().json;\nconst fileInfo = $input.first().json;\n\n// SANITIZED: Replaced hardcoded token\nconst botToken = 'YOUR_TELEGRAM_BOT_TOKEN';\nconst photoUrl = `https://api.telegram.org/file/bot${botToken}/${fileInfo.file_path}`;\n\nreturn {\n  json: {\n    ...prevData,\n    photoUrl\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "925ddd69-bbaf-4c28-963a-ab9df95d2e04",
      "name": "No Photo",
      "type": "n8n-nodes-base.code",
      "position": [
        -3504,
        -2352
      ],
      "parameters": {
        "jsCode": "const prevData = $('Extract Input').first().json;\n\nreturn {\n  json: {\n    ...prevData,\n    photoUrl: null\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "7010b454-c5dd-40df-b4ff-488704d063d5",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        -3184,
        -2464
      ],
      "parameters": {},
      "typeVersion": 2
    },
    {
      "id": "23c0c1bb-3933-4185-9ba7-72892b39e00b",
      "name": "Send Prompt Status",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -2928,
        -2464
      ],
      "webhookId": "96a6cf0c-6c32-46ab-9dc5-a3b3ea1c9040",
      "parameters": {
        "text": "✨ *Got it!*\n\nCrafting your video prompt and preparing everything. This usually takes a few seconds...",
        "chatId": "={{ $json.chatId }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "34e628d0-0e3d-41a3-b072-f33ab70fd21d",
      "name": "Generate Prompt & Metadata",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2688,
        -2464
      ],
      "parameters": {
        "url": "https://api.openai.com/v1/chat/completions",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ (() => {\n  const data = $json;\n  const hasPhoto = !!data.photoUrl;\n\n  // ===============================\n  // ✅ TEMPLATE SELECTION (FIXED)\n  // ===============================\n  // Priority:\n  // 1) Use template from previous node\n  // 2) Fallback to slash command\n  // 3) Default to general\n\nconst DetermineNode = $('Determine Route').first().json;\n\n  let template = (DetermineNode.template || '').toLowerCase();\n\n  if (!template) {\n    const rawText = (data.textInput || '').toLowerCase();\n    if (rawText.startsWith('/story')) template = 'story';\n    else if (rawText.startsWith('/lost')) template = 'lost';\n    else if (rawText.startsWith('/3d')) template = '3d';\n    else template = 'general';\n  }\n\n  // ===============================\n  // 🔹 CLEAN USER INPUT\n  // ===============================\n  const cleanTextInput = (DetermineNode.userPrompt || data.textInput || '')\n    .replace(/^\\/story\\s*/i, '')\n    .replace(/^\\/lost\\s*/i, '')\n    .replace(/^\\/3d\\s*/i, '')\n    .trim();\n\n  // ===============================\n  // TEMPLATE SYSTEM PROMPTS\n  // ===============================\n  const templates = {\n\n    lost: {\n      name: 'Lost & Abandoned',\n      systemPrompt: `\nYou are a specialist in viral LOST OBJECT / FOUND FOOTAGE AI videos.\n\nYour goal is to generate a LONG, DETAILED, hyper-realistic discovery video\nthat feels like someone explored an abandoned place and accidentally found\nsomething disturbing or mysterious.\n\nMANDATORY RULES:\n- If no user details are given, invent everything.\n- If partial details are given, expand them fully.\n- NEVER explain what the object is or why it exists.\n- NEVER include celebrities, brands, or real people.\n- The mystery must remain unresolved.\n\nVISUAL STYLE:\n- Smartphone flashlight footage\n- Dark, dusty basements, cellars, attics, or ruined houses\n- Thick dust, cobwebs, cracked walls, peeling paint\n- Objects look untouched for years\n- Strong flashlight contrast with darkness\n- Heavy atmosphere and silence\n\nCAMERA:\n- Handheld POV with subtle shake\n- Slow cautious walking\n- Flashlight beam reveals details gradually\n- Focus hunting in low light\n- Lingering close-ups on unsettling details\n\nAUDIO:\n- Footsteps\n- Breathing\n- Distant creaks or drips\n- NO music, narration, or dialogue\n\nFORMAT:\n- Vertical 9:16\n- 12–15 seconds\n- One continuous shot (max 2 cuts)\n\nOUTPUT REQUIREMENTS:\n- videoPrompt must be VERY detailed (scene, camera, lighting, texture)\n- title must be curiosity-driven\n- description should hint mystery without explaining\n- tags must be relevant\n- shortSummary must be 1 strong sentence\n\nOUTPUT:\nReturn ONLY JSON with:\nvideoPrompt, title, description, tags, shortSummary\n`\n    },\n\n    story: {\n      name: 'Cat Story',\n      systemPrompt: `\nYou are a master storyteller creating LONG, EMOTIONAL short-form STORY videos.\nALL stories MUST center on a CAT as the main character.\n\nMANDATORY RULES:\n- If no details are provided, invent a full original cat story.\n- If partial details are provided, expand them fully.\n- NO celebrities, NO real people, NO pop culture.\n- NO dialogue required.\n\nSTORY STRUCTURE:\n- 4–6 clearly described scenes\n- Emotional arc:\n  struggle → sacrifice → conflict → compassion → resolution\n- Emotions shown through body language and environment\n- Story must feel complete within 12–15 seconds\n\nSCENE DESIGN:\n- Poor or modest home\n- Action scene (store, street, market, shelter)\n- Conflict (caught, judged, confronted)\n- Resolution (forgiveness, help, warmth)\n\nVISUAL STYLE:\n- Realistic cinematic visuals\n- Anthropomorphic animals with subtle clothing\n- Emotional facial expressions\n- Natural lighting\n- Smooth transitions\n\nAUDIO:\n- Ambient only\n- Optional soft emotional tone\n- NO narration\n\nOUTPUT REQUIREMENTS:\n- videoPrompt must describe EACH scene in detail\n- title must be emotional\n- description must deepen empathy\n- tags must reflect story theme\n- shortSummary must be impactful\n\nOUTPUT:\nReturn ONLY JSON with:\nvideoPrompt, title, description, tags, shortSummary\n`\n    },\n\n    '3d': {\n      name: '3D Explainer',\n      systemPrompt: `\nYou create VIRAL 3D TALKING OBJECT explainer videos.\n\nMANDATORY RULES:\n- The object MUST speak.\n- If no object is specified, invent one.\n- If an object is specified, expand it fully.\n\nCHARACTER:\n- 3D anthropomorphic fruit, vegetable, or body part\n- Big expressive eyes\n- Visible mouth with synced speech\n- Friendly, educational personality\n\nCONTENT:\n- Immediate hook\n- Clear explanation of benefits or function\n- One memorable takeaway\n\nVISUAL STYLE:\n- Clean Pixar-style 3D\n- Bright kitchen, lab, or educational space\n- Soft lighting\n- Shallow depth of field\n\nAUDIO:\n- Clear friendly voice\n- Optional light background music\n\nFORMAT:\n- Vertical 9:16\n- 12–15 seconds\n\nOUTPUT REQUIREMENTS:\n- videoPrompt MUST include dialogue in quotes\n- Rich detail in visuals and animation\n- title, description, tags, shortSummary required\n\nOUTPUT:\nReturn ONLY JSON with:\nvideoPrompt, title, description, tags, shortSummary\n`\n    },\n\n    general: {\n      name: 'General Creative',\n      systemPrompt: `\nYou create HIGH-QUALITY creative short-form videos.\n\nRULES:\n- Invent a concept if none provided\n- Expand any idea fully\n- Use celebrities ONLY if explicitly requested\n\nFORMAT:\n- Vertical 9:16\n- 12–15 seconds\n\nOUTPUT REQUIREMENTS:\n- videoPrompt must be detailed and visual\n- title, description, tags, shortSummary required\n\nOUTPUT:\nReturn ONLY JSON with:\nvideoPrompt, title, description, tags, shortSummary\n`\n    }\n  };\n\n  const selectedTemplate = templates[template] || templates.general;\n\n  // ===============================\n  // USER CONTENT\n  // ===============================\n  let userContent;\n\n  if (hasPhoto) {\n    userContent = [\n      {\n        type: \"text\",\n        text:\n          \"User idea: \" +\n          (cleanTextInput || \"Invent a complete concept.\") +\n          \". Generate a long, highly detailed video prompt in the \" +\n          selectedTemplate.name +\n          \" style.\"\n      },\n      {\n        type: \"image_url\",\n        image_url: { url: data.photoUrl }\n      }\n    ];\n  } else {\n    userContent =\n      \"User idea: \" +\n      (cleanTextInput || \"Invent a complete concept.\") +\n      \". Generate a long, highly detailed video prompt in the \" +\n      selectedTemplate.name +\n      \" style.\";\n  }\n\n  return JSON.stringify({\n    model: \"gpt-4o\",\n    messages: [\n      { role: \"system\", content: selectedTemplate.systemPrompt },\n      { role: \"user\", content: userContent }\n    ],\n    response_format: { type: \"json_object\" }\n  });\n})() }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "Zci0FoiGA77tU9ss",
          "name": "OpenAI - Header Auth"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "4af0464b-242b-4b74-81c6-12c055a4e972",
      "name": "Format Session",
      "type": "n8n-nodes-base.code",
      "position": [
        -2464,
        -2464
      ],
      "parameters": {
        "jsCode": "const inputData = $('Merge').first().json;\nconst openaiResponse = $input.first().json;\n\nlet promptData;\ntry {\n  promptData = JSON.parse(openaiResponse.choices[0].message.content);\n} catch (e) {\n  throw new Error('Failed to parse OpenAI response: ' + e.message);\n}\n\nconst sessionData = {\n  sessionId: inputData.sessionId,\n  chatId: inputData.chatId,\n  messageId: inputData.messageId,\n  template: inputData.template || 'general',\n  originalInput: {\n    text: inputData.textInput,\n    photoUrl: inputData.photoUrl\n  },\n  generatedPrompt: promptData.videoPrompt,\n  youtubeMetadata: {\n    title: promptData.title,\n    description: promptData.description,\n    tags: promptData.tags || []\n  },\n  shortSummary: promptData.shortSummary,\n  status: 'prompt_ready',\n  createdAt: inputData.timestamp,\n  updatedAt: new Date().toISOString()\n};\n\nreturn { json: sessionData };"
      },
      "typeVersion": 2
    },
    {
      "id": "418f5b83-b9d1-429e-addf-600ebb67d360",
      "name": "Store Session",
      "type": "n8n-nodes-base.redis",
      "position": [
        -2240,
        -2464
      ],
      "parameters": {
        "key": "=session:{{ $json.sessionId }}",
        "ttl": 86400,
        "value": "={{ JSON.stringify($json) }}",
        "expire": true,
        "operation": "set"
      },
      "credentials": {
        "redis": {
          "id": "pytuc2MEjhEpHMDU",
          "name": "Apello Version"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "f613992f-e549-441a-9342-0900c89a4ad4",
      "name": "Send Prompt for Approval",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -2032,
        -2464
      ],
      "webhookId": "ce524b68-d8bf-4969-a044-d5efdd8314eb",
      "parameters": {
        "text": "=**Video Prompt Ready** ({{ $('Format Session').first().json.template || 'general' }} template)\n\n**Your idea:** {{ $('Format Session').first().json.originalInput.text || 'Based on your image' }}\n\n---\n\n**Generated Prompt:**\n{{ $('Format Session').first().json.generatedPrompt }}\n\n---\n\n**YouTube Metadata:**\n**Title:** {{ $('Format Session').first().json.youtubeMetadata.title }}\n\n**Tags:** {{ $('Format Session').first().json.youtubeMetadata.tags.slice(0, 5).join(', ') }}\n\n---\n\n**Select Video Model:**",
        "chatId": "={{ $('Format Session').first().json.chatId }}",
        "replyMarkup": "inlineKeyboard",
        "inlineKeyboard": {
          "rows": [
            {
              "row": {
                "buttons": [
                  {
                    "text": "Veo Fast (60cr)",
                    "additionalFields": {
                      "callback_data": "=model:veo3_fast:{{ $('Format Session').first().json.sessionId }}"
                    }
                  },
                  {
                    "text": "Veo Quality (250cr)",
                    "additionalFields": {
                      "callback_data": "=model:veo3:{{ $('Format Session').first().json.sessionId }}"
                    }
                  }
                ]
              }
            },
            {
              "row": {
                "buttons": [
                  {
                    "text": "Sora 2 (30cr)",
                    "additionalFields": {
                      "callback_data": "=model:sora2:{{ $('Format Session').first().json.sessionId }}"
                    }
                  },
                  {
                    "text": "Seedance (84cr)",
                    "additionalFields": {
                      "callback_data": "=model:seedance:{{ $('Format Session').first().json.sessionId }}"
                    }
                  }
                ]
              }
            },
            {
              "row": {
                "buttons": [
                  {
                    "text": "Cancel",
                    "additionalFields": {
                      "callback_data": "=cancel:{{ $('Format Session').first().json.sessionId }}"
                    }
                  }
                ]
              }
            }
          ]
        },
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "dddf8b39-c30f-4627-b1a1-fa024de1234a",
      "name": "Parse Model Selection",
      "type": "n8n-nodes-base.code",
      "position": [
        -4048,
        -2064
      ],
      "parameters": {
        "jsCode": "const callbackQuery = $input.first().json.callback_query;\nconst callbackData = callbackQuery.data;\n\n// Parse model:modelName:sessionId format\nconst parts = callbackData.split(':');\nconst selectedModel = parts[1];\nconst sessionId = parts[2];\n\n// Model configurations\nconst modelConfig = {\n  'veo3_fast': { type: 'veo', credits: 60, name: 'Veo 3.1 Fast' },\n  'veo3': { type: 'veo', credits: 250, name: 'Veo 3.1 Quality' },\n  'sora2': { type: 'market', credits: 30, name: 'Sora 2' },\n  'seedance': { type: 'market', credits: 84, name: 'Seedance 1.5 Pro' }\n};\n\nconst config = modelConfig[selectedModel] || modelConfig['veo3_fast'];\n\nreturn {\n  json: {\n    sessionId,\n    selectedModel,\n    modelType: config.type,\n    modelName: config.name,\n    estimatedCredits: config.credits,\n    chatId: callbackQuery.message.chat.id,\n    messageId: callbackQuery.message.message_id,\n    callbackQueryId: callbackQuery.id,\n    action: 'generate_with_model'\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "e730ff10-6fa5-41c0-aa91-f69e636c2d20",
      "name": "Answer Generate",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -3840,
        -2064
      ],
      "webhookId": "51593b7d-9485-4bc1-b36b-f93703618d1b",
      "parameters": {
        "queryId": "={{ $json.callbackQueryId }}",
        "resource": "callback",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "01f01642-e908-47de-af10-6e74fcef6443",
      "name": "Get Session",
      "type": "n8n-nodes-base.redis",
      "position": [
        -3632,
        -2064
      ],
      "parameters": {
        "key": "=session:{{ $('Parse Model Selection').first().json.sessionId }}",
        "options": {},
        "operation": "get"
      },
      "credentials": {
        "redis": {
          "id": "pytuc2MEjhEpHMDU",
          "name": "Apello Version"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "f14b4710-d9af-4e04-944d-d5c4554d70c2",
      "name": "Parse Session",
      "type": "n8n-nodes-base.code",
      "position": [
        -3424,
        -2064
      ],
      "parameters": {
        "jsCode": "const redisResult = $input.first().json;\n// Redis returns data in propertyName field\nconst redisValue = redisResult.propertyName || redisResult;\nconst session = typeof redisValue === 'string' ? JSON.parse(redisValue) : redisValue;\nconst modelData = $('Parse Model Selection').first().json;\n\nreturn {\n  json: {\n    ...session,\n    selectedModel: modelData.selectedModel,\n    modelType: modelData.modelType,\n    modelName: modelData.modelName,\n    estimatedCredits: modelData.estimatedCredits,\n    callbackChatId: modelData.chatId,\n    callbackMessageId: modelData.messageId,\n    status: 'generating',\n    updatedAt: new Date().toISOString()\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "5576f367-d30e-453a-9563-174d297b2528",
      "name": "Start Video Generation",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2464,
        -2080
      ],
      "parameters": {
        "url": "={{ (() => {\n  const model = $input.first().json.selectedModel || 'veo3_fast';\n  const endpoints = {\n    'veo3_fast': 'https://api.kie.ai/api/v1/veo/generate',\n    'veo3': 'https://api.kie.ai/api/v1/veo/generate',\n    'sora2': 'https://api.kie.ai/api/v1/jobs/createTask',\n    'seedance': 'https://api.kie.ai/api/v1/jobs/createTask'\n  };\n  return endpoints[model] || endpoints['veo3_fast'];\n})() }}",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ (() => {\n  const data = $input.first().json;\n  const model = data.selectedModel || 'veo3_fast';\n  const prompt = data.generatedPrompt;\n  \n  // Model-specific body configurations\n  const bodies = {\n    'veo3_fast': {\n      prompt: prompt,\n      model: 'veo3_fast',\n      generationType: 'TEXT_2_VIDEO',\n      aspectRatio: '9:16',\n      enableTranslation: true\n    },\n    'veo3': {\n      prompt: prompt,\n      model: 'veo3',\n      generationType: 'TEXT_2_VIDEO',\n      aspectRatio: '9:16',\n      enableTranslation: true\n    },\n    'sora2': {\n      model: 'sora-2-text-to-video',\n      input: {\n        prompt: prompt,\n        aspect_ratio: 'portrait'\n      }\n    },\n    'seedance': {\n      model: 'bytedance/seedance-1.5-pro',\n      input: {\n        prompt: JSON.stringify(prompt),\n        aspect_ratio: '9:16',\n        resolution: '720p',\n        duration: '12',\n        fixed_lens: false,\n        generate_audio: true\n      }\n    }\n  };\n  \n  return JSON.stringify(bodies[model] || bodies['veo3_fast']);\n})() }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "XGMcHSm9f7phTQ8X",
          "name": "KieAI - Header Auth"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "965bde5a-1f6b-4a3c-b967-8628099c725f",
      "name": "Add Task ID",
      "type": "n8n-nodes-base.code",
      "position": [
        -2256,
        -2080
      ],
      "parameters": {
        "jsCode": "// Session data flows through: Parse Session → Check Balance → Validate Balance → Balance OK? → Start Video Generation\nconst sessionData = $('Validate Balance').first().json;\nconst kieResponse = $input.first().json;\n\nif (kieResponse.code !== 200) {\n  throw new Error('KIE.ai error: ' + kieResponse.msg);\n}\n\n// Status endpoints based on model type\nconst statusEndpoints = {\n  'veo': '/api/v1/veo/record-info',\n  'market': '/api/v1/jobs/recordInfo'\n};\n\nconst modelType = sessionData.modelType || 'veo';\nconst statusEndpoint = statusEndpoints[modelType] || statusEndpoints['veo'];\n\nreturn {\n  json: {\n    ...sessionData,\n    taskId: kieResponse.data.taskId,\n    statusEndpoint: statusEndpoint,\n    balanceAtGeneration: sessionData.balance,\n    pollAttempt: 0,\n    updatedAt: new Date().toISOString()\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "a994acc5-2da0-4db3-889e-fea83bda5e1e",
      "name": "Store Generating Session",
      "type": "n8n-nodes-base.redis",
      "position": [
        -2048,
        -2080
      ],
      "parameters": {
        "key": "=session:{{ $json.sessionId }}",
        "ttl": 86400,
        "value": "={{ JSON.stringify($json) }}",
        "expire": true,
        "operation": "set"
      },
      "credentials": {
        "redis": {
          "id": "pytuc2MEjhEpHMDU",
          "name": "Apello Version"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "be35fc57-d409-4759-8bd8-a89611fcda5e",
      "name": "Send Generating Status",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -1808,
        -2080
      ],
      "webhookId": "39ab8983-8a86-4be1-a31c-c137a3fa61ad",
      "parameters": {
        "text": "=**Video Generation Started**\n\n**Model:** {{ $json.modelName || 'Veo 3.1' }}\n**Task ID:** {{ $json.taskId }}\n\nGenerating your video... This typically takes 2-5 minutes.\n\n_Checking status every minute..._",
        "chatId": "={{ $('Validate Balance').first().json.callbackChatId }}",
        "messageId": "={{ $('Validate Balance').first().json.callbackMessageId }}",
        "operation": "editMessageText",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "b3c1a6d4-a3cf-49e7-8af5-e6379b609219",
      "name": "Trigger Polling",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1568,
        -2080
      ],
      "parameters": {
        "url": "https://your-n8n-instance.com/webhook/video-poll",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify({\n  sessionId: $('Add Task ID').first().json.sessionId,\n  taskId: $('Add Task ID').first().json.taskId,\n  chatId: $('Add Task ID').first().json.chatId,\n  messageId: $('Validate Balance').first().json.callbackMessageId,\n  selectedModel: $('Add Task ID').first().json.selectedModel,\n  modelType: $('Add Task ID').first().json.modelType,\n  statusEndpoint: $('Add Task ID').first().json.statusEndpoint,\n  attempt: 1\n}) }}",
        "sendBody": true,
        "specifyBody": "json"
      },
      "typeVersion": 4.2
    },
    {
      "id": "10429dc2-ed34-480c-b715-37a22c5df33e",
      "name": "Parse Publish",
      "type": "n8n-nodes-base.code",
      "position": [
        -4048,
        -1696
      ],
      "parameters": {
        "jsCode": "const callbackQuery = $input.first().json.callback_query;\nconst callbackData = callbackQuery.data;\nconst sessionId = callbackData.split(':')[1];\n\nreturn {\n  json: {\n    sessionId,\n    chatId: callbackQuery.message.chat.id,\n    messageId: callbackQuery.message.message_id,\n    callbackQueryId: callbackQuery.id,\n    action: 'publish'\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "c7566b25-bbdb-4d41-aa7d-1054a3ba4713",
      "name": "Answer Publish",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -3840,
        -1696
      ],
      "webhookId": "3968dbab-3772-4e71-b2bf-66dcba7b1b90",
      "parameters": {
        "queryId": "={{ $json.callbackQueryId }}",
        "resource": "callback",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "4c98ffb4-b7ff-4be4-91be-c8fdcfebc20f",
      "name": "Get Session (Publish)",
      "type": "n8n-nodes-base.redis",
      "position": [
        -3632,
        -1696
      ],
      "parameters": {
        "key": "=session:{{ $('Parse Publish').first().json.sessionId }}",
        "options": {},
        "operation": "get"
      },
      "credentials": {
        "redis": {
          "id": "pytuc2MEjhEpHMDU",
          "name": "Apello Version"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ededbb21-81e7-4bf0-9a4b-6db1dd429c73",
      "name": "Parse Session (Publish)",
      "type": "n8n-nodes-base.code",
      "position": [
        -3408,
        -1696
      ],
      "parameters": {
        "jsCode": "const redisResult = $input.first().json;\n// Redis returns data in propertyName field\nconst redisValue = redisResult.propertyName || redisResult;\nconst session = typeof redisValue === 'string' ? JSON.parse(redisValue) : redisValue;\nconst callbackData = $('Parse Publish').first().json;\n\nreturn {\n  json: {\n    ...session,\n    callbackChatId: callbackData.chatId,\n    callbackMessageId: callbackData.messageId\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "d29460e7-55d4-4192-83fb-bc141ffc00d5",
      "name": "Download Video",
      "type": "n8n-nodes-base.s3",
      "position": [
        -3200,
        -1696
      ],
      "parameters": {
        "fileKey": "=videos/{{ $json.sessionId }}.mp4",
        "bucketName": "shorts"
      },
      "credentials": {
        "s3": {
          "id": "2g8pNTKJVmd3Qz0i",
          "name": "S3 account Apello"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "e42050ba-efc9-40c0-a0bd-30d22dd3da06",
      "name": "Upload to YouTube",
      "type": "n8n-nodes-base.youTube",
      "position": [
        -2976,
        -1696
      ],
      "parameters": {
        "title": "={{ $('Parse Session (Publish)').first().json.youtubeMetadata.title }}",
        "options": {
          "tags": "={{ $('Parse Session (Publish)').first().json.youtubeMetadata.tags.join(',') }}",
          "description": "={{ $('Parse Session (Publish)').first().json.youtubeMetadata.tags.join(',') }}\n\n---------\n\n{{ $('Parse Session (Publish)').first().json.youtubeMetadata.description }}",
          "privacyStatus": "public",
          "defaultLanguage": "en",
          "notifySubscribers": true,
          "selfDeclaredMadeForKids": false
        },
        "resource": "video",
        "operation": "upload",
        "categoryId": "23",
        "regionCode": "US"
      },
      "credentials": {
        "youTubeOAuth2Api": {
          "id": "scs6dQ0JVuM1kMwb",
          "name": "YouTube account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "1b8d6d1f-762a-4b54-b376-db4043ce35e9",
      "name": "Format Results",
      "type": "n8n-nodes-base.code",
      "position": [
        -2768,
        -1696
      ],
      "parameters": {
        "jsCode": "const session = $('Parse Session (Publish)').first().json;\nconst youtubeResult = $input.first().json;\n\nconst videoId = youtubeResult.uploadId;\nconst youtubeUrl = 'https://youtube.com/shorts/' + videoId;\n\nreturn {\n  json: {\n    ...session,\n    status: 'published',\n    youtubeVideoId: videoId,\n    youtubeUrl: youtubeUrl,\n    updatedAt: new Date().toISOString()\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "cf2fbefd-7944-417c-85f7-b0605dad6b17",
      "name": "Update Session",
      "type": "n8n-nodes-base.redis",
      "position": [
        -2560,
        -1696
      ],
      "parameters": {
        "key": "=session:{{ $json.sessionId }}",
        "ttl": 604800,
        "value": "={{ JSON.stringify($json) }}",
        "expire": true,
        "operation": "set"
      },
      "credentials": {
        "redis": {
          "id": "pytuc2MEjhEpHMDU",
          "name": "Apello Version"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "9c19010b-75ad-4c89-b84e-3b14727cf103",
      "name": "Send Success",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -2336,
        -1696
      ],
      "webhookId": "5eb8d657-514e-4e55-8ea8-9ad6576839e3",
      "parameters": {
        "text": "=**Published to YouTube!**\n\n**Title:** {{ $json.youtubeMetadata.title }}\n\n**Watch now:** {{ $json.youtubeUrl }}",
        "chatId": "={{ $json.callbackChatId }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "e2821666-88ee-4f21-9113-aa617b9cef28",
      "name": "Parse Cancel",
      "type": "n8n-nodes-base.code",
      "position": [
        -4048,
        -1456
      ],
      "parameters": {
        "jsCode": "const callbackQuery = $input.first().json.callback_query;\nconst callbackData = callbackQuery.data;\nconst sessionId = callbackData.split(':')[1];\n\nreturn {\n  json: {\n    sessionId,\n    chatId: callbackQuery.message.chat.id,\n    messageId: callbackQuery.message.message_id,\n    callbackQueryId: callbackQuery.id,\n    action: 'cancel'\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "deaf12b1-83e6-4d75-9750-96fba3dd41a1",
      "name": "Answer Cancel",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -3840,
        -1456
      ],
      "webhookId": "fe0470bc-03ee-4769-9204-4f16755dec1c",
      "parameters": {
        "queryId": "={{ $json.callbackQueryId }}",
        "resource": "callback",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "bb77e879-37d7-4fb2-a7f9-ee09d07a095a",
      "name": "Delete Session",
      "type": "n8n-nodes-base.redis",
      "position": [
        -3632,
        -1456
      ],
      "parameters": {
        "key": "=session:{{ $('Parse Cancel').first().json.sessionId }}",
        "operation": "delete"
      },
      "credentials": {
        "redis": {
          "id": "pytuc2MEjhEpHMDU",
          "name": "Apello Version"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "fcf3c8a7-221c-4cc8-98b0-f05dd35e8973",
      "name": "Send Cancel",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -3408,
        -1456
      ],
      "webhookId": "c67d9d34-fb1f-4c10-86fc-0462997a6d9e",
      "parameters": {
        "text": "**Cancelled**\n\nSend a new message to start again.",
        "chatId": "={{ $('Parse Cancel').first().json.chatId }}",
        "messageId": "={{ $('Parse Cancel').first().json.messageId }}",
        "operation": "editMessageText",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "f5d372e9-595e-44b4-b812-fdac5ba64a14",
      "name": "Check Balance",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -3216,
        -2064
      ],
      "parameters": {
        "url": "https://api.kie.ai/api/v1/chat/credit",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "XGMcHSm9f7phTQ8X",
          "name": "KieAI - Header Auth"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "ab75f008-1671-429f-85b4-3bbcfdb1d338",
      "name": "Validate Balance",
      "type": "n8n-nodes-base.code",
      "position": [
        -2992,
        -2064
      ],
      "parameters": {
        "jsCode": "const balanceResponse = $input.first().json;\nconst sessionData = $('Parse Session').first().json;\n\nconst balance = balanceResponse.data || 0;\nconst requiredCredits = sessionData.estimatedCredits || 60;\nconst hasEnough = balance >= requiredCredits;\n\nreturn {\n  json: {\n    ...sessionData,\n    balance,\n    requiredCredits,\n    hasEnough,\n    shortfall: hasEnough ? 0 : requiredCredits - balance\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "ac66ca1b-8546-4711-8da7-fc10013b08bd",
      "name": "Balance OK?",
      "type": "n8n-nodes-base.if",
      "position": [
        -2800,
        -2064
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.hasEnough }}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "10ac5081-636f-4cc8-bc97-88bd1fa314e2",
      "name": "Send Insufficient Balance",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -2608,
        -1952
      ],
      "webhookId": "fa1da905-6389-493a-98d1-2f11da9b6544",
      "parameters": {
        "text": "=**Insufficient Credits**\n\nYour balance: {{ $json.balance }} credits\nRequired: {{ $json.requiredCredits }} credits\nShortfall: {{ $json.shortfall }} credits\n\nPlease top up your KIE.ai account to continue.",
        "chatId": "={{ $json.callbackChatId }}",
        "messageId": "={{ $json.callbackMessageId }}",
        "operation": "editMessageText",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "2341b6a5-2d5e-4a4f-800b-7eb4498f3db8",
      "name": "Send Help",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -4048,
        -1296
      ],
      "webhookId": "a7ef3f86-1ed0-4534-84a7-0554e12ca256",
      "parameters": {
        "text": "**Video Generator Commands**\n\nUse a command + your idea:\n\n**Styles:**\n/general - Creative, versatile (default)\n/lost - Found footage, abandoned places\n/3d - 3D talking objects (educational)\n/story - Character story across scenes\n\n**Examples:**\n`/general a dog surfing on ocean waves`\n`/lost a rusty car in foggy forest`\n`/3d a tomato explaining its vitamins`\n`/story a cat's journey from kitten to adult`\n\nOr send your idea without a command for general style.",
        "chatId": "={{ $json.message.chat.id }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "498119ab-337e-495e-949f-76ee564f4a9c",
      "name": "Parse Extend",
      "type": "n8n-nodes-base.code",
      "position": [
        -4048,
        -1120
      ],
      "parameters": {
        "jsCode": "const callbackQuery = $input.first().json.callback_query;\nconst callbackData = callbackQuery.data;\nconst sessionId = callbackData.split(':')[1];\n\nreturn {\n  json: {\n    sessionId,\n    chatId: callbackQuery.message.chat.id,\n    messageId: callbackQuery.message.message_id,\n    callbackQueryId: callbackQuery.id,\n    action: 'extend'\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "8f13c7d9-0e22-4cc9-aa53-aab3f28eb538",
      "name": "Answer Extend",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -3808,
        -1120
      ],
      "webhookId": "answer-extend-webhook",
      "parameters": {
        "queryId": "={{ $json.callbackQueryId }}",
        "resource": "callback",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "b46cbb74-4dab-48af-9b40-9d5a8e46107f",
      "name": "Get Session (Extend)",
      "type": "n8n-nodes-base.redis",
      "position": [
        -3584,
        -1120
      ],
      "parameters": {
        "key": "=session:{{ $('Parse Extend').first().json.sessionId }}",
        "options": {},
        "operation": "get"
      },
      "credentials": {
        "redis": {
          "id": "pytuc2MEjhEpHMDU",
          "name": "Apello Version"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "d30142ab-b838-42f1-9c8b-e2b09d22c417",
      "name": "Parse Session (Extend)",
      "type": "n8n-nodes-base.code",
      "position": [
        -3360,
        -1120
      ],
      "parameters": {
        "jsCode": "const redisResult = $input.first().json;\nconst redisValue = redisResult.propertyName || redisResult;\nconst session = typeof redisValue === 'string' ? JSON.parse(redisValue) : redisValue;\nconst callbackData = $('Parse Extend').first().json;\n\n// Check if session has a taskId from video generation\nif (!session.taskId) {\n  throw new Error('Cannot extend: No original video task found in session');\n}\n\n// Check if this is a Veo model (only Veo supports extend)\nconst isVeoModel = session.selectedModel === 'veo3' || session.selectedModel === 'veo3_fast';\nif (!isVeoModel) {\n  throw new Error('Extend is only available for Veo 3 videos');\n}\n\nreturn {\n  json: {\n    ...session,\n    callbackChatId: callbackData.chatId,\n    callbackMessageId: callbackData.messageId,\n    originalTaskId: session.taskId,\n    extendCredits: 60,\n    action: 'extend'\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "f9215165-3357-41b9-a96e-8d9a17d6d23f",
      "name": "Check Extend Balance",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -3136,
        -1120
      ],
      "parameters": {
        "url": "https://api.kie.ai/api/v1/chat/credit",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "XGMcHSm9f7phTQ8X",
          "name": "KieAI - Header Auth"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "cae903eb-0a1b-42d4-b85f-bcb602a8b3da",
      "name": "Validate Extend Balance",
      "type": "n8n-nodes-base.code",
      "position": [
        -2912,
        -1120
      ],
      "parameters": {
        "jsCode": "const balanceResponse = $input.first().json;\nconst sessionData = $('Parse Session (Extend)').first().json;\n\nconst balance = balanceResponse.data || 0;\nconst requiredCredits = sessionData.extendCredits || 60;\nconst hasEnough = balance >= requiredCredits;\n\nreturn {\n  json: {\n    ...sessionData,\n    balance,\n    requiredCredits,\n    hasEnough,\n    shortfall: hasEnough ? 0 : requiredCredits - balance\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "b29b45f0-1151-4540-8b75-35e146d88598",
      "name": "Extend Balance OK?",
      "type": "n8n-nodes-base.if",
      "position": [
        -2688,
        -1120
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.hasEnough }}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "9159f626-1d48-4e07-a64e-a165cc83ea6a",
      "name": "Send Insufficient Extend Balance",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -2464,
        -1104
      ],
      "webhookId": "06da22ee-5a5c-453f-babc-0031cf73f9c7",
      "parameters": {
        "text": "=**Insufficient Credits for Extend**\n\nYour balance: {{ $json.balance }} credits\nRequired: {{ $json.requiredCredits }} credits\nShortfall: {{ $json.shortfall }} credits\n\nPlease top up your KIE.ai account to extend videos.",
        "chatId": "={{ $json.callbackChatId }}",
        "messageId": "={{ $json.callbackMessageId }}",
        "operation": "editMessageText",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "f046e01d-4cc8-47b2-80ac-1658d76689d2",
      "name": "Generate Extend Prompt",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2464,
        -1312
      ],
      "parameters": {
        "url": "https://api.openai.com/v1/chat/completions",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify({\n  model: 'gpt-4o-mini',\n  messages: [\n    {\n      role: 'system',\n      content: 'You are an expert at creating video extension prompts. Given an original video prompt, create a CONTINUATION prompt that describes what should happen NEXT in the video. The continuation should:\\n\\n1. Naturally flow from the end of the original video\\n2. Maintain the same visual style, mood, and atmosphere\\n3. Describe specific actions, movements, or scene progressions\\n4. Be 2-3 sentences maximum\\n5. Use present tense and active descriptions\\n\\nExamples of good continuation prompts:\\n- \"The camera slowly pulls back revealing more of the abandoned warehouse, dust particles floating in the flashlight beam as footsteps echo deeper into the darkness\"\\n- \"The character turns to face the camera with a knowing smile, the golden hour light catching their silhouette as they begin to walk toward the horizon\"\\n- \"The 3D tomato bounces excitedly and spins around, gesturing with its tiny arms while continuing to explain its nutritional benefits\"\\n\\nReturn ONLY the continuation prompt text, nothing else.'\n    },\n    {\n      role: 'user',\n      content: 'Original video prompt: ' + $json.generatedPrompt + '\\n\\nCreate a continuation prompt for extending this video by 8 more seconds.'\n    }\n  ],\n  max_tokens: 200\n}) }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "Zci0FoiGA77tU9ss",
          "name": "OpenAI - Header Auth"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "b89f704b-66fd-4164-9ecf-c4d17096cede",
      "name": "Parse Extend Prompt",
      "type": "n8n-nodes-base.code",
      "position": [
        -2240,
        -1312
      ],
      "parameters": {
        "jsCode": "const sessionData = $('Validate Extend Balance').first().json;\nconst openaiResponse = $input.first().json;\n\n// Extract the continuation prompt from OpenAI response\nconst extendPrompt = openaiResponse.choices[0].message.content.trim();\n\nreturn {\n  json: {\n    ...sessionData,\n    extendPrompt: extendPrompt\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "7f19f697-0570-478c-a09b-3e1ff1cafadc",
      "name": "Start Extend Generation",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2016,
        -1312
      ],
      "parameters": {
        "url": "https://api.kie.ai/api/v1/veo/extend",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify({\n  taskId: $json.originalTaskId,\n  prompt: $json.extendPrompt\n}) }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "XGMcHSm9f7phTQ8X",
          "name": "KieAI - Header Auth"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "d7e4bd08-d45c-49d7-b03f-1b871df153e8",
      "name": "Add Extend Task ID",
      "type": "n8n-nodes-base.code",
      "position": [
        -1792,
        -1312
      ],
      "parameters": {
        "jsCode": "const sessionData = $('Parse Extend Prompt').first().json;\nconst kieResponse = $input.first().json;\n\nif (kieResponse.code !== 200) {\n  throw new Error('KIE.ai extend error: ' + kieResponse.msg);\n}\n\n// Create a new session ID for the extended video\nconst extendSessionId = sessionData.sessionId + '_ext_' + Date.now();\n\nreturn {\n  json: {\n    ...sessionData,\n    sessionId: extendSessionId,\n    parentSessionId: sessionData.sessionId,\n    taskId: kieResponse.data.taskId,\n    statusEndpoint: '/api/v1/veo/record-info',\n    modelType: 'veo',\n    selectedModel: sessionData.selectedModel,\n    modelName: 'Veo 3.1 Extend',\n    status: 'extending',\n    isExtend: true,\n    extendPrompt: sessionData.extendPrompt,\n    balanceAtGeneration: sessionData.balance,\n    pollAttempt: 0,\n    updatedAt: new Date().toISOString()\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "a7b0309f-1638-468b-8c75-c6ea9aa00174",
      "name": "Store Extending Session",
      "type": "n8n-nodes-base.redis",
      "position": [
        -1584,
        -1312
      ],
      "parameters": {
        "key": "=session:{{ $json.sessionId }}",
        "ttl": 86400,
        "value": "={{ JSON.stringify($json) }}",
        "expire": true,
        "operation": "set"
      },
      "credentials": {
        "redis": {
          "id": "pytuc2MEjhEpHMDU",
          "name": "Apello Version"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "518d4f97-8c72-4577-932e-43fe6e10c686",
      "name": "Send Extending Status",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -1376,
        -1312
      ],
      "webhookId": "send-extending-status-webhook",
      "parameters": {
        "text": "=**Extending Video**\n\n**Original Task:** {{ $('Validate Extend Balance').first().json.originalTaskId }}\n**New Task ID:** {{ $json.taskId }}\n\nExtending your video by ~8 seconds... This typically takes 2-5 minutes.\n\n_Checking status every minute..._",
        "chatId": "={{ $('Validate Extend Balance').first().json.callbackChatId }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "fe0a4a6f-f55f-4fbf-9939-6400b65f095c",
      "name": "Trigger Extend Polling",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1168,
        -1312
      ],
      "parameters": {
        "url": "https://your-n8n-instance.com/webhook/video-poll",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify({\n  sessionId: $('Add Extend Task ID').first().json.sessionId,\n  taskId: $('Add Extend Task ID').first().json.taskId,\n  chatId: $('Add Extend Task ID').first().json.chatId,\n  messageId: $('Validate Extend Balance').first().json.callbackMessageId,\n  selectedModel: $('Add Extend Task ID').first().json.selectedModel,\n  modelType: 'veo',\n  statusEndpoint: '/api/v1/veo/record-info',\n  attempt: 1,\n  isExtend: true,\n  parentSessionId: $('Add Extend Task ID').first().json.parentSessionId\n}) }}",
        "sendBody": true,
        "specifyBody": "json"
      },
      "typeVersion": 4.2
    },
    {
      "id": "d32fad11-c093-4b7e-9b73-7b3afcf0c2ac",
      "name": "Parse TikTok",
      "type": "n8n-nodes-base.code",
      "position": [
        -4032,
        -272
      ],
      "parameters": {
        "jsCode": "const callbackQuery = $input.first().json.callback_query;\nconst callbackData = callbackQuery.data;\nconst sessionId = callbackData.split(':')[1];\n\nreturn {\n  json: {\n    sessionId,\n    platform: 'tiktok',\n    chatId: callbackQuery.message.chat.id,\n    messageId: callbackQuery.message.message_id,\n    callbackQueryId: callbackQuery.id\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "d7acf57e-eaea-4bce-8e35-618bee73a43e",
      "name": "Answer TikTok",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -3808,
        -272
      ],
      "webhookId": "3ac07212-acb4-401a-84ae-00da42d589e2",
      "parameters": {
        "queryId": "={{ $json.callbackQueryId }}",
        "resource": "callback",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "c00b4f9f-8ca7-447a-a424-fec01cb53d64",
      "name": "Send TikTok Status",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -3584,
        -272
      ],
      "webhookId": "62c5244f-a30e-4609-9605-618ee7818db2",
      "parameters": {
        "text": "⏳ *Publishing to TikTok...*\n\nHang tight! We're preparing your video for TikTok. You'll be notified once it's live.",
        "chatId": "={{ $('Parse TikTok').first().json.chatId }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "666848d6-3a4b-49c6-a90f-b389080714ed",
      "name": "Get Session (TikTok)",
      "type": "n8n-nodes-base.redis",
      "position": [
        -3360,
        -272
      ],
      "parameters": {
        "key": "=session:{{ $('Parse TikTok').first().json.sessionId }}",
        "options": {},
        "operation": "get"
      },
      "credentials": {
        "redis": {
          "id": "pytuc2MEjhEpHMDU",
          "name": "Apello Version"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ddcf04ee-172c-44d0-8b7f-5d979750bf78",
      "name": "Publish to TikTok",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2464,
        -272
      ],
      "parameters": {
        "url": "https://getlate.dev/api/v1/posts",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify({ content: $json.content, mediaItems: [{ url: $json.videoUrl, type: 'video' }], platforms: [{ platform: 'tiktok', accountId: 'YOUR_TIKTOK_ACCOUNT_ID' }], tiktokSettings: { privacy_level: 'PUBLIC_TO_EVERYONE', allow_comment: true, allow_duet: true, allow_stitch: true, content_preview_confirmed: true, express_consent_given: true }, publishNow: true }) }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "wwUfkWcXQXlzYpIs",
          "name": "Late API"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "855fcb1b-cd97-4ce7-bdc3-dd91af3048dc",
      "name": "Prepare KIE Request (TikTok)",
      "type": "n8n-nodes-base.code",
      "position": [
        -3136,
        -272
      ],
      "parameters": {
        "jsCode": "// Parse session from Redis\nconst redisData = $input.first().json;\nconst sessionStr = redisData.propertyName;\nconst session = typeof sessionStr === 'string' ? JSON.parse(sessionStr) : sessionStr;\n\n// Use the status endpoint from session (same as KIE Webhook)\nconst statusEndpoint = session.statusEndpoint || '/api/v1/veo/record-info';\nconst apiUrl = 'https://api.kie.ai' + statusEndpoint;\nconst modelType = session.modelType || 'veo';\n\nreturn {\n  json: {\n    session,\n    apiUrl,\n    taskId: session.taskId,\n    modelType\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "901a2f7c-3b8e-4418-a925-6d1a7d96c0d3",
      "name": "Fetch Video URL (TikTok)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2912,
        -272
      ],
      "parameters": {
        "url": "={{ $json.apiUrl }}",
        "options": {},
        "sendQuery": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "queryParameters": {
          "parameters": [
            {
              "name": "taskId",
              "value": "={{ $json.taskId }}"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "XGMcHSm9f7phTQ8X",
          "name": "KieAI - Header Auth"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "336d283f-a212-4d69-ac6d-9713e04f42c1",
      "name": "Prepare Publish (TikTok)",
      "type": "n8n-nodes-base.code",
      "position": [
        -2688,
        -272
      ],
      "parameters": {
        "jsCode": "// Get session from earlier node\nconst prepData = $('Prepare KIE Request (TikTok)').first().json;\nconst session = prepData.session;\nconst modelType = prepData.modelType;\n\n// Get video URL from KIE API response\nconst kieResponse = $input.first().json;\nlet videoUrl = null;\n\nif (modelType === 'veo') {\n  const originUrls = kieResponse.data?.response?.originUrls || [];\n  const resultUrls = kieResponse.data?.response?.resultUrls || [];\n  videoUrl = originUrls[0] || resultUrls[0];\n} else {\n  // Market API (Sora2, Seedance)\n  videoUrl = kieResponse.data?.videoUrl || kieResponse.data?.output?.video;\n  if (!videoUrl && kieResponse.data?.resultJson) {\n    try {\n      const resultJson = JSON.parse(kieResponse.data.resultJson);\n      videoUrl = resultJson.resultUrls?.[0] || resultJson.videoUrl || resultJson.video_url;\n    } catch (e) {}\n  }\n}\n\n// Generate human-like caption with hashtags\nconst title = session.youtubeMetadata?.title || '';\nconst summary = session.shortSummary || '';\nconst tags = session.youtubeMetadata?.tags || [];\n\n// Create casual captions\nconst captions = [\n  `${summary} 👀`,\n  `This one hits different 🔥 ${summary}`,\n  `Had to share this with you all ✨`,\n  `POV: ${summary}`,\n  `Wait for it... ${summary}`,\n  `${summary} 💫`\n];\nconst caption = captions[Math.floor(Math.random() * captions.length)];\n\n// Convert tags to hashtags\nconst hashtags = tags.slice(0, 5).map(tag => '#' + tag.replace(/\\s+/g, '')).join(' ');\nconst commonHashtags = '#fyp #viral #foryou #trending';\n\nconst content = `${caption}\\n\\n${hashtags} ${commonHashtags}`;\n\nreturn {\n  json: {\n    content: content,\n    videoUrl: videoUrl,\n    session: session\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "dc2710b7-8fef-4862-8b91-ac44cdbd54c2",
      "name": "Send TikTok Success",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -2240,
        -272
      ],
      "webhookId": "3fe27e61-cf2d-436e-8bc1-05e98f99dd65",
      "parameters": {
        "text": "=**Published to TikTok!**\n\nYour video has been published to TikTok.\n\n_Note: It may take a few minutes to appear on your profile._",
        "chatId": "={{ $('Parse TikTok').first().json.chatId }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "cd514af2-7505-4bce-aa7d-9059ab989ebc",
      "name": "Parse Instagram",
      "type": "n8n-nodes-base.code",
      "position": [
        -4032,
        -80
      ],
      "parameters": {
        "jsCode": "const callbackQuery = $input.first().json.callback_query;\nconst callbackData = callbackQuery.data;\nconst sessionId = callbackData.split(':')[1];\n\nreturn {\n  json: {\n    sessionId,\n    platform: 'instagram',\n    chatId: callbackQuery.message.chat.id,\n    messageId: callbackQuery.message.message_id,\n    callbackQueryId: callbackQuery.id\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "392a4dfb-c7a1-4f64-9d6e-e8b36fa7e1bb",
      "name": "Answer Instagram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -3808,
        -80
      ],
      "webhookId": "aca141c5-b3af-4b04-a4d2-27c4c4e92255",
      "parameters": {
        "queryId": "={{ $json.callbackQueryId }}",
        "resource": "callback",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "a49e2b14-88b9-4524-92f6-a50ccc6056e2",
      "name": "Send Instagram Status",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -3584,
        -80
      ],
      "webhookId": "74c1fcfe-4a9b-4da2-8f94-6a3a8b42e379",
      "parameters": {
        "text": "⏳ *Publishing to Instagram...*\n\nWorking on your Reel! We'll let you know when it's posted.",
        "chatId": "={{ $('Parse Instagram').first().json.chatId }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "ca50e3a9-9c84-458d-8e17-17bcc9faa42c",
      "name": "Get Session (Instagram)",
      "type": "n8n-nodes-base.redis",
      "position": [
        -3360,
        -80
      ],
      "parameters": {
        "key": "=session:{{ $('Parse Instagram').first().json.sessionId }}",
        "options": {},
        "operation": "get"
      },
      "credentials": {
        "redis": {
          "id": "pytuc2MEjhEpHMDU",
          "name": "Apello Version"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "7fb93d2b-25e4-4fa3-a6df-0af17c68242e",
      "name": "Publish to Instagram",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2464,
        -80
      ],
      "parameters": {
        "url": "https://getlate.dev/api/v1/posts",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify({ content: $json.content, mediaItems: [{ url: $json.videoUrl, type: 'video' }], platforms: [{ platform: 'instagram', accountId: 'YOUR_INSTAGRAM_ACCOUNT_ID' }], publishNow: true }) }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "wwUfkWcXQXlzYpIs",
          "name": "Late API"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "8e92b041-dbee-4e60-8aa8-cbfab8fdd2ef",
      "name": "Prepare KIE Request (Instagram)",
      "type": "n8n-nodes-base.code",
      "position": [
        -3136,
        -80
      ],
      "parameters": {
        "jsCode": "// Parse session from Redis\nconst redisData = $input.first().json;\nconst sessionStr = redisData.propertyName;\nconst session = typeof sessionStr === 'string' ? JSON.parse(sessionStr) : sessionStr;\n\n// Use the status endpoint from session (same as KIE Webhook)\nconst statusEndpoint = session.statusEndpoint || '/api/v1/veo/record-info';\nconst apiUrl = 'https://api.kie.ai' + statusEndpoint;\nconst modelType = session.modelType || 'veo';\n\nreturn {\n  json: {\n    session,\n    apiUrl,\n    taskId: session.taskId,\n    modelType\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "27b8f324-897d-49e6-9473-42e37f94b758",
      "name": "Fetch Video URL (Instagram)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2912,
        -80
      ],
      "parameters": {
        "url": "={{ $json.apiUrl }}",
        "options": {},
        "sendQuery": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "queryParameters": {
          "parameters": [
            {
              "name": "taskId",
              "value": "={{ $json.taskId }}"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "XGMcHSm9f7phTQ8X",
          "name": "KieAI - Header Auth"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "f0677ed4-f962-437d-96fe-8e50c7ddfbbc",
      "name": "Prepare Publish (Instagram)",
      "type": "n8n-nodes-base.code",
      "position": [
        -2688,
        -80
      ],
      "parameters": {
        "jsCode": "// Get session from earlier node\nconst prepData = $('Prepare KIE Request (Instagram)').first().json;\nconst session = prepData.session;\nconst modelType = prepData.modelType;\n\n// Get video URL from KIE API response\nconst kieResponse = $input.first().json;\nlet videoUrl = null;\n\nif (modelType === 'veo') {\n  const originUrls = kieResponse.data?.response?.originUrls || [];\n  const resultUrls = kieResponse.data?.response?.resultUrls || [];\n  videoUrl = originUrls[0] || resultUrls[0];\n} else {\n  // Market API (Sora2, Seedance)\n  videoUrl = kieResponse.data?.videoUrl || kieResponse.data?.output?.video;\n  if (!videoUrl && kieResponse.data?.resultJson) {\n    try {\n      const resultJson = JSON.parse(kieResponse.data.resultJson);\n      videoUrl = resultJson.resultUrls?.[0] || resultJson.videoUrl || resultJson.video_url;\n    } catch (e) {}\n  }\n}\n\n// Generate human-like caption with hashtags for Instagram\nconst title = session.youtubeMetadata?.title || '';\nconst summary = session.shortSummary || '';\nconst description = session.youtubeMetadata?.description || '';\nconst tags = session.youtubeMetadata?.tags || [];\n\n// Create casual Instagram captions\nconst captions = [\n  `${summary} ✨\\n\\nWhat do you think? Drop a comment below! 👇`,\n  `Just made this and had to share 🎬\\n\\n${summary}`,\n  `${summary} 🔥\\n\\nSave this for later! 📌`,\n  `This took forever but it was worth it 💫\\n\\n${summary}`,\n  `New drop alert 🚨\\n\\n${summary}`\n];\nconst caption = captions[Math.floor(Math.random() * captions.length)];\n\n// Convert tags to hashtags\nconst hashtags = tags.slice(0, 8).map(tag => '#' + tag.replace(/\\s+/g, '')).join(' ');\nconst commonHashtags = '#reels #reelsinstagram #explorepage #viral';\n\nconst content = `${caption}\\n\\n${hashtags} ${commonHashtags}`;\n\nreturn {\n  json: {\n    content: content,\n    videoUrl: videoUrl,\n    session: session\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "9bc231d2-48bf-4c44-978c-a8c0e6096c93",
      "name": "Send Instagram Success",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -2240,
        -80
      ],
      "webhookId": "552d9edc-44fb-4b6d-a88d-581891a06b59",
      "parameters": {
        "text": "=**Published to Instagram Reels!**\n\nYour video has been published to Instagram.\n\n_Note: It may take a few minutes to appear on your profile._",
        "chatId": "={{ $('Parse Instagram').first().json.chatId }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "7a4e692c-8dd9-4b4f-ac3b-b3b87714190a",
      "name": "Parse PublishAll",
      "type": "n8n-nodes-base.code",
      "position": [
        -4032,
        112
      ],
      "parameters": {
        "jsCode": "const callbackQuery = $input.first().json.callback_query;\nconst callbackData = callbackQuery.data;\nconst sessionId = callbackData.split(':')[1];\n\nreturn {\n  json: {\n    sessionId,\n    platform: 'all',\n    chatId: callbackQuery.message.chat.id,\n    messageId: callbackQuery.message.message_id,\n    callbackQueryId: callbackQuery.id\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "959edf38-8ef6-4961-95d0-c5d3b29f5de5",
      "name": "Answer PublishAll",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -3808,
        112
      ],
      "webhookId": "3634c6d9-3227-4201-8bf7-fd9efe2738ef",
      "parameters": {
        "queryId": "={{ $json.callbackQueryId }}",
        "resource": "callback",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "3f9883d0-5a57-4a89-8ce2-31ad9735d756",
      "name": "Send PublishAll Status",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -3584,
        112
      ],
      "webhookId": "57fb989d-b7db-4f27-8395-3c2079d21cce",
      "parameters": {
        "text": "⏳ *Publishing to All Platforms...*\n\nGetting your video ready for TikTok and Instagram. Sit back, we'll notify you when everything's live!",
        "chatId": "={{ $('Parse PublishAll').first().json.chatId }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "66b79127-e78b-4875-bfff-c1f5a4426773",
      "name": "Get Session (PublishAll)",
      "type": "n8n-nodes-base.redis",
      "position": [
        -3360,
        112
      ],
      "parameters": {
        "key": "=session:{{ $('Parse PublishAll').first().json.sessionId }}",
        "options": {},
        "operation": "get"
      },
      "credentials": {
        "redis": {
          "id": "pytuc2MEjhEpHMDU",
          "name": "Apello Version"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "fd99fb4b-caf2-435f-bbc3-a9a617aa40ab",
      "name": "Publish to All Platforms",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2464,
        112
      ],
      "parameters": {
        "url": "https://getlate.dev/api/v1/posts",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify({ content: $json.content, mediaItems: [{ url: $json.videoUrl, type: 'video' }], platforms: [{ platform: 'tiktok', accountId: 'YOUR_TIKTOK_ACCOUNT_ID' }, { platform: 'instagram', accountId: 'YOUR_INSTAGRAM_ACCOUNT_ID' }], tiktokSettings: { privacy_level: 'PUBLIC_TO_EVERYONE', allow_comment: true, allow_duet: true, allow_stitch: true, content_preview_confirmed: true, express_consent_given: true }, publishNow: true }) }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "wwUfkWcXQXlzYpIs",
          "name": "Late API"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "5058d989-0e72-47cd-8e1b-b12a7e01a692",
      "name": "Prepare KIE Request (PublishAll)",
      "type": "n8n-nodes-base.code",
      "position": [
        -3136,
        112
      ],
      "parameters": {
        "jsCode": "// Parse session from Redis\nconst redisData = $input.first().json;\nconst sessionStr = redisData.propertyName;\nconst session = typeof sessionStr === 'string' ? JSON.parse(sessionStr) : sessionStr;\n\n// Use the status endpoint from session (same as KIE Webhook)\nconst statusEndpoint = session.statusEndpoint || '/api/v1/veo/record-info';\nconst apiUrl = 'https://api.kie.ai' + statusEndpoint;\nconst modelType = session.modelType || 'veo';\n\nreturn {\n  json: {\n    session,\n    apiUrl,\n    taskId: session.taskId,\n    modelType\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "652264ec-20ad-41ce-a1bd-9f3570e86b34",
      "name": "Fetch Video URL (PublishAll)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2912,
        112
      ],
      "parameters": {
        "url": "={{ $json.apiUrl }}",
        "options": {},
        "sendQuery": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "queryParameters": {
          "parameters": [
            {
              "name": "taskId",
              "value": "={{ $json.taskId }}"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "XGMcHSm9f7phTQ8X",
          "name": "KieAI - Header Auth"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "fe22a7b2-8d31-4dc2-ba29-b8c0bbb3b0c2",
      "name": "Prepare Publish (PublishAll)",
      "type": "n8n-nodes-base.code",
      "position": [
        -2688,
        112
      ],
      "parameters": {
        "jsCode": "// Get session from earlier node\nconst prepData = $('Prepare KIE Request (PublishAll)').first().json;\nconst session = prepData.session;\nconst modelType = prepData.modelType;\n\n// Get video URL from KIE API response\nconst kieResponse = $input.first().json;\nlet videoUrl = null;\n\nif (modelType === 'veo') {\n  const originUrls = kieResponse.data?.response?.originUrls || [];\n  const resultUrls = kieResponse.data?.response?.resultUrls || [];\n  videoUrl = originUrls[0] || resultUrls[0];\n} else {\n  // Market API (Sora2, Seedance)\n  videoUrl = kieResponse.data?.videoUrl || kieResponse.data?.output?.video;\n  if (!videoUrl && kieResponse.data?.resultJson) {\n    try {\n      const resultJson = JSON.parse(kieResponse.data.resultJson);\n      videoUrl = resultJson.resultUrls?.[0] || resultJson.videoUrl || resultJson.video_url;\n    } catch (e) {}\n  }\n}\n\n// Generate human-like caption with hashtags (works for both TikTok and IG)\nconst title = session.youtubeMetadata?.title || '';\nconst summary = session.shortSummary || '';\nconst tags = session.youtubeMetadata?.tags || [];\n\n// Create casual captions that work on both platforms\nconst captions = [\n  `${summary} 🎬✨`,\n  `Just dropped this 🔥 ${summary}`,\n  `You guys asked for more content like this 👀`,\n  `POV: ${summary} 💫`,\n  `This one's for you ❤️ ${summary}`\n];\nconst caption = captions[Math.floor(Math.random() * captions.length)];\n\n// Convert tags to hashtags\nconst hashtags = tags.slice(0, 5).map(tag => '#' + tag.replace(/\\s+/g, '')).join(' ');\nconst commonHashtags = '#fyp #viral #reels #trending #foryou';\n\nconst content = `${caption}\\n\\n${hashtags} ${commonHashtags}`;\n\nreturn {\n  json: {\n    content: content,\n    videoUrl: videoUrl,\n    session: session\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "640bfa01-b90e-4429-87c2-66ff6d3b1ac7",
      "name": "Send PublishAll Success",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -2240,
        112
      ],
      "webhookId": "f4df3b6a-0ff8-476f-a7b5-11d6d357e8b8",
      "parameters": {
        "text": "=**Published to All Platforms!**\n\nYour video has been published to:\n- TikTok\n- Instagram Reels\n\n_Note: It may take a few minutes to appear on your profiles._",
        "chatId": "={{ $('Parse PublishAll').first().json.chatId }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "9558dc7c-1597-49d5-86ef-f22aa828fd31",
      "name": "Parse AutoMerge",
      "type": "n8n-nodes-base.code",
      "position": [
        -4016,
        -800
      ],
      "parameters": {
        "jsCode": "const callbackQuery = $input.first().json.callback_query;\nconst callbackData = callbackQuery.data;\n// Format: am:sessionId (shorter format)\nconst sessionId = callbackData.replace('am:', '');\n\n// Extract parentSessionId from sessionId (format: parentId_ext_timestamp)\nconst parentSessionId = sessionId.includes('_ext_') \n  ? sessionId.split('_ext_')[0] \n  : sessionId;\n\nreturn {\n  json: {\n    sessionId,\n    parentSessionId,\n    chatId: callbackQuery.message.chat.id,\n    messageId: callbackQuery.message.message_id,\n    callbackQueryId: callbackQuery.id,\n    action: 'automerge'\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "41f5f5a4-a2a9-45b2-80a5-09809b9e508e",
      "name": "Parse ManualMerge",
      "type": "n8n-nodes-base.code",
      "position": [
        -4016,
        -560
      ],
      "parameters": {
        "jsCode": "const callbackQuery = $input.first().json.callback_query;\nconst callbackData = callbackQuery.data;\n// Format: mm:sessionId (shorter format)\nconst sessionId = callbackData.replace('mm:', '');\n\n// Extract parentSessionId from sessionId (format: parentId_ext_timestamp)\nconst parentSessionId = sessionId.includes('_ext_') \n  ? sessionId.split('_ext_')[0] \n  : sessionId;\n\nreturn {\n  json: {\n    sessionId,\n    parentSessionId,\n    chatId: callbackQuery.message.chat.id,\n    messageId: callbackQuery.message.message_id,\n    callbackQueryId: callbackQuery.id,\n    action: 'manualmerge'\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "61070423-1845-4ab7-902e-cb80c6fac942",
      "name": "Send Merging Status",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -3056,
        -800
      ],
      "webhookId": "84450bb1-583d-48ce-8bad-a2d12a1bd630",
      "parameters": {
        "text": "🔄 **Merging videos...**\n\nUsing Transloadit to combine your original and extended video.\n\nThis usually takes 30-60 seconds...",
        "chatId": "={{ $json.chatId }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "faaa8eee-0848-426c-97ea-e077f500777c",
      "name": "Send Manual Links",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -3136,
        -560
      ],
      "webhookId": "7bff7cdd-63c4-4090-a983-1b6e63c18d5f",
      "parameters": {
        "text": "=🔗 **Video Links for Manual Merge**\n\n**Original Video:**\n{{ $json.originalVideoUrl }}\n\n**Extended Video:**\n{{ $json.extensionVideoUrl }}\n\n**How to merge:**\n1. Download both videos\n2. Use a free tool like:\n   • [CapCut](https://www.capcut.com/) (mobile/web)\n   • [Canva](https://www.canva.com/video-editor/) (web)\n   • [ClipChamp](https://clipchamp.com/) (web)\n3. Import both videos and place them in sequence\n4. Export your merged video!",
        "chatId": "={{ $json.chatId }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false,
          "disable_web_page_preview": true
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "7673cb96-33a6-4b44-bc62-60449ffaab98",
      "name": "Get Manual URLs",
      "type": "n8n-nodes-base.code",
      "position": [
        -3360,
        -560
      ],
      "parameters": {
        "jsCode": "const input = $('Parse ManualMerge').first().json;\n\n// Parse sessions from Redis\nconst originalRedis = $('Fetch Original (Manual)').first().json;\nconst extensionRedis = $input.first().json;\n\nconst originalSession = typeof originalRedis.propertyName === 'string' \n  ? JSON.parse(originalRedis.propertyName) \n  : originalRedis.propertyName || originalRedis;\n\nconst extensionSession = typeof extensionRedis.propertyName === 'string'\n  ? JSON.parse(extensionRedis.propertyName)\n  : extensionRedis.propertyName || extensionRedis;\n\n// Helper function to find video URL in session\nfunction getVideoUrl(session) {\n  if (session.videoUrl) return session.videoUrl;\n  if (session.s3Url) return session.s3Url;\n  if (session.rawResponse?.data?.response?.originUrls?.[0]) {\n    return session.rawResponse.data.response.originUrls[0];\n  }\n  if (session.rawResponse?.data?.response?.resultUrls?.[0]) {\n    return session.rawResponse.data.response.resultUrls[0];\n  }\n  if (session.video?.url) return session.video.url;\n  return null;\n}\n\nconst originalVideoUrl = getVideoUrl(originalSession);\nconst extensionVideoUrl = getVideoUrl(extensionSession);\n\nreturn {\n  json: {\n    ...input,\n    originalVideoUrl,\n    extensionVideoUrl,\n    originalTitle: originalSession.youtubeMetadata?.title || 'Original Video'\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "33626c67-bd19-42b1-997e-a30dd4f552b6",
      "name": "Create Transloadit Assembly",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2848,
        -800
      ],
      "parameters": {
        "url": "https://api2.transloadit.com/assemblies",
        "method": "POST",
        "options": {
          "timeout": 120000
        },
        "sendBody": true,
        "contentType": "multipart-form-data",
        "bodyParameters": {
          "parameters": [
            {
              "name": "params",
              "value": "={{ $json.transloaditParams }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "d1c8fdfa-86c8-4dbe-9d0f-751e88fbfae6",
      "name": "Parse Assembly Response",
      "type": "n8n-nodes-base.code",
      "position": [
        -2640,
        -800
      ],
      "parameters": {
        "jsCode": "const response = $input.first().json;\nconst prevData = $('Get Merge URLs').first().json;\n\nif (response.error) {\n  throw new Error('Transloadit error: ' + response.error);\n}\n\nreturn {\n  json: {\n    ...prevData,\n    assemblyId: response.assembly_id,\n    assemblyUrl: response.assembly_ssl_url,\n    assemblyStatus: response.ok,\n    pollUrl: response.assembly_ssl_url\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "7355a230-cff2-40ec-bf05-1f4a8e3a1597",
      "name": "Wait for Merge",
      "type": "n8n-nodes-base.wait",
      "position": [
        -2416,
        -800
      ],
      "webhookId": "72447b94-1a80-4cee-a37e-690dab058acf",
      "parameters": {
        "amount": 10
      },
      "typeVersion": 1.1
    },
    {
      "id": "52ce532a-3c78-4389-b2b6-b365c790349d",
      "name": "Poll Assembly Status",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2192,
        -800
      ],
      "parameters": {
        "url": "={{ $json.pollUrl }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "a43b684c-a410-45f9-808b-17130536384d",
      "name": "Check Merge Complete",
      "type": "n8n-nodes-base.code",
      "position": [
        -1968,
        -800
      ],
      "parameters": {
        "jsCode": "const response = $input.first().json;\nconst prevData = $('Parse Assembly Response').first().json;\n\nconst isComplete = response.ok === 'ASSEMBLY_COMPLETED';\nconst isExecuting = response.ok === 'ASSEMBLY_EXECUTING' || response.ok === 'ASSEMBLY_UPLOADING';\nconst isFailed = !isComplete && !isExecuting;\n\nlet mergedVideoUrl = null;\nif (isComplete && response.results && response.results.exported) {\n  mergedVideoUrl = response.results.exported[0]?.ssl_url || response.results.exported[0]?.url;\n}\n\n// If still executing, continue polling\n// If complete, send result\n// If failed, send error\n\nreturn {\n  json: {\n    ...prevData,\n    isComplete,\n    isExecuting,\n    isFailed,\n    mergedVideoUrl,\n    status: response.ok,\n    error: response.error || response.message || null\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "0fba91d6-8416-49cd-a24a-9c7918cdf61a",
      "name": "Merge Complete?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1792,
        -800
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.isComplete }}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "7ace11d3-c980-4a69-bcd1-7375901a144c",
      "name": "Still Executing",
      "type": "n8n-nodes-base.code",
      "position": [
        -1360,
        -640
      ],
      "parameters": {
        "jsCode": "// Pass through for another poll iteration\nconst data = $input.first().json;\nreturn { json: data };"
      },
      "typeVersion": 2
    },
    {
      "id": "395fe5b0-c39e-47f6-b434-7ba8ff5a54e2",
      "name": "Send Merge Failed",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -1360,
        -816
      ],
      "webhookId": "0537ca6d-ac59-4af2-a2bc-a462c04ffc34",
      "parameters": {
        "text": "=❌ **Merge Failed**\n\nSorry, there was an error merging your videos.\n\n**Error:** {{ $json.error || 'Unknown error' }}\n\n**You can still merge manually:**\n• Original: {{ $json.originalS3Url }}\n• Extension: {{ $json.extensionS3Url }}",
        "chatId": "={{ $json.chatId }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "2e3b7e93-85ca-494f-a0bd-52c702eac140",
      "name": "Check Failed?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1568,
        -768
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.isFailed }}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "da9be349-be2c-4e8a-a174-4a88df70bda7",
      "name": "Download Merged Video",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1568,
        -960
      ],
      "parameters": {
        "url": "={{ $json.mergedVideoUrl }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "39d3f66f-7965-4346-ad80-7d6bc45712b0",
      "name": "Prepare Merged Session",
      "type": "n8n-nodes-base.code",
      "position": [
        -1360,
        -960
      ],
      "parameters": {
        "jsCode": "const prevData = $('Check Merge Complete').first().json;\nconst mergedVideoUrl = prevData.mergedVideoUrl;\n\n// Create merged session ID\nconst mergedSessionId = prevData.parentSessionId + '_merged';\n\nreturn {\n  json: {\n    sessionId: mergedSessionId,\n    parentSessionId: prevData.parentSessionId,\n    extensionSessionId: prevData.sessionId,\n    chatId: prevData.chatId,\n    messageId: prevData.messageId,\n    mergedVideoUrl: mergedVideoUrl,\n    isMerged: true\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "bdf1d50f-9f41-47cf-addd-feedad0b6122",
      "name": "Upload Merged to S3",
      "type": "n8n-nodes-base.s3",
      "position": [
        -1152,
        -960
      ],
      "parameters": {
        "fileName": "=videos/{{ $json.sessionId }}.mp4",
        "operation": "upload",
        "bucketName": "shorts",
        "additionalFields": {}
      },
      "credentials": {
        "s3": {
          "id": "2g8pNTKJVmd3Qz0i",
          "name": "S3 account Apello"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "e136f900-062e-41de-946b-2df721a0ac91",
      "name": "Get Original Session",
      "type": "n8n-nodes-base.redis",
      "position": [
        -944,
        -960
      ],
      "parameters": {
        "key": "=session:{{ $('Prepare Merged Session').first().json.parentSessionId }}",
        "options": {},
        "operation": "get"
      },
      "credentials": {
        "redis": {
          "id": "pytuc2MEjhEpHMDU",
          "name": "Apello Version"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "8d078ac0-4b08-4077-805b-2b9537553d4e",
      "name": "Create Merged Session",
      "type": "n8n-nodes-base.code",
      "position": [
        -720,
        -960
      ],
      "parameters": {
        "jsCode": "const mergedData = $('Prepare Merged Session').first().json;\nconst redisResult = $input.first().json;\n\n// Parse original session\nconst redisValue = redisResult.propertyName || redisResult;\nconst originalSession = typeof redisValue === 'string' ? JSON.parse(redisValue) : redisValue;\n\n// Create merged session with original metadata\nconst mergedSession = {\n  sessionId: mergedData.sessionId,\n  parentSessionId: mergedData.parentSessionId,\n  extensionSessionId: mergedData.extensionSessionId,\n  chatId: mergedData.chatId,\n  messageId: mergedData.messageId,\n  isMerged: true,\n  // Copy metadata from original session\n  youtubeMetadata: originalSession.youtubeMetadata,\n  shortSummary: originalSession.shortSummary,\n  generatedPrompt: originalSession.generatedPrompt,\n  template: originalSession.template,\n  originalInput: originalSession.originalInput,\n  // Video URLs\n  videoUrl: 'https://your-s3-endpoint.com/shorts/videos/' + mergedData.sessionId + '.mp4',\n  s3Url: 'https://your-s3-endpoint.com/shorts/videos/' + mergedData.sessionId + '.mp4',\n  status: 'merged_ready',\n  createdAt: originalSession.createdAt,\n  mergedAt: new Date().toISOString()\n};\n\nreturn { json: mergedSession };"
      },
      "typeVersion": 2
    },
    {
      "id": "c6ead8cb-a606-4110-a2a8-db5f6e19eb21",
      "name": "Store Merged Session",
      "type": "n8n-nodes-base.redis",
      "position": [
        -496,
        -960
      ],
      "parameters": {
        "key": "=session:{{ $json.sessionId }}",
        "ttl": 86400,
        "value": "={{ JSON.stringify($json) }}",
        "expire": true,
        "operation": "set"
      },
      "credentials": {
        "redis": {
          "id": "pytuc2MEjhEpHMDU",
          "name": "Apello Version"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "a2fcacd2-33e3-42bc-9412-b099e26167b3",
      "name": "Download Merged for Telegram",
      "type": "n8n-nodes-base.s3",
      "position": [
        -304,
        -960
      ],
      "parameters": {
        "fileKey": "=videos/{{ $('Create Merged Session').first().json.sessionId }}.mp4",
        "bucketName": "shorts"
      },
      "credentials": {
        "s3": {
          "id": "2g8pNTKJVmd3Qz0i",
          "name": "S3 account Apello"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "83f3d6fc-b0a5-422a-96d9-3541b6ea10a9",
      "name": "Send Merged Video",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -112,
        -960
      ],
      "webhookId": "8f66f84e-bdc3-4b3e-b1fb-8cc0db962293",
      "parameters": {
        "chatId": "={{ $('Create Merged Session').first().json.chatId }}",
        "operation": "sendVideo",
        "binaryData": true,
        "replyMarkup": "inlineKeyboard",
        "inlineKeyboard": {
          "rows": [
            {
              "row": {
                "buttons": [
                  {
                    "text": "TikTok",
                    "additionalFields": {
                      "callback_data": "=tiktok:{{ $('Create Merged Session').first().json.sessionId }}"
                    }
                  },
                  {
                    "text": "Instagram",
                    "additionalFields": {
                      "callback_data": "=instagram:{{ $('Create Merged Session').first().json.sessionId }}"
                    }
                  }
                ]
              }
            },
            {
              "row": {
                "buttons": [
                  {
                    "text": "YouTube",
                    "additionalFields": {
                      "callback_data": "=publish:{{ $('Create Merged Session').first().json.sessionId }}"
                    }
                  },
                  {
                    "text": "All Platforms",
                    "additionalFields": {
                      "callback_data": "=publishall:{{ $('Create Merged Session').first().json.sessionId }}"
                    }
                  }
                ]
              }
            }
          ]
        },
        "additionalFields": {
          "caption": "=✅ **Merged Video Ready!**\n\n**Title:** {{ $('Create Merged Session').first().json.youtubeMetadata.title }}\n\n_Original + Extended video merged successfully!_",
          "parse_mode": "Markdown"
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "IiBYtPFsXSQ5PhBk",
          "name": "Content Automation"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "a348068e-0f8c-4af7-a39d-2f6ff1fe3c08",
      "name": "Fetch Original Session",
      "type": "n8n-nodes-base.redis",
      "position": [
        -3632,
        -800
      ],
      "parameters": {
        "key": "=session:{{ $json.parentSessionId }}",
        "options": {},
        "operation": "get"
      },
      "credentials": {
        "redis": {
          "id": "pytuc2MEjhEpHMDU",
          "name": "Apello Version"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "801f6ad6-fd74-44bd-91c7-8d6cf672f517",
      "name": "Fetch Extension Session",
      "type": "n8n-nodes-base.redis",
      "position": [
        -3440,
        -800
      ],
      "parameters": {
        "key": "=session:{{ $('Parse AutoMerge').first().json.sessionId }}",
        "options": {},
        "operation": "get"
      },
      "credentials": {
        "redis": {
          "id": "pytuc2MEjhEpHMDU",
          "name": "Apello Version"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "caed3b47-542e-4e95-b227-2d7ce7330777",
      "name": "Get Merge URLs",
      "type": "n8n-nodes-base.code",
      "position": [
        -3248,
        -800
      ],
      "parameters": {
        "jsCode": "const input = $('Parse AutoMerge').first().json;\n\n// Parse sessions from Redis\nconst originalRedis = $('Fetch Original Session').first().json;\nconst extensionRedis = $input.first().json;\n\nconst originalSession = typeof originalRedis.propertyName === 'string' \n  ? JSON.parse(originalRedis.propertyName) \n  : originalRedis.propertyName || originalRedis;\n\nconst extensionSession = typeof extensionRedis.propertyName === 'string'\n  ? JSON.parse(extensionRedis.propertyName)\n  : extensionRedis.propertyName || extensionRedis;\n\n// Helper function to find video URL in session (try multiple locations)\nfunction getVideoUrl(session) {\n  // Try direct videoUrl\n  if (session.videoUrl) return session.videoUrl;\n  \n  // Try s3Url\n  if (session.s3Url) return session.s3Url;\n  \n  // Try rawResponse (KIE response)\n  if (session.rawResponse?.data?.response?.originUrls?.[0]) {\n    return session.rawResponse.data.response.originUrls[0];\n  }\n  if (session.rawResponse?.data?.response?.resultUrls?.[0]) {\n    return session.rawResponse.data.response.resultUrls[0];\n  }\n  \n  // Try nested video object\n  if (session.video?.url) return session.video.url;\n  \n  return null;\n}\n\n// Get KIE video URLs\nconst originalVideoUrl = getVideoUrl(originalSession);\nconst extensionVideoUrl = getVideoUrl(extensionSession);\n\n// Debug: log what we found\nconsole.log('Original session keys:', Object.keys(originalSession));\nconsole.log('Original videoUrl:', originalVideoUrl);\nconsole.log('Extension session keys:', Object.keys(extensionSession));\nconsole.log('Extension videoUrl:', extensionVideoUrl);\n\nif (!originalVideoUrl || !extensionVideoUrl) {\n  throw new Error('Video URLs not found! Original: ' + originalVideoUrl + ', Extension: ' + extensionVideoUrl + '. Session keys: ' + Object.keys(originalSession).join(', '));\n}\n\n// SANITIZED: Replaced hardcoded key\nconst authKey = 'YOUR_TRANSLOADIT_AUTH_KEY';\n\n// Generate expiry (1 hour from now)\nconst expiry = new Date(Date.now() + 3600000).toISOString().replace('T', ' ').slice(0, 19) + '+00:00';\n\n// Transloadit params\nconst params = {\n  auth: {\n    key: authKey,\n    expires: expiry\n  },\n  steps: {\n    imported_original: {\n      robot: '/http/import',\n      url: originalVideoUrl\n    },\n    imported_extension: {\n      robot: '/http/import', \n      url: extensionVideoUrl\n    },\n    merged: {\n      robot: '/video/concat',\n      use: {\n        steps: [\n          { name: 'imported_original', as: 'video' },\n          { name: 'imported_extension', as: 'video' }\n        ],\n        bundle_steps: true\n      },\n      preset: 'iphone-high',\n      ffmpeg_stack: 'v6.0.0'\n    },\n    exported: {\n      robot: '/file/export/serve',\n      use: 'merged'\n    }\n  }\n};\n\nreturn {\n  json: {\n    ...input,\n    originalVideoUrl,\n    extensionVideoUrl,\n    transloaditParams: JSON.stringify(params)\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "4bfb70cf-a105-42b6-9c1c-09fa7f0b9a70",
      "name": "Fetch Original (Manual)",
      "type": "n8n-nodes-base.redis",
      "position": [
        -3808,
        -560
      ],
      "parameters": {
        "key": "=session:{{ $json.parentSessionId }}",
        "options": {},
        "operation": "get"
      },
      "credentials": {
        "redis": {
          "id": "pytuc2MEjhEpHMDU",
          "name": "Apello Version"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "929f5bdc-6c16-4dbe-b0d0-d96a8fa20ea3",
      "name": "Fetch Extension (Manual)",
      "type": "n8n-nodes-base.redis",
      "position": [
        -3568,
        -560
      ],
      "parameters": {
        "key": "=session:{{ $('Parse ManualMerge').first().json.sessionId }}",
        "options": {},
        "operation": "get"
      },
      "credentials": {
        "redis": {
          "id": "pytuc2MEjhEpHMDU",
          "name": "Apello Version"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "bddaab45-8be7-4500-899a-902278f27226",
      "name": "Video Merge1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4048,
        -608
      ],
      "parameters": {
        "color": 5,
        "width": 1104,
        "height": 208,
        "content": "**Manual-Merge** — Send original + extended video for manual merge"
      },
      "typeVersion": 1
    },
    {
      "id": "0f0f19a9-de86-48cb-9fac-7b8e725d2b0f",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4720,
        -2416
      ],
      "parameters": {
        "width": 496,
        "height": 384,
        "content": "[![Workflow Demo Video](https://img.youtube.com/vi/OI_oJ_2F1O0/hqdefault.jpg)](https://youtu.be/OI_oJ_2F1O0)\n"
      },
      "typeVersion": 1
    }
  ],
  "active": true,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "ac1a0e1e-b967-4293-95fe-54b7abcce180",
  "connections": {
    "Merge": {
      "main": [
        [
          {
            "node": "Send Prompt Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "No Photo": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Has Photo?": {
      "main": [
        [
          {
            "node": "Get Photo URL",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No Photo",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add Task ID": {
      "main": [
        [
          {
            "node": "Store Generating Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Balance OK?": {
      "main": [
        [
          {
            "node": "Start Video Generation",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Insufficient Balance",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Session": {
      "main": [
        [
          {
            "node": "Parse Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route Input": {
      "main": [
        [
          {
            "node": "Extract Input",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Parse Model Selection",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Parse Publish",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Parse Cancel",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Help",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Parse Extend",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Parse TikTok",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Parse Instagram",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Parse PublishAll",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Parse AutoMerge",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Parse ManualMerge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Cancel": {
      "main": [
        [
          {
            "node": "Answer Cancel",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Extend": {
      "main": [
        [
          {
            "node": "Answer Extend",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse TikTok": {
      "main": [
        [
          {
            "node": "Answer TikTok",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Answer Cancel": {
      "main": [
        [
          {
            "node": "Delete Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Answer Extend": {
      "main": [
        [
          {
            "node": "Get Session (Extend)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Answer TikTok": {
      "main": [
        [
          {
            "node": "Send TikTok Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Balance": {
      "main": [
        [
          {
            "node": "Validate Balance",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Failed?": {
      "main": [
        [
          {
            "node": "Send Merge Failed",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Still Executing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Input": {
      "main": [
        [
          {
            "node": "Has Photo?",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Initial Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Photo URL": {
      "main": [
        [
          {
            "node": "Build Photo URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Publish": {
      "main": [
        [
          {
            "node": "Answer Publish",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Session": {
      "main": [
        [
          {
            "node": "Check Balance",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store Session": {
      "main": [
        [
          {
            "node": "Send Prompt for Approval",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Answer Publish": {
      "main": [
        [
          {
            "node": "Get Session (Publish)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Delete Session": {
      "main": [
        [
          {
            "node": "Send Cancel",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Video": {
      "main": [
        [
          {
            "node": "Upload to YouTube",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Results": {
      "main": [
        [
          {
            "node": "Update Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Session": {
      "main": [
        [
          {
            "node": "Store Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Merge URLs": {
      "main": [
        [
          {
            "node": "Send Merging Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Session": {
      "main": [
        [
          {
            "node": "Send Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait for Merge": {
      "main": [
        [
          {
            "node": "Poll Assembly Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Answer Generate": {
      "main": [
        [
          {
            "node": "Get Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Photo URL": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Determine Route": {
      "main": [
        [
          {
            "node": "Route Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Manual URLs": {
      "main": [
        [
          {
            "node": "Send Manual Links",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Complete?": {
      "main": [
        [
          {
            "node": "Download Merged Video",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Check Failed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse AutoMerge": {
      "main": [
        [
          {
            "node": "Fetch Original Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Instagram": {
      "main": [
        [
          {
            "node": "Answer Instagram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Still Executing": {
      "main": [
        [
          {
            "node": "Wait for Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Answer Instagram": {
      "main": [
        [
          {
            "node": "Send Instagram Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse PublishAll": {
      "main": [
        [
          {
            "node": "Answer PublishAll",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram Trigger": {
      "main": [
        [
          {
            "node": "Determine Route",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Balance": {
      "main": [
        [
          {
            "node": "Balance OK?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Answer PublishAll": {
      "main": [
        [
          {
            "node": "Send PublishAll Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse ManualMerge": {
      "main": [
        [
          {
            "node": "Fetch Original (Manual)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Publish to TikTok": {
      "main": [
        [
          {
            "node": "Send TikTok Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload to YouTube": {
      "main": [
        [
          {
            "node": "Format Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add Extend Task ID": {
      "main": [
        [
          {
            "node": "Store Extending Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extend Balance OK?": {
      "main": [
        [
          {
            "node": "Generate Extend Prompt",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Insufficient Extend Balance",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Prompt Status": {
      "main": [
        [
          {
            "node": "Generate Prompt & Metadata",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send TikTok Status": {
      "main": [
        [
          {
            "node": "Get Session (TikTok)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Extend Prompt": {
      "main": [
        [
          {
            "node": "Start Extend Generation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Merging Status": {
      "main": [
        [
          {
            "node": "Create Transloadit Assembly",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Merged to S3": {
      "main": [
        [
          {
            "node": "Get Original Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Extend Balance": {
      "main": [
        [
          {
            "node": "Validate Extend Balance",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Merge Complete": {
      "main": [
        [
          {
            "node": "Merge Complete?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Original Session": {
      "main": [
        [
          {
            "node": "Create Merged Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Session (Extend)": {
      "main": [
        [
          {
            "node": "Parse Session (Extend)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Session (TikTok)": {
      "main": [
        [
          {
            "node": "Prepare KIE Request (TikTok)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Poll Assembly Status": {
      "main": [
        [
          {
            "node": "Check Merge Complete",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Publish to Instagram": {
      "main": [
        [
          {
            "node": "Send Instagram Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store Merged Session": {
      "main": [
        [
          {
            "node": "Download Merged for Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Merged Session": {
      "main": [
        [
          {
            "node": "Store Merged Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Merged Video": {
      "main": [
        [
          {
            "node": "Prepare Merged Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Session (Publish)": {
      "main": [
        [
          {
            "node": "Parse Session (Publish)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Model Selection": {
      "main": [
        [
          {
            "node": "Answer Generate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Extending Status": {
      "main": [
        [
          {
            "node": "Trigger Extend Polling",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Instagram Status": {
      "main": [
        [
          {
            "node": "Get Session (Instagram)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Original Session": {
      "main": [
        [
          {
            "node": "Fetch Extension Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Extend Prompt": {
      "main": [
        [
          {
            "node": "Parse Extend Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Session (Extend)": {
      "main": [
        [
          {
            "node": "Check Extend Balance",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Merged Session": {
      "main": [
        [
          {
            "node": "Upload Merged to S3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Generating Status": {
      "main": [
        [
          {
            "node": "Trigger Polling",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send PublishAll Status": {
      "main": [
        [
          {
            "node": "Get Session (PublishAll)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start Video Generation": {
      "main": [
        [
          {
            "node": "Add Task ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Extension Session": {
      "main": [
        [
          {
            "node": "Get Merge URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Original (Manual)": {
      "main": [
        [
          {
            "node": "Fetch Extension (Manual)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Session (Instagram)": {
      "main": [
        [
          {
            "node": "Prepare KIE Request (Instagram)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Assembly Response": {
      "main": [
        [
          {
            "node": "Wait for Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Session (Publish)": {
      "main": [
        [
          {
            "node": "Download Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start Extend Generation": {
      "main": [
        [
          {
            "node": "Add Extend Task ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store Extending Session": {
      "main": [
        [
          {
            "node": "Send Extending Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Extend Balance": {
      "main": [
        [
          {
            "node": "Extend Balance OK?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Extension (Manual)": {
      "main": [
        [
          {
            "node": "Get Manual URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Video URL (TikTok)": {
      "main": [
        [
          {
            "node": "Prepare Publish (TikTok)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Session (PublishAll)": {
      "main": [
        [
          {
            "node": "Prepare KIE Request (PublishAll)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Publish (TikTok)": {
      "main": [
        [
          {
            "node": "Publish to TikTok",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Publish to All Platforms": {
      "main": [
        [
          {
            "node": "Send PublishAll Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store Generating Session": {
      "main": [
        [
          {
            "node": "Send Generating Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Prompt & Metadata": {
      "main": [
        [
          {
            "node": "Format Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Transloadit Assembly": {
      "main": [
        [
          {
            "node": "Parse Assembly Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Video URL (Instagram)": {
      "main": [
        [
          {
            "node": "Prepare Publish (Instagram)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Publish (Instagram)": {
      "main": [
        [
          {
            "node": "Publish to Instagram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Merged for Telegram": {
      "main": [
        [
          {
            "node": "Send Merged Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Video URL (PublishAll)": {
      "main": [
        [
          {
            "node": "Prepare Publish (PublishAll)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare KIE Request (TikTok)": {
      "main": [
        [
          {
            "node": "Fetch Video URL (TikTok)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Publish (PublishAll)": {
      "main": [
        [
          {
            "node": "Publish to All Platforms",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare KIE Request (Instagram)": {
      "main": [
        [
          {
            "node": "Fetch Video URL (Instagram)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare KIE Request (PublishAll)": {
      "main": [
        [
          {
            "node": "Fetch Video URL (PublishAll)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}