AutomationFlowsSlack & Telegram › Yt Shorts Pilot V0.4 (full Autonomy: Gpt Scenario -> Render -> Publish)

Yt Shorts Pilot V0.4 (full Autonomy: Gpt Scenario -> Render -> Publish)

YT Shorts Pilot v0.4 (full autonomy: GPT scenario -> render -> publish). Uses httpRequest, telegram. Scheduled trigger; 26 nodes.

Cron / scheduled trigger★★★★☆ complexity26 nodesHTTP RequestTelegram
Slack & Telegram Trigger: Cron / scheduled Nodes: 26 Complexity: ★★★★☆ Added:

This workflow follows the HTTP Request → Telegram recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "YT Shorts Pilot v0.4 (full autonomy: GPT scenario -> render -> publish)",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 9 * * *"
            }
          ]
        },
        "timezone": "Europe/Berlin",
        "triggerAtStartup": false
      },
      "id": "cron-0900-daily",
      "name": "Schedule 09:00 Berlin daily",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.1,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "yt-pilot-v04-now",
        "options": {}
      },
      "id": "webhook-manual-v04",
      "name": "Webhook manual v04",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1.1,
      "position": [
        240,
        500
      ]
    },
    {
      "parameters": {
        "method": "PATCH",
        "url": "={{$env.SUPABASE_GENESIS_URL}}/rest/v1/topics",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "status",
              "value": "eq.queued"
            },
            {
              "name": "order",
              "value": "priority.desc.nullslast,hype_score.desc.nullslast,discovered_at.asc"
            },
            {
              "name": "limit",
              "value": "1"
            }
          ]
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Prefer",
              "value": "return=representation"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\"status\":\"drafting\"}",
        "options": {
          "timeout": 15000
        }
      },
      "id": "supabase-topic",
      "name": "Supabase: claim_next_topic (L1 atomic)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        460,
        400
      ],
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// n8n splits array response into items, normalize\nlet rows = $input.all().map(x => x.json);\nif (rows.length === 1 && Array.isArray(rows[0])) rows = rows[0];\nconst topic = rows.find(r => r && r.slug_en) || null;\nif (!topic) {\n  return [{ json: { empty: true, today: new Date().toISOString().slice(0,10) } }];\n}\nreturn [{ json: { ...topic, today: new Date().toISOString().slice(0,10) } }];"
      },
      "id": "normalize-topic",
      "name": "Normalize topic",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        680,
        400
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "c1",
              "leftValue": "={{ $json.empty }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "if-no-topic",
      "name": "No topic in queue?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        900,
        400
      ]
    },
    {
      "parameters": {
        "chatId": "={{$env.TELEGRAM_TARGET_CHAT}}",
        "text": "={{ 'YT Pilot v0.4: no queued topics for ' + $json.today + '. Module A (G_A_topic_distiller) should have populated the queue \u2014 check.' }}",
        "additionalFields": {
          "parse_mode": "HTML"
        }
      },
      "id": "tg-no-topic",
      "name": "TG no topic",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        1120,
        250
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.openai.com/v1/chat/completions",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "openAiApi",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({\n  model: 'gpt-4o',\n  temperature: 0.7,\n  max_tokens: 4000,\n  response_format: {type: 'json_object'},\n  messages: [\n    {role: 'system', content: ($json.sys || \"\u0422\u044b \u2014 Denis Shokhirev, Enterprise AI \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u043e\u0440 \u0438\u0437 Erlangen, \u0413\u0435\u0440\u043c\u0430\u043d\u0438\u044f. \u041a\u0430\u043d\u0430\u043b @ger_dennis_ai (DennisCraft AI Studio). \u0410\u0443\u0434\u0438\u0442\u043e\u0440\u0438\u044f: \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438, AI-\u0438\u043d\u0436\u0435\u043d\u0435\u0440\u044b, \u0442\u0435\u0445\u043b\u0438\u0434\u044b, founders \u0432 DACH/RU. \u0422\u043e\u043d: \u043f\u0440\u044f\u043c\u043e\u0439, \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u043d\u044b\u0439, \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 \u0447\u0438\u0441\u043b\u0430, \u0431\u0435\u0437 \u0432\u043e\u0434\u044b, \u0431\u0435\u0437 \u0445\u0430\u0439\u043f\u0430. \u0411\u0415\u0417 \u0444\u0440\u0430\u0437 \\\"\u043a\u0440\u0430\u0439\u043d\u0435 \u0432\u0430\u0436\u043d\u043e\\\", \\\"\u044d\u0442\u043e \u043f\u0435\u0440\u0435\u043b\u043e\u043c\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442\\\", \\\"\u043f\u043e\u0433\u0440\u0443\u0436\u0430\u0435\u043c\u0441\u044f\\\", \\\"\u0440\u0435\u0432\u043e\u043b\u044e\u0446\u0438\u044f\\\", \\\"\u043f\u0435\u0440\u0435\u0432\u043e\u0440\u043e\u0442\\\".\\n\\n\u0422\u0432\u043e\u044f \u0437\u0430\u0434\u0430\u0447\u0430: \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c scenario.json \u0434\u043b\u044f YouTube Short (45 \u0441\u0435\u043a\u0443\u043d\u0434, 7 \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u043e\u0432, \u0444\u043e\u0440\u043c\u0430\u0442 SHORT_LAYOUT v2).\\n\\n\u041a\u0420\u0418\u0422\u0418\u0427\u041d\u041e: \u0432\u0435\u0440\u043d\u0438 \u0422\u041e\u041b\u042c\u041a\u041e \u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0439 JSON, \u0431\u0435\u0437 markdown \u043e\u0431\u0451\u0440\u0442\u043e\u043a, \u0431\u0435\u0437 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0435\u0432.\\n\\n\u0421\u0422\u0420\u0423\u041a\u0422\u0423\u0420\u0410:\\n{\\n  \\\"title\\\": \\\"<title_ru>\\\",\\n  \\\"publish_date\\\": \\\"<YYYY-MM-DD \u0441\u0435\u0433\u043e\u0434\u043d\u044f>\\\",\\n  \\\"slug\\\": \\\"R<NNNN>_<slug_en_underscore>\\\",\\n  \\\"format\\\": \\\"short\\\",\\n  \\\"lang\\\": \\\"ru\\\",\\n  \\\"total_duration_target\\\": 45.0,\\n  \\\"header_tag\\\": \\\"> <KEYWORD>.<kebab>\\\",\\n  \\\"segments\\\": [\\n    // 1. HOOK (id=1, duration=5.0, start=0.0, act='hook')\\n    //    voice_text: 2 \u0444\u0440\u0430\u0437\u044b 12-18 \u0441\u043b\u043e\u0432, hook-\u0432\u043e\u043f\u0440\u043e\u0441 \u0438\u043b\u0438 \u043f\u0440\u043e\u0432\u043e\u043a\u0430\u0446\u0438\u044f\\n    //    visual_type: 'hook_layered'\\n    //    visual_params: {header_tag, shadow_text (2 \u0441\u043b\u043e\u0432\u0430 UPPERCASE \\\\n, 4-9 \u0431\u0443\u043a\u0432), punch_word (1-2 \u0441\u043b\u043e\u0432\u0430 \u0441 \u0447\u0438\u0441\u043b\u043e\u043c/\u043f\u0440\u043e\u0432\u043e\u043a\u0430\u0446\u0438\u0435\u0439), punch_color (yellow|red|blue|green), small_caption (3-6 \u0441\u043b\u043e\u0432), bottom_title (1-2 \u0441\u043b\u043e\u0432\u0430 UPPERCASE)}\\n    //    bottom_subtitle: 1-3 \u0441\u043b\u043e\u0432\u0430 UPPERCASE punch\\n    \\n    // 2-6. BODY (duration=6-7s, start=5/12/19/26/33, act='body')\\n    //    voice_text: 2-3 \u0444\u0440\u0430\u0437\u044b 18-25 \u0441\u043b\u043e\u0432\\n    //    visual_type: \u041e\u0411\u042f\u0417\u0410\u0422\u0415\u041b\u042c\u041d\u041e 3+ \u0440\u0430\u0437\u043d\u044b\u0445 \u0442\u0438\u043f\u0430 \u0438\u0437:\\n    //      - numbered_macbook (\u0434\u043b\u044f code/config/cli) \u2014 sections[{number, title UPPERCASE, subtitle 6-10 \u0441\u043b\u043e\u0432 lowercase, code_filename, code_lines:[{text, color: key|string|comment|default}] 5-8 \u0441\u0442\u0440\u043e\u043a, cue_word 1-2 \u0441\u043b\u043e\u0432\u0430, cue_color}]\\n    //      - project_chips (\u0441\u043f\u0438\u0441\u043e\u043a \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432/features) \u2014 title, subtitle, chips[], bottom_lead, bottom_punch\\n    //      - comparison_strikethrough (X vs Y vs Z \u0441 \u0437\u0430\u0447\u0451\u0440\u043a\u0438\u0432\u0430\u043d\u0438\u0435\u043c) \u2014 eyebrow UPPERCASE 1-2, eyebrow_punch UPPERCASE \u0441 :, strikethrough_items[], cue_word, cue_color (yellow|red), bottom_highlight UPPERCASE\\n    //      - diagram_boxes (\u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u044b\u0439 flow [A]\u2192[B]\u2192[C]) \u2014 title, subtitle, boxes:[{text, color}], cue_word, cue_color\\n    //    avatar_position \u0432 visual_params: \u0447\u0435\u0440\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u0435 top-right \u2192 top-left \u2192 top-right \u2192 off \u2192 off \u2192 top-right\\n    //    bottom_subtitle: \u043a\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 1-3 \u0441\u043b\u043e\u0432\u0430 UPPERCASE punch (\u041d\u0415 \u043f\u0435\u0440\u0435\u0441\u043a\u0430\u0437 voice_text!)\\n    \\n    // 7. CTA (id=7, duration=6.0, start=39.0, act='cta')\\n    //    voice_text: \\\"\u041f\u043e\u043b\u043d\u044b\u0439 \u0433\u0430\u0439\u0434 \u043d\u0430 \u0441\u0430\u0439\u0442\u0435 gerdennisai.com. \u041f\u043e\u0434\u043f\u0438\u0441\u044b\u0432\u0430\u0439\u0441\u044f.\\\"\\n    //    visual_type: 'cta_buttons'\\n    //    visual_params: {avatar_position: 'top-center', lead UPPERCASE \u0432\u043e\u043f\u0440\u043e\u0441 3-5 \u0441\u043b\u043e\u0432, button_yes:{text:'\u2713 <UPPERCASE 2-3>', color:'green'}, button_no:{text:'\u2717 <UPPERCASE 2-4>', color:'red'}, username:'@ger_dennis_ai', cta:'\u041f\u041e\u0414\u041f\u0418\u0428\u0418\u0421\u042c \u2193'}\\n    //    bottom_subtitle: \\\"\\\"\\n  ]\\n}\\n\\n\u041f\u0420\u0410\u0412\u0418\u041b\u0410:\\n1. \u0414\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438: hook=5\u0441, body=6-7\u0441, cta=6\u0441. \u0418\u0422\u041e\u0413\u041e 43-45 \u0441\u0435\u043a.\\n2. start: \u043d\u0430\u043a\u043e\u043f\u0438\u0442\u0435\u043b\u044c\u043d\u043e 0.0\u21925.0\u219212.0\u219219.0\u219226.0\u219233.0\u219239.0\\n3. header_tag: > KEYWORD.kebab (CLAUDE.code, AI.agents, VIBE.coding)\\n4. bottom_subtitle \u041f\u0423\u041d\u0427 \u2014 \u041d\u0415 \u043f\u0435\u0440\u0435\u0441\u043a\u0430\u0437 voice_text. \u041f\u0440\u0438\u043c\u0435\u0440\u044b: \\\"$3K \u0412 \u041c\u0415\u0421\u042f\u0426\\\", \\\"\u0422\u042b CTO\\\", \\\"0 \u0421\u0422\u0420\u041e\u041a \u0420\u0423\u041a\u0410\u041c\u0418\\\", \\\"3 \u0427\u0410\u0421\u0410 \u0412 \u041f\u0420\u041e\u0414\u0415\\\"\\n5. visual_type \u0440\u0430\u0437\u043d\u043e\u043e\u0431\u0440\u0430\u0437\u0438\u0435 \u2014 \u043c\u0438\u043d\u0438\u043c\u0443\u043c 3 \u0440\u0430\u0437\u043d\u044b\u0445 \u0442\u0438\u043f\u0430 \u0437\u0430 7 \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u043e\u0432\\n6. code_lines 5-8 \u0441\u0442\u0440\u043e\u043a \u0440\u0435\u0430\u043b\u0438\u0441\u0442\u0438\u0447\u043d\u044b\u0439 \u043f\u0441\u0435\u0432\u0434\u043e\u043a\u043e\u0434. keys=\u0436\u0451\u043b\u0442\u044b\u0435, strings=\u0437\u0435\u043b\u0451\u043d\u044b\u0435, comments=\u0441\u0435\u0440\u044b\u0435\\n7. cue_color: 3-4 \u0440\u0430\u0437\u043d\u044b\u0445 \u0446\u0432\u0435\u0442\u0430 \u043d\u0430 \u0432\u0438\u0434\u0435\u043e \u0434\u043b\u044f \u043a\u043e\u043d\u0442\u0440\u0430\u0441\u0442\u0430\\n8. avatar_position: 3-4 \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u0430 \u0441 avatar, \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 off\\n9. voice_text \u043d\u0430 \u0427\u0418\u0421\u0422\u041e\u041c \u0440\u0443\u0441\u0441\u043a\u043e\u043c. English \u0442\u0435\u0440\u043c\u0438\u043d\u044b \u043c\u043e\u0436\u043d\u043e \u0435\u0441\u043b\u0438 \u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044f. \\\"Lovable\\\" \u2192 \\\"\u041b\u0430\u0432\u0430\u0431\u043b\\\" \u0435\u0441\u043b\u0438 \u043f\u043b\u043e\u0445\u043e \u043f\u0440\u043e\u0438\u0437\u043d\u043e\u0441\u0438\u0442 ElevenLabs\\n10. NO HYPE: \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \\\"\u0440\u0435\u0432\u043e\u043b\u044e\u0446\u0438\u044f/\u043f\u0435\u0440\u0435\u0432\u043e\u0440\u043e\u0442/\u0438\u0437\u043c\u0435\u043d\u0438\u0442 \u043c\u0438\u0440/\u043a\u0440\u0430\u0439\u043d\u0435 \u0432\u0430\u0436\u043d\u043e/\u043f\u043e\u0433\u0440\u0443\u0436\u0430\u0435\u043c\u0441\u044f\\\". \u041a\u043e\u043d\u043a\u0440\u0435\u0442\u0438\u043a\u0430: \u0447\u0438\u0441\u043b\u0430, \u0438\u043c\u0435\u043d\u0430 \u0442\u0443\u043b\u043e\u0432, \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u0435\u0439\u043d\u044b.\\n\\n\u0412\u0435\u0440\u043d\u0438 \u0422\u041e\u041b\u042c\u041a\u041e JSON.\")},\n    {role: 'user', content: '\u0422\u0435\u043c\u0430: ' + $json.title_ru + '\\nSlug: ' + $json.slug_en + '\\nHype: ' + ($json.hype_score||'?') + '/100\\n\u041a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u0442\u0435\u0437\u0438\u0441\u044b: ' + ($json.notes||'\u043d\u0435\u0442') + '\\n\u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f: ' + ($json.topic_category||'?') + '\\n\\n\u0421\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0439 scenario.json \u043f\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u043c \u0432\u044b\u0448\u0435.'}\n  ]\n}) }}",
        "options": {
          "timeout": 120000
        }
      },
      "id": "gpt-scenario",
      "name": "GPT-4o: generate scenario",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1120,
        550
      ],
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Parse OpenAI API response: {choices:[{message:{content:'<json>'}}]}\nconst topic = $('Normalize topic').first().json;\nconst raw = $input.first().json;\nlet parsed;\ntry {\n  const content = raw.choices?.[0]?.message?.content;\n  if (!content) return [{ json: { error: 'OpenAI no content', raw_keys: Object.keys(raw) } }];\n  parsed = JSON.parse(content);\n} catch(e) {\n  return [{ json: { error: 'JSON parse fail: ' + e.message, raw: JSON.stringify(raw).slice(0,400) } }];\n}\n// GPT may return in several shapes:\n//   1. {segments: [...], title, ...} \u2014 correct\n//   2. {scenario: [...]} \u2014 array under scenario key\n//   3. {scenario: {segments: [...], title, ...}} \u2014 double-nested\n//   4. [...segments...] \u2014 bare array\nlet scenario = null;\nif (Array.isArray(parsed)) {\n  scenario = { segments: parsed };\n} else if (Array.isArray(parsed.scenario)) {\n  scenario = { ...parsed, segments: parsed.scenario };\n  delete scenario.scenario;\n} else if (parsed.scenario && parsed.scenario.segments) {\n  scenario = parsed.scenario;\n} else if (parsed.segments) {\n  scenario = parsed;\n}\nif (!scenario || !Array.isArray(scenario.segments) || scenario.segments.length === 0) {\n  return [{ json: { error: 'no segments in scenario', raw: JSON.stringify(parsed).slice(0,400) } }];\n}\n// Validate segment colors \u2014 GPT may return hex instead of color names\nconst validColors = ['yellow','red','blue','green','pink','white'];\nfor (const seg of scenario.segments) {\n  const vp = seg.visual_params || (seg.visual_params = {});\n  if (vp.punch_color && !validColors.includes(vp.punch_color)) vp.punch_color = 'yellow';\n  if (vp.cue_color && !validColors.includes(vp.cue_color)) vp.cue_color = 'blue';\n  if (vp.sections) {\n    for (const sec of vp.sections) {\n      if (sec.cue_color && !validColors.includes(sec.cue_color)) sec.cue_color = 'blue';\n    }\n  }\n}\n// FORCE avatar_position: \u0432\u0441\u0435\u0433\u0434\u0430 top-right \u043d\u0430 \u0412\u0421\u0415\u0425 \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u0430\u0445 (\u0432\u043a\u043b\u044e\u0447\u0430\u044f hook+cta).\n// \u0414\u043e 2026-05-06 schedule \u0431\u044b\u043b ['off','top-right','top-left','top-right','top-left','off','top-center']\n// \u0438 \u0430\u0432\u0430\u0442\u0430\u0440 \"\u043f\u0440\u044b\u0433\u0430\u043b\" \u043f\u043e \u043f\u043e\u0437\u0438\u0446\u0438\u044f\u043c + \u043f\u0440\u043e\u043f\u0430\u0434\u0430\u043b \u043d\u0430 hook+seg6 \u2014 \u0414\u0435\u043d\u0438\u0441 05.05 \u0436\u0430\u043b\u043e\u0431\u0430.\n// \u0422\u0435\u043f\u0435\u0440\u044c \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u0430\u044f \u0435\u0434\u0438\u043d\u0430\u044f \u043f\u043e\u0437\u0438\u0446\u0438\u044f \u2014 POS_XY \u0432 composer_v2 \u0442\u043e\u0436\u0435 \u0432\u0441\u0435 \u0441\u043c\u0430\u043f\u043b\u0435\u043d\u044b \u043d\u0430 (730, 200).\nfor (let i = 0; i < scenario.segments.length; i++) {\n  const seg = scenario.segments[i];\n  const vp = seg.visual_params;\n  vp.avatar_position = 'top-right';\n}\nconst today = topic.today;\nscenario.publish_date = today;\nscenario.format = scenario.format || 'short';\nscenario.lang = scenario.lang || 'ru';\nconst slug = scenario.slug || `R${Date.now().toString().slice(-4)}_${(topic.slug_en||'topic').replace(/[^a-zA-Z0-9_]/g,'_')}`;\nscenario.slug = slug;\nreturn [{ json: { topic_id: topic.id, slug, scenario, today } }];"
      },
      "id": "build-scenario-payload",
      "name": "Build scenario payload",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1340,
        550
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "c1",
              "leftValue": "={{ $json.error }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "if-gpt-fail",
      "name": "GPT fail?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1560,
        550
      ]
    },
    {
      "parameters": {
        "chatId": "={{$env.TELEGRAM_TARGET_CHAT}}",
        "text": "={{ '<b>YT Pilot v0.4 GPT scenario fail</b>\\n\\n' + $json.error + '\\n\\nRaw: <code>' + JSON.stringify($json.scenario || $json.raw || {}).slice(0, 400) + '</code>' }}",
        "additionalFields": {
          "parse_mode": "HTML"
        }
      },
      "id": "tg-gpt-fail",
      "name": "TG GPT fail",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        1780,
        400
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.RECEIVER_BASE_URL}}/scenario/save",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Receiver-Secret",
              "value": "={{$env.RECEIVER_SECRET}}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ slug: $json.slug, scenario: $json.scenario }) }}",
        "options": {
          "timeout": 15000
        }
      },
      "id": "post-scenario-save",
      "name": "POST /scenario/save",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1780,
        700
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.RECEIVER_BASE_URL}}/render_queued",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Receiver-Secret",
              "value": "={{$env.RECEIVER_SECRET}}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ slug: $('Build scenario payload').first().json.slug, voice_engine: 'elevenlabs' }) }}",
        "options": {
          "timeout": 2400000
        }
      },
      "id": "post-render-queued",
      "name": "POST /render_queued (sync, up to 40 min)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2000,
        700
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "c1",
              "leftValue": "={{ $json.ok }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "if-render-ok",
      "name": "Render ok?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        2220,
        700
      ]
    },
    {
      "parameters": {
        "chatId": "={{$env.TELEGRAM_TARGET_CHAT}}",
        "text": "={{ '<b>YT Pilot v0.4 render fail</b>\\n\\nslug=' + $('Build scenario payload').first().json.slug + '\\nerror=' + ($json.error || 'unknown') + '\\nlog=<code>' + ($json.log || 'no log').toString().slice(-300) + '</code>' }}",
        "additionalFields": {
          "parse_mode": "HTML"
        }
      },
      "id": "tg-render-fail",
      "name": "TG render fail",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        2440,
        850
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Build YT upload payload from render result + scenario\nconst render = $input.first().json;\nconst scn = $('Build scenario payload').first().json;\nconst topic = $('Normalize topic').first().json;\nconst title = (render.scenario_title || scn.scenario.title || topic.title_ru || scn.slug).slice(0, 95) + ' #shorts';\nconst authorName = $env.AUTHOR_NAME || 'Author';\nconst authorUrl = $env.AUTHOR_URL || 'https://example.com';\nconst description = `${topic.title_ru}\\n\\n${authorName} \u00b7 ${authorUrl}\\n\\n#AI #shorts #${(topic.slug_en || '').replace(/-/g,'')}`;\nreturn [{ json: {\n  slug: scn.slug,\n  topic_id: topic.id,\n  mp4_path: render.mp4_path,\n  title: title.slice(0, 100),\n  description,\n  tags: ['AI','shorts',(topic.slug_en || '').slice(0,30)].filter(Boolean),\n  lang: 'ru',\n  privacy: 'public'\n}}];"
      },
      "id": "build-yt-payload",
      "name": "Build YT payload",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2440,
        600
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.RECEIVER_BASE_URL}}/youtube/upload",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Receiver-Secret",
              "value": "={{$env.RECEIVER_SECRET}}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ mp4_path: $json.mp4_path, title: $json.title, description: $json.description, tags: $json.tags, lang: $json.lang, privacy: $json.privacy, slug: $json.slug, topic_id: $json.topic_id }) }}",
        "options": {
          "timeout": 600000
        }
      },
      "id": "post-yt-upload",
      "name": "POST /youtube/upload",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2660,
        600
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "c1",
              "leftValue": "={{ $json.ok }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "if-upload-ok",
      "name": "Upload ok?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        2880,
        600
      ]
    },
    {
      "parameters": {
        "chatId": "={{$env.TELEGRAM_TARGET_CHAT}}",
        "text": "={{ '<b>YT upload FAIL v0.4</b>\\n\\nslug=' + $('Build YT payload').first().json.slug + '\\nexit=' + ($json.exit_code ?? '?') + '\\n<code>' + ($json.log || $json.error || '').toString().slice(-300) + '</code>' }}",
        "additionalFields": {
          "parse_mode": "HTML"
        }
      },
      "id": "tg-upload-fail",
      "name": "TG upload fail",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        3100,
        750
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.telegram.org/bot{{ $env.TELEGRAM_BOT_TOKEN }}/sendMessage",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ chat_id: $env.TELEGRAM_PUBLIC_CHANNEL_ID, text: 'New YouTube Short!\\n\\n' + $('Build YT payload').first().json.title.replace(' #shorts','') + '\\n\\n' + $json.url, parse_mode: 'HTML' }) }}",
        "options": {
          "timeout": 15000
        }
      },
      "id": "tg-channel-post",
      "name": "TG public channel post",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3100,
        500
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.SUPABASE_GENESIS_URL}}/rest/v1/yt_published",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Prefer",
              "value": "return=minimal"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ slug: $('Build YT payload').first().json.slug, video_id: $('POST /youtube/upload').first().json.video_id, youtube_url: $('POST /youtube/upload').first().json.url, tg_message_id: ($json.result && $json.result.message_id) || null, privacy: 'public', title: $('Build YT payload').first().json.title, notes: 'Auto-published by YT Shorts Pilot v0.4 (full autonomy: GPT scenario + render + upload)' }) }}",
        "options": {
          "timeout": 15000
        }
      },
      "id": "supabase-yt-published",
      "name": "Supabase: insert yt_published",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3320,
        500
      ],
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "PATCH",
        "url": "={{$env.SUPABASE_GENESIS_URL}}/rest/v1/topics?id=eq.{{ $('Normalize topic').first().json.id }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ status: 'published', published_at: new Date().toISOString(), has_video: true, video_slug: $('Build YT payload').first().json.slug, notes: 'YT: ' + $('POST /youtube/upload').first().json.url }) }}",
        "options": {
          "timeout": 15000
        }
      },
      "id": "supabase-topic-mark",
      "name": "Supabase: mark topic.yt_video_id",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3540,
        500
      ],
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "chatId": "={{$env.TELEGRAM_TARGET_CHAT}}",
        "text": "={{ '<b>YT Pilot v0.4 ZERO-TOUCH DONE</b>\\n\\nslug: ' + $('Build YT payload').first().json.slug + '\\nyoutube: ' + $('POST /youtube/upload').first().json.url + '\\nhype: ' + $('Normalize topic').first().json.hype_score + '\\nrender: ' + $('POST /render_queued (sync, up to 40 min)').first().json.duration_s + 's, upload: ' + $('POST /youtube/upload').first().json.duration_s + 's' }}",
        "additionalFields": {
          "disable_web_page_preview": false,
          "parse_mode": "HTML"
        }
      },
      "id": "tg-success",
      "name": "TG success",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        3760,
        500
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "id": "440e9896-255a-4c47-8444-0c5591d00f2b",
      "name": "Build IG/FB captions",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3100,
        760
      ],
      "parameters": {
        "jsCode": "// IG/FB captions (PUBLISHING_RULES: IG <=5 \u0445\u0435\u0448\u0442\u0435\u0433\u043e\u0432 hard cap, FB descriptive <=3)\nconst topic = $('Normalize topic').first().json;\nconst yt = $('Build YT payload').first().json;\nconst titleRu = (topic.title_ru || yt.title || 'AI').replace(/\\s*#shorts\\s*$/i,'').trim();\nconst hook = titleRu.slice(0,120);\nconst cta = '\u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0448\u044c \u0441 AI/\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0435\u0439? \u041d\u0430\u043f\u0438\u0448\u0438 \u0410\u0413\u0415\u041d\u0422 \u2014 \u043f\u0440\u0438\u0448\u043b\u044e pipeline \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e \u2b07\ufe0f';\nconst ig_caption = [hook,'',\n  'Genesis OS \u0434\u0435\u043b\u0430\u0435\u0442 \u044d\u0442\u043e \u0430\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u043e: \u0442\u0435\u043c\u0430 \u2192 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u2192 \u0440\u0435\u043d\u0434\u0435\u0440 \u2192 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044f. \u0411\u0435\u0437 \u0440\u0443\u0447\u043d\u043e\u0433\u043e \u0442\u0440\u0443\u0434\u0430.','',\n  cta,'',\n  '#AI #\u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0438 #\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f #\u0418\u0418 #ContentMachine'].join('\\n');\nconst fb_description = [hook + '.','',\n  '\u0410\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u044b\u0439 AI-\u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d (\u0430\u0433\u0435\u043d\u0442\u044b + n8n) \u0441\u0430\u043c \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0438 \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u0442 \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u2014 \u043e\u0442 \u0432\u044b\u0431\u043e\u0440\u0430 \u0442\u0435\u043c\u044b \u0434\u043e \u0440\u0435\u043d\u0434\u0435\u0440\u0430 \u0432\u0438\u0434\u0435\u043e.','',\n  '#AI #\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f'].join('\\n');\nreturn [{ json: { mp4_path: yt.mp4_path, ig_caption, fb_description } }];"
      }
    },
    {
      "id": "3b4383f0-f488-46a9-b674-38d30abb9a67",
      "name": "POST /instagram/upload",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3340,
        760
      ],
      "onError": "continueRegularOutput",
      "alwaysOutputData": true,
      "parameters": {
        "method": "POST",
        "url": "={{$env.RECEIVER_BASE_URL}}/instagram/upload",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Receiver-Secret",
              "value": "={{$env.RECEIVER_SECRET}}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ mp4_path: $('Build IG/FB captions').first().json.mp4_path, caption: $('Build IG/FB captions').first().json.ig_caption }) }}",
        "options": {
          "timeout": 360000
        }
      }
    },
    {
      "id": "1c9e3b8a-07c6-47cd-8034-ff600e6ea404",
      "name": "POST /facebook/upload",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3580,
        760
      ],
      "onError": "continueRegularOutput",
      "alwaysOutputData": true,
      "parameters": {
        "method": "POST",
        "url": "={{$env.RECEIVER_BASE_URL}}/facebook/upload",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Receiver-Secret",
              "value": "={{$env.RECEIVER_SECRET}}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ mp4_path: $('Build IG/FB captions').first().json.mp4_path, description: $('Build IG/FB captions').first().json.fb_description }) }}",
        "options": {
          "timeout": 180000
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const item = $input.first().json;\nlet sys = null;\ntry {\n  const r = await this.helpers.httpRequest({ method: 'GET', url: 'https://raw.githubusercontent.com/DenisShokhirev041279/genesis-content-os/main/prompts/scenario_v2.md', returnFullResponse: false });\n  const md = typeof r === 'string' ? r : (r.body || '');\n  const m = md.match(/## System prompt\\s*```(.*?)```/s);\n  if (m && m[1].trim().length > 200) sys = m[1].trim();\n} catch (e) { sys = null; }\nreturn [{ json: { ...item, sys } }];\n"
      },
      "id": "fetch-scenario-prompt-node",
      "name": "Fetch scenario prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        900,
        550
      ]
    }
  ],
  "connections": {
    "Schedule 09:00 Berlin daily": {
      "main": [
        [
          {
            "node": "Supabase: claim_next_topic (L1 atomic)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook manual v04": {
      "main": [
        [
          {
            "node": "Supabase: claim_next_topic (L1 atomic)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize topic": {
      "main": [
        [
          {
            "node": "No topic in queue?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "No topic in queue?": {
      "main": [
        [
          {
            "node": "TG no topic",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Fetch scenario prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GPT-4o: generate scenario": {
      "main": [
        [
          {
            "node": "Build scenario payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build scenario payload": {
      "main": [
        [
          {
            "node": "GPT fail?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GPT fail?": {
      "main": [
        [
          {
            "node": "TG GPT fail",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "POST /scenario/save",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "POST /scenario/save": {
      "main": [
        [
          {
            "node": "POST /render_queued (sync, up to 40 min)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "POST /render_queued (sync, up to 40 min)": {
      "main": [
        [
          {
            "node": "Render ok?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Render ok?": {
      "main": [
        [
          {
            "node": "Build YT payload",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "TG render fail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build YT payload": {
      "main": [
        [
          {
            "node": "POST /youtube/upload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "POST /youtube/upload": {
      "main": [
        [
          {
            "node": "Upload ok?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload ok?": {
      "main": [
        [
          {
            "node": "TG public channel post",
            "type": "main",
            "index": 0
          },
          {
            "node": "Build IG/FB captions",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "TG upload fail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "TG public channel post": {
      "main": [
        [
          {
            "node": "Supabase: insert yt_published",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Supabase: insert yt_published": {
      "main": [
        [
          {
            "node": "Supabase: mark topic.yt_video_id",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Supabase: mark topic.yt_video_id": {
      "main": [
        [
          {
            "node": "TG success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Supabase: claim_next_topic (L1 atomic)": {
      "main": [
        [
          {
            "node": "Normalize topic",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build IG/FB captions": {
      "main": [
        [
          {
            "node": "POST /instagram/upload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "POST /instagram/upload": {
      "main": [
        [
          {
            "node": "POST /facebook/upload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch scenario prompt": {
      "main": [
        [
          {
            "node": "GPT-4o: generate scenario",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

YT Shorts Pilot v0.4 (full autonomy: GPT scenario -> render -> publish). Uses httpRequest, telegram. Scheduled trigger; 26 nodes.

Source: https://github.com/DenisShokhirev041279/genesis-content-os/blob/957c61e0b66b96bd8a30ee8e5d49d6bc8955ad84/workflows/YT_Shorts_Pilot_v0_4.json — original creator credit. Request a take-down →

More Slack & Telegram workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Slack & Telegram

Auto Hunt. Uses httpRequest, googleSheets, rssFeedRead, telegram. Scheduled trigger; 78 nodes.

HTTP Request, Google Sheets, RSS Feed Read +1
Slack & Telegram

Auto-Hunt. Uses httpRequest, googleSheets, rssFeedRead, telegram. Scheduled trigger; 78 nodes.

HTTP Request, Google Sheets, RSS Feed Read +1
Slack & Telegram

This workflow runs daily at 9 AM, uses Perplexity to compile vaping industry news, optionally captures article screenshots via Browserless, and generates a HeyGen avatar video from a template. It then

Telegram, Google Drive, HTTP Request
Slack & Telegram

. Uses googleSheets, telegram, httpRequest, wise. Scheduled trigger; 36 nodes.

Google Sheets, Telegram, HTTP Request +2
Slack & Telegram

GNCA AI News Pipeline. Uses rssFeedRead, httpRequest, telegram, errorTrigger. Scheduled trigger; 31 nodes.

RSS Feed Read, HTTP Request, Telegram +1