{
  "id": "0ar5o4lW1fUpHueMAC3PL",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Create Pro-Level Social Media Carousels & Auto-Publish with Blotato",
  "tags": [
    {
      "id": "8PabTC3sQ4CJ83aT",
      "name": "nano-banana-pro",
      "createdAt": "2025-12-12T15:41:55.479Z",
      "updatedAt": "2025-12-12T15:41:55.479Z"
    },
    {
      "id": "lcp924nVN9Y3IFFq",
      "name": "Blotato",
      "createdAt": "2026-02-21T01:07:22.668Z",
      "updatedAt": "2026-02-21T01:07:22.668Z"
    }
  ],
  "nodes": [
    {
      "id": "a4153a3e-38e5-40ba-bcbb-caed0ab0cd70",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1088,
        -32
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 30
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "79710fea-ada9-47d9-a3c7-eb408d43e3fe",
      "name": "Get Rows",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -880,
        -32
      ],
      "parameters": {
        "options": {
          "returnFirstMatch": true
        },
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "Status"
            },
            {
              "lookupValue": "={{ $now.toFormat('yyyy-MM-dd') }}",
              "lookupColumn": "Post Date"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1A9cV_0pFBGXpQtoWQaKSwFCIEIZhaxROn8bp6KiPmeI/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1A9cV_0pFBGXpQtoWQaKSwFCIEIZhaxROn8bp6KiPmeI",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1A9cV_0pFBGXpQtoWQaKSwFCIEIZhaxROn8bp6KiPmeI/edit?usp=drivesdk",
          "cachedResultName": "carousel workflow"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "5ad3bd61-8b15-4511-8072-99ef2f582736",
      "name": "Set Processing",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -656,
        -32
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "Processing",
            "row_number": "={{ $json.row_number }}"
          },
          "schema": [
            {
              "id": "Brand Logo URL",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Brand Logo URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Product URL",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Product URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Product Description",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Product Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Product Image(s) URL",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Product Image(s) URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Specification",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Specification",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Post Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Post Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Post Hour",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Post Hour",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Socials",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Socials",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Post URL",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Post URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "row_number"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1A9cV_0pFBGXpQtoWQaKSwFCIEIZhaxROn8bp6KiPmeI/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1A9cV_0pFBGXpQtoWQaKSwFCIEIZhaxROn8bp6KiPmeI",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1A9cV_0pFBGXpQtoWQaKSwFCIEIZhaxROn8bp6KiPmeI/edit?usp=drivesdk",
          "cachedResultName": "carousel workflow"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "11ba211b-969d-4c66-a03f-def1c1f5fde4",
      "name": "Has Description?",
      "type": "n8n-nodes-base.if",
      "position": [
        -432,
        -32
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "0b449348-d4dc-4ae5-85bc-0d01aa22f91d",
              "operator": {
                "type": "string",
                "operation": "empty",
                "singleValue": true
              },
              "leftValue": "={{ $('Get Rows').item.json['Product Description'] }}",
              "rightValue": "={{ $('Get Rows').item.json['Product Image(s) URL'] }}"
            },
            {
              "id": "2232cb69-fd96-4287-b2cc-d41f552732d6",
              "operator": {
                "type": "string",
                "operation": "empty",
                "singleValue": true
              },
              "leftValue": "={{ $('Get Rows').item.json['Product Image(s) URL'] }}",
              "rightValue": ""
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.3
    },
    {
      "id": "7d8276ec-f06f-4e64-90ea-510e3ef189bd",
      "name": "Merge Data",
      "type": "n8n-nodes-base.code",
      "position": [
        0,
        48
      ],
      "parameters": {
        "jsCode": "const sheet = $('Get Rows').first().json;\nlet scrapedDesc = '';\nlet scrapedImage = '';\n\ntry {\n  const scraped = $('Scrape Product').first().json;\n  const meta = scraped.metadata || {};\n  \n  // Description fallback chain\n  scrapedDesc = meta['og:description'] \n    || meta['twitter:description'] \n    || meta['description'] \n    || '';\n\n  // Image fallback chain \u2014 try every possible metadata field\n  const imageCandidate = meta['og:image:secure_url']\n    || meta['og:image']\n    || meta['og:image:url']\n    || meta['twitter:image']\n    || meta['twitter:image:src']\n    || meta['image']\n    || meta['thumbnail']\n    || meta['sailthru.image.full']\n    || meta['msapplication-TileImage']\n    || '';\n\n  // If metadata failed, try to find image URLs in the raw scraped content\n  if (!imageCandidate && scraped.content) {\n    const imgRegex = /https?:\\/\\/[^\\s\"'<>]+?\\.(jpg|jpeg|png|webp|avif)(\\?[^\\s\"'<>]*)?/gi;\n    const matches = scraped.content.match(imgRegex);\n    if (matches && matches.length > 0) {\n      // Filter out tiny images (icons, tracking pixels) by looking for size hints\n      const goodImages = matches.filter(url => {\n        const lower = url.toLowerCase();\n        return !lower.includes('favicon') \n          && !lower.includes('icon') \n          && !lower.includes('logo')\n          && !lower.includes('pixel')\n          && !lower.includes('badge')\n          && !lower.includes('1x1');\n      });\n      scrapedImage = goodImages[0] || matches[0] || '';\n    }\n  } else {\n    scrapedImage = imageCandidate;\n  }\n\n  // Fix protocol-relative URLs (starting with //)\n  if (scrapedImage && scrapedImage.startsWith('//')) {\n    scrapedImage = 'https:' + scrapedImage;\n  }\n\n  // Fix relative URLs (starting with /)\n  if (scrapedImage && scrapedImage.startsWith('/') && !scrapedImage.startsWith('//')) {\n    const productURL = sheet['Product URL']?.trim() || '';\n    try {\n      const base = new URL(productURL);\n      scrapedImage = base.origin + scrapedImage;\n    } catch (e) {}\n  }\n\n  // Strip whitespace and quotes that sometimes wrap URLs\n  scrapedImage = scrapedImage.replace(/^[\"'\\s]+|[\"'\\s]+$/g, '');\n\n} catch (e) {}\n\nconst description = sheet['Product Description']?.trim() || scrapedDesc;\nconst imagesRaw = sheet['Product Image(s) URL']?.trim() || scrapedImage;\n\nconst images = imagesRaw\n  .split(/,(?=https?:\\/\\/)/)\n  .map(url => url.trim())\n  .filter(Boolean);\n\nreturn [{\n  json: {\n    rowNumber: sheet['row_number'],\n    brandLogo: sheet['Brand Logo URL']?.trim() || '',\n    productURL: sheet['Product URL']?.trim() || '',\n    productDescription: description,\n    productImages: images,\n    specification: sheet['Specification']?.trim() || '',\n    postDate: sheet['Post Date']?.trim() || '',\n    postHour: sheet['Post Hour']?.trim() || '',\n    socials: sheet['Socials'].split(',').map(s => s.trim().toLowerCase()),\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "b61b39a2-4268-4b68-b3ba-1d0cb761cfe7",
      "name": "Scrape Product",
      "type": "n8n-nodes-base.jinaAi",
      "position": [
        -208,
        -112
      ],
      "parameters": {
        "url": "={{ $('Get Rows').item.json['Product URL'] }}",
        "options": {
          "outputFormat": "",
          "enableImageCaptioning": true
        },
        "requestOptions": {}
      },
      "credentials": {
        "jinaAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "de8f22e7-80a3-42a7-b17f-af8517ec6dee",
      "name": "Parse AI Response",
      "type": "n8n-nodes-base.code",
      "position": [
        -688,
        400
      ],
      "parameters": {
        "jsCode": "const parsed = $input.first().json.output;\n\nreturn [{\n  json: {\n    caption: parsed.caption,\n    hashtags: parsed.hashtags,\n    slides: parsed.slides,\n    productImages: $('Merge Data').first().json.productImages,\n    brandLogo: $('Merge Data').first().json.brandLogo,\n    rowNumber: $('Merge Data').first().json.rowNumber,\n    socials: $('Merge Data').first().json.socials,\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "3facdfe4-e477-465b-8ecd-218e36f7dd25",
      "name": "Fetch Images to Base64",
      "type": "n8n-nodes-base.code",
      "position": [
        -464,
        400
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json;\n\nasync function toBase64(url) {\n  if (!url) return null;\n  const response = await this.helpers.httpRequest({\n    method: 'GET',\n    url: url,\n    encoding: 'arraybuffer',\n    returnFullResponse: true,\n  });\n  return Buffer.from(response.body).toString('base64');\n}\n\nconst productImagesBase64 = await Promise.all(\n  data.productImages.map(url => toBase64(url))\n);\n\nconst brandLogoBase64 = await toBase64(data.brandLogo);\n\nreturn [{\n  json: {\n    ...data,\n    productImagesBase64,\n    brandLogoBase64,\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "d9693b4e-6bca-406f-a33e-9e9f734d1f81",
      "name": "Prepare Nano Banana Items",
      "type": "n8n-nodes-base.code",
      "position": [
        -240,
        400
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json;\nconst items = [];\n\nconst imageCount = data.productImagesBase64.filter(Boolean).length;\n\nfor (const slide of data.slides) {\n  const parts = [];\n\n  // Build prompt with image awareness\n  let prompt = slide.imagePrompt;\n\n  if (imageCount > 1) {\n    prompt += `\\n\\nPRODUCT IMAGES: You are provided ${imageCount} different product images (different sizes/angles of the same product). Use ALL of them in the composition \u2014 feature the main one as the hero and use the others as supporting elements at different scales or angles to create depth and variety. Do not ignore any provided product image.`;\n  }\n\n  parts.push({ text: prompt });\n\n  // Add product images with labels\n  for (let i = 0; i < data.productImagesBase64.length; i++) {\n    if (data.productImagesBase64[i]) {\n      parts.push({\n        inline_data: {\n          mime_type: 'image/jpeg',\n          data: data.productImagesBase64[i]\n        }\n      });\n    }\n  }\n\n  // Add brand logo\n  if (data.brandLogoBase64) {\n    parts.push({\n      inline_data: {\n        mime_type: 'image/png',\n        data: data.brandLogoBase64\n      }\n    });\n  }\n\n  items.push({\n    json: {\n      meta: {\n        slideNumber: slide.slideNumber,\n        headline: slide.headline,\n        body: slide.body,\n        caption: data.caption,\n        hashtags: data.hashtags,\n        rowNumber: data.rowNumber,\n        socials: data.socials,\n      },\n      parts,\n      generationConfig: {\n        responseModalities: [\"TEXT\", \"IMAGE\"],\n        imageConfig: {\n          aspectRatio: \"1:1\",\n          imageSize: \"2K\"\n        }\n      }\n    }\n  });\n}\n\nreturn items;"
      },
      "typeVersion": 2
    },
    {
      "id": "8fa5c209-84fd-43fd-b9d8-06b6b32625b6",
      "name": "Edit Fields1",
      "type": "n8n-nodes-base.set",
      "position": [
        -528,
        848
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "b5b58c86-29ff-4815-84dd-e54331612abb",
              "name": "data",
              "type": "string",
              "value": "={{ $json.candidates[0].content.parts[0].inlineData.data }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "01127227-53f3-433f-808b-7bc8a5045910",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -1200,
        848
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "2e051a6b-d4bf-4471-8e62-b7b73ae55e5a",
      "name": "Nano Banana Pro",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -752,
        848
      ],
      "parameters": {
        "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-3-pro-image-preview:generateContent",
        "method": "POST",
        "options": {
          "timeout": 1000000
        },
        "jsonBody": "={{ { contents: $json.contents, generationConfig: $json.generationConfig } }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "googlePalmApi"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.3
    },
    {
      "id": "ad05a6ab-5efc-46a0-a551-a906724800d2",
      "name": "Add Previous Slides",
      "type": "n8n-nodes-base.code",
      "position": [
        -976,
        848
      ],
      "parameters": {
        "jsCode": "const item = $input.first().json;\nconst slideNumber = item.meta.slideNumber;\nconst staticData = $getWorkflowStaticData('global');\n\nif (slideNumber === 1) {\n  staticData.slideUrls = [];\n}\n\nconst urls = staticData.slideUrls || [];\nconst basePrompt = item.parts[0].text;\n\nlet finalPrompt;\n\nconst renderingBlock = [\n  'RENDERING INSTRUCTIONS:',\n  '- The provided product photograph must be used as the MAIN SUBJECT.',\n  '- The product must fill at least 50% of the frame \u2014 LARGE and dominant.',\n  '- The provided brand logo must appear in the top-right or top-left corner at a tasteful size.',\n  '- The product must be the BRIGHTEST and SHARPEST element in the entire frame.',\n  '- Product shadow must be SOFT and SUBTLE \u2014 gentle contact shadow only. No heavy dark cast shadows.',\n  '- Background must have DEPTH and TEXTURE \u2014 never plain flat beige, linen, or cream.',\n  '- Typography must be LARGE and BOLD \u2014 headlines should fill at least 30% of the frame width.',\n  '- All text described in the prompt must be rendered ON the image with the exact words specified.',\n  '- Output must be exactly 1:1 square format (1024x1024).',\n  '- Quality standard: this must look like a paid campaign from a luxury brand.',\n  '- AVOID: organic wave shapes, scattered leaves, botanical decorations, heavy dark shadows, plain linen or beige textures, small product placement, soft pastel washes, Canva-style templates, decorative borders, blob shapes, flat backgrounds, competing fonts, purely decorative elements'\n].join('\\n');\n\nconst consistencyBlock = [\n  'CRITICAL - VISUAL CONSISTENCY:',\n  '- The PREVIOUS SLIDES from this carousel are attached as reference images.',\n  '- You MUST match their exact color palette, typography style, font weight, lighting direction, lighting color temperature, background texture family, and signature design element.',\n  '- The ONLY things that should change are: product position, product scale, text placement, composition layout, and background tone variation.',\n  '- If the previous slides have a specific text rendering style, you MUST replicate that exact same style.',\n  '- This slide must look like it belongs to the SAME campaign.'\n].join('\\n');\n\nif (slideNumber === 1) {\n  finalPrompt = basePrompt + '\\n\\n' + renderingBlock;\n} else {\n  finalPrompt = basePrompt + '\\n\\n' + renderingBlock + '\\n\\n' + consistencyBlock;\n}\n\nconst parts = [{ text: finalPrompt }];\n\nfor (let i = 1; i < item.parts.length; i++) {\n  parts.push(item.parts[i]);\n}\n\nfor (const url of urls) {\n  const response = await this.helpers.httpRequest({\n    method: 'GET',\n    url: url,\n    encoding: 'arraybuffer',\n    returnFullResponse: true,\n  });\n  const base64 = Buffer.from(response.body).toString('base64');\n  parts.push({\n    inline_data: {\n      mime_type: 'image/jpeg',\n      data: base64\n    }\n  });\n}\n\nreturn [{\n  json: {\n    meta: item.meta,\n    contents: [{ parts: parts }],\n    generationConfig: item.generationConfig,\n    _debug: {\n      slideNumber: slideNumber,\n      previousUrlsCount: urls.length,\n      previousUrls: urls.slice(),\n      totalParts: parts.length\n    }\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "31253ae7-1329-4efe-8024-567ff4a00480",
      "name": "Save Slide to Memory",
      "type": "n8n-nodes-base.code",
      "position": [
        144,
        928
      ],
      "parameters": {
        "jsCode": "const staticData = $getWorkflowStaticData('global');\n\nif (!staticData.slideUrls) {\n  staticData.slideUrls = [];\n}\n\nconst mediaUrl = $('Blotato Upload').first().json.url;\nstaticData.slideUrls.push(mediaUrl);\n\nreturn [{\n  json: {\n    meta: $('Add Previous Slides').first().json.meta,\n    mediaUrl: mediaUrl,\n    // DEBUG \u2014 remove after confirming\n    _debug: {\n      savedUrl: mediaUrl,\n      totalStoredUrls: staticData.slideUrls.length,\n      allStoredUrls: [...staticData.slideUrls],\n    }\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "66385310-9796-4fcc-947e-16c6e076bd28",
      "name": "Convert to File",
      "type": "n8n-nodes-base.convertToFile",
      "position": [
        -304,
        848
      ],
      "parameters": {
        "options": {},
        "operation": "toBinary",
        "sourceProperty": "data"
      },
      "typeVersion": 1.1
    },
    {
      "id": "b4c16c23-a029-43a6-8406-22a166f2f07e",
      "name": "Prepare Post Payload",
      "type": "n8n-nodes-base.code",
      "position": [
        -832,
        1488
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst sheet = $('Get Rows').first().json;\n\nconst sorted = items.sort((a, b) =>\n  a.json.meta.slideNumber - b.json.meta.slideNumber\n);\n\nconst mediaUrls = sorted.map(item => item.json.mediaUrl);\nconst first = sorted[0].json.meta;\n\nconst postHour = sheet['Post Hour']?.toString().trim().toLowerCase();\nlet scheduledTime = '';\n\nif (postHour && postHour !== 'now') {\n  const today = new Date().toISOString().split('T')[0];\n  let hours = 0;\n  let minutes = 0;\n\n  if (postHour.includes('am') || postHour.includes('pm')) {\n    const isPM = postHour.includes('pm');\n    const cleaned = postHour.replace(/[ap]m/gi, '').trim();\n    const timeParts = cleaned.split(':');\n    hours = parseInt(timeParts[0]);\n    minutes = timeParts[1] ? parseInt(timeParts[1]) : 0;\n    if (isPM && hours !== 12) hours += 12;\n    if (!isPM && hours === 12) hours = 0;\n  } else if (postHour.includes(':')) {\n    const timeParts = postHour.split(':');\n    hours = parseInt(timeParts[0]);\n    minutes = parseInt(timeParts[1]);\n  } else {\n    hours = parseInt(postHour);\n  }\n\n  const hh = String(hours).padStart(2, '0');\n  const mm = String(minutes).padStart(2, '0');\n  scheduledTime = `${today}T${hh}:${mm}:00`;\n}\n\nreturn [{\n  json: {\n    mediaUrls: mediaUrls.join(','),\n    caption: `${first.caption} ${first.hashtags}`,\n    scheduledTime,\n    rowNumber: first.rowNumber,\n    socials: first.socials,\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "6f956016-8ae7-4668-be08-f846612466cb",
      "name": "Switch",
      "type": "n8n-nodes-base.switch",
      "position": [
        -608,
        1472
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "instagram",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "e59147c7-6c37-463d-8aab-7d7babda8831",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    },
                    "leftValue": "={{ $json.socials.includes('instagram') }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "facebook",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "a9951e3d-be9d-4c95-8829-c2d42238154c",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    },
                    "leftValue": "={{ $json.socials.includes('facebook') }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "X",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "2cd0c6b6-b1c6-482a-8299-88eaf64511c1",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    },
                    "leftValue": "={{ $json.socials.includes('x') }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "allMatchingOutputs": true
        },
        "looseTypeValidation": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "955c7268-fe99-4597-b1ad-37acd1b45843",
      "name": "instagram",
      "type": "@blotato/n8n-nodes-blotato.blotato",
      "position": [
        -176,
        1344
      ],
      "parameters": {
        "options": {
          "scheduledTime": "={{ $json.scheduledTime || '' }}"
        },
        "accountId": {
          "__rl": true,
          "mode": "list",
          "value": "20209",
          "cachedResultUrl": "https://backend.blotato.com/v2/accounts/20209",
          "cachedResultName": "no_code_hack"
        },
        "postContentText": "={{ $json.caption }}",
        "postContentMediaUrls": "={{ $json.mediaUrls.split(',') }}"
      },
      "credentials": {
        "blotatoApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "5e6ce38b-e062-4bb5-b0b9-a96e63d39c82",
      "name": "facebook",
      "type": "@blotato/n8n-nodes-blotato.blotato",
      "position": [
        -176,
        1504
      ],
      "parameters": {
        "options": {
          "scheduledTime": "={{ $json.scheduledTime || '' }}"
        },
        "platform": "facebook",
        "accountId": {
          "__rl": true,
          "mode": "list",
          "value": "13039",
          "cachedResultUrl": "https://backend.blotato.com/v2/accounts/13039",
          "cachedResultName": "Nicole Jacobsy"
        },
        "facebookPageId": {
          "__rl": true,
          "mode": "list",
          "value": "703622779508585",
          "cachedResultUrl": "https://backend.blotato.com/v2/accounts/13039/subaccounts/703622779508585",
          "cachedResultName": "N8n connection"
        },
        "postContentText": "={{ $json.caption }}",
        "postContentMediaUrls": "={{ $json.mediaUrls.split(',') }}"
      },
      "credentials": {
        "blotatoApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "d71e1d16-2031-4cc8-9f80-956ff6cc9f55",
      "name": "X",
      "type": "@blotato/n8n-nodes-blotato.blotato",
      "position": [
        -176,
        1664
      ],
      "parameters": {
        "options": {
          "scheduledTime": "={{ $json.scheduledTime || '' }}"
        },
        "platform": "twitter",
        "accountId": {
          "__rl": true,
          "mode": "list",
          "value": "9184",
          "cachedResultUrl": "https://backend.blotato.com/v2/accounts/9184",
          "cachedResultName": "seif_nocodehack"
        },
        "postContentText": "={{ $json.caption }}",
        "postContentMediaUrls": "={{ $json.mediaUrls.split(',') }}"
      },
      "credentials": {
        "blotatoApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "3422245f-08a5-4b69-b52b-f5768840fa86",
      "name": "Blotato Upload",
      "type": "@blotato/n8n-nodes-blotato.blotato",
      "position": [
        -80,
        848
      ],
      "parameters": {
        "resource": "media",
        "useBinaryData": true
      },
      "credentials": {
        "blotatoApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "fa650309-45ce-41b7-9987-67a0cbadd208",
      "name": "Basic LLM Chain",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        -1040,
        400
      ],
      "parameters": {
        "text": "=Create an Instagram carousel with EXACTLY 3 slides for this product.\n\nProduct Description: {{$json.productDescription}}\nSpecification/Theme: {{$json.specification || 'Clean, premium, lifestyle-focused'}}\n\n{{ $json.productImages.length > 1 ? 'This product has multiple images (different sizes/angles). Each slide should showcase different product images or combinations to create visual variety across the carousel. Mention in each imagePrompt which product images to feature.' : '' }}\n\nCREATIVE BRIEF:\n\nYou are designing a micro-campaign for this product. Not a social media post \u2014 a visual experience.\n\nStep 1: Study the product. Understand its personality, its audience, its emotional hook. What feeling does this product sell? Design for that feeling.\n\nStep 2: Architect the visual identity. Lock in your palette, typography, lighting, signature element, and texture BEFORE you write a single slide. This is your campaign's DNA \u2014 every slide inherits from it.\n\nStep 3: Design slide 1 as if it were a billboard on Times Square. The product must be the unmistakable hero \u2014 large, centered, dramatically lit. The headline must be bold enough to read from across a room. The composition must have depth, tension, and a reason to exist. This single frame must make someone stop scrolling and think \"what is this?\"\n\nStep 4: Slide 2 builds the story. Same DNA, completely different composition. Move the product. Move the text. Change everything about the spatial arrangement while keeping the visual identity locked.\n\nStep 5: Slide 3 is the CTA. Close the deal. Make them act. Same visual system, but the energy is now about urgency and action.\n\nMANDATORY CONSTRAINTS:\n- EXACTLY 3 slides \u2014 no more, no less\n- Product must be LARGE and HERO-POSITIONED in every slide (minimum 40-50% of frame)\n- Slide 1: product CENTER or CENTER-DOMINANT\n- Every slide must use a DIFFERENT layout and composition from the others\n- Typography must be a design element \u2014 not a label floating in space\n- Background must have physical texture and depth \u2014 no flat fills, no plain linen, no plain beige\n- No decorative filler whatsoever\n- Product shadows must be SOFT and SUBTLE only \u2014 no heavy dark cast shadows\n- Every imagePrompt must end with an AVOID block listing what NOT to generate\n- Every element in the frame must serve the composition\n\nFor slides 2 and 3: the previously generated slides will be attached as reference images. Match the visual identity exactly. Change only the composition and layout.\n\nDesign standard: Would this get featured on Behance? Would a creative director at a top agency sign off on this? If the answer is no, push harder.",
        "batching": {},
        "messages": {
          "messageValues": [
            {
              "message": "You are the Executive Creative Director of a world-class visual design studio that produces editorial campaigns for brands like Aesop, Byredo, Le Labo, Glossier, and Apple. Your carousels have been featured in Communication Arts, It's Nice That, and Behance's top picks. You do not make social media posts \u2014 you make visual experiences that happen to live on Instagram.  YOUR DESIGN PHILOSOPHY: Every carousel is a micro-campaign. Slide 1 is a billboard. Slide 2 is a magazine spread. Slide 3 is the storefront window. Each frame must justify its existence \u2014 if a slide doesn't make someone's thumb pause, it doesn't ship.  YOU MUST GENERATE EXACTLY 3 SLIDES. No more. No less.  \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 PHASE 1 \u2014 VISUAL IDENTITY (before writing ANY slide) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550  Before generating a single slide, you must architect a complete visual identity for this product's carousel. Think of this as the brand guidelines for a one-off campaign:  1. COLOR SYSTEM    - Select exactly 2-3 hex colors    - One must be a dominant background tone (covers 60%+ of the canvas)    - One must be a high-contrast accent for typography    - Optional third as a subtle supporting tone    - Colors must feel intentional and premium \u2014 no random pastels, no generic gradients  2. TYPOGRAPHIC HIERARCHY    - Define a headline treatment: weight, case, tracking, and visual behavior      Example: \"Ultra-bold condensed sans-serif, all caps, -2% tracking, headlines scale to fill 40% of the frame width\"    - Define a body treatment: weight, case, style      Example: \"Light weight serif, sentence case, generous line height, set at 1/5 the headline size\"    - Typography must have PERSONALITY \u2014 it should feel like a deliberate creative choice, not a default font  3. LIGHTING BLUEPRINT    - Define one cinematic lighting setup that applies to the product across ALL slides    - Be specific: direction, quality, color temperature, shadow behavior      Example: \"Hard key light from 45 degrees upper-left, warm 3200K, creating a sharp diagonal shadow that falls to bottom-right. Soft fill from the right at 20% intensity to open up shadow detail. Subtle rim light on the product edge to separate it from the background\"    - CRITICAL: Product shadows must be SOFT and SUBTLE \u2014 a gentle contact shadow or soft diffused shadow only. Never a heavy, dark, dramatic cast shadow. The shadow should enhance realism, not dominate the composition    - The product must ALWAYS be the brightest, sharpest element in the frame  4. SIGNATURE DESIGN LANGUAGE    - Define ONE recurring design element that ties all slides together \u2014 this is NOT decoration, it is a design system component      Examples: \"A single thin geometric line that frames different parts of the composition in each slide\" / \"A bold diagonal color block that slices through each frame at a different angle\" / \"A halftone dot pattern that appears at different scales and positions\"    - This element must appear in every slide but NEVER in the same way twice  5. TEXTURE AND MATERIALITY    - Define the tactile quality of the backgrounds      Examples: \"Polished concrete with fine aggregate visible\" / \"Deep matte black with micro-noise texture\" / \"Brushed metal with directional grain\" / \"Raw plaster with subtle surface variation\"    - Backgrounds must feel PHYSICAL, not digital    - NEVER use plain linen, plain beige paper, or plain cream textures \u2014 these are generic and safe  Output the full visual identity as a \"visualSystem\" object AND paste it at the top of every imagePrompt.  \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 PHASE 2 \u2014 SLIDE ARCHITECTURE (EXACTLY 3 SLIDES) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550  SLIDE 1 \u2014 THE SCROLL STOPPER: This is your billboard. This is the frame that earns the swipe. It must be visually AGGRESSIVE.  Mandatory requirements for slide 1: - The product must occupy at least 50% of the frame \u2014 it is the undeniable hero - The product must be placed CENTER or CENTER-DOMINANT (not tucked to a side) - The headline must be MASSIVE \u2014 think 30-40% of the frame. It can overlap the product, sit behind it, wrap around it, or break across it. Typography is a DESIGN ELEMENT, not a label - The composition must have a clear focal hierarchy: product first, headline second, body text third - The background must have depth and dimension \u2014 a dramatic color with texture, not a flat fill - The overall mood must be cinematic \u2014 think the opening shot of a film, not a product listing photo  Creative concepts for slide 1 (pick one and commit fully): - CINEMATIC ISOLATION: Product on a deep textured background with dramatic directional light, massive headline text partially behind the product creating depth layers - TYPOGRAPHIC COLLISION: Oversized 3D headline text that physically interacts with the product \u2014 wrapping around it, shattered by it, growing out of it - BOLD SPLIT: Frame divided into two stark zones (e.g. dark/light, color/texture) with the product bridging both, creating visual tension - SUSPENDED DRAMA: Product floating with a subtle shadow below, surrounded by negative space, with bold text anchoring one edge \u2014 minimal but powerful - DECONSTRUCTED LAYOUT: Product at an unexpected angle with graphic shapes, lines, and text blocks arranged in an asymmetric editorial grid  SLIDE 2 \u2014 THE STORY: This slide builds desire. It reveals something about the product that makes the viewer want it.  Mandatory requirements: - MUST use a completely DIFFERENT layout from slide 1 - MUST reposition the product to a different part of the frame - MUST reposition typography to a different relationship with the product - The signature design element must appear in a new configuration - Background tone can shift while staying in the same texture family - For this slide, the previously generated slide 1 will be attached as a reference image. Your imagePrompt MUST include: \"The attached previous slide is the exact visual reference \u2014 match its color palette, typography style, lighting quality, and design language precisely. Apply a completely different composition, product placement, and text arrangement.\"  SLIDE 3 \u2014 THE CTA: The final slide closes the deal. Confident, clear, compelling.  Mandatory requirements: - MUST use a completely DIFFERENT layout from slides 1 AND 2 - The CTA text must feel like a natural climax, not a generic afterthought - Same visual system but energy shifted toward action and urgency - The brand name can appear here in the headline if it serves the CTA - For this slide, previous slides 1 and 2 will be attached as reference images. Your imagePrompt MUST include: \"The attached previous slides are the exact visual reference \u2014 match their color palette, typography style, lighting quality, and design language precisely. Apply a completely different composition, product placement, and text arrangement.\"  \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 PHASE 3 \u2014 IMAGE PROMPT ENGINEERING \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550  Every imagePrompt must follow this exact structure (minimum 80 words):  1. VISUAL SYSTEM BLOCK \u2014 paste the full identity (palette, typography, lighting, design language, texture) 2. LAYOUT DECLARATION \u2014 state which composition approach this slide uses 3. BACKGROUND \u2014 describe the exact background: color, texture, depth, any atmospheric elements 4. PRODUCT DIRECTION \u2014 describe: exact position in frame (use compass: center, upper-third, lower-right, etc.), relative size (% of frame), angle/perspective, how light hits it specifically 5. TYPOGRAPHY DIRECTION \u2014 describe: exact headline text to render, position relative to product, size relative to frame, visual treatment (behind product? overlapping? in a color block?). Then the body text: exact words, position, size 6. DESIGN ELEMENTS \u2014 describe: where the signature element appears, any supporting graphic elements (lines, shapes, blocks), how they interact with the product and text 7. MOOD INSTRUCTION \u2014 one sentence describing the emotional quality: \"This should feel like...\" followed by a vivid reference 8. NEGATIVE PROMPT \u2014 every imagePrompt must end with: \"AVOID: organic wave shapes, scattered leaves or botanical decorations, heavy dark drop shadows, generic linen or beige paper textures, product placed small or to one side, soft pastel washes, Canva-style templates, decorative borders, blob shapes, flat untextured backgrounds, multiple competing fonts, any element that exists purely as decoration\"  \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 BANNED ELEMENTS \u2014 ABSOLUTE RESTRICTIONS \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550  NEVER use any of the following \u2014 these are hallmarks of cheap, templated design: - Organic flowing wave shapes or curves as background decoration - Scattered botanical illustrations, leaves, vines, or nature elements as filler - Generic starburst, sparkle, or glow effects - Product placed small on one side with large empty space on the other - Every slide having the same layout with text-left / product-right - Soft watercolor textures or dreamy pastel washes - Heavy dark cast shadows \u2014 only soft subtle contact shadows allowed - Drop shadows that look like default Canva/PowerPoint shadows - Rounded blob shapes or amoeba forms - Decorative borders or ornamental frames - Multiple competing fonts or decorative typefaces - Any element that exists purely as decoration rather than serving the composition - Flat, textureless, solid color backgrounds with nothing else going on - Plain linen, plain beige, plain cream, or plain paper backgrounds without additional design treatment  YOUR STANDARD IS: Would a senior art director at Pentagram approve this? If not, redesign it.  \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 COPY DIRECTION \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550  Headlines: - Maximum 6 words \u2014 every word must earn its place - Write like a provocative billboard, not a product description - Use tension, contrast, or an unexpected angle - Examples of great headlines: \"Your Soap Is Lying\" / \"Skin This Soft Is Illegal\" / \"Goodbye Bathroom Shelf Regret\" - Examples of BAD headlines: \"Natural Body Wash For You\" / \"Clean Ingredients Inside\" / \"Try Our New Product\"  Body text: - Maximum 15 words - Conversational and human \u2014 sounds like a friend giving advice, not a brand making claims - One idea per body text, no lists, no multi-point arguments  ABSOLUTELY NO: - Dashes, bullet points, colons in headlines - Generic marketing language (\"premium quality\", \"best in class\", \"natural goodness\") - AI-sounding punctuation or phrasing - Exclamation marks in headlines  \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 OUTPUT FORMAT \u2014 ONLY VALID JSON \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550  Return ONLY this JSON structure. No explanation. No markdown. No text before or after.  {   \"caption\": \"engaging Instagram caption under 150 characters with personality\",   \"hashtags\": \"10 relevant hashtags\",   \"visualSystem\": {     \"palette\": [\"#hex1\", \"#hex2\", \"#hex3\"],     \"typography\": \"full description of headline + body type treatment\",     \"lighting\": \"full cinematic lighting blueprint with SOFT shadow instruction\",     \"designLanguage\": \"signature recurring element description\",     \"texture\": \"background materiality description \u2014 never plain linen or beige\"   },   \"slides\": [     {       \"slideNumber\": 1,       \"headline\": \"...\",       \"body\": \"...\",       \"layoutApproach\": \"CINEMATIC ISOLATION / TYPOGRAPHIC COLLISION / BOLD SPLIT / SUSPENDED DRAMA / DECONSTRUCTED LAYOUT\",       \"imagePrompt\": \"... minimum 80 words, following the exact structure above, ending with AVOID negative prompt ...\"     },     {       \"slideNumber\": 2,       \"headline\": \"...\",       \"body\": \"...\",       \"layoutApproach\": \"must differ from slide 1\",       \"imagePrompt\": \"... includes reference to attached previous slides + AVOID block ...\"     },     {       \"slideNumber\": 3,       \"headline\": \"...\",       \"body\": \"...\",       \"layoutApproach\": \"must differ from slides 1 and 2\",       \"imagePrompt\": \"... CTA energy + reference to attached previous slides + AVOID block ...\"     }   ] }"
            }
          ]
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.9
    },
    {
      "id": "1bbd210a-c06c-412c-9b75-7ed47ea7cddf",
      "name": "Anthropic Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "position": [
        -1040,
        544
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "claude-sonnet-4-5-20250929",
          "cachedResultName": "Claude Sonnet 4.5"
        },
        "options": {
          "thinking": true,
          "maxTokensToSample": 4096
        }
      },
      "credentials": {
        "anthropicApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "79d00904-3a8a-4186-a215-9c368285d679",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        -896,
        544
      ],
      "parameters": {
        "jsonSchemaExample": "{\n  \"caption\": \"engaging Instagram caption under 150 characters with personality\",\n  \"hashtags\": \"10 relevant hashtags\",\n  \"visualSystem\": {\n    \"palette\": [\"#hex1\", \"#hex2\", \"#hex3\"],\n    \"typography\": \"full description of headline + body type treatment\",\n    \"lighting\": \"full cinematic lighting blueprint with SOFT shadow instruction\",\n    \"designLanguage\": \"signature recurring element description\",\n    \"texture\": \"background materiality description \u2014 never plain linen or beige\"\n  },\n  \"slides\": [\n    {\n      \"slideNumber\": 1,\n      \"headline\": \"...\",\n      \"body\": \"...\",\n      \"layoutApproach\": \"CINEMATIC ISOLATION / TYPOGRAPHIC COLLISION / BOLD SPLIT / SUSPENDED DRAMA / DECONSTRUCTED LAYOUT\",\n      \"imagePrompt\": \"... minimum 80 words, following the exact structure above, ending with AVOID negative prompt ...\"\n    },\n    {\n      \"slideNumber\": 2,\n      \"headline\": \"...\",\n      \"body\": \"...\",\n      \"layoutApproach\": \"must differ from slide 1\",\n      \"imagePrompt\": \"... includes reference to attached previous slides + AVOID block ...\"\n    },\n    {\n      \"slideNumber\": 3,\n      \"headline\": \"...\",\n      \"body\": \"...\",\n      \"layoutApproach\": \"must differ from slides 1 and 2\",\n      \"imagePrompt\": \"... CTA energy + reference to attached previous slides + AVOID block ...\"\n    }\n  ]\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "245db2ba-fecb-462c-ace0-12ddda9d2cbe",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1232,
        -192
      ],
      "parameters": {
        "color": 7,
        "width": 1584,
        "height": 384,
        "content": "# \ud83d\udd39 Step 1: Fetch Product Data & Prepare Content"
      },
      "typeVersion": 1
    },
    {
      "id": "1a154f49-913d-4be9-964a-807f38c9ba65",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1232,
        256
      ],
      "parameters": {
        "color": 7,
        "width": 1584,
        "height": 416,
        "content": "# \ud83d\udd39 Step 2: AI Creative Direction & Image Preparation"
      },
      "typeVersion": 1
    },
    {
      "id": "229806dc-9667-434a-b246-277164450b9d",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1232,
        736
      ],
      "parameters": {
        "color": 7,
        "width": 1584,
        "height": 384,
        "content": "# \ud83d\udd39 Step 3: Generate Slides with Visual Consistency Loop"
      },
      "typeVersion": 1
    },
    {
      "id": "98f68613-01e1-43a4-8c35-602295926192",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1232,
        1200
      ],
      "parameters": {
        "color": 4,
        "width": 1584,
        "height": 656,
        "content": "# \ud83d\udd39 Step 4: Publish"
      },
      "typeVersion": 1
    },
    {
      "id": "2e63b1f8-2802-4175-a4dc-697d08c91808",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1936,
        -192
      ],
      "parameters": {
        "width": 652,
        "height": 2052,
        "content": "# \ud83d\ude80 Social Media Carousel Generator AI-Powered Design + Auto-Publish\n### By @nocodehack\n@[youtube](A_QT-9qUkxc)\n---\n## \u2699\ufe0f What This Workflow Does\nThis workflow automates the complete lifecycle of an Instagram carousel campaign:\n1. Reads product data from Google Sheets\n2. Scrapes product info if missing (via Jina.ai)\n3. Generates creative direction with Claude AI (visual system, copy, image prompts)\n4. Generates each slide image with Gemini (Nano Banana Pro)\n5. Maintains visual consistency by feeding previous slides as reference\n6. Uploads media and publishes to Instagram, Facebook, and X via Blotato\n7. Updates the sheet with status\n\nIt eliminates manual design, exporting, uploading, and publishing,  everything runs in one automated pipeline.\n\n---\n\n## \ud83d\udd10 Setup\n\n### 1. Credentials Required\n- **Google Sheets** \u2014 read/write access\n- **[Anthropic](https://anthropic.com)** API (Claude \u2014 creative direction)\n- **[Google AI / Gemini](https://ai.google.dev)** API (image generation)\n- **[Blotato](https://blotato.com/?ref=nocodehack)** API (media upload + publishing)\n- **Telegram Bot** \u2014 notifications (optional)\n\n### 2. Google Sheet Structure\nColumns: Brand Logo URL | Product URL | Product Description | Product Image(s) URL | Specification | Post Date | Post Hour | Socials | Status | Post URL\n\n- Leave Description + Images empty to auto-scrape from Product URL\n- Socials: comma-separated (instagram, facebook, x)\n- Post Hour: `now` for immediate, or `14:00` / `2pm` for scheduled\n- Status: leave empty \u2014> filled automatically by the workflow\n\n### 3. Configuration Steps\n- Add all required credentials inside n8n\n- Set your Blotato profile IDs in each publishing node\n- Map each platform output in the Switch node\n- Verify the Gemini image generation endpoint\n- Test one execution before activating production mode\n\n---\n\n## \ud83c\udfa8 How to Customize\n- **Change AI model:** Swap Claude model in the LLM Chain node\n- **Change slide count:** Edit system + user prompts (currently locked to 3)\n- **Change image style:** Edit the system prompt creative direction\n- **Add platforms:** Add outputs to the Switch node + new Blotato publish nodes\n- **Add scheduling:** Use Post Hour column (supports `now`, `2pm`, `14:30`)\n- **Add approval step:** Insert a Wait node before publishing\n- **Change image hosting:** Swap Blotato Upload for Cloudinary or any S3\n\n---\n\n## \ud83d\udcb0 Workflow Cost Per Carousel (approx.)\n- Claude (creative direction): ~8K tokens \u2248 $0.02\n- Gemini (3 slide images): \u2248 $0.03\n- Blotato: per plan\n- **Total: ~$0.05 per carousel**\n\n---\n\n## \ud83d\udc4b Need help or want to customize?\n\ud83d\udce9 Contact: [LinkedIn](https://www.linkedin.com/in/ing-seif/)\n\ud83d\udcfa YouTube: [@nocodehack](https://youtube.com/@nocodehack)"
      },
      "typeVersion": 1
    },
    {
      "id": "22a8b9be-c8b0-4b8e-8c97-6d802c105baf",
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        -384,
        1344
      ],
      "parameters": {
        "amount": 10
      },
      "typeVersion": 1.1
    },
    {
      "id": "3ebe489d-1ac0-4f04-91b2-b3595247c5ee",
      "name": "Wait1",
      "type": "n8n-nodes-base.wait",
      "position": [
        -384,
        1504
      ],
      "parameters": {
        "amount": 10
      },
      "typeVersion": 1.1
    },
    {
      "id": "0476addb-8930-44e1-b267-1c196b2117d4",
      "name": "Wait2",
      "type": "n8n-nodes-base.wait",
      "position": [
        -384,
        1664
      ],
      "parameters": {
        "amount": 10
      },
      "typeVersion": 1.1
    }
  ],
  "active": false,
  "settings": {
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "16737161-80b0-4c0f-b281-2bc55db2fc98",
  "connections": {
    "Wait": {
      "main": [
        [
          {
            "node": "instagram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait1": {
      "main": [
        [
          {
            "node": "facebook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait2": {
      "main": [
        [
          {
            "node": "X",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Rows": {
      "main": [
        [
          {
            "node": "Set Processing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "instagram": {
      "main": [
        []
      ]
    },
    "Merge Data": {
      "main": [
        [
          {
            "node": "Basic LLM Chain",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields1": {
      "main": [
        [
          {
            "node": "Convert to File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Blotato Upload": {
      "main": [
        [
          {
            "node": "Save Slide to Memory",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape Product": {
      "main": [
        [
          {
            "node": "Merge Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Processing": {
      "main": [
        [
          {
            "node": "Has Description?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Basic LLM Chain": {
      "main": [
        [
          {
            "node": "Parse AI Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert to File": {
      "main": [
        [
          {
            "node": "Blotato Upload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [
          {
            "node": "Prepare Post Payload",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Add Previous Slides",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Nano Banana Pro": {
      "main": [
        [
          {
            "node": "Edit Fields1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Description?": {
      "main": [
        [
          {
            "node": "Scrape Product",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Merge Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse AI Response": {
      "main": [
        [
          {
            "node": "Fetch Images to Base64",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add Previous Slides": {
      "main": [
        [
          {
            "node": "Nano Banana Pro",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Anthropic Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Basic LLM Chain",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Post Payload": {
      "main": [
        [
          {
            "node": "Switch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Slide to Memory": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Images to Base64": {
      "main": [
        [
          {
            "node": "Prepare Nano Banana Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Basic LLM Chain",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Nano Banana Items": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}