{
  "name": "Larry TikTok Growth Engine",
  "nodes": [
    {
      "parameters": {
        "content": "## Larry TikTok Growth Engine\n\n**Self-improving AI with persistent skill memory**\n\nFully sequential flow - no deadlocks.\n\n### Flow:\n1. Read sheet + collect per-post stats\n2. Read profile analytics + agent skill\n3. LLM analyzes all data (uses accumulated skill)\n4. Generates 4 slides with TEXT\n5. Publishes to TikTok\n6. Logs + updates skill file with new learnings\n",
        "height": 428,
        "width": 360,
        "color": 5
      },
      "id": "2890ca23-9ed7-4e6e-b5e6-1af43567894b",
      "name": "Note Setup",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -464,
        -368
      ]
    },
    {
      "parameters": {
        "content": "## Phase 1: Collect Stats ",
        "height": 672,
        "width": 1720,
        "color": 4
      },
      "id": "3c8470e3-5d8e-44b8-a788-1c63167ec314",
      "name": "Note P1",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -464,
        112
      ]
    },
    {
      "parameters": {
        "content": "## Phase 2: Read Context + Generate",
        "height": 656,
        "width": 900,
        "color": 3
      },
      "id": "2a9d3171-ad6b-4a19-af58-d31b0f5804c9",
      "name": "Note P2",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1280,
        112
      ]
    },
    {
      "parameters": {
        "content": "## Phase 3: Images",
        "height": 772,
        "width": 708,
        "color": 6
      },
      "id": "23090b08-7556-43c2-bb36-731d5e45a4ad",
      "name": "Note P3",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        2192,
        0
      ]
    },
    {
      "parameters": {
        "content": "## Phase 4: Publish + Skill Update",
        "height": 656,
        "width": 1396,
        "color": 2
      },
      "id": "2b502824-1a6b-4221-a7f9-d28387f8ab4c",
      "name": "Note P4",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        2896,
        0
      ]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 24
            }
          ]
        }
      },
      "id": "d36e661e-5e1e-4c87-bbe1-c8a89ab2d2ae",
      "name": "Daily Schedule",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -640,
        304
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "name": "product_name",
              "type": "string",
              "value": "Upload Post",
              "id": "1809cc51-6da2-4aef-9946-89517d372cbc"
            },
            {
              "name": "product_description",
              "type": "string",
              "value": "Upload Post is a SaaS that lets you publish photos, videos, and text to TikTok, Instagram, YouTube, X, LinkedIn, Facebook, Threads, Pinterest, and Reddit with a single API call. One click, all platforms.",
              "id": "79675585-aaf7-46ef-b2e3-687ec3316671"
            },
            {
              "name": "upload_post_user",
              "type": "string",
              "value": "automated-tests",
              "id": "334f4cf1-f3a5-4313-845f-8bd494bf6472"
            },
            {
              "name": "upload_post_api_key",
              "type": "string",
              "value": "PASTE_YOUR_UPLOAD_POST_API_KEY_HERE",
              "id": "7c29629e-7b8c-49c2-ae2b-211810d01337"
            },
            {
              "name": "spreadsheet_id",
              "type": "string",
              "value": "1o2NZ5yXQbrrvmUR_gICVcq2Y4svnRsGa5LBsxK4PqhE",
              "id": "ac725e41-af66-40e2-b4db-1d5e97c57865"
            }
          ]
        },
        "options": {}
      },
      "id": "4dbb4d96-5b0d-4687-8f27-9cc7a62bfcf8",
      "name": "Set Config",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -448,
        304
      ]
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "={{ $('Set Config').first().json.spreadsheet_id }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Performance Log",
          "mode": "name"
        },
        "options": {}
      },
      "id": "016aac30-4c12-4277-a247-61310171e452",
      "name": "Read Performance Log",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        -208,
        304
      ],
      "alwaysOutputData": true,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "3IOU2VjBnR4hGohx",
          "name": "Google Sheets account"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nconst results = [];\n\nfor (const item of items) {\n  const d = item.json;\n  if (d.request_id && d.request_id.length > 5 && d.request_id !== 'pending') {\n    const hasStats = d.views && String(d.views).length > 0 && Number(d.views) > 0;\n    const postDate = d.post_date ? new Date(d.post_date) : null;\n    const isRecent = postDate ? (Date.now() - postDate.getTime()) < 7*24*60*60*1000 : true;\n    if (!hasStats || isRecent) {\n      results.push({ json: { request_id: d.request_id, _skip: false } });\n    }\n  }\n}\n\n// ALWAYS output at least 1 item so the chain never stalls\nif (results.length === 0) {\n  return [{ json: { request_id: 'skip', _skip: true } }];\n}\nreturn results;\n"
      },
      "id": "5d9a3622-7422-436c-b98f-b0966fd37566",
      "name": "Filter Posts for Stats",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        32,
        304
      ]
    },
    {
      "parameters": {
        "url": "=https://api.upload-post.com/api/uploadposts/post-analytics/{{ $json.request_id }}?platform=tiktok",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "c55a136a-8311-452d-910c-ef0ba2d404e5",
      "name": "Get Post Analytics",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        272,
        304
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "47dO31ED0WIaJkR6",
          "name": "Upload-post mutonseo"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nconst filterItems = $('Filter Posts for Stats').all();\nconst results = [];\n\nfor (let i = 0; i < items.length; i++) {\n  const filterItem = filterItems[i]?.json || {};\n  const a = items[i].json;\n\n  // Skip if this was a pass-through item\n  if (filterItem._skip) {\n    results.push({ json: { request_id: 'skip', _skip: true, views: '0', likes: '0', comments: '0', shares: '0', engagement_rate: '0%' } });\n    continue;\n  }\n\n  let views = 0, likes = 0, comments = 0, shares = 0;\n  const sources = [a?.platforms?.tiktok?.post_metrics, a?.tiktok?.post_metrics, a?.post_metrics, a];\n  for (const m of sources) {\n    if (m && (m.views || m.view_count || m.play_count)) {\n      views = m.views || m.view_count || m.play_count || 0;\n      likes = m.likes || m.like_count || m.digg_count || 0;\n      comments = m.comments || m.comment_count || 0;\n      shares = m.shares || m.share_count || 0;\n      break;\n    }\n  }\n  const eng = views > 0 ? (((likes+comments+shares)/views)*100).toFixed(2)+'%' : '0%';\n  results.push({ json: { request_id: filterItem.request_id, _skip: false, views: String(views), likes: String(likes), comments: String(comments), shares: String(shares), engagement_rate: eng } });\n}\n\nreturn results;\n"
      },
      "id": "3e0b27fd-b6be-407d-a46f-1a5ef4c07f45",
      "name": "Extract Stats",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        512,
        304
      ]
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "value": "={{ $('Set Config').first().json.spreadsheet_id }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Performance Log",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "request_id": "={{ $json.request_id }}",
            "views": "={{ $json.views }}",
            "likes": "={{ $json.likes }}",
            "comments": "={{ $json.comments }}",
            "shares": "={{ $json.shares }}",
            "engagement_rate": "={{ $json.engagement_rate }}"
          },
          "matchingColumns": [
            "request_id"
          ],
          "schema": [
            {
              "id": "request_id",
              "displayName": "request_id",
              "required": false,
              "defaultMatch": true,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "views",
              "displayName": "views",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            },
            {
              "id": "likes",
              "displayName": "likes",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            },
            {
              "id": "comments",
              "displayName": "comments",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            },
            {
              "id": "shares",
              "displayName": "shares",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            },
            {
              "id": "engagement_rate",
              "displayName": "engagement_rate",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            }
          ]
        },
        "options": {}
      },
      "id": "22463928-dab1-4fb4-9098-9701e8de6efe",
      "name": "Update Stats",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        720,
        304
      ],
      "alwaysOutputData": true,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "3IOU2VjBnR4hGohx",
          "name": "Google Sheets account"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "aggregate": "aggregateAllItemData",
        "options": {}
      },
      "id": "9d9083ac-6cb6-4667-b87c-1293dafc61f8",
      "name": "Aggregate",
      "type": "n8n-nodes-base.aggregate",
      "typeVersion": 1,
      "position": [
        944,
        304
      ]
    },
    {
      "parameters": {
        "url": "=https://api.upload-post.com/api/analytics/{{ $('Set Config').first().json.upload_post_user }}?platforms=tiktok",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "89284274-5477-4607-81fc-4b1eee0d827c",
      "name": "Get Profile Analytics",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1120,
        304
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "47dO31ED0WIaJkR6",
          "name": "Upload-post mutonseo"
        }
      }
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "={{ $('Set Config').first().json.spreadsheet_id }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Agent Skill",
          "mode": "name"
        },
        "options": {}
      },
      "id": "0eef627e-b5ef-4144-b23b-ad184aa60200",
      "name": "Read Agent Skill",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1280,
        304
      ],
      "alwaysOutputData": true,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "3IOU2VjBnR4hGohx",
          "name": "Google Sheets account"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "={{ $('Set Config').first().json.spreadsheet_id }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Performance Log",
          "mode": "name"
        },
        "options": {}
      },
      "id": "5af68d2e-5097-48e7-8314-b14d058da2d7",
      "name": "Read Fresh Data",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1472,
        304
      ],
      "alwaysOutputData": true,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "3IOU2VjBnR4hGohx",
          "name": "Google Sheets account"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const config = $('Set Config').first().json;\nconst sheetItems = $('Read Fresh Data').all();\n\n// Agent Skill\nlet agentSkill = \"No learnings yet. First run!\";\ntry { const s = $('Read Agent Skill').all(); if (s?.[0]?.json?.skill_content) agentSkill = s[0].json.skill_content; } catch(e) {}\n\n// Profile Analytics\nlet profile = \"Not available.\";\ntry {\n  const ad = $('Get Profile Analytics').first().json;\n  if (ad && !ad.error) {\n    const tk = ad.tiktok || ad;\n    profile = `Followers: ${tk.followers||'?'} | Impressions: ${tk.impressions||'?'} | Likes: ${tk.likes||'?'}`;\n    if (tk.reach_timeseries?.length > 0) {\n      profile += '\\nLast 7d views: ' + tk.reach_timeseries.slice(-7).map(d => `${d.date||'?'}:${d.value||d.views||'?'}`).join(', ');\n    }\n  }\n} catch(e) {}\n\n// Post History\nlet posts = \"\"; let total = 0;\nconst hasData = sheetItems?.length > 0 && sheetItems[0].json?.post_date;\nif (hasData) {\n  const p = sheetItems.map(i => i.json); total = p.length;\n  posts = p.map((x,i) => `Post ${i+1} (${x.post_date}): \"${x.title}\" | Views:${x.views||'?'} Likes:${x.likes||'?'} Comments:${x.comments||'?'} Shares:${x.shares||'?'} Eng:${x.engagement_rate||'?'} | Strategy: ${x.strategy_notes||'?'}`).join('\\n');\n  const wv = p.filter(x => Number(x.views) > 0);\n  if (wv.length > 0) {\n    const avg = k => (wv.reduce((s,x) => s+Number(x[k]||0),0)/wv.length).toFixed(0);\n    posts += `\\n\\nAVG: Views ${avg('views')} | Likes ${avg('likes')} | Comments ${avg('comments')}`;\n    const best = [...wv].sort((a,b) => Number(b.views)-Number(a.views))[0];\n    posts += `\\nBEST: \"${best.title}\" (${best.views} views) Strategy: ${best.strategy_notes}`;\n  }\n} else { posts = \"FIRST POST - No history. Establish baseline!\"; }\n\nreturn { json: { agent_skill: agentSkill, profile_analytics: profile, performance_summary: posts, total_posts: total, product_name: config.product_name, product_description: config.product_description }};"
      },
      "id": "088aa2f3-0865-4720-ac1d-3906e55926ca",
      "name": "Prepare Context",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1616,
        304
      ]
    },
    {
      "parameters": {
        "jsonSchemaExample": "{\"title\": \"Stop posting one by one\", \"strategy_notes\": \"Testing question hooks - skill says 3x more views\", \"prompt1\": \"9:16 bold white text 'STILL POSTING ONE BY ONE?' on dark gradient, modern TikTok style\", \"prompt2\": \"9:16 clean slide 'ONE CLICK = ALL PLATFORMS' with social icons circle, gradient bg\", \"prompt3\": \"9:16 slide 'SAVE 4+ HOURS WEEKLY' warm lifestyle bg, creator at desk\", \"prompt4\": \"9:16 bold CTA 'TRY UPLOAD POST FREE' vibrant gradient, arrow down\", \"hashtags\": \"#socialmedia #contentcreator #automation #tiktokgrowth #fyp #viral #saas #marketing\", \"engagement_hook\": \"How many platforms do you post to?\"}"
      },
      "id": "af4eb90b-cfd9-4678-9fe7-39ff9afc7fe2",
      "name": "Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "typeVersion": 1.2,
      "position": [
        2032,
        560
      ]
    },
    {
      "parameters": {},
      "id": "263aa0a9-3cc7-4e37-a75b-b0fc286624f6",
      "name": "Think Tool",
      "type": "@n8n/n8n-nodes-langchain.toolThink",
      "typeVersion": 1,
      "position": [
        1920,
        560
      ]
    },
    {
      "parameters": {
        "modelName": "models/gemini-3.1-pro-preview",
        "options": {}
      },
      "id": "617342f1-a386-47e3-8aa4-7bd58d75d892",
      "name": "Gemini 2.5 Pro",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "typeVersion": 1,
      "position": [
        1824,
        560
      ],
      "credentials": {
        "googlePalmApi": {
          "id": "Pz5rkKL1ZRGrSYoQ",
          "name": "key studio google"
        }
      }
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=== PRODUCT ===\n{{ $('Prepare Context').first().json.product_name }}: {{ $('Prepare Context').first().json.product_description }}\n\n=== TIKTOK PROFILE ===\n{{ $('Prepare Context').first().json.profile_analytics }}\n\n=== POST HISTORY ===\n{{ $('Prepare Context').first().json.performance_summary }}\n\n=== TASK ===\nCreate an optimized TikTok carousel using your skill + this data.\n\nReturn ONLY valid JSON:\n{\n  \"title\": \"<TikTok caption with hook, max 150 chars>\",\n  \"strategy_notes\": \"<What and WHY based on learnings, max 200 chars>\",\n  \"prompt1\": \"<Slide 1: HOOK - visual + bold text to display, 9:16>\",\n  \"prompt2\": \"<Slide 2: FEATURE - visual + text about product, 9:16>\",\n  \"prompt3\": \"<Slide 3: BENEFIT - visual + text, 9:16>\",\n  \"prompt4\": \"<Slide 4: CTA - visual + call-to-action text, 9:16>\",\n  \"hashtags\": \"<8-12 hashtags>\",\n  \"engagement_hook\": \"<Question for comments>\"\n}",
        "hasOutputParser": true,
        "options": {
          "systemMessage": "You are Larry, an elite TikTok growth strategist with persistent memory.\n\n=== YOUR ACCUMULATED SKILL (learnings from ALL past runs) ===\n{{ $('Prepare Context').first().json.agent_skill }}\n=== END SKILL ===\n\nCreate 4-image TikTok carousel slideshows (9:16 vertical) to promote a SaaS product.\n\nCRITICAL - Each image MUST contain visible designed TEXT:\n- Slide 1: Powerful HOOK text (big, bold, scroll-stopping)\n- Slide 2: Key feature/benefit with text overlay\n- Slide 3: Another angle/use case/proof with text\n- Slide 4: Strong CTA text\n\nApply your skill learnings. Build on winners. Avoid losers.\nOutput strictly valid compact JSON only."
        }
      },
      "id": "24667f3d-8fbc-4129-8699-59af8a9c1132",
      "name": "Strategy Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 1.9,
      "position": [
        1824,
        304
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "name": "title",
              "type": "string",
              "value": "={{ $json.output.title }}",
              "id": "c8f58cec-81df-4d3a-9ec7-e5f360d4a42c"
            },
            {
              "name": "strategy_notes",
              "type": "string",
              "value": "={{ $json.output.strategy_notes }}",
              "id": "fe06b575-9f23-49a0-b5bc-e52ada16dd79"
            },
            {
              "name": "prompt1",
              "type": "string",
              "value": "={{ $json.output.prompt1 }}",
              "id": "161e1e7a-dbc9-404b-804f-e2db57eec887"
            },
            {
              "name": "prompt2",
              "type": "string",
              "value": "={{ $json.output.prompt2 }}",
              "id": "709d9b21-c21e-4761-bf4f-8db530275058"
            },
            {
              "name": "prompt3",
              "type": "string",
              "value": "={{ $json.output.prompt3 }}",
              "id": "ee55fc5a-142d-4dc7-b34e-e608845ff933"
            },
            {
              "name": "prompt4",
              "type": "string",
              "value": "={{ $json.output.prompt4 }}",
              "id": "6940ebd9-539e-4a6f-85c7-c0c2d7fe408a"
            },
            {
              "name": "hashtags",
              "type": "string",
              "value": "={{ $json.output.hashtags }}",
              "id": "4d17e727-2c75-4e11-a508-715f92a0c264"
            },
            {
              "name": "engagement_hook",
              "type": "string",
              "value": "={{ $json.output.engagement_hook }}",
              "id": "e599377a-5324-40f3-b464-13e5d6aebf9e"
            }
          ]
        },
        "options": {}
      },
      "id": "ee904cb1-a478-42dd-bccc-e6b5399ca032",
      "name": "Set Vars",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        2176,
        304
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://fal.run/fal-ai/nano-banana-2",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify({ prompt: $('Set Vars').first().json.prompt1, num_images: 1, aspect_ratio: '9:16', output_format: 'png' }) }}",
        "options": {}
      },
      "id": "490414ec-1ebe-4d31-8870-9e92f0a621eb",
      "name": "Generate Image 1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2368,
        64
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "lj8v2XS8OSqBUXwk",
          "name": "fal.ai victor"
        }
      }
    },
    {
      "parameters": {
        "fieldToSplitOut": "images",
        "options": {}
      },
      "id": "f7fef427-9734-4426-8cea-cab126811d62",
      "name": "Extract URL 1",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        2560,
        64
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://fal.run/fal-ai/nano-banana-2",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify({ prompt: $('Set Vars').first().json.prompt2, num_images: 1, aspect_ratio: '9:16', output_format: 'png' }) }}",
        "options": {}
      },
      "id": "2579c31e-17c0-4aa4-b964-0d065cf74668",
      "name": "Generate Image 2",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2368,
        224
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "lj8v2XS8OSqBUXwk",
          "name": "fal.ai victor"
        }
      }
    },
    {
      "parameters": {
        "fieldToSplitOut": "images",
        "options": {}
      },
      "id": "3279ac16-2265-490e-aa57-6185e20c8431",
      "name": "Extract URL 2",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        2560,
        224
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://fal.run/fal-ai/nano-banana-2",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify({ prompt: $('Set Vars').first().json.prompt3, num_images: 1, aspect_ratio: '9:16', output_format: 'png' }) }}",
        "options": {}
      },
      "id": "1e28bcc4-7eda-45c1-aabd-d3f3258691dd",
      "name": "Generate Image 3",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2368,
        384
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "lj8v2XS8OSqBUXwk",
          "name": "fal.ai victor"
        }
      }
    },
    {
      "parameters": {
        "fieldToSplitOut": "images",
        "options": {}
      },
      "id": "a9935c6f-54ea-4ce9-a2a2-7baebbed05ac",
      "name": "Extract URL 3",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        2560,
        384
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://fal.run/fal-ai/nano-banana-2",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify({ prompt: $('Set Vars').first().json.prompt4, num_images: 1, aspect_ratio: '9:16', output_format: 'png' }) }}",
        "options": {}
      },
      "id": "5d784b25-52b4-4390-b53f-8dea07a40f28",
      "name": "Generate Image 4",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2368,
        544
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "lj8v2XS8OSqBUXwk",
          "name": "fal.ai victor"
        }
      }
    },
    {
      "parameters": {
        "fieldToSplitOut": "images",
        "options": {}
      },
      "id": "1fa984cd-ef7e-4b3a-b849-4dc4340b5ece",
      "name": "Extract URL 4",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        2560,
        544
      ]
    },
    {
      "parameters": {
        "numberInputs": 4
      },
      "id": "b41373ce-c47f-4f4e-8453-337195544a9b",
      "name": "Merge Images",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.1,
      "position": [
        2768,
        224
      ]
    },
    {
      "parameters": {
        "aggregate": "aggregateAllItemData",
        "options": {}
      },
      "id": "568262ab-b916-4112-85f5-c6b792e2a1d9",
      "name": "Aggregate Images",
      "type": "n8n-nodes-base.aggregate",
      "typeVersion": 1,
      "position": [
        2976,
        256
      ]
    },
    {
      "parameters": {
        "modelId": {
          "__rl": true,
          "value": "gpt-4.1",
          "mode": "list",
          "cachedResultName": "GPT-4.1"
        },
        "messages": {
          "values": [
            {
              "content": "You write viral TikTok carousel descriptions. Short, punchy, emojis ok.",
              "role": "system"
            },
            {
              "content": "=Write TikTok description for {{ $('Set Config').first().json.product_name }}.\nHook: {{ $('Set Vars').first().json.title }}\nHashtags: {{ $('Set Vars').first().json.hashtags }}\nQuestion: {{ $('Set Vars').first().json.engagement_hook }}\n\n1.Hook 2.Value 3.CTA 4.Hashtags 5.Question\nMax 300 chars. ONLY the description."
            }
          ]
        },
        "options": {}
      },
      "id": "6c53693c-13f5-4ea0-ad60-fd621882780a",
      "name": "Generate Description",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1.4,
      "position": [
        3136,
        256
      ],
      "credentials": {
        "openAiApi": {
          "id": "XJdxgMSXFgwReSsh",
          "name": "n8n key"
        }
      }
    },
    {
      "parameters": {
        "user": "__manual_user__",
        "userManual": "={{ $('Set Config').first().json.upload_post_user }}",
        "platform": [
          "tiktok"
        ],
        "title": "={{ $json.message.content }}",
        "tiktokTitle": "={{ $('Set Vars').first().json.title }}",
        "photos": "={{ $('Extract URL 1').first().json.url }},{{ $('Extract URL 2').first().json.url }},{{ $('Extract URL 3').first().json.url }},{{ $('Extract URL 4').first().json.url }}",
        "tiktokAutoAddMusic": true,
        "tiktokPhotoDescription": "={{ $json.message.content }}"
      },
      "id": "074fcaeb-a280-4a6b-8e30-275976b0cc22",
      "name": "Upload to TikTok",
      "type": "n8n-nodes-upload-post.uploadPost",
      "typeVersion": 1,
      "position": [
        3408,
        256
      ],
      "credentials": {
        "uploadPostApi": {
          "id": "frehTItx3BCdCJHh",
          "name": "Upload Post account 2"
        }
      }
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "={{ $('Set Config').first().json.spreadsheet_id }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Performance Log",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "post_date": "={{ $now.format('yyyy-MM-dd HH:mm') }}",
            "request_id": "={{ $json.request_id || '' }}",
            "post_url": "={{ $json.post_url || '' }}",
            "platform_post_id": "={{ $json.platform_post_id || '' }}",
            "title": "={{ $('Set Vars').first().json.title }}",
            "description": "={{ $('Generate Description').first().json.message.content }}",
            "strategy_notes": "={{ $('Set Vars').first().json.strategy_notes }}",
            "prompts_summary": "={{ $('Set Vars').first().json.prompt1.substring(0,100) }}...",
            "views": "",
            "likes": "",
            "comments": "",
            "shares": "",
            "engagement_rate": ""
          },
          "schema": [
            {
              "id": "post_date",
              "displayName": "post_date",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "request_id",
              "displayName": "request_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "post_url",
              "displayName": "post_url",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "platform_post_id",
              "displayName": "platform_post_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "title",
              "displayName": "title",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "description",
              "displayName": "description",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "strategy_notes",
              "displayName": "strategy_notes",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "prompts_summary",
              "displayName": "prompts_summary",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "views",
              "displayName": "views",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "likes",
              "displayName": "likes",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "comments",
              "displayName": "comments",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "shares",
              "displayName": "shares",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "engagement_rate",
              "displayName": "engagement_rate",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            }
          ]
        },
        "options": {}
      },
      "id": "074de448-6e63-4521-a38b-869b636501ba",
      "name": "Log to Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        3568,
        256
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "3IOU2VjBnR4hGohx",
          "name": "Google Sheets account"
        }
      }
    },
    {
      "parameters": {
        "modelId": {
          "__rl": true,
          "value": "gpt-4.1",
          "mode": "list",
          "cachedResultName": "GPT-4.1"
        },
        "messages": {
          "values": [
            {
              "content": "You maintain a concise skill file for an AI content creator. Analyze data, write actionable learnings.",
              "role": "system"
            },
            {
              "content": "=== CURRENT SKILL ===\n{{ $('Prepare Context').first().json.agent_skill }}\n\n=== LATEST POST ===\nTitle: {{ $('Set Vars').first().json.title }}\nStrategy: {{ $('Set Vars').first().json.strategy_notes }}\n\n=== ALL DATA ===\n{{ $('Prepare Context').first().json.performance_summary }}\nProfile: {{ $('Prepare Context').first().json.profile_analytics }}\n\nWrite an UPDATED skill file (max 2000 chars). Sections:\n## What Works (data-backed)\n## What Doesn't Work\n## Key Metrics (averages, best post, trend)\n## Strategy Playbook (top 3 to try next)\n## Product Angles (best ways to promote)\n\nBe specific with numbers. Update existing insights, don't just append. Output ONLY the skill content."
            }
          ]
        },
        "options": {}
      },
      "id": "5153b194-c65e-4d9e-a6e0-da1fd06ece9a",
      "name": "Update Skill",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1.4,
      "position": [
        3744,
        256
      ],
      "credentials": {
        "openAiApi": {
          "id": "XJdxgMSXFgwReSsh",
          "name": "n8n key"
        }
      }
    },
    {
      "parameters": {
        "operation": "appendOrUpdate",
        "documentId": {
          "__rl": true,
          "value": "={{ $('Set Config').first().json.spreadsheet_id }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Agent Skill",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "id": "skill",
            "skill_content": "={{ $json.message.content }}",
            "last_updated": "={{ $now.format('yyyy-MM-dd HH:mm') }}"
          },
          "matchingColumns": [
            "id"
          ],
          "schema": [
            {
              "id": "id",
              "displayName": "id",
              "required": false,
              "defaultMatch": true,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "skill_content",
              "displayName": "skill_content",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            },
            {
              "id": "last_updated",
              "displayName": "last_updated",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            }
          ]
        },
        "options": {}
      },
      "id": "d595715b-88ac-4b9c-9946-ce8379a6728b",
      "name": "Write Skill",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        4048,
        256
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "3IOU2VjBnR4hGohx",
          "name": "Google Sheets account"
        }
      }
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -640,
        528
      ],
      "id": "1a6eae6d-042c-48d2-a8f5-c425b4d6c8c8",
      "name": "When clicking ‘Execute workflow’"
    },
    {
      "parameters": {
        "content": "## 🛠️ Setup Guide\n\n  ### 1. Set Config Node\n  Edit the **Set Config** node with your values:\n  - `product_name` — Your product name\n  - `product_description` — Short description of your product\n  - `upload_post_user` — Your Upload Post profile username\n  - `upload_post_api_key` — Your Upload Post API key (from\n  upload-post.com dashboard)\n  - `spreadsheet_id` — Google Sheet ID (already set)\n\n  ### 2. Google Sheet\n  The sheet needs 2 tabs:\n  - **Performance Log** — columns: `post_date, request_id,\n  post_url, platform_post_id, title, description, strategy_notes,\n  prompts_summary, views, likes, comments, shares, engagement_rate`\n  - **Agent Skill** — columns: `id, skill_content, last_updated`\n - Copy to your drive this template and put the id : https://docs.google.com/spreadsheets/d/1jk1GvBd6b4DopVH1VXkIsY0yPoCmE-DsA9Kun0ZAfQU/edit?usp=sharing \n\n  ### 3. Credentials Required\n  - **Google Sheets OAuth2** — for reading/writing the sheet\n  - **Google PaLM (Gemini)** — for the Strategy Agent (Gemini 2.5\n  Pro)\n  - **fal.ai (Header Auth)** — for image generation\n  - **OpenAI** — for descriptions + skill updates (GPT-4.1)\n  - **Upload Post** — for publishing to TikTok\n  - **Upload Post (Header Auth)** — same API key, for analytics\n  endpoints\n\n  ### 4. How It Works\n  Every run:\n  1. Reads all posts from the sheet\n  2. Fetches fresh TikTok stats for each post via Upload Post API\n  3. Updates stats in the sheet (views, likes, etc.)\n  4. Reads the Agent Skill file (accumulated learnings)\n  5. Gemini 2.5 Pro creates an optimized carousel strategy based on\n   ALL past data + skill\n  6. Generates 4 images (9:16) with text via fal.ai\n  7. GPT-4.1 writes the TikTok description\n  8. Publishes to TikTok via Upload Post\n  9. Logs the new post to the sheet\n  10. GPT-4.1 analyzes all data and updates the skill file\n\n  ### 5. First Run\n  On the first run, the sheet is empty so the agent has no history.\n   It will create a baseline post. From the 2nd run onwards, it\n  starts learning and improving.",
        "height": 1100,
        "width": 744,
        "color": 5
      },
      "id": "8f742164-ac55-480c-842d-99ad108455f9",
      "name": "Note Setup1",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -1392,
        112
      ]
    }
  ],
  "pinData": {},
  "connections": {
    "Daily Schedule": {
      "main": [
        [
          {
            "node": "Set Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Config": {
      "main": [
        [
          {
            "node": "Read Performance Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Performance Log": {
      "main": [
        [
          {
            "node": "Filter Posts for Stats",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Posts for Stats": {
      "main": [
        [
          {
            "node": "Get Post Analytics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Post Analytics": {
      "main": [
        [
          {
            "node": "Extract Stats",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Stats": {
      "main": [
        [
          {
            "node": "Update Stats",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Stats": {
      "main": [
        [
          {
            "node": "Aggregate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate": {
      "main": [
        [
          {
            "node": "Get Profile Analytics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Profile Analytics": {
      "main": [
        [
          {
            "node": "Read Agent Skill",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Agent Skill": {
      "main": [
        [
          {
            "node": "Read Fresh Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Fresh Data": {
      "main": [
        [
          {
            "node": "Prepare Context",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini 2.5 Pro": {
      "ai_languageModel": [
        [
          {
            "node": "Strategy Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Think Tool": {
      "ai_tool": [
        [
          {
            "node": "Strategy Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Strategy Agent",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Context": {
      "main": [
        [
          {
            "node": "Strategy Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Strategy Agent": {
      "main": [
        [
          {
            "node": "Set Vars",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Vars": {
      "main": [
        [
          {
            "node": "Generate Image 1",
            "type": "main",
            "index": 0
          },
          {
            "node": "Generate Image 2",
            "type": "main",
            "index": 0
          },
          {
            "node": "Generate Image 3",
            "type": "main",
            "index": 0
          },
          {
            "node": "Generate Image 4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Image 1": {
      "main": [
        [
          {
            "node": "Extract URL 1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract URL 1": {
      "main": [
        [
          {
            "node": "Merge Images",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Image 2": {
      "main": [
        [
          {
            "node": "Extract URL 2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract URL 2": {
      "main": [
        [
          {
            "node": "Merge Images",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Generate Image 3": {
      "main": [
        [
          {
            "node": "Extract URL 3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract URL 3": {
      "main": [
        [
          {
            "node": "Merge Images",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Generate Image 4": {
      "main": [
        [
          {
            "node": "Extract URL 4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract URL 4": {
      "main": [
        [
          {
            "node": "Merge Images",
            "type": "main",
            "index": 3
          }
        ]
      ]
    },
    "Merge Images": {
      "main": [
        [
          {
            "node": "Aggregate Images",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Images": {
      "main": [
        [
          {
            "node": "Generate Description",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Description": {
      "main": [
        [
          {
            "node": "Upload to TikTok",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload to TikTok": {
      "main": [
        [
          {
            "node": "Log to Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log to Sheets": {
      "main": [
        [
          {
            "node": "Update Skill",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Skill": {
      "main": [
        [
          {
            "node": "Write Skill",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking ‘Execute workflow’": {
      "main": [
        [
          {
            "node": "Set Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "callerPolicy": "workflowsFromSameOwner",
    "availableInMCP": false
  },
  "versionId": "1ce6e012-d178-4970-8785-008a6d6d5634",
  "meta": {
    "instanceId": "3378b0d68c3b7ebfc71b79896d94e1a044dec38e99a1160aed4e9c323910fbe2"
  },
  "id": "GqeKzQgvF2M1wvNz",
  "tags": []
}