{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "6c19a117-9bac-4598-9618-432ca067a02f",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -5712,
        64
      ],
      "parameters": {
        "text": "=Current date and time: {{$now}}\n\nTASK: Create high-quality SEO content and generate a relevant featured image for the keyword below.\n\nKeyword: {{ $json['main keyword'] }}\n\nRequirements:\n1. Write 600-800 words of engaging, professional but accessible content.\n2. Include the keyword naturally 2-3 times: once in the introduction, once in a subheading, and once in the conclusion.\n3. Create a compelling title (50-60 characters).\n4. Write a meta description (150-160 characters).\n5. Structure the article with clear sections:\n   - Introduction\n   - Key Benefits\n   - How It Works / Practical Advice\n   - Conclusion / Next Steps\n6. Target audience: general readers searching for this keyword (not academic or overly technical).\n7. Generate ONE relevant professional image. The image should:\n   - Visually represent the keyword topic (not generic SEO/marketing visuals)\n   - Use a clean, modern, professional style\n   - Be suitable as a blog featured image\n   - IMPORTANT: Generate only ONE image per article\n\nCRITICAL: Call the image generation tool exactly ONCE per article. Do not generate multiple images or variations.\n\n8. Provide descriptive **alt text** for the image, optimized for accessibility and SEO.\n\nOutput Format:\nReturn ONLY this valid JSON structure as raw JSON (no markdown code blocks, no json tags, no extra text, notes, or explanations):\n{\n\"title\": \"SEO optimized title here\",\n\"slug\": \"{{ $('Load Pending Keywords').item.json.slug }}\", \n\"metaDescription\": \"Compelling meta description here\",\n\"content\": \"Full article content here (600-800 words). CRITICAL FORMATTING REQUIREMENTS: Start with ### Introduction as the first heading. Use ### for ALL section headings (Key Benefits, How It Works / Practical Advice, Conclusion / Next Steps). Write complete paragraphs separated by blank lines. Never mix plain text with markdown formatting. Each section must have substantial content with specific examples and actionable advice. Include the target keyword naturally 2-3 times throughout the content. Ensure consistent markdown structure from start to finish.\",\n\"main keyword\": \"{{ $json['main keyword'] }}\",\n\"imageUrl\": \"URL from image generation tool\", \n\"altText\": \"Descriptive and SEO-friendly alt text here\"\n}\n\nFORMATTING VALIDATION: Before returning JSON, verify that:\n1. Content starts with ### Introduction \n2. All headings use ### format consistently\n3. Each paragraph is properly separated with blank lines\n4. No mixing of plain text and markdown syntax\n5. Content flows logically between sections\n6. Target keyword appears naturally in introduction and conclusion\n\nCRITICAL: Your response must start with { and end with }. Do not wrap the JSON in markdown formatting or code blocks. Return the raw JSON object only.",
        "options": {
          "systemMessage": "You are an expert SEO content writer and AI coordinator. Your responsibilities: Always follow the Agent Prompt exactly. Always generate both content and a relevant professional image. Always return valid JSON only, with no extra commentary or notes. Never skip image generation. Never output incomplete or invalid JSON. Ensure the content is engaging, structured, and optimized for readers searching for the keyword."
        },
        "promptType": "define"
      },
      "typeVersion": 2.2
    },
    {
      "id": "9081f1d4-5dac-4350-b910-e17ad918208f",
      "name": "Simple Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        -5632,
        352
      ],
      "parameters": {
        "sessionKey": "={{ $('Load Pending Keywords').item.json['main keyword'] }}",
        "sessionIdType": "customKey"
      },
      "typeVersion": 1.3
    },
    {
      "id": "13743c5a-d649-49a3-8cd6-4c03494d9749",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        -5760,
        352
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini"
        },
        "options": {
          "temperature": 0.1
        }
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "97ed3faa-f65c-4b47-a024-9800c24763fd",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -6528,
        -528
      ],
      "parameters": {
        "width": 576,
        "height": 448,
        "content": "### \ud83d\udccb Automated SEO Content Generation for Webflow\n\nThis workflow generates SEO-optimized articles and saves them as Webflow drafts.\n\n**What it does:**\n- Reads keywords from Google Sheets (status = \"pending\")\n- AI writes 600+ word articles with proper structure\n- Generates custom featured images via Gemini AI\n- Creates drafts in Webflow (you publish manually after review)\n- Tracks results in Google Sheets\n\n**Best for:** Creating multiple articles targeting long-tail keyword variations (e.g., \"best running shoes for winter\", \"best running shoes for trails\")\n\n**Setup Steps:**\n1. Configure your Google Sheet or [clone this template](https://docs.google.com/spreadsheets/d/1_4wVEuu1fVZBXs0JhImQyzZYv9QC0RLZjxZFwHcJHPw/edit?gid=183091813#gid=183091813)\n2. Add API credentials (OpenAI, OpenRouter, Webflow)\n3. Set Webflow site/collection IDs\n4. Test with 1 keyword before bulk processing"
      },
      "typeVersion": 1
    },
    {
      "id": "d10c11b9-0cbe-4725-ae66-fe4c381666c5",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -6432,
        64
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "months"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "85a2baaf-7fd0-4cb9-b200-7f56d3781275",
      "name": "Load Pending Keywords",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -6208,
        64
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "pending",
              "lookupColumn": "status"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 183091813,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1qd1H_IY_KEtB1C-ZJzpdmw9_yQEqdpnrf4ZdlV02gW4/edit#gid=183091813",
          "cachedResultName": "keywords"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1qd1H_IY_KEtB1C-ZJzpdmw9_yQEqdpnrf4ZdlV02gW4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1qd1H_IY_KEtB1C-ZJzpdmw9_yQEqdpnrf4ZdlV02gW4/edit?usp=drivesdk",
          "cachedResultName": "N8N SHEETS TEST"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "3474f5e5-84e7-44c5-8751-e53ffedffd1e",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -5984,
        64
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "3403fae8-07ba-4faf-9db5-0ad221b1360e",
      "name": "Content Quality Check",
      "type": "n8n-nodes-base.if",
      "position": [
        -5072,
        64
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "word-count-check",
              "operator": {
                "type": "number",
                "operation": "gte"
              },
              "leftValue": "={{ $json.wordCount }}",
              "rightValue": 600
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "ebe7106d-a3bb-4396-989c-5aba594e828d",
      "name": "Process Agent Output",
      "type": "n8n-nodes-base.code",
      "position": [
        -5296,
        64
      ],
      "parameters": {
        "jsCode": "// Original keyword item from the loop\nconst original = $json;\n\n// Extract and clean the agent output with multiple fallbacks\nlet agentOutput = {};\ntry {\n  let rawOutput = original.output || \"\";\n  \n  // Security: Handle multiple markdown variations\n  let cleanedOutput = rawOutput\n    .replace(/```json\\s*/g, '')    // Remove ```json\n    .replace(/```\\s*$/g, '')       // Remove closing ```\n    .replace(/^```\\s*/g, '')       // Remove opening ``` \n    .replace(/```$/g, '')          // Remove trailing ```\n    .trim();\n  \n  // Try parsing the cleaned output\n  agentOutput = JSON.parse(cleanedOutput);\n  \n} catch (parseError) {\n  // Security fallback: Create safe structure if parsing fails\n  console.log(\"JSON Parse Error:\", parseError.message);\n  \n  agentOutput = { \n    title: `Error: ${original[\"main keyword\"] || \"Unknown keyword\"}`,\n    slug: `error-${Date.now()}`,\n    metaDescription: \"Content generation failed\",\n    content: \"Failed to generate content. Please check agent configuration.\",\n    \"main keyword\": original[\"main keyword\"] || \"unknown\",\n    imageUrl: \"\",\n    altText: \"Error generating content\",\n    parseError: parseError.message,\n    rawAgentOutput: original.output\n  };\n}\n\n// Compute word count from the content\nconst content = agentOutput.content || \"\";\nconst wordCount = content.split(/\\s+/).filter(Boolean).length;\n\n// Return wrapped in json object to avoid n8n reserved key conflicts\nreturn [{\n  json: {\n    ...original,          \n    ...agentOutput,       \n    wordCount,\n    status: wordCount >= 600 ? \"content_ready\" : \"needs_expansion\",\n    generatedAt: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "457c8c37-c678-4299-8676-4ec41c2ba56d",
      "name": "Expand Content",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        -4848,
        160
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini"
        },
        "options": {
          "maxTokens": 4000,
          "temperature": 0.3
        },
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "You are an expert content expansion specialist. Always return ONLY valid JSON with no extra text, notes, or explanations. Follow the exact format specified in the user prompt."
            },
            {
              "content": "=Expand the following content from {{ $json.wordCount }} words to 600+ words while maintaining the same structure, tone, and keyword optimization.\n\nTARGET KEYWORD: {{ $('Loop Over Items').item.json['main keyword'] }}\n\nORIGINAL CONTENT: {{ $json.content }}\n\nEXPANSION GUIDELINES:\n1. Keep all existing headings and sections intact\n2. Add specific examples, real-world scenarios, and practical tips within each section  \n3. Include 2-3 brand mentions or product examples where relevant\n4. Add actionable advice readers can implement immediately\n5. Maintain the same markdown formatting (###, **, etc.)\n6. Preserve keyword density and natural integration\n7. Keep the original writing style and tone\n\nReturn ONLY this valid JSON structure (no extra text, notes, or explanations):\n\n{\n  \"title\": \"{{ $json.title }}\",\n  \"slug\": \"{{ $json.slug }}\",\n  \"metaDescription\": \"{{ $json.metaDescription }}\",\n  \"content\": \"Expanded article content here (600+ words)\",\n  \"main keyword\": \"{{ $json['main keyword'] }}\",\n  \"imageUrl\": \"{{ $json.imageUrl }}\",\n  \"altText\": \"{{ $json.altText }}\"\n}"
            }
          ]
        }
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1.8
    },
    {
      "id": "6a7bc453-cf01-4bed-88a9-7b8d478793a2",
      "name": "Format Agent Output",
      "type": "n8n-nodes-base.code",
      "position": [
        -4496,
        160
      ],
      "parameters": {
        "jsCode": "const originalData = $json;\nconst expandedResponse = $('Expand Content').first().json.message.content;\n\n// Parse the JSON response from the expanded content\nlet expandedData = {};\ntry {\n  expandedData = JSON.parse(expandedResponse);\n} catch (e) {\n  expandedData = { \n    error: \"Invalid JSON from expansion\", \n    raw: expandedResponse,\n    content: expandedResponse // fallback to raw content\n  };\n}\n\n// Calculate word count from the expanded content\nconst content = expandedData.content || \"\";\nconst wordCount = content.split(/\\s+/).filter(Boolean).length;\n\nreturn [{\n  json: {\n    ...originalData,     // Keep original data (main keyword, etc.)\n    ...expandedData,     // Add all expanded fields\n    wordCount,\n    status: wordCount >= 600 ? \"content_ready\" : \"needs_expansion\",\n    wasExpanded: true,\n    expandedAt: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "0a272acd-2853-45c9-bdc6-c69d876648f8",
      "name": "Merge Content Paths",
      "type": "n8n-nodes-base.merge",
      "position": [
        -4272,
        64
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combinationMode": "mergeByPosition"
      },
      "typeVersion": 2.1
    },
    {
      "id": "7554230a-0c6b-4654-ab87-5f527675912f",
      "name": "Convert to HTML",
      "type": "n8n-nodes-base.markdown",
      "position": [
        -4048,
        64
      ],
      "parameters": {
        "mode": "markdownToHtml",
        "options": {},
        "markdown": "={{ $json.content }}"
      },
      "typeVersion": 1
    },
    {
      "id": "a3f5e05d-a525-4fdd-a214-998806a344b0",
      "name": "Create New Post",
      "type": "n8n-nodes-base.webflow",
      "maxTries": 3,
      "position": [
        -3760,
        64
      ],
      "parameters": {
        "siteId": "653a17a1b31a397763f3ce40",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "name",
              "fieldValue": "={{ $('Convert to HTML').item.json.title }}"
            },
            {
              "fieldId": "slug",
              "fieldValue": "={{ $('Convert to HTML').item.json.slug }}"
            },
            {
              "fieldId": "page-content",
              "fieldValue": "={{ $('Convert to HTML').item.json.data }}"
            },
            {
              "fieldId": "metadescription",
              "fieldValue": "={{ $('Convert to HTML').item.json.metaDescription }}"
            },
            {
              "fieldId": "featured-image",
              "fieldValue": "={{ $('Convert to HTML').item.json.imageUrl }}"
            }
          ]
        },
        "operation": "create",
        "collectionId": "68c01d34f15ae8b8c30ead31"
      },
      "credentials": {
        "webflowOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 2
    },
    {
      "id": "4755da23-5ea0-4420-b5a2-7976e41411d1",
      "name": "Check Success",
      "type": "n8n-nodes-base.if",
      "position": [
        -3200,
        64
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "success-check",
              "operator": {
                "type": "string",
                "operation": "isNotEmpty"
              },
              "leftValue": "={{ $json.id }}",
              "rightValue": ""
            },
            {
              "id": "4bdc0525-b8fe-4faa-a3a0-d701102ed47a",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.id }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "a013bc1b-70af-4c27-9c88-c5df8a913bd1",
      "name": "Mark as Complete",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -2976,
        -48
      ],
      "parameters": {
        "columns": {
          "value": {
            "status": "created",
            "main keyword": "={{ $('Convert to HTML').item.json['main keyword'] }}"
          },
          "schema": [
            {
              "id": "id",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "id",
              "defaultMatch": true,
              "canBeUsedToMatch": true
            },
            {
              "id": "main keyword",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "main keyword",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "head terms",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "head terms",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "modifiers",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "modifiers",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "slug",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "slug",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "main keyword"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 183091813,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1qd1H_IY_KEtB1C-ZJzpdmw9_yQEqdpnrf4ZdlV02gW4/edit#gid=183091813",
          "cachedResultName": "keywords"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1qd1H_IY_KEtB1C-ZJzpdmw9_yQEqdpnrf4ZdlV02gW4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1qd1H_IY_KEtB1C-ZJzpdmw9_yQEqdpnrf4ZdlV02gW4/edit?usp=drivesdk",
          "cachedResultName": "N8N SHEETS TEST"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "d833be28-654b-41f1-af4d-bcf18e8aa3ec",
      "name": "Log Error",
      "type": "n8n-nodes-base.code",
      "position": [
        -2976,
        160
      ],
      "parameters": {
        "jsCode": "const error = $json.error || 'Creation failed';\nconst keyword = $('Convert to HTML').first()?.json?.['main keyword'] || 'Unknown';\n\nreturn {\n  json: {\n    keyword: keyword,\n    error: error,\n    timestamp: new Date().toISOString(),\n    status: 'failed'\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "ab10a761-91e4-4b64-8164-c87134bcbb45",
      "name": "Save Success Results",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -2752,
        -48
      ],
      "parameters": {
        "columns": {
          "value": {
            "id": "={{ $('Merge post result').item.json.id }}",
            "slug": "={{ $('Merge post result').item.json.fieldData.slug }}",
            "content": "={{ $('Merge post result').item.json.fieldData['page-content'] }}",
            "updated on": "={{ $('Merge post result').item.json.lastUpdated }}",
            "generatedAt": "={{ $('Merge post result').item.json.createdOn }}",
            "meta description": "={{ $('Merge post result').item.json.fieldData.metadescription }}"
          },
          "schema": [
            {
              "id": "id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "id",
              "defaultMatch": true,
              "canBeUsedToMatch": true
            },
            {
              "id": "slug",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "slug",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "meta description",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "meta description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "content",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "content",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "generatedAt",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "generatedAt",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "updated on",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "updated on",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "published on",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "published on",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 977377122,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1qd1H_IY_KEtB1C-ZJzpdmw9_yQEqdpnrf4ZdlV02gW4/edit#gid=977377122",
          "cachedResultName": "content_created"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1qd1H_IY_KEtB1C-ZJzpdmw9_yQEqdpnrf4ZdlV02gW4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1qd1H_IY_KEtB1C-ZJzpdmw9_yQEqdpnrf4ZdlV02gW4/edit?usp=drivesdk",
          "cachedResultName": "N8N SHEETS TEST"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "632a7c39-687c-4ea9-95e0-dd57b302c191",
      "name": "Save Error",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -2752,
        160
      ],
      "parameters": {
        "columns": {
          "value": {
            "error": "={{ $json.error }}",
            "status": "={{ $json.status }}",
            "keyword": "={{ $('Convert to HTML').item.json['main keyword'] }}",
            "timestamp": "={{ $json.timestamp }}"
          },
          "schema": [
            {
              "id": "keyword",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "keyword",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "error",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "error",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "keyword"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1205462190,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1qd1H_IY_KEtB1C-ZJzpdmw9_yQEqdpnrf4ZdlV02gW4/edit#gid=1205462190",
          "cachedResultName": "webflow_error_logs"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1qd1H_IY_KEtB1C-ZJzpdmw9_yQEqdpnrf4ZdlV02gW4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1qd1H_IY_KEtB1C-ZJzpdmw9_yQEqdpnrf4ZdlV02gW4/edit?usp=drivesdk",
          "cachedResultName": "N8N SHEETS TEST"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "5f4abc50-4c29-4424-9668-b3eb1494fb66",
      "name": "Wait a few seconds",
      "type": "n8n-nodes-base.wait",
      "position": [
        -2528,
        160
      ],
      "parameters": {
        "amount": 3
      },
      "typeVersion": 1.1
    },
    {
      "id": "af480c9a-5086-45af-9d1e-0b403a387463",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -6464,
        240
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "content": "## STEP 1\n\nLoads all keywords with status='pending' from Google Sheets"
      },
      "typeVersion": 1
    },
    {
      "id": "7747856f-3922-46a8-8f09-ecc007cabeb0",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5728,
        -128
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 144,
        "content": "## STEP 2\n\nAgent creates full article + generates featured image via tool call. Content is checked for 600+ words."
      },
      "typeVersion": 1
    },
    {
      "id": "99d5b7cd-b330-4519-b166-76c3fdd1ac51",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4896,
        384
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 144,
        "content": "## STEP 3\n\nQuality Control\nIf under 600 words \u2192 expand content\nIf 600+ words \u2192 proceed to publishing"
      },
      "typeVersion": 1
    },
    {
      "id": "2100b5a6-2e31-42d5-bbe1-259b81f38933",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4096,
        -128
      ],
      "parameters": {
        "color": 7,
        "width": 192,
        "height": 144,
        "content": "## STEP 4\n\n\nConverts markdown to HTML and merges with image data"
      },
      "typeVersion": 1
    },
    {
      "id": "57f327c8-15c0-4d61-a2bc-afe23897add6",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3792,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 176,
        "height": 112,
        "content": "## STEP 6\n\nCreates new one"
      },
      "typeVersion": 1
    },
    {
      "id": "c444b15e-d08f-474d-a10b-30414590af77",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3168,
        -224
      ],
      "parameters": {
        "color": 7,
        "width": 528,
        "height": 144,
        "content": "## STEP 7\n\n- saves to content_created sheet\n- Error \u2192 logs to webflow_error_logs sheet"
      },
      "typeVersion": 1
    },
    {
      "id": "af2bab6c-5315-4c8a-9ebe-e0d71b2def54",
      "name": "No Keywords Available",
      "type": "n8n-nodes-base.noOp",
      "position": [
        -5760,
        -336
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "bc529bba-49c5-4ba7-95f2-9388f4440ede",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3920,
        416
      ],
      "parameters": {
        "color": 5,
        "width": 704,
        "height": 400,
        "content": "\n### \u2699\ufe0f Webflow OAuth Setup Required\n\n**Before running this workflow, configure Webflow OAuth2 credentials in n8n:**\n\n1. **In n8n:** Credentials \u2192 Create new \u2192 Webflow OAuth2 API \u2192 Copy the OAuth Redirect URL\n\n2. **In Webflow:** Workspace Settings \u2192 Apps & Integrations \u2192 Develop \u2192 Create an app\n\n3. **Configure your app:**\n   - App name & description (required)\n   - App homepage URL: your n8n instance URL\n   - Toggle on \"Data Client REST API\"\n   - Copy Client ID and Client Secret \u2192 paste into n8n credentials\n   - Paste the OAuth Redirect URL from n8n\n   - Select permissions (recommend read-write for CMS collections)\n\nFind your Site/Collection IDs in Webflow's Designer or CMS settings."
      },
      "typeVersion": 1
    },
    {
      "id": "c3becf95-359b-4a4a-8dc7-4e9faacf5b81",
      "name": "AI Image Generation Tool",
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "position": [
        -5504,
        352
      ],
      "parameters": {
        "workflowId": {
          "__rl": true,
          "mode": "list",
          "value": "iYw38zcdqp4HuOZR",
          "cachedResultName": "AI image generation for automatic content creation"
        },
        "description": "Generate ONE AI image for content. Pass imagePrompt, imageTitle, and keyword to create a single relevant image. This tool should be called exactly once per article.",
        "workflowInputs": {
          "value": {
            "altText": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('altText', `A clear, descriptive, and SEO-friendly alternative text for the image. Should naturally include the image and remain under 125 characters for accessibility.`, 'string') }}",
            "keyword": "={{ $json['main keyword'] }}",
            "imageTitle": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('imageTitle', `A concise, professional title for the featured image that clearly reflects the main topic of the article`, 'string') }}",
            "imagePrompt": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('imagePrompt', `A concise, professional title for the featured image that clearly reflects the main topic of the article`, 'string') }}"
          },
          "schema": [
            {
              "id": "imageTitle",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "imageTitle",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "imagePrompt",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "imagePrompt",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "keyword",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "keyword",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "altText",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "altText",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "4aa37110-3c5b-4086-bf30-3c3f0c2b47c7",
      "name": "When Executed by Another Workflow",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "position": [
        -6208,
        1616
      ],
      "parameters": {
        "workflowInputs": {
          "values": [
            {
              "name": "imageTitle"
            },
            {
              "name": "imagePrompt"
            },
            {
              "name": "keyword"
            },
            {
              "name": "altText"
            }
          ]
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "4828babb-1f1c-4e19-86a3-effc98f5e4f4",
      "name": "Result",
      "type": "n8n-nodes-base.set",
      "position": [
        -4624,
        1600
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "c66c7c13-57ca-4191-9af3-5ab306dfedfc",
              "name": "Response",
              "type": "string",
              "value": "=The image was created and named \"{{ $json.name }}\" and the following altText \"{{ $('When Executed by Another Workflow').item.json.altText }}\"\n\nHere is the link to the image in Google Drive: {{ $json.webViewLink }}\n\nHere's the download link: {{ $json.webContentLink }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "9830cce4-9d2f-450f-a61a-2937b3d9831c",
      "name": "Upload to Google Drive",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        -5088,
        1600
      ],
      "parameters": {
        "name": "={{ $('When Executed by Another Workflow').item.json.imageTitle }}.png.png",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive",
          "cachedResultUrl": "https://drive.google.com/drive/my-drive",
          "cachedResultName": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "list",
          "value": "1CYgRUELLCpM1tHx5cM6hBpf5RdfxItR0",
          "cachedResultUrl": "https://drive.google.com/drive/folders/1CYgRUELLCpM1tHx5cM6hBpf5RdfxItR0",
          "cachedResultName": "AI images"
        }
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "4c611c1f-8a67-482f-a8c2-3faccc234d84",
      "name": "Get Download Links",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        -4848,
        1600
      ],
      "parameters": {
        "fileId": {
          "__rl": true,
          "mode": "url",
          "value": "={{ $json.webViewLink }}"
        },
        "options": {},
        "operation": "download"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "281da2fe-0d6e-47c7-9073-ada1b0f83343",
      "name": "Generate Image",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "position": [
        -5936,
        1616
      ],
      "parameters": {
        "url": "https://openrouter.ai/api/v1/chat/completions",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"model\": \"google/gemini-2.5-flash-image-preview\",\n  \"messages\": [\n    {\n      \"role\": \"user\", \n      \"content\": \"Generate an image: {{ $json.imagePrompt }}\"\n    }\n  ],\n  \"modalities\": [\"image\", \"text\"],\n  \"max_tokens\": 2000\n}",
        "sendBody": true,
        "specifyBody": "json"
      },
      "retryOnFail": true,
      "typeVersion": 4.2
    },
    {
      "id": "057b7038-0135-486c-9d85-e58c420280a2",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4368,
        1376
      ],
      "parameters": {
        "width": 528,
        "height": 496,
        "content": "### \ud83c\udfa8 AI Image Generation Sub-Workflow\n\nGenerates custom AI images for SEO posts.\n\n**How it works:**\n1. Called by parent workflow with image parameters\n2. Gemini 2.5 Flash creates image from prompt\n3. Uploads to Google Drive, returns shareable link\n\n**Inputs Required:**\n- imageTitle: filename/title\n- imagePrompt: AI generation instructions\n- keyword: topic keyword\n- altText: accessibility text\n\n**Output:**\n- Google Drive download link\n- Alt text for SEO\n- Image metadata\n\n**Setup:** Configure OpenRouter API key and Google Drive folder ID before use.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "610f756e-b237-4712-b602-4fae1cf0d842",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -6000,
        1424
      ],
      "parameters": {
        "color": 7,
        "height": 352,
        "content": "## Step 1\n\nCalls OpenRouter with Gemini 2.5 Flash to create image based on prompt. You can modify based on your own tools and preference."
      },
      "typeVersion": 1
    },
    {
      "id": "6051dd14-fc03-4a39-b781-67c7a8c405e4",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5728,
        1424
      ],
      "parameters": {
        "color": 7,
        "width": 208,
        "height": 352,
        "content": "## Step 2\n\nGets the image file from the response"
      },
      "typeVersion": 1
    },
    {
      "id": "f6299361-eb86-4f48-86cf-8d4c661d198d",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5440,
        1424
      ],
      "parameters": {
        "color": 7,
        "width": 208,
        "height": 352,
        "content": "## Step 3\n\n\nConverts file for upload"
      },
      "typeVersion": 1
    },
    {
      "id": "7cfff106-510f-46d9-aed2-f8d5cde4369b",
      "name": "Sticky Note16",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4688,
        1408
      ],
      "parameters": {
        "color": 7,
        "width": 208,
        "height": 352,
        "content": "## Step 5\n\nFormats final output with image URL, alt text, and confirmation message"
      },
      "typeVersion": 1
    },
    {
      "id": "8a0e7ba0-5c3d-4c72-a5c6-10318074769c",
      "name": "Sticky Note18",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -6224,
        1216
      ],
      "parameters": {
        "color": 3,
        "width": 2896,
        "height": 144,
        "content": "\n# Important\nThis workflow should be created as an individual one. Then, in the Agent Tool node, you need to select it as the tool. DON'T try to run it inside the same workflow."
      },
      "typeVersion": 1
    },
    {
      "id": "7f20a1aa-3b62-460c-afc7-05f4910f3aad",
      "name": "Get Image URL",
      "type": "n8n-nodes-base.set",
      "position": [
        -5680,
        1600
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "4409adb8-cde7-450d-82b3-f9224b32787a",
              "name": "image_file",
              "type": "string",
              "value": "={{ $json.choices[0].message.images[0].image_url.url.split(\",\")[1] }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "784ba28b-cdde-4950-99dd-414d0c695a7c",
      "name": "Convert File",
      "type": "n8n-nodes-base.convertToFile",
      "position": [
        -5392,
        1600
      ],
      "parameters": {
        "options": {},
        "operation": "toBinary",
        "sourceProperty": "image_file"
      },
      "typeVersion": 1.1
    },
    {
      "id": "b743d732-0624-4572-b286-f30e6839784b",
      "name": "Sticky Note15",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5152,
        1424
      ],
      "parameters": {
        "color": 7,
        "width": 208,
        "height": 352,
        "content": "## Step 4\n\n\nUploads image to Google Drive with descriptive filename"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "AI Agent": {
      "main": [
        [
          {
            "node": "Process Agent Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Error": {
      "main": [
        [
          {
            "node": "Save Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Error": {
      "main": [
        [
          {
            "node": "Wait a few seconds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert File": {
      "main": [
        [
          {
            "node": "Upload to Google Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Success": {
      "main": [
        [
          {
            "node": "Mark as Complete",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Image URL": {
      "main": [
        [
          {
            "node": "Convert File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory": {
      "ai_memory": [
        [
          {
            "node": "AI Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Expand Content": {
      "main": [
        [
          {
            "node": "Format Agent Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Image": {
      "main": [
        [
          {
            "node": "Get Image URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert to HTML": {
      "main": [
        [
          {
            "node": "Create New Post",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create New Post": {
      "main": [
        [
          {
            "node": "Check Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [
          {
            "node": "No Keywords Available",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark as Complete": {
      "main": [
        [
          {
            "node": "Save Success Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Load Pending Keywords",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Get Download Links": {
      "main": [
        [
          {
            "node": "Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait a few seconds": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Agent Output": {
      "main": [
        [
          {
            "node": "Merge Content Paths",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge Content Paths": {
      "main": [
        [
          {
            "node": "Convert to HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Agent Output": {
      "main": [
        [
          {
            "node": "Content Quality Check",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Success Results": {
      "main": [
        [
          {
            "node": "Wait a few seconds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Content Quality Check": {
      "main": [
        [
          {
            "node": "Merge Content Paths",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Expand Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Load Pending Keywords": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload to Google Drive": {
      "main": [
        [
          {
            "node": "Get Download Links",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Image Generation Tool": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "When Executed by Another Workflow": {
      "main": [
        [
          {
            "node": "Generate Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}