{
  "id": "BBGHaZpPUQXWrFaQ",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Poll video generation status from KIE.ai and deliver via Telegram (Sanitized)",
  "tags": [],
  "nodes": [
    {
      "id": "b174f32b-dc84-426e-9f24-9ae3ebab200b",
      "name": "Main",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3232,
        368
      ],
      "parameters": {
        "width": 380,
        "height": 576,
        "content": "## How it works\n\n1. Receives a webhook call with video task ID from the main workflow\n2. Waits 1 minute, then checks KIE.ai API for generation status\n3. If still processing, retries (up to 15 attempts / 15 min)\n4. Once complete, downloads video and uploads to S3\n5. Retrieves session metadata from Redis\n6. Sends video preview to user via Telegram with publish buttons\n\nSupports Veo 3.1, Sora 2, and Seedance models.\n\n## Setup steps\n\n1. Add your KIE.ai API key (Header Auth)\n2. Configure Redis connection for session storage\n3. Set up S3 bucket credentials\n4. Add Telegram Bot API credentials\n5. Update the webhook URL in \"Retry Poll\" node to match your n8n instance"
      },
      "typeVersion": 1
    },
    {
      "id": "6d2ef622-0e8e-4e7f-9ef1-b81603f5087f",
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2704,
        448
      ],
      "parameters": {
        "color": 7,
        "width": 520,
        "height": 80,
        "content": "**Webhook & Parse** \u2014 Receives polling request, parses input data, responds OK immediately"
      },
      "typeVersion": 1
    },
    {
      "id": "8707a955-08d5-4494-8fac-14f1894968e5",
      "name": "Status Polling",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1968,
        464
      ],
      "parameters": {
        "color": 4,
        "width": 696,
        "height": 352,
        "content": "**Status Polling** \u2014 Waits 1 min, checks KIE.ai status, parses response for completion"
      },
      "typeVersion": 1
    },
    {
      "id": "b45a0f9e-b1e9-4c94-aabf-9e4c384f7d30",
      "name": "Retry Logic",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1184,
        576
      ],
      "parameters": {
        "color": 3,
        "width": 508,
        "height": 384,
        "content": "**Retry Logic** \u2014 Retries up to 15x or sends timeout message"
      },
      "typeVersion": 1
    },
    {
      "id": "3357d5d2-a485-454c-b6dc-02981f3292db",
      "name": "Video Download",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1184,
        320
      ],
      "parameters": {
        "color": 2,
        "width": 920,
        "height": 224,
        "content": "**Video Processing** \u2014 Downloads from KIE.ai, uploads to S3, checks if extend flow"
      },
      "typeVersion": 1
    },
    {
      "id": "6aabf895-fa67-4e3b-8263-7e6ef92d22ad",
      "name": "Session Storage",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        400,
        256
      ],
      "parameters": {
        "color": 7,
        "width": 1112,
        "height": 368,
        "content": "**Session & Metadata** \u2014 Retrieves session from Redis, creates metadata, updates status"
      },
      "typeVersion": 1
    },
    {
      "id": "c028e9e5-56aa-4b68-a80b-acbc7465b8fe",
      "name": "Telegram Delivery",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1744,
        144
      ],
      "parameters": {
        "color": 4,
        "width": 488,
        "height": 608,
        "content": "**Telegram Delivery** \u2014 Sends video preview with publish action buttons"
      },
      "typeVersion": 1
    },
    {
      "id": "ed1d90e8-a6c5-4dd7-a2c1-32ac354fa348",
      "name": "Extend Flow",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -112,
        176
      ],
      "parameters": {
        "color": 7,
        "width": 340,
        "height": 80,
        "content": "**Extend Flow** \u2014 Add Merge Option if its an extend request, if initial request skip."
      },
      "typeVersion": 1
    },
    {
      "id": "b3b9a267-f069-41ca-a55f-3e3092ae3e6b",
      "name": "Poll Trigger",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -2672,
        576
      ],
      "parameters": {
        "path": "video-poll",
        "options": {
          "rawBody": true
        },
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "adfb0fc8-4a08-45b7-990a-8733b026cf68",
      "name": "Parse Input",
      "type": "n8n-nodes-base.code",
      "position": [
        -2448,
        576
      ],
      "parameters": {
        "jsCode": "const input = $input.first().json;\n\n// Handle different webhook data structures:\n// 1. rawBody enabled: body is a JSON string\n// 2. rawBody disabled: body is an object or data is at root level\nlet body;\nif (typeof input.body === 'string') {\n  body = JSON.parse(input.body);\n} else if (input.body && typeof input.body === 'object') {\n  body = input.body;\n} else {\n  body = input;\n}\n\nreturn {\n  json: {\n    sessionId: body.sessionId,\n    taskId: body.taskId,\n    chatId: body.chatId,\n    messageId: body.messageId,\n    selectedModel: body.selectedModel || 'veo3_fast',\n    modelType: body.modelType || 'veo',\n    statusEndpoint: body.statusEndpoint || '/api/v1/veo/record-info',\n    attempt: body.attempt || 1,\n    isExtend: body.isExtend || false,\n    parentSessionId: body.parentSessionId || null\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "d4c934e6-4b24-4c33-b08c-9dddcc77055f",
      "name": "Respond OK",
      "type": "n8n-nodes-base.respondToWebhook",
      "onError": "continueRegularOutput",
      "position": [
        -2224,
        576
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ success: true, attempt: $json.attempt }) }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "7019d8f9-4cbc-4e88-ad7e-08121dce9801",
      "name": "Wait 1 Minute",
      "type": "n8n-nodes-base.wait",
      "position": [
        -2000,
        576
      ],
      "parameters": {
        "amount": 60
      },
      "typeVersion": 1.1
    },
    {
      "id": "436c017b-1e8c-4c6b-b2d3-07d932b94bf3",
      "name": "Check KIE Status",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        -1776,
        576
      ],
      "parameters": {
        "url": "=https://api.kie.ai{{ $('Parse Input').first().json.statusEndpoint }}",
        "options": {},
        "sendQuery": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "queryParameters": {
          "parameters": [
            {
              "name": "taskId",
              "value": "={{ $('Parse Input').first().json.taskId }}"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "ac9fba69-a574-4382-83a6-c01cfbd6f4b4",
      "name": "Parse Status",
      "type": "n8n-nodes-base.code",
      "position": [
        -1552,
        576
      ],
      "parameters": {
        "jsCode": "const inputData = $('Parse Input').first().json;\nconst statusResponse = $input.first().json;\nconst modelType = inputData.modelType || 'veo';\n\nlet isCompleted = false;\nlet isFailed = false;\nlet videoUrl = null;\nlet status = 'processing';\n\nif (modelType === 'veo') {\n  // Veo response: successFlag 0=Generating, 1=Success, 2=Failed, 3=Generation Failed\n  const successFlag = statusResponse.data?.successFlag;\n  isCompleted = successFlag === 1;\n  isFailed = successFlag === 2 || successFlag === 3;\n  // For 9:16 aspect ratio, use originUrls; otherwise use resultUrls\n  const resultUrls = statusResponse.data?.response?.resultUrls || [];\n  const originUrls = statusResponse.data?.response?.originUrls || [];\n  videoUrl = originUrls[0] || resultUrls[0] || null;\n} else if (modelType === 'market') {\n  // Market API response format (Sora2, Seedance)\n  // State can be: 'pending', 'processing', 'success', 'failed'\n  const taskState = statusResponse.data?.state || statusResponse.data?.status;\n  isCompleted = taskState === 'success' || taskState === 'completed' || taskState === 'COMPLETED';\n  isFailed = taskState === 'failed' || taskState === 'FAILED' || taskState === 'error';\n  \n  // Try multiple locations for video URL\n  // 1. Direct fields\n  videoUrl = statusResponse.data?.videoUrl || statusResponse.data?.output?.video || statusResponse.data?.result?.video_url;\n  \n  // 2. Parse resultJson if it exists\n  if (!videoUrl && statusResponse.data?.resultJson) {\n    try {\n      const resultJson = JSON.parse(statusResponse.data.resultJson);\n      videoUrl = resultJson.resultUrls?.[0] || resultJson.videoUrl || resultJson.video_url || resultJson.url;\n    } catch (e) {\n      // resultJson parsing failed, continue with null\n    }\n  }\n  \n  // 3. Check response object\n  if (!videoUrl && statusResponse.data?.response) {\n    const response = typeof statusResponse.data.response === 'string' \n      ? JSON.parse(statusResponse.data.response) \n      : statusResponse.data.response;\n    videoUrl = response.resultUrls?.[0] || response.videoUrl || response.video_url;\n  }\n}\n\nif (isCompleted) status = 'completed';\nif (isFailed) status = 'failed';\n\nreturn {\n  json: {\n    ...inputData,\n    status,\n    isCompleted,\n    isFailed,\n    videoUrl,\n    rawResponse: statusResponse\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "531e9b09-c985-488e-b8ad-1c0cac398d30",
      "name": "Completed?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1328,
        576
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.isCompleted }}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "bd98177f-9a95-4f18-abef-f8c1006bac30",
      "name": "Get 1080p Video",
      "type": "n8n-nodes-base.code",
      "position": [
        -1104,
        384
      ],
      "parameters": {
        "jsCode": "// Pass through - video URL already obtained from record-info\nreturn $input.all();"
      },
      "typeVersion": 2
    },
    {
      "id": "f7539aaa-b9a2-41f3-9dae-5774b2799903",
      "name": "Get Final URL",
      "type": "n8n-nodes-base.code",
      "position": [
        -880,
        384
      ],
      "parameters": {
        "jsCode": "// videoUrl already available from Parse Status via record-info endpoint\nconst inputData = $('Parse Status').first().json;\nconst parseInputData = $('Parse Input').first().json;\n\nreturn {\n  json: {\n    ...inputData,\n    isExtend: parseInputData.isExtend || false,\n    parentSessionId: parseInputData.parentSessionId || null\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "47b24e8c-810c-4c87-b028-d07a1e234608",
      "name": "Download Video",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -656,
        384
      ],
      "parameters": {
        "url": "={{ $json.videoUrl }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "df9664c3-8d88-4b67-a543-f906db0faf8e",
      "name": "Get Session",
      "type": "n8n-nodes-base.redis",
      "position": [
        464,
        384
      ],
      "parameters": {
        "key": "=session:{{ $('Get Final URL').first().json.sessionId }}",
        "options": {},
        "operation": "get"
      },
      "credentials": {
        "redis": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4ccd466e-32eb-4465-83f1-433b5664198d",
      "name": "Merge Session Data",
      "type": "n8n-nodes-base.code",
      "position": [
        688,
        384
      ],
      "parameters": {
        "jsCode": "// Get data from Merge After Concat (via Skip Concat or Concatenate path)\nconst mergeData = $('Merge After Concat').first().json;\nconst redisResult = $input.first().json;\n\n// Redis returns data in propertyName field\nconst redisValue = redisResult.propertyName || redisResult;\nconst session = typeof redisValue === 'string' ? JSON.parse(redisValue) : redisValue;\n\nconst s3Url = `https://your-s3-endpoint.com/shorts/videos/${session.sessionId}.mp4`;\n\n// Get video URL from merge data (passed through Skip Concat or Concat path)\nlet videoUrl = mergeData.videoUrl;\n\n// Fallback: try to get from rawResponse\nif (!videoUrl && mergeData.rawResponse) {\n  const raw = mergeData.rawResponse;\n  videoUrl = raw.data?.response?.originUrls?.[0] || raw.data?.response?.resultUrls?.[0];\n}\n\n// Fallback 2: try Get Final URL directly\nif (!videoUrl) {\n  try {\n    const finalUrlData = $('Get Final URL').first().json;\n    videoUrl = finalUrlData.videoUrl;\n    if (!videoUrl && finalUrlData.rawResponse) {\n      videoUrl = finalUrlData.rawResponse.data?.response?.originUrls?.[0] || finalUrlData.rawResponse.data?.response?.resultUrls?.[0];\n    }\n  } catch (e) {\n    // Get Final URL not accessible\n  }\n}\n\n// Store video URL and raw response for publishing\nreturn {\n  json: {\n    ...session,\n    videoUrl: videoUrl,\n    rawResponse: mergeData.rawResponse,\n    s3Url: s3Url,\n    status: 'video_ready',\n    updatedAt: new Date().toISOString()\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "e2c2c634-d4ad-4be6-84cd-be68f136237c",
      "name": "Update Session",
      "type": "n8n-nodes-base.redis",
      "position": [
        1360,
        384
      ],
      "parameters": {
        "key": "=session:{{ $json.sessionId }}",
        "ttl": 86400,
        "value": "={{ JSON.stringify($json) }}",
        "expire": true,
        "operation": "set"
      },
      "credentials": {
        "redis": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "2d0606a9-2ab0-4ffd-a32f-89dca688dce6",
      "name": "Download for Telegram",
      "type": "n8n-nodes-base.s3",
      "position": [
        1584,
        384
      ],
      "parameters": {
        "fileKey": "=videos/{{ $('Create Metadata').first().json.sessionId }}.mp4",
        "bucketName": "shorts"
      },
      "credentials": {
        "s3": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "bba58015-be7e-4a45-9749-b9a52e864e20",
      "name": "Is Veo Model?",
      "type": "n8n-nodes-base.if",
      "position": [
        1808,
        384
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $('Create Metadata').first().json.selectedModel?.startsWith('veo') }}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "d7161b85-5598-4711-b0c1-efecfb6a1bff",
      "name": "Send Video Preview (Veo)",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2032,
        288
      ],
      "parameters": {
        "chatId": "={{ $('Create Metadata').first().json.chatId }}",
        "operation": "sendVideo",
        "binaryData": true,
        "replyMarkup": "inlineKeyboard",
        "inlineKeyboard": {
          "rows": [
            {
              "row": {
                "buttons": [
                  {
                    "text": "YouTube",
                    "additionalFields": {
                      "callback_data": "=publish:{{ $('Create Metadata').first().json.sessionId }}"
                    }
                  },
                  {
                    "text": "TikTok",
                    "additionalFields": {
                      "callback_data": "=tiktok:{{ $('Create Metadata').first().json.sessionId }}"
                    }
                  }
                ]
              }
            },
            {
              "row": {
                "buttons": [
                  {
                    "text": "Instagram",
                    "additionalFields": {
                      "callback_data": "=instagram:{{ $('Create Metadata').first().json.sessionId }}"
                    }
                  },
                  {
                    "text": "All Platforms",
                    "additionalFields": {
                      "callback_data": "=publishall:{{ $('Create Metadata').first().json.sessionId }}"
                    }
                  }
                ]
              }
            },
            {
              "row": {
                "buttons": [
                  {
                    "text": "Extend Video (+8s)",
                    "additionalFields": {
                      "callback_data": "=extend:{{ $('Create Metadata').first().json.sessionId }}"
                    }
                  }
                ]
              }
            },
            {
              "row": {
                "buttons": [
                  {
                    "text": "Cancel",
                    "additionalFields": {
                      "callback_data": "=cancel:{{ $('Create Metadata').first().json.sessionId }}"
                    }
                  }
                ]
              }
            }
          ]
        },
        "additionalFields": {
          "caption": "=**Video Ready!**\n\n**Title:** {{ $('Create Metadata').first().json.youtubeMetadata.title }}\n",
          "parse_mode": "Markdown"
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "a64aabf2-f9b9-472b-a7ed-af09abff099f",
      "name": "Send Video Preview (Other)",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2032,
        480
      ],
      "parameters": {
        "chatId": "={{ $('Create Metadata').first().json.chatId }}",
        "operation": "sendVideo",
        "binaryData": true,
        "replyMarkup": "inlineKeyboard",
        "inlineKeyboard": {
          "rows": [
            {
              "row": {
                "buttons": [
                  {
                    "text": "YouTube",
                    "additionalFields": {
                      "callback_data": "=publish:{{ $('Create Metadata').first().json.sessionId }}"
                    }
                  },
                  {
                    "text": "TikTok",
                    "additionalFields": {
                      "callback_data": "=tiktok:{{ $('Create Metadata').first().json.sessionId }}"
                    }
                  }
                ]
              }
            },
            {
              "row": {
                "buttons": [
                  {
                    "text": "Instagram",
                    "additionalFields": {
                      "callback_data": "=instagram:{{ $('Create Metadata').first().json.sessionId }}"
                    }
                  },
                  {
                    "text": "All Platforms",
                    "additionalFields": {
                      "callback_data": "=publishall:{{ $('Create Metadata').first().json.sessionId }}"
                    }
                  }
                ]
              }
            },
            {
              "row": {
                "buttons": [
                  {
                    "text": "Cancel",
                    "additionalFields": {
                      "callback_data": "=cancel:{{ $('Create Metadata').first().json.sessionId }}"
                    }
                  }
                ]
              }
            }
          ]
        },
        "additionalFields": {
          "caption": "=**Video Ready!**\n\n**Title:** {{ $('Create Metadata').first().json.youtubeMetadata.title }}\n",
          "parse_mode": "Markdown"
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "75afd09e-e746-449d-9c05-80f4f354bb11",
      "name": "Can Retry?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1120,
        720
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.attempt < 15 }}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "fa1860dd-48a5-4174-b33e-eadf07e6ee23",
      "name": "Retry Poll",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -880,
        640
      ],
      "parameters": {
        "url": "https://your-n8n-instance.com/webhook/video-poll",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify({\n  sessionId: $json.sessionId,\n  taskId: $json.taskId,\n  chatId: $json.chatId,\n  messageId: $json.messageId,\n  selectedModel: $json.selectedModel,\n  modelType: $json.modelType,\n  statusEndpoint: $json.statusEndpoint,\n  attempt: $json.attempt + 1\n}) }}",
        "sendBody": true,
        "specifyBody": "json"
      },
      "typeVersion": 4.2
    },
    {
      "id": "0fa23fe4-5fe5-4c82-9e5d-06e9650225bc",
      "name": "Send Timeout",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -880,
        800
      ],
      "parameters": {
        "text": "=**Video Generation Timeout**\n\nThe video is taking longer than expected ({{ $json.attempt }} attempts).\n\nPlease try again later or send a new idea.\n\n_Task ID: {{ $json.taskId }}_",
        "chatId": "={{ $json.chatId }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "6379dbc9-ddcc-4a67-aba4-d4b5c5c1faf0",
      "name": "Upload a file",
      "type": "n8n-nodes-base.s3",
      "position": [
        -432,
        384
      ],
      "parameters": {
        "fileName": "=videos/{{ $('Get Final URL').first().json.sessionId }}.mp4",
        "operation": "upload",
        "bucketName": "shorts",
        "additionalFields": {}
      },
      "credentials": {
        "s3": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "3cdb8933-6193-469c-bd59-ddd6fbcc1fb1",
      "name": "Create Metadata",
      "type": "n8n-nodes-base.code",
      "position": [
        912,
        384
      ],
      "parameters": {
        "jsCode": "const session = $input.first().json;\n\nconst metadata = `Video Metadata\n==============\nSession ID: ${session.sessionId}\nGenerated: ${session.createdAt || 'N/A'}\nCompleted: ${new Date().toISOString()}\n\nOriginal Input\n--------------\nText: ${session.originalInput?.text || 'N/A'}\nPhoto: ${session.originalInput?.photoUrl ? 'Yes' : 'No'}\n\nVideo Generation\n----------------\nModel: ${session.selectedModel || 'veo3'}\nModel Name: ${session.modelName || 'Veo 3.1'}\nTask ID: ${session.taskId || 'N/A'}\nTemplate: ${session.template || 'general'}\nBalance at Generation: ${session.balanceAtGeneration || 'N/A'} credits\n\nYouTube Metadata\n----------------\nTitle: ${session.youtubeMetadata?.title || 'N/A'}\nDescription: ${session.youtubeMetadata?.description || 'N/A'}\nTags: ${session.youtubeMetadata?.tags?.join(', ') || 'N/A'}\n\nGenerated Prompt\n----------------\n${session.generatedPrompt || 'N/A'}\n\nURLs\n----\nS3: ${session.s3Url || 'N/A'}\nKIE: ${session.videoUrl || 'N/A'}`;\n\n// Convert text to binary for S3 upload\nconst binaryData = Buffer.from(metadata, 'utf-8');\n\nreturn {\n  json: {\n    ...session,\n    metadataKey: `videos/${session.sessionId}_metadata.txt`\n  },\n  binary: {\n    metadata: {\n      data: binaryData.toString('base64'),\n      mimeType: 'text/plain',\n      fileName: `${session.sessionId}_metadata.txt`\n    }\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "676e3f5f-2994-49b7-a19b-1065cf1623f4",
      "name": "Upload Metadata",
      "type": "n8n-nodes-base.s3",
      "position": [
        1136,
        384
      ],
      "parameters": {
        "fileName": "={{ $json.metadataKey }}",
        "operation": "upload",
        "bucketName": "shorts",
        "additionalFields": {},
        "binaryPropertyName": "metadata"
      },
      "credentials": {
        "s3": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "6253a26f-4c47-4452-ade7-691dca7d2b9f",
      "name": "Is Extend?",
      "type": "n8n-nodes-base.if",
      "position": [
        -208,
        384
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $('Get Final URL').first().json.isExtend }}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "9f7d4cf8-9b9e-400a-82d3-3c00bda97742",
      "name": "Skip Concat",
      "type": "n8n-nodes-base.code",
      "position": [
        16,
        480
      ],
      "parameters": {
        "jsCode": "// Pass through for non-extend videos\nconst inputData = $('Get Final URL').first().json;\nreturn {\n  json: {\n    sessionId: inputData.sessionId,\n    isExtend: false,\n    videoUrl: inputData.videoUrl,\n    rawResponse: inputData.rawResponse\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "021a9e58-04b5-47d9-b4aa-d6d3e402df02",
      "name": "Merge After Concat",
      "type": "n8n-nodes-base.merge",
      "position": [
        240,
        384
      ],
      "parameters": {},
      "typeVersion": 2
    },
    {
      "id": "3f7921d2-1352-4bd4-a8b1-8740d39f6b21",
      "name": "Send Merge Options",
      "type": "n8n-nodes-base.telegram",
      "position": [
        16,
        288
      ],
      "parameters": {
        "text": "=**Extended Video Ready!** \ud83c\udfac\n\nYour video has been extended by ~8 seconds.\n\n**Choose how to merge:**",
        "chatId": "={{ $('Parse Input').first().json.chatId }}",
        "replyMarkup": "inlineKeyboard",
        "inlineKeyboard": {
          "rows": [
            {
              "row": {
                "buttons": [
                  {
                    "text": "\ud83d\udd04 Auto Merge (Transloadit)",
                    "additionalFields": {
                      "callback_data": "=am:{{ $('Get Final URL').first().json.sessionId }}"
                    }
                  }
                ]
              }
            },
            {
              "row": {
                "buttons": [
                  {
                    "text": "\ud83d\udd17 Get Video Links (Manual)",
                    "additionalFields": {
                      "callback_data": "=mm:{{ $('Get Final URL').first().json.sessionId }}"
                    }
                  }
                ]
              }
            }
          ]
        },
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    }
  ],
  "active": true,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "1ea2783d-6705-40e7-897d-1135e709e438",
  "connections": {
    "Can Retry?": {
      "main": [
        [
          {
            "node": "Retry Poll",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Timeout",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Completed?": {
      "main": [
        [
          {
            "node": "Get 1080p Video",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Can Retry?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Extend?": {
      "main": [
        [
          {
            "node": "Send Merge Options",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Skip Concat",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Respond OK": {
      "main": [
        [
          {
            "node": "Wait 1 Minute",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Session": {
      "main": [
        [
          {
            "node": "Merge Session Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Input": {
      "main": [
        [
          {
            "node": "Respond OK",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Skip Concat": {
      "main": [
        [
          {
            "node": "Merge After Concat",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Parse Status": {
      "main": [
        [
          {
            "node": "Completed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Poll Trigger": {
      "main": [
        [
          {
            "node": "Parse Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Final URL": {
      "main": [
        [
          {
            "node": "Download Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Veo Model?": {
      "main": [
        [
          {
            "node": "Send Video Preview (Veo)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Video Preview (Other)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload a file": {
      "main": [
        [
          {
            "node": "Is Extend?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 1 Minute": {
      "main": [
        [
          {
            "node": "Check KIE Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Video": {
      "main": [
        [
          {
            "node": "Upload a file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Session": {
      "main": [
        [
          {
            "node": "Download for Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Metadata": {
      "main": [
        [
          {
            "node": "Upload Metadata",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get 1080p Video": {
      "main": [
        [
          {
            "node": "Get Final URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Metadata": {
      "main": [
        [
          {
            "node": "Update Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check KIE Status": {
      "main": [
        [
          {
            "node": "Parse Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge After Concat": {
      "main": [
        [
          {
            "node": "Get Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Session Data": {
      "main": [
        [
          {
            "node": "Create Metadata",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Merge Options": {
      "main": [
        [
          {
            "node": "Merge After Concat",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download for Telegram": {
      "main": [
        [
          {
            "node": "Is Veo Model?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}