AutomationFlows โ€บ Web Scraping โ€บ Publish Blog Post (wolf Pack Pipeline)

Publish Blog Post (wolf Pack Pipeline)

Original n8n title: ๐Ÿ“ Publish Blog Post (wolf Pack Pipeline)

๐Ÿ“ Publish Blog Post (Wolf Pack Pipeline). Uses httpRequest. Webhook trigger; 15 nodes.

Webhook triggerโ˜…โ˜…โ˜…โ˜…โ˜† complexity15 nodesHTTP Request
Web Scraping Trigger: Webhook Nodes: 15 Complexity: โ˜…โ˜…โ˜…โ˜…โ˜† Added:

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide โ†’

Download .json
{
  "name": "\ud83d\udcdd Publish Blog Post (Wolf Pack Pipeline)",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "publish-blog-post",
        "authentication": "basicAuth",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        0,
        300
      ],
      "id": "webhook-trigger",
      "name": "Blog Post Webhook"
    },
    {
      "parameters": {
        "jsCode": "// Normalize and validate input\nconst input = $input.first().json;\n\nconst title = input.title;\nconst content = input.content;\nconst slug = input.slug || title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');\nconst excerpt = input.excerpt || content.substring(0, 200).replace(/[#*\\n]/g, ' ').trim() + '...';\nconst tags = input.tags || ['stashdog'];\nconst metaDescription = input.meta_description || excerpt.substring(0, 155);\nconst authorId = input.author_id || '1dd2a897-03ac-4a5f-b374-d8894d4394d2';\nconst generateImage = input.generate_image !== false; // default true\nconst imageStyle = input.image_style || 'default'; // 'default' uses StashDog brand SVG\n\nif (!title || !content) {\n  throw new Error('Missing required fields: title and content are required');\n}\n\nreturn [{\n  json: {\n    title,\n    content,\n    slug,\n    excerpt,\n    tags: Array.isArray(tags) ? tags : tags.split(',').map(t => t.trim()),\n    metaDescription,\n    authorId,\n    generateImage,\n    imageStyle\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        250,
        300
      ],
      "id": "normalize-input",
      "name": "Normalize Input"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "check-generate-image",
              "leftValue": "={{ $json.generateImage }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        500,
        300
      ],
      "id": "check-image-needed",
      "name": "Generate Image?"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://openrouter.ai/api/v1/chat/completions",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"google/gemini-2.5-flash-preview\",\n  \"messages\": [\n    {\n      \"role\": \"system\",\n      \"content\": \"You are a graphic designer for StashDog, a home inventory app. Generate ONLY raw SVG code (no markdown fences, no explanation) for a blog hero image. Brand: dark background (#0a0a0a), yellow accent (#fcd900), white text. Dimensions: viewBox='0 0 1200 675' width='1200' height='675'. Include the blog title as large text on the left side, a subtle tag line reading 'STASHDOG' and 'stashdog.io/blog' at the bottom left, and abstract visual elements on the right side representing the blog topic (use inventory cards, grid patterns, icons). Use modern, clean design with glass-morphism effects. The SVG must be self-contained with no external dependencies.\"\n    },\n    {\n      \"role\": \"user\",\n      \"content\": \"Create an SVG hero image for this blog post: {{ $json.title }}\"\n    }\n  ],\n  \"max_tokens\": 8000,\n  \"temperature\": 0.7\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        750,
        200
      ],
      "id": "generate-hero-svg",
      "name": "Generate Hero SVG (LLM)",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Extract SVG from LLM response\nconst response = $input.first().json;\nconst llmContent = response.choices?.[0]?.message?.content || '';\n\n// Extract SVG \u2014 strip markdown fences if present\nlet svg = llmContent;\nif (svg.includes('<svg')) {\n  svg = svg.substring(svg.indexOf('<svg'));\n  const endIdx = svg.lastIndexOf('</svg>');\n  if (endIdx !== -1) {\n    svg = svg.substring(0, endIdx + 6);\n  }\n}\n\n// Get the normalized data from earlier in the chain\nconst blogData = $('Normalize Input').first().json;\nconst fileName = `blog-${blogData.slug}-hero.svg`;\n\nreturn [{\n  json: {\n    ...blogData,\n    svgContent: svg,\n    fileName\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1000,
        200
      ],
      "id": "extract-svg",
      "name": "Extract SVG"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://gmchczeyburroiyzefie.supabase.co/storage/v1/object/blog-assets/{{ $json.fileName }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "image/svg+xml"
            },
            {
              "name": "x-upsert",
              "value": "true"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "body": "={{ $json.svgContent }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1250,
        200
      ],
      "id": "upload-to-supabase-storage",
      "name": "Upload SVG to Supabase Storage",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Build the public image URL and prepare for DB insert\nconst data = $input.first().json;\nconst imageUrl = `https://gmchczeyburroiyzefie.supabase.co/storage/v1/object/public/blog-assets/${data.fileName}`;\n\nreturn [{\n  json: {\n    ...data,\n    featuredImageUrl: imageUrl\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1500,
        200
      ],
      "id": "build-image-url",
      "name": "Build Image URL"
    },
    {
      "parameters": {
        "jsCode": "// Skip image gen \u2014 just pass through with no image\nconst blogData = $('Normalize Input').first().json;\n\nreturn [{\n  json: {\n    ...blogData,\n    featuredImageUrl: null,\n    fileName: null\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1500,
        450
      ],
      "id": "skip-image",
      "name": "Skip Image Gen"
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [
        1750,
        300
      ],
      "id": "merge-paths",
      "name": "Merge"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://gmchczeyburroiyzefie.supabase.co/rest/v1/blog_posts",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Accept-Profile",
              "value": "content"
            },
            {
              "name": "Content-Profile",
              "value": "content"
            },
            {
              "name": "Prefer",
              "value": "return=representation"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"title\": {{ JSON.stringify($json.title) }},\n  \"content\": {{ JSON.stringify($json.content) }},\n  \"slug\": {{ JSON.stringify($json.slug) }},\n  \"excerpt\": {{ JSON.stringify($json.excerpt) }},\n  \"author_id\": {{ JSON.stringify($json.authorId) }},\n  \"published\": true,\n  \"featured_image_url\": {{ JSON.stringify($json.featuredImageUrl) }},\n  \"tags\": {{ JSON.stringify($json.tags) }},\n  \"meta_description\": {{ JSON.stringify($json.metaDescription) }}\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2000,
        300
      ],
      "id": "insert-blog-post",
      "name": "Insert into content.blog_posts",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Update marketing_content pipeline status if matching title exists\nconst blogPost = $input.first().json;\nconst blogData = $('Normalize Input').first().json;\n\nconst postId = Array.isArray(blogPost) ? blogPost[0]?.id : blogPost?.id;\nconst slug = blogData.slug;\n\nreturn [{\n  json: {\n    postId,\n    slug,\n    title: blogData.title,\n    url: `https://stashdog.io/blog/${slug}/`\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2250,
        300
      ],
      "id": "prepare-response",
      "name": "Prepare Response"
    },
    {
      "parameters": {
        "method": "PATCH",
        "url": "=https://gmchczeyburroiyzefie.supabase.co/rest/v1/marketing_content?title=eq.{{ encodeURIComponent($json.title) }}&channel=eq.blog",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Prefer",
              "value": "return=minimal"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={ \"status\": \"published\", \"published_at\": \"{{ new Date().toISOString() }}\", \"updated_at\": \"{{ new Date().toISOString() }}\" }",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2500,
        200
      ],
      "id": "update-marketing-content",
      "name": "Update marketing_content \u2192 published",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://gmchczeyburroiyzefie.supabase.co/rest/v1/marketing_activities",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Prefer",
              "value": "return=minimal"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"agent\": \"n8n\",\n  \"action\": \"content_published\",\n  \"description\": \"Published blog post via n8n pipeline: {{ $('Prepare Response').first().json.title }} \u2192 {{ $('Prepare Response').first().json.url }}\",\n  \"channel\": \"blog\",\n  \"metadata\": { \"slug\": \"{{ $('Prepare Response').first().json.slug }}\", \"pipeline\": \"publish-blog-post\" }\n}",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2500,
        400
      ],
      "id": "log-activity",
      "name": "Log to marketing_activities",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\n  \"success\": true,\n  \"post\": {\n    \"id\": \"{{ $('Prepare Response').first().json.postId }}\",\n    \"title\": \"{{ $('Prepare Response').first().json.title }}\",\n    \"slug\": \"{{ $('Prepare Response').first().json.slug }}\",\n    \"url\": \"{{ $('Prepare Response').first().json.url }}\",\n    \"status\": \"published\"\n  },\n  \"message\": \"Blog post published. Gatsby rebuild needed to deploy to stashdog.io.\"\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        2750,
        300
      ],
      "id": "respond",
      "name": "Respond to Webhook"
    },
    {
      "parameters": {
        "content": "## \ud83d\udcdd Publish Blog Post Pipeline\n\n**Webhook:** POST `/webhook/publish-blog-post`\n\n**Input JSON:**\n```json\n{\n  \"title\": \"Required \u2014 blog post title\",\n  \"content\": \"Required \u2014 full markdown content\",\n  \"slug\": \"Optional \u2014 auto-generated from title\",\n  \"excerpt\": \"Optional \u2014 auto-generated from content\",\n  \"tags\": [\"array\", \"of\", \"tags\"],\n  \"meta_description\": \"Optional \u2014 max 155 chars\",\n  \"author_id\": \"Optional \u2014 defaults to Raz\",\n  \"generate_image\": true,\n  \"image_style\": \"default\"\n}\n```\n\n**Pipeline:**\n1. Validate & normalize input\n2. Generate branded SVG hero image (LLM via OpenRouter)\n3. Upload SVG to Supabase Storage (blog-assets bucket)\n4. Insert into `content.blog_posts` (published=true)\n5. Update `marketing_content` status \u2192 published\n6. Log to `marketing_activities`\n7. Respond with post URL\n\n**Credentials needed:**\n- `OpenRouter API` \u2014 httpHeaderAuth with `Authorization: Bearer <key>`\n- `Supabase Service Key` \u2014 httpHeaderAuth with `apikey: <service_role_key>` AND `Authorization: Bearer <service_role_key>`\n\n**Note:** After publishing, a Gatsby rebuild is needed to deploy. Consider adding a Firebase deploy webhook trigger.",
        "height": 680,
        "width": 500,
        "color": 5
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -400,
        100
      ],
      "id": "docs-note",
      "name": "Documentation"
    }
  ],
  "connections": {
    "Blog Post Webhook": {
      "main": [
        [
          {
            "node": "Normalize Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Input": {
      "main": [
        [
          {
            "node": "Generate Image?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Image?": {
      "main": [
        [
          {
            "node": "Generate Hero SVG (LLM)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Skip Image Gen",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Hero SVG (LLM)": {
      "main": [
        [
          {
            "node": "Extract SVG",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract SVG": {
      "main": [
        [
          {
            "node": "Upload SVG to Supabase Storage",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload SVG to Supabase Storage": {
      "main": [
        [
          {
            "node": "Build Image URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Image URL": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Skip Image Gen": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "Insert into content.blog_posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insert into content.blog_posts": {
      "main": [
        [
          {
            "node": "Prepare Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Response": {
      "main": [
        [
          {
            "node": "Update marketing_content \u2192 published",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log to marketing_activities",
            "type": "main",
            "index": 0
          },
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "callerPolicy": "workflowsFromSameOwner",
    "availableInMCP": true
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing โ€” you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

๐Ÿ“ Publish Blog Post (Wolf Pack Pipeline). Uses httpRequest. Webhook trigger; 15 nodes.

Source: https://github.com/dogfoodlab-io/stashdog.io/blob/b7a15c1b17cd54961d55e23883faf21904072db5/n8n-workflows/publish-blog-post.json โ€” original creator credit. Request a take-down โ†’

More Web Scraping workflows โ†’ ยท Browse all categories โ†’

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Web Scraping

This n8n template provides enterprise-level version control for your workflows using GitHub integration. Stop losing hours to broken workflows and manual exports โ€“ get proper commit history, visual di

n8n, Execute Workflow Trigger, HTTP Request +1
Web Scraping

This flow creates dummy files for every item added in your *Arrs (Radarr/Sonarr) with the tag .

HTTP Request, Ssh
Web Scraping

eek-Go v2 (Batch-Then-Review). Uses httpRequest. Webhook trigger; 75 nodes.

HTTP Request
Web Scraping

This workflow receives webhook requests from a content calendar and uses the X API v2 to publish text posts, threads, image/video posts, and polls, as well as delete existing posts and run a credentia

HTTP Request
Web Scraping

This workflow acts as a central API gateway for all technical indicator agents in the Binance Spot Market Quant AI system. It listens for incoming webhook requests and dynamically routes them to the c

HTTP Request