{
  "name": "Sanity Blog Article Generator",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "sanity-blog-generator",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "0d754534-2a8d-4686-b144-42072002828e",
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1.1,
      "position": [
        -272,
        -96
      ],
      "webhookId": "sanity-blog-generator"
    },
    {
      "parameters": {
        "url": "https://YOUR_SANITY_PROJECT_ID.api.sanity.io/v2024-01-01/data/query/production",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "query",
              "value": "*[_type == \"category\"] { _id, title, slug }"
            }
          ]
        },
        "options": {}
      },
      "id": "caf3a350-ba77-46e9-9ad9-3beb97c9bc9b",
      "name": "Fetch Sanity Categories",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -80,
        -96
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "wFpi4JPGZnuRew9W",
          "name": "Sanity Auth"
        }
      }
    },
    {
      "parameters": {
        "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "contents",
              "value": "={{ [{\"parts\": [{\"text\": \"Research the topic: \" + $json.body.title + \". Keywords: \" + ($json.body.seoKeywords || []).join(\", \") + \". Provide a comprehensive overview covering: key concepts, current trends, practical applications, common challenges, and expert insights. Output as structured research notes.\"}]}] }}"
            },
            {
              "name": "generationConfig",
              "value": "={{ {\"temperature\": 0.7, \"maxOutputTokens\": 2048} }}"
            }
          ]
        },
        "options": {}
      },
      "id": "23845095-b3a2-4c17-92eb-8343e712c93f",
      "name": "Research Topic (Gemini)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        128,
        -96
      ],
      "credentials": {
        "httpQueryAuth": {
          "id": "YNehr2pC1lQrLikW",
          "name": "Gemini API Key"
        }
      }
    },
    {
      "parameters": {
        "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "contents",
              "value": "={{ [{\"parts\": [{\"text\": \"Based on this research:\\n\" + $node[\"Research Topic (Gemini)\"].json.candidates[0].content.parts[0].text + \"\\n\\nCreate a comprehensive blog article about: \" + $node[\"Webhook Trigger\"].json.body.title + \"\\n\\nTarget word count: \" + ($node[\"Webhook Trigger\"].json.body.wordCount || 1200) + \" words\\n\\nGenerate the article with the following structure in JSON format:\\n{\\n  \\\"content\\\": \\\"Full article in markdown format with ## headings, **bold**, lists, etc.\\\",\\n  \\\"tldr\\\": \\\"One sentence quick answer (max 150 chars)\\\",\\n  \\\"excerpt\\\": \\\"Brief summary for preview (150-160 chars)\\\",\\n  \\\"keyTakeaways\\\": [\\\"takeaway 1\\\", \\\"takeaway 2\\\", \\\"takeaway 3\\\"],\\n  \\\"timeSaved\\\": 5,\\n  \\\"badges\\\": [\\\"actionable\\\", \\\"expert\\\"],\\n  \\\"faqs\\\": [{\\\"question\\\": \\\"Q1\\\", \\\"answer\\\": \\\"A1\\\"}, {\\\"question\\\": \\\"Q2\\\", \\\"answer\\\": \\\"A2\\\"}],\\n  \\\"metaDescription\\\": \\\"SEO meta description (max 160 chars)\\\",\\n  \\\"tags\\\": [\\\"tag1\\\", \\\"tag2\\\", \\\"tag3\\\"],\\n  \\\"categoryMatch\\\": \\\"Best matching category from: \" + $node[\"Fetch Sanity Categories\"].json.result.map(c => c.title).join(\", \") + \"\\\"\\n}\\n\\nOutput ONLY valid JSON, no markdown formatting.\"}]}] }}"
            },
            {
              "name": "generationConfig",
              "value": "={{ {\"temperature\": 0.8, \"maxOutputTokens\": 8192, \"responseMimeType\": \"application/json\"} }}"
            }
          ]
        },
        "options": {}
      },
      "id": "a3f0f12e-24ba-4277-9662-fda5efbf0c87",
      "name": "Generate Article Content (Gemini)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        336,
        -96
      ],
      "credentials": {
        "httpQueryAuth": {
          "id": "YNehr2pC1lQrLikW",
          "name": "Gemini API Key"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const geminiResponse = $input.item.json.candidates[0].content.parts[0].text;\nconst articleData = JSON.parse(geminiResponse);\nconst webhookData = $node[\"Webhook Trigger\"].json.body;\nconst categories = $node[\"Fetch Sanity Categories\"].json.result;\n\n// Find matching category\nconst matchedCategory = categories.find(c => \n  c.title.toLowerCase() === articleData.categoryMatch.toLowerCase()\n) || categories[0];\n\n// Generate slug\nconst slug = webhookData.title\n  .toLowerCase()\n  .replace(/[^a-z0-9]+/g, '-')\n  .replace(/^-|-$/g, '');\n\n// Convert markdown to Portable Text blocks (simplified)\nconst contentLines = articleData.content.split('\\n\\n');\nconst portableTextBlocks = contentLines.map(line => {\n  if (line.startsWith('## ')) {\n    return {\n      _type: 'block',\n      style: 'h2',\n      children: [{_type: 'span', text: line.replace('## ', '')}]\n    };\n  } else if (line.startsWith('### ')) {\n    return {\n      _type: 'block',\n      style: 'h3',\n      children: [{_type: 'span', text: line.replace('### ', '')}]\n    };\n  } else {\n    let cleanText = line\n      .replace(/\\*\\*(.+?)\\*\\*/g, '$1')\n      .replace(/\\*(.+?)\\*/g, '$1');\n    return {\n      _type: 'block',\n      style: 'normal',\n      children: [{_type: 'span', text: cleanText}]\n    };\n  }\n});\n\nreturn {\n  json: {\n    webhookData,\n    articleData,\n    matchedCategory,\n    slug,\n    portableTextBlocks\n  }\n};"
      },
      "id": "2b5bc92f-0f20-431c-800d-7f980f026b3b",
      "name": "Process Article Data",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        528,
        -96
      ]
    },
    {
      "parameters": {
        "url": "={{ \"https://api.unsplash.com/photos/random?query=\" + encodeURIComponent($json.webhookData.title) + \"&orientation=landscape\" }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {}
      },
      "id": "203ee80f-8962-4904-9b5c-12e27e947f27",
      "name": "Fetch Unsplash Image",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        736,
        -96
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "GfTxJP9BbEE7Tykj",
          "name": "Unsplash Auth"
        }
      }
    },
    {
      "parameters": {
        "url": "={{ $json.urls.regular }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "id": "434f3aa0-bd80-4b7b-b2f1-6815bd64af6c",
      "name": "Download Image",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        928,
        -96
      ]
    },
    {
      "parameters": {
        "url": "https://YOUR_SANITY_PROJECT_ID.api.sanity.io/v2024-01-01/assets/images/production",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "contentType": "binaryData",
        "binaryPropertyName": "data",
        "options": {}
      },
      "id": "47281de5-bf09-484b-aeeb-ac047cd5b317",
      "name": "Upload Image to Sanity",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1136,
        -96
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "wFpi4JPGZnuRew9W",
          "name": "Sanity Auth"
        }
      }
    },
    {
      "parameters": {
        "url": "https://YOUR_SANITY_PROJECT_ID.api.sanity.io/v2024-01-01/data/mutate/production",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "contentType": "json",
        "bodyParameters": {
          "parameters": [
            {
              "name": "mutations",
              "value": "={{ [\n  {\n    \"create\": {\n      \"_type\": \"post\",\n      \"title\": $node[\"Process Article Data\"].json.webhookData.title,\n      \"slug\": {\n        \"_type\": \"slug\",\n        \"current\": $node[\"Process Article Data\"].json.slug\n      },\n      \"excerpt\": $node[\"Process Article Data\"].json.articleData.excerpt,\n      \"body\": $node[\"Process Article Data\"].json.portableTextBlocks,\n      \"publishedAt\": $now.toISO(),\n      \"isDraft\": $node[\"Process Article Data\"].json.webhookData.isDraft !== false,\n      \"featured\": false,\n      \"author\": {\n        \"_type\": \"reference\",\n        \"_ref\": \"*[_type == 'author' && slug.current == '\" + ($node[\"Process Article Data\"].json.webhookData.authorSlug || \"victoria-lane\") + \"'][0]._id\"\n      },\n      \"categories\": [{\n        \"_type\": \"reference\",\n        \"_ref\": $node[\"Process Article Data\"].json.matchedCategory._id\n      }],\n      \"tags\": $node[\"Process Article Data\"].json.articleData.tags,\n      \"featuredImage\": {\n        \"_type\": \"image\",\n        \"asset\": {\n          \"_type\": \"reference\",\n          \"_ref\": $node[\"Upload Image to Sanity\"].json.document._id\n        },\n        \"alt\": $node[\"Process Article Data\"].json.webhookData.title\n      },\n      \"tldr\": {\n        \"title\": \"TL;DR - Quick Answer\",\n        \"description\": $node[\"Process Article Data\"].json.articleData.tldr,\n        \"readTime\": Math.ceil($node[\"Process Article Data\"].json.articleData.content.split(' ').length / 200)\n      },\n      \"keyTakeaways\": {\n        \"items\": $node[\"Process Article Data\"].json.articleData.keyTakeaways,\n        \"timeSaved\": $node[\"Process Article Data\"].json.articleData.timeSaved || 5,\n        \"badges\": $node[\"Process Article Data\"].json.articleData.badges || []\n      },\n      \"faq\": {\n        \"enabled\": true,\n        \"items\": $node[\"Process Article Data\"].json.articleData.faqs\n      },\n      \"seo\": {\n        \"metaDescription\": $node[\"Process Article Data\"].json.articleData.metaDescription,\n        \"metaKeywords\": $node[\"Process Article Data\"].json.webhookData.seoKeywords || [],\n        \"noIndex\": false\n      }\n    }\n  }\n] }}"
            }
          ]
        },
        "options": {}
      },
      "id": "11629648-dec7-4add-8d7f-f52bef14144e",
      "name": "Create Sanity Post",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1328,
        -96
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "wFpi4JPGZnuRew9W",
          "name": "Sanity Auth"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ {\n  \"success\": true,\n  \"postId\": $json.results[0].id,\n  \"slug\": $node[\"Process Article Data\"].json.slug,\n  \"draftUrl\": \"https://your-site.com/studio/structure/post;\" + $json.results[0].id,\n  \"previewUrl\": \"https://your-site.com/blog/\" + $node[\"Process Article Data\"].json.slug,\n  \"isDraft\": $node[\"Process Article Data\"].json.webhookData.isDraft !== false,\n  \"message\": \"Blog post created successfully. \" + ($node[\"Process Article Data\"].json.webhookData.isDraft !== false ? \"Review draft in Sanity Studio.\" : \"Post is live!\")\n} }}",
        "options": {}
      },
      "id": "31e3bd9e-5fa1-4720-900a-17254a54a9e6",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        1536,
        -96
      ]
    }
  ],
  "connections": {
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Fetch Sanity Categories",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Sanity Categories": {
      "main": [
        [
          {
            "node": "Research Topic (Gemini)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Research Topic (Gemini)": {
      "main": [
        [
          {
            "node": "Generate Article Content (Gemini)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Article Content (Gemini)": {
      "main": [
        [
          {
            "node": "Process Article Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Article Data": {
      "main": [
        [
          {
            "node": "Fetch Unsplash Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Unsplash Image": {
      "main": [
        [
          {
            "node": "Download Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Image": {
      "main": [
        [
          {
            "node": "Upload Image to Sanity",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Image to Sanity": {
      "main": [
        [
          {
            "node": "Create Sanity Post",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Sanity Post": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}
