AutomationFlowsAI & RAG › Poststack: AI Image Posts From Trends

Poststack: AI Image Posts From Trends

PostStack: AI Image Posts from Trends. Uses rssFeedRead, openAi, httpRequest. Scheduled trigger; 17 nodes.

Cron / scheduled trigger★★★★☆ complexityAI-powered17 nodesRSS Feed ReadOpenAIHTTP Request
AI & RAG Trigger: Cron / scheduled Nodes: 17 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow follows the HTTP Request → OpenAI recipe pattern — see all workflows that pair these two integrations.

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": "PostStack: AI Image Posts from Trends",
  "nodes": [
    {
      "id": "sticky-overview",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -760,
        -400
      ],
      "parameters": {
        "color": 1,
        "width": 720,
        "height": 460,
        "content": "## Generate AI image posts from trending topics and publish with PostStack\n\n### How it works\nOn a schedule this workflow picks a trending topic from an RSS feed (Google Trends by default), asks OpenAI to write a caption plus an image prompt in your niche's style (strict JSON), generates the image with OpenAI's image model, and publishes the finished post through [PostStack](https://poststack.techskills.academy) - a self-hosted social media publishing platform - to every channel you list (Instagram, Facebook and more, one API). Fresh, on-trend visual content with zero manual work.\n\n### Setup\n1. In PostStack create an API key (**Settings \u2192 API keys**). API access is a PostStack Pro feature.\n2. Copy the channel ids you want to post to from **Channels**.\n3. Fill in **Configuration (EDIT ME)**: PostStack URL, API key, comma-separated channel ids, topic feed URL and your niche/style brief (drives both the caption and the image).\n4. Connect your OpenAI account on **Write the Concept** and **Generate the Image** (billed by OpenAI - BYOK), and grab a free API key from api.imgbb.com for **Host the Image** (PostStack ingests images from a public URL).\n5. Adjust the **Daily Trigger** schedule and activate.\n\n### Customization tips\nTune `nicheStyle` for a consistent visual identity. Swap the feed for your industry's RSS. Prefer another image model? Replace **Generate the Image** with a Replicate/Flux HTTP call - the only contract is a public image URL handed to **Build Posts per Channel**."
      }
    },
    {
      "id": "sticky-topic",
      "name": "Section: Pick a Topic",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -760,
        340
      ],
      "parameters": {
        "color": 7,
        "width": 1120,
        "height": 220,
        "content": "## Pick a Topic\nRuns on a schedule, reads the trends feed and picks the top topic of the day."
      }
    },
    {
      "id": "sticky-ai",
      "name": "Section: Create with AI",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        440,
        340
      ],
      "parameters": {
        "color": 7,
        "width": 1120,
        "height": 220,
        "content": "## Create with AI\nOpenAI writes the caption + image prompt, the image model renders the visual, imgbb hosts it."
      }
    },
    {
      "id": "sticky-publish",
      "name": "Section: Publish via PostStack",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1640,
        340
      ],
      "parameters": {
        "color": 7,
        "width": 1420,
        "height": 220,
        "content": "## Publish via PostStack\nResolves your channels, creates one post per channel with the generated image and publishes each."
      }
    },
    {
      "id": "trigger-1",
      "name": "Daily Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -700,
        400
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "days",
              "triggerAtHour": 9
            }
          ]
        }
      },
      "notes": "Runs once a day at 09:00. Adjust to your posting rhythm."
    },
    {
      "id": "config-1",
      "name": "Configuration (EDIT ME)",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -400,
        400
      ],
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "a1",
              "name": "postStackUrl",
              "value": "https://your-poststack.example.com",
              "type": "string"
            },
            {
              "id": "a2",
              "name": "apiKey",
              "value": "rs_live_your-api-key",
              "type": "string"
            },
            {
              "id": "a3",
              "name": "channelIds",
              "value": "channel-uuid-1, channel-uuid-2",
              "type": "string"
            },
            {
              "id": "a4",
              "name": "topicFeedUrl",
              "value": "https://trends.google.com/trending/rss?geo=US",
              "type": "string"
            },
            {
              "id": "a5",
              "name": "nicheStyle",
              "value": "A tech-savvy small business audience. Clean, modern flat illustration style, bold colors, no text in the image.",
              "type": "string"
            },
            {
              "id": "a6",
              "name": "imgbbApiKey",
              "value": "your-free-imgbb-api-key",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {
          "stripBinary": false
        }
      },
      "notes": "channelIds: comma-separated ids from PostStack -> Channels. imgbbApiKey: free key from api.imgbb.com - hosts the generated image so PostStack can ingest it (swap for S3/R2 if you prefer)."
    },
    {
      "id": "rss-1",
      "name": "Fetch Trending Topics",
      "type": "n8n-nodes-base.rssFeedRead",
      "typeVersion": 1.1,
      "position": [
        -100,
        400
      ],
      "parameters": {
        "url": "={{ $('Configuration (EDIT ME)').item.json.topicFeedUrl }}",
        "options": {}
      }
    },
    {
      "id": "pick-1",
      "name": "Pick a Topic",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        200,
        400
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nif (!items.length) throw new Error('The topic feed returned no items');\nconst top = items[0].json;\n\n// Google Trends carries an item picture; other feeds may not - Build Posts falls back to config imageUrl.\nconst picture = top['ht:picture'] || top['media:content'] || null;\n\nreturn [{ json: {\n  topic: top.title,\n  context: (top.contentSnippet || top.content || '').slice(0, 500),\n  link: top.link || '',\n  picture,\n} }];"
      },
      "notes": "Takes the top feed item. Swap for a random pick or a loop over the top N."
    },
    {
      "id": "ai-1",
      "name": "Write the Concept",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1.8,
      "position": [
        500,
        400
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "gpt-4o-mini"
        },
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "={{ 'You are a social media creative director for: ' + $('Configuration (EDIT ME)').item.json.nicheStyle + ' Reply with STRICT JSON only, no markdown fences: {\"caption\": \"post text, max 400 chars, no hashtags inside\", \"hashtags\": \"#three #relevant #tags\", \"imagePrompt\": \"a detailed prompt for an image generator, matching the style brief\"}' }}"
            },
            {
              "content": "=Create a social media post concept about this trending topic: {{ $json.topic }}\n\nContext: {{ $json.context }}"
            }
          ]
        },
        "options": {}
      },
      "notes": "Connect your OpenAI credentials here."
    },
    {
      "id": "parse-1",
      "name": "Parse the Concept",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        800,
        400
      ],
      "parameters": {
        "jsCode": "const out = $input.first().json;\nlet text = (out.message && out.message.content) || out.output || out.text || (out.choices && out.choices[0] && out.choices[0].message.content) || '';\nif (typeof text !== 'string') text = JSON.stringify(text);\n\nlet parsed = {};\ntry {\n  const match = text.match(/\\{[\\s\\S]*\\}/);\n  parsed = JSON.parse(match ? match[0] : text);\n} catch (e) {\n  throw new Error('The model did not return valid JSON: ' + text.slice(0, 200));\n}\nconst caption = String(parsed.caption || '').trim();\nconst imagePrompt = String(parsed.imagePrompt || '').trim();\nif (!caption || !imagePrompt) throw new Error('Missing caption or imagePrompt in the model output');\n\nreturn [{ json: { caption, hashtags: String(parsed.hashtags || '').trim(), imagePrompt } }];"
      }
    },
    {
      "id": "img-1",
      "name": "Generate the Image",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1100,
        400
      ],
      "parameters": {
        "method": "POST",
        "url": "https://api.openai.com/v1/images/generations",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "openAiApi",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ model: 'gpt-image-1', prompt: $json.imagePrompt, size: '1024x1024', quality: 'medium' }) }}",
        "options": {
          "timeout": 180000
        }
      },
      "notes": "Calls the OpenAI Images API directly (gpt-image-1 returns base64). Billed by OpenAI - BYOK."
    },
    {
      "id": "host-1",
      "name": "Host the Image",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1400,
        400
      ],
      "parameters": {
        "method": "POST",
        "url": "https://api.imgbb.com/1/upload",
        "sendBody": true,
        "contentType": "form-urlencoded",
        "bodyParameters": {
          "parameters": [
            {
              "name": "key",
              "value": "={{ $('Configuration (EDIT ME)').item.json.imgbbApiKey }}"
            },
            {
              "name": "image",
              "value": "={{ $json.data[0].b64_json }}"
            }
          ]
        },
        "options": {
          "timeout": 120000
        }
      },
      "notes": "Free image hosting so PostStack has a public URL to ingest. Swap for your own S3/R2 upload if you prefer."
    },
    {
      "id": "channels-1",
      "name": "Get Channels",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1700,
        400
      ],
      "parameters": {
        "method": "GET",
        "url": "={{ $('Configuration (EDIT ME)').item.json.postStackUrl }}/api/v1/channels",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ 'Bearer ' + $('Configuration (EDIT ME)').item.json.apiKey }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "options": {
          "timeout": 30000
        }
      }
    },
    {
      "id": "plan-1",
      "name": "Build Posts per Channel",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2000,
        400
      ],
      "parameters": {
        "jsCode": "const config = $('Configuration (EDIT ME)').first().json;\nconst draft = $('Parse the Concept').first().json;\nconst image = $('Host the Image').first().json;\nconst channels = $input.first().json.data || [];\n\nconst imageUrl = image.data && image.data.url;\nif (!imageUrl) throw new Error('Image generation returned no URL');\n\nconst baseUrl = String(config.postStackUrl || '').replace(/\\/+$/, '');\nconst wanted = String(config.channelIds || '').split(',').map((s) => s.trim()).filter(Boolean);\nif (!wanted.length) throw new Error('channelIds in Configuration is empty');\n\nconst byId = new Map(channels.map((c) => [c.id, c]));\nreturn wanted.map((id) => {\n  const channel = byId.get(id);\n  if (!channel) throw new Error(`Channel ${id} not found in PostStack - check channelIds`);\n  return { json: {\n    channelId: channel.id,\n    platform: channel.platform,\n    caption: draft.caption,\n    hashtags: draft.hashtags,\n    imageUrl,\n    createUrl: `${baseUrl}/api/v1/posts`,\n  } };\n});"
      }
    },
    {
      "id": "create-1",
      "name": "Create Post in PostStack",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2300,
        400
      ],
      "parameters": {
        "method": "POST",
        "url": "={{ $json.createUrl }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ 'Bearer ' + $('Configuration (EDIT ME)').item.json.apiKey }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ platform: $json.platform, description: $json.caption, hashtags: $json.hashtags, mediaUrl: $json.imageUrl }) }}",
        "options": {
          "timeout": 60000
        }
      }
    },
    {
      "id": "prep-1",
      "name": "Prepare Publish",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2600,
        400
      ],
      "parameters": {
        "jsCode": "const config = $('Configuration (EDIT ME)').first().json;\nconst baseUrl = String(config.postStackUrl || '').replace(/\\/+$/, '');\n\nreturn $input.all().map((item, i) => {\n  const postId = item.json.data && item.json.data.id;\n  if (!postId) throw new Error('Post creation did not return an id');\n  const plan = $('Build Posts per Channel').all()[i].json;\n  return { json: {\n    publishUrl: `${baseUrl}/api/v1/posts/${postId}/publish`,\n    channelId: plan.channelId,\n    channelName: plan.channelName,\n  } };\n});"
      },
      "notes": "Pairs each created post with its target channel. URLs built here - n8n template validation dislikes multi-part expressions in URL fields."
    },
    {
      "id": "publish-1",
      "name": "Publish Now",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2900,
        400
      ],
      "parameters": {
        "method": "POST",
        "url": "={{ $json.publishUrl }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ 'Bearer ' + $('Configuration (EDIT ME)').item.json.apiKey }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ channelId: $json.channelId, when: 'now' }) }}",
        "options": {
          "timeout": 60000
        }
      },
      "notes": "Change when to an ISO timestamp to schedule instead of publishing immediately. Pair with the post-alerts workflow to get notified about the outcome."
    }
  ],
  "connections": {
    "Daily Trigger": {
      "main": [
        [
          {
            "node": "Configuration (EDIT ME)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Configuration (EDIT ME)": {
      "main": [
        [
          {
            "node": "Fetch Trending Topics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Trending Topics": {
      "main": [
        [
          {
            "node": "Pick a Topic",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pick a Topic": {
      "main": [
        [
          {
            "node": "Write the Concept",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Write the Concept": {
      "main": [
        [
          {
            "node": "Parse the Concept",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse the Concept": {
      "main": [
        [
          {
            "node": "Generate the Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate the Image": {
      "main": [
        [
          {
            "node": "Host the Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Channels": {
      "main": [
        [
          {
            "node": "Build Posts per Channel",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Posts per Channel": {
      "main": [
        [
          {
            "node": "Create Post in PostStack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Post in PostStack": {
      "main": [
        [
          {
            "node": "Prepare Publish",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Publish": {
      "main": [
        [
          {
            "node": "Publish Now",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Host the Image": {
      "main": [
        [
          {
            "node": "Get Channels",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": true
  }
}
Pro

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

About this workflow

PostStack: AI Image Posts from Trends. Uses rssFeedRead, openAi, httpRequest. Scheduled trigger; 17 nodes.

Source: https://github.com/jurczykpawel/poststack/blob/2db876898a7cd87f3816a2fc6611f850d8f5c541/examples/n8n/trends-ai-image-to-social.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

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

AI & RAG

Workflow runs at 08:00 (UK), AI stories from TechCrunch, The Verge, and the OpenAI Blog via RSS, AI to select and editorially shape one signal into a post and image prompt, generates and QA-checks a p

RSS Feed Read, OpenAI, Airtable +2
AI & RAG

News Automation - Main Workflow. Uses rssFeedRead, postgres, openAi, httpRequest. Scheduled trigger; 28 nodes.

RSS Feed Read, Postgres, OpenAI +4
AI & RAG

RSS Summary. Uses github, discord, openAi, httpRequest. Scheduled trigger; 26 nodes.

GitHub, Discord, OpenAI +3
AI & RAG

Daily Economic News Brief for Israel (Hebrew, RTL, GPT-4o)

Email Send, HTTP Request, OpenAI +1
AI & RAG

This workflow automatically creates a daily market intelligence brief for your stock portfolio. Instead of checking prices, news, and social media separately, it brings everything together into one cl

HTTP Request, RSS Feed Read, Google Sheets +2