{
  "id": "hssRrEvGUFcYNM41",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Shopify Product to SEO Blog AI Agent",
  "tags": [],
  "nodes": [
    {
      "id": "f041cca3-e1bb-484c-b268-eaeb590e6681",
      "name": "Shopify",
      "type": "n8n-nodes-base.shopify",
      "position": [
        -880,
        -256
      ],
      "parameters": {
        "limit": 3,
        "resource": "product",
        "operation": "getAll",
        "authentication": "accessToken",
        "additionalFields": {}
      },
      "typeVersion": 1
    },
    {
      "id": "7feceb53-f3f4-4d16-b45d-0878c7041d8f",
      "name": "Product Data in Google Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -512,
        -256
      ],
      "parameters": {
        "columns": {
          "value": {
            "ID": "={{ $json.id }}",
            "Title": "={{ $json.title }}",
            "Description": "={{ $json.body_html }}"
          },
          "schema": [
            {
              "id": "ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Title",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Description",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1gnygfCXJWa1uploTP-iwzdLHBnT_1r10meVsqlvTe8o/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1gnygfCXJWa1uploTP-iwzdLHBnT_1r10meVsqlvTe8o",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1gnygfCXJWa1uploTP-iwzdLHBnT_1r10meVsqlvTe8o/edit?usp=drivesdk",
          "cachedResultName": "Shopify Products Data "
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "d35f2faf-8a52-4f5f-b979-42add744a6a5",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -160,
        -256
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "f5b1adde-9ef9-4444-934e-547650dba9df",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        0,
        -240
      ],
      "parameters": {
        "text": "=You are an AI agent responsible for automating blog creation for Shopify products\n\nWrite an SEO-friendly blog post that helps promote the following Shopify product. Use the product title and description below to create a compelling, engaging, and informative article that:\n\nNaturally incorporates relevant keywords from the product title and description.\n\nIncludes an attention-grabbing introduction.\n\nHighlights the product features, benefits, and use cases.\n\nBuilds trust with the reader using persuasive and friendly language.\n\nEnds with a clear call-to-action (CTA) encouraging the reader to explore or purchase the product.\n\nOptimize the blog for search engines (on-page SEO: headings, keyword placement, meta description, etc.)\n\n- SEO-Friendly Blog Title\n- Meta Description\n- Formatted Blog Content in clean, structured HTML:\n  - Introduction summary\n  - H2/H3 subheadings\n  - Bullet points (key benefits, nutrients)\n  - Nutrient callouts or tables (if available)\n  - Closing paragraph or CTA\n  - Add internal keywords (tags)\n\n\nProduct Title: {{ $('variables').item.json.title }}\nProduct Description: {{ $('variables').item.json.body_html }}\n\n\n### Provide ONLY the JSON object with the title and description.\n\nThe JSON object must have exactly two keys:\n1.  `blogTitle`: A string containing a compelling, SEO-friendly title for the blog post.\n2.  `blogContent`: A string containing the full blog post formatted in clean, structured HTML. The HTML should include headings (H2, H3), paragraphs, bullet points, and a clear call-to-action.\n\nHere is an example of the required output format:\n{\n  \"blogTitle\": \"Example SEO-Friendly Title\",\n  \"blogContent\": \"<h1>Example Heading</h1><p>This is the full HTML content of the blog post...</p>\"\n}\n\n\n\n\n\n\n\n\n\n",
        "options": {},
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.9
    },
    {
      "id": "13d8032e-c843-4c2c-8b1c-bb7043d6916d",
      "name": "Simple Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        112,
        0
      ],
      "parameters": {
        "sessionKey": "={{ $('variables').item.json.id }}",
        "sessionIdType": "customKey"
      },
      "typeVersion": 1.3
    },
    {
      "id": "6502dc9b-332c-462b-ad20-96f36da900c9",
      "name": "Code",
      "type": "n8n-nodes-base.code",
      "position": [
        384,
        -240
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// This code will run for each item coming from the AI Agent.\nconst item = $input.item;\nconst aiOutput = item.json.output;\n\n// Use a try-catch block to safely handle potential JSON parsing errors\ntry {\n  // Regular expression to find a JSON object within the AI's output string.\n  const jsonMatch = aiOutput.match(/{.*}/s);\n\n  if (!jsonMatch) {\n    throw new Error(\"No JSON object found in the AI output.\");\n  }\n\n  const jsonString = jsonMatch[0];\n  const parsedData = JSON.parse(jsonString);\n\n  // --- RESILIENT KEY CHECKING ---\n  // This is the important part. It checks for multiple possible key names.\n  const title = parsedData.blogTitle || parsedData.title;\n  const content = parsedData.blogContent || parsedData.description;\n  \n  // If either the title or content could not be found, throw an error.\n  if (!title || !content) {\n    throw new Error(\"The parsed JSON is missing a recognizable title or content field.\");\n  }\n  \n  // Return the data using the consistent keys that the next nodes expect.\n  return {\n    json: {\n      ...item.json, // Carry over original data\n      blogTitle: title,\n      blogContent: content\n    }\n  };\n\n} catch (error) {\n  // If anything goes wrong, log the error and prevent this item from proceeding.\n  console.error(\"Failed to parse AI output:\", error.message, \"Raw Output:\", aiOutput);\n  return {\n    json: {\n      error: \"Failed to parse AI output\",\n      errorMessage: error.message\n    }\n  };\n}"
      },
      "typeVersion": 2
    },
    {
      "id": "f3abf95d-0a58-43ef-8680-eb6daac45a63",
      "name": "Google Sheets1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        640,
        -240
      ],
      "parameters": {
        "columns": {
          "value": {
            "Title": "={{ $json.blogTitle }}",
            "Description": "={{ $json.blogContent }}"
          },
          "schema": [
            {
              "id": "Title",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Description",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Title"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 867989363,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1gnygfCXJWa1uploTP-iwzdLHBnT_1r10meVsqlvTe8o/edit#gid=867989363",
          "cachedResultName": "Blogs"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1gnygfCXJWa1uploTP-iwzdLHBnT_1r10meVsqlvTe8o",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1gnygfCXJWa1uploTP-iwzdLHBnT_1r10meVsqlvTe8o/edit?usp=drivesdk",
          "cachedResultName": "Shopify Products Data "
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "3f85d06c-e5a7-4ef0-b905-27490331c650",
      "name": "When clicking \u2018Test workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -1056,
        -256
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "926dd53e-9b31-4b37-989b-c3b4467b02dc",
      "name": "Create Blog As Draft",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1008,
        -240
      ],
      "parameters": {
        "url": "=https://{{ $('variables').item.json.shop }}.myshopify.com/admin/api/2024-01/blogs/{{ $('variables').item.json.blogId }}/articles.json",
        "method": "POST",
        "options": {},
        "jsonBody": "={{\n  {\n    \"article\": {\n      \"title\": $('Code').item.json.blogTitle,\n      \"body_html\": $('Code').item.json.blogContent,\n      \"tags\": \"nutrition, wellness, product, shopify\",\n      \"published\": false // Changed to true to make it visible, or keep as false for a draft\n    }\n  }\n}}",
        "sendBody": true,
        "specifyBody": "=json",
        "authentication": "predefinedCredentialType",
        "bodyParameters": {
          "parameters": [
            {}
          ]
        },
        "nodeCredentialType": "shopifyAccessTokenApi"
      },
      "typeVersion": 4.2
    },
    {
      "id": "94ab20f8-297a-4ec0-a87b-3046b8940fce",
      "name": "Gmail",
      "type": "n8n-nodes-base.gmail",
      "position": [
        80,
        -608
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "I am your AI Agent: Good news, I have added all the blog posts as draft after reading all your product data.",
        "options": {},
        "subject": "Blog Posts Are Created "
      },
      "typeVersion": 2.1
    },
    {
      "id": "e31eaacf-722b-4e42-9f7a-63b34aa3414d",
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        -192,
        -608
      ],
      "parameters": {
        "amount": 1
      },
      "executeOnce": true,
      "typeVersion": 1.1
    },
    {
      "id": "7030fb0a-73d1-4e49-8584-96240e028b16",
      "name": "OpenRouter Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        -48,
        0
      ],
      "parameters": {
        "model": "deepseek/deepseek-r1:free",
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "e109a401-5b5b-4506-b8dd-88bcecaaef05",
      "name": "variables",
      "type": "n8n-nodes-base.set",
      "position": [
        -656,
        -256
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "93cb8cc0-cdcd-44da-8187-9df85b770293",
              "name": "title",
              "type": "string",
              "value": "={{ $json.title }}"
            },
            {
              "id": "95990abc-b64d-4cb4-8d4e-88145395a3fc",
              "name": "id",
              "type": "number",
              "value": "=  {{ $json.id }}"
            },
            {
              "id": "d833cfa1-9abf-4a9d-86ed-a713acbc1974",
              "name": "body_html",
              "type": "string",
              "value": "={{ $json.body_html }}"
            },
            {
              "id": "65890399-7618-40c9-8cf6-f9fa49085893",
              "name": "shop",
              "type": "string",
              "value": "developmentwebsensepro"
            },
            {
              "id": "eab7febd-523d-4cda-9956-ebf3fc4efb31",
              "name": "blogId",
              "type": "string",
              "value": "94126407876"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "2a809527-40c4-4036-a077-9c5a2f1d5353",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1056,
        -416
      ],
      "parameters": {
        "color": 6,
        "width": 288,
        "height": 116,
        "content": "## Fetch Products\nStarts the workflow and fetches the latest product list from your Shopify store."
      },
      "typeVersion": 1
    },
    {
      "id": "b868c136-4f98-4706-a10b-ed292f000933",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1664,
        -880
      ],
      "parameters": {
        "width": 508,
        "height": 683,
        "content": "## Free AI Agent by WebSensePro\n\n**Complete Free AI agent to read Shopify products and create new blogs based on product Title and Description**\n\n**How it works**\n\nThis workflow automatically creates SEO-friendly blog posts from your Shopify products. It starts by loading each product\u2019s title, description, and ID, then passes that information to an AI agent that writes a structured blog article. The AI response is validated to ensure it contains a clean JSON output with both a blog title and HTML content.\n\nNext, the workflow saves the product and blog data to Google Sheets so you can track everything that has been processed. After logging, it uploads the generated article to Shopify as a draft blog post, giving you the chance to review and edit before publishing.\n\nEach product runs through the workflow individually, so issues with one product won\u2019t stop the rest. This design makes the automation reliable, safe to retry, and easy to monitor. When everything is complete, an optional email notification can be sent.\n\n**Setup steps**\n\n- Fill in your Shopify shop name and Blog ID inside the Variables node.\n- Connect your accounts for Shopify, Google Sheets, OpenRouter, and Gmail (optional).\n- Ensure your Google Sheet contains a tab for storing logs.\n- Add your required API keys into the AI and Shopify nodes.\n- Run the workflow using Test or activate it on a schedule.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "76bc4066-18ce-4d59-befe-4368da6e6a43",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1664,
        -176
      ],
      "parameters": {
        "width": 508,
        "height": 132,
        "content": " ## Video Tutorial\nWatch the full setup walkthrough on YouTube.\n\nhttps://www.youtube.com/watch?v=RCwFc57V6oo"
      },
      "typeVersion": 1
    },
    {
      "id": "a96804f5-6345-48fb-8270-a667c152dff9",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -672,
        -416
      ],
      "parameters": {
        "color": 6,
        "width": 272,
        "height": 128,
        "content": "## Data Backup\nSets necessary variables and saves the raw product data into Google Sheets for record-keeping."
      },
      "typeVersion": 1
    },
    {
      "id": "20898332-ece9-4ab2-a493-496ce8736f3f",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -192,
        -752
      ],
      "parameters": {
        "color": 6,
        "width": 368,
        "height": 112,
        "content": "## Completion Notify\nWaits for the loop to finish processing all items, then sends an email confirmation."
      },
      "typeVersion": 1
    },
    {
      "id": "040930d1-6acb-4171-be6f-901fbb6a05d4",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -160,
        -400
      ],
      "parameters": {
        "color": 6,
        "width": 384,
        "height": 128,
        "content": "## AI Generation Loop\nIterates through each product. The AI Agent writes an SEO-friendly blog post for each item."
      },
      "typeVersion": 1
    },
    {
      "id": "d880d68f-7ec1-42da-9419-c54c484c4d97",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        384,
        -384
      ],
      "parameters": {
        "color": 6,
        "width": 336,
        "height": 112,
        "content": "## Parse & Log\nCleans the AI output using code and saves the final blog title and content to Google Sheets."
      },
      "typeVersion": 1
    },
    {
      "id": "ea9920e8-e97d-4194-ade3-91185b012f74",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -80,
        160
      ],
      "parameters": {
        "color": 6,
        "width": 304,
        "height": 112,
        "content": "## AI Model Config\nDefines the LLM (DeepSeek) and memory settings used by the AI Agent."
      },
      "typeVersion": 1
    },
    {
      "id": "8992dd57-5656-46f8-b2f1-ae4740d4c256",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        960,
        -400
      ],
      "parameters": {
        "color": 6,
        "width": 224,
        "height": 144,
        "content": "## Upload Draft\nUploads the generated content to Shopify as a draft blog post, ready for review."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "6eaa4b1e-735a-4262-a75c-062e25f20f1d",
  "connections": {
    "Code": {
      "main": [
        [
          {
            "node": "Google Sheets1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait": {
      "main": [
        [
          {
            "node": "Gmail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Shopify": {
      "main": [
        [
          {
            "node": "variables",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "variables": {
      "main": [
        [
          {
            "node": "Product Data in Google Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory": {
      "ai_memory": [
        [
          {
            "node": "AI Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets1": {
      "main": [
        [
          {
            "node": "Create Blog As Draft",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Blog As Draft": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Product Data in Google Sheet": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Test workflow\u2019": {
      "main": [
        [
          {
            "node": "Shopify",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}