AutomationFlowsAI & RAG › Generate Branded Social Captions and Images with Openai and Postfast

Generate Branded Social Captions and Images with Openai and Postfast

ByPetar Georgiev @peturgeorgievv on n8n.io

This workflow runs three times a week to generate a branded social caption and AI image with OpenAI, then uploads the image via PostFast pre-signed URLs and schedules the post across selected connected social accounts. Runs on a schedule every Monday, Wednesday, and Friday at…

Cron / scheduled trigger★★★★☆ complexityAI-powered16 nodesN8N Nodes PostfastOpenAIHTTP Request
AI & RAG Trigger: Cron / scheduled Nodes: 16 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #16961 — we link there as the canonical source.

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": "Generate branded AI images with OpenAI and post them to social media via PostFast",
  "nodes": [
    {
      "id": "t2-sticky-main",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        16,
        16
      ],
      "parameters": {
        "width": 544,
        "height": 768,
        "content": "## Generate branded AI images with OpenAI and post them to social media via PostFast\n\n### How it works\n\nRuns Monday, Wednesday and Friday mornings. Loads your brand settings, retrieves connected PostFast accounts, filters the target platforms, asks OpenAI for a caption plus an image prompt, renders the image with gpt-image-1, then uploads one copy per selected account through pre-signed S3 URLs and schedules each account's post with its own media key.\n\n### Setup steps\n\n- Install the verified PostFast community node (n8n Cloud: enable Verified Community Nodes in the Admin Panel; self-hosted: install n8n-nodes-postfast).\n- Connect your PostFast credentials (API key from Settings > API) on the three PostFast nodes.\n- Connect OpenAI credentials on both OpenAI nodes.\n- Update Set Brand and Style: brand name, image style, topics, target platforms.\n- Run once manually and review the scheduled posts in your PostFast dashboard before activating.\n\n### Customization\n\nAdjust the image style, topics, posting days, image size or platform filter. Up to 8 accounts per run (the upload endpoint issues at most 8 image URLs per request)."
      },
      "typeVersion": 1
    },
    {
      "id": "t2-sticky-1",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        592,
        32
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 400,
        "content": "## Start and configure\n\nTriggers three times a week and sets the brand, style, topics and platform filter for the run."
      },
      "typeVersion": 1
    },
    {
      "id": "t2-sticky-2",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1104,
        32
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 400,
        "content": "## Fetch target accounts\n\nGets connected PostFast accounts and keeps the platforms you selected."
      },
      "typeVersion": 1
    },
    {
      "id": "t2-sticky-3",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1616,
        32
      ],
      "parameters": {
        "color": 7,
        "width": 880,
        "height": 400,
        "content": "## Create the visual\n\nOpenAI writes the caption and image prompt, PostFast issues one pre-signed upload URL per account, then gpt-image-1 renders the image."
      },
      "typeVersion": 1
    },
    {
      "id": "t2-sticky-4",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2560,
        32
      ],
      "parameters": {
        "color": 7,
        "width": 912,
        "height": 400,
        "content": "## Upload and schedule\n\nThe image is uploaded once per account (each PUT sends a matching Content-Type header), then every account gets its own scheduled post with its own media key, one hour ahead."
      },
      "typeVersion": 1
    },
    {
      "id": "t2-trigger",
      "name": "When Mon Wed Fri at 7am",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        656,
        240
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1,
                3,
                5
              ],
              "triggerAtHour": 7
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "t2-config",
      "name": "Set Brand and Style",
      "type": "n8n-nodes-base.set",
      "position": [
        864,
        240
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "b1",
              "name": "brandName",
              "type": "string",
              "value": "YOUR BRAND NAME"
            },
            {
              "id": "b2",
              "name": "imageStyle",
              "type": "string",
              "value": "clean flat vector illustration, soft pastel palette, blue accents, white background, no text"
            },
            {
              "id": "b3",
              "name": "topics",
              "type": "string",
              "value": "topic one, topic two, topic three"
            },
            {
              "id": "b4",
              "name": "platformFilter",
              "type": "string",
              "value": "INSTAGRAM, FACEBOOK, LINKEDIN, X"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "t2-accounts",
      "name": "Fetch PostFast Accounts",
      "type": "n8n-nodes-postfast.postFast",
      "position": [
        1168,
        240
      ],
      "parameters": {
        "resource": "socialAccount"
      },
      "typeVersion": 1
    },
    {
      "id": "t2-filter",
      "name": "Filter Target Accounts",
      "type": "n8n-nodes-base.code",
      "position": [
        1392,
        240
      ],
      "parameters": {
        "jsCode": "// Keep only connected accounts on the platforms chosen in the configuration.\nconst cfg = $('Set Brand and Style').first().json;\nconst wanted = String(cfg.platformFilter || '').split(',').map(s => s.trim().toUpperCase()).filter(Boolean);\nconst accounts = $input.all().map(i => i.json);\n\nconst selected = accounts.filter(a => {\n  const status = String(a.connectionStatus || 'CONNECTED').toUpperCase();\n  const platform = String(a.platform || '').toUpperCase();\n  return status === 'CONNECTED' && (wanted.length === 0 || wanted.includes(platform));\n});\n\nif (selected.length === 0) {\n  throw new Error('No connected accounts matched the platform filter. Check the Set Brand and Style node and your PostFast connections.');\n}\nif (selected.length > 8) {\n  throw new Error('More than 8 matching accounts. The upload endpoint issues at most 8 image URLs per request, so narrow the platform filter.');\n}\n\nreturn [{ json: {\n  accountIds: selected.map(a => a.id ?? a.socialMediaId)\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "t2-caption",
      "name": "Write Caption and Image Prompt",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        1680,
        240
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "gpt-4o-mini"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "content": "=You are the social media voice of {{ $('Set Brand and Style').first().json.brandName }}. Pick ONE topic from: {{ $('Set Brand and Style').first().json.topics }}.\n\nReturn ONLY valid JSON: {\"caption\": \"an engaging 40-120 word social post about the topic with 2-3 hashtags\", \"imagePrompt\": \"a detailed prompt for an image that matches the caption, in this style: {{ $('Set Brand and Style').first().json.imageStyle }}\"}"
            }
          ]
        },
        "jsonOutput": true
      },
      "typeVersion": 1.8
    },
    {
      "id": "t2-uploadurl",
      "name": "Get Upload URL",
      "type": "n8n-nodes-postfast.postFast",
      "position": [
        2000,
        240
      ],
      "parameters": {
        "count": "={{ $('Filter Target Accounts').first().json.accountIds.length }}",
        "resource": "file",
        "contentType": "image/png"
      },
      "typeVersion": 1
    },
    {
      "id": "t2-image",
      "name": "Generate Post Image",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        2176,
        240
      ],
      "parameters": {
        "prompt": "={{ $('Write Caption and Image Prompt').first().json.message.content.imagePrompt }}",
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-image-1",
          "cachedResultName": "gpt-image-1"
        },
        "options": {
          "size": "1024x1024"
        },
        "resource": "image"
      },
      "typeVersion": 2.3
    },
    {
      "id": "t2-prepare",
      "name": "Prepare Uploads",
      "type": "n8n-nodes-base.code",
      "position": [
        2624,
        240
      ],
      "parameters": {
        "jsCode": "// One upload per account: pair each pre-signed URL with an account and attach the image binary.\nconst raw = $('Get Upload URL').all().map(i => i.json);\nlet urls = raw;\nif (raw.length === 1 && Array.isArray(raw[0])) urls = raw[0];\nelse if (raw.length === 1 && Array.isArray(raw[0].data)) urls = raw[0].data;\nelse if (raw.length === 1 && Array.isArray(raw[0].urls)) urls = raw[0].urls;\n\nconst accountIds = $('Filter Target Accounts').first().json.accountIds;\nif (urls.length < accountIds.length) {\n  throw new Error(`Got ${urls.length} signed URLs for ${accountIds.length} accounts. Check the Get Upload URL node's count.`);\n}\n\nconst binary = $('Generate Post Image').first().binary;\nreturn accountIds.map((accountId, i) => ({\n  json: { accountId, signedUrl: urls[i].signedUrl, key: urls[i].key },\n  binary\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "t2-put",
      "name": "Upload Image to S3",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2848,
        240
      ],
      "parameters": {
        "url": "={{ $json.signedUrl }}",
        "method": "PUT",
        "options": {},
        "sendBody": true,
        "contentType": "binaryData",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "image/png"
            }
          ]
        },
        "inputDataFieldName": "data"
      },
      "typeVersion": 4.2
    },
    {
      "id": "t2-build",
      "name": "Build Posts per Account",
      "type": "n8n-nodes-base.code",
      "position": [
        3072,
        240
      ],
      "parameters": {
        "jsCode": "// One scheduled post per account, each with its own uploaded media key.\nconst uploads = $('Prepare Uploads').all().map(i => i.json);\nconst caption = $('Write Caption and Image Prompt').first().json.message.content.caption;\nconst when = new Date(Date.now() + 60 * 60 * 1000).toISOString();\n\nreturn uploads.map(u => ({ json: {\n  socialMediaId: u.accountId,\n  content: caption,\n  scheduledAt: when,\n  mediaItemsJson: JSON.stringify([{ key: u.key, type: 'IMAGE', sortOrder: 0 }])\n} }));"
      },
      "typeVersion": 2
    },
    {
      "id": "t2-create",
      "name": "PostFast Scheduler",
      "type": "n8n-nodes-postfast.postFast",
      "position": [
        3296,
        240
      ],
      "parameters": {
        "posts": {
          "post": [
            {
              "content": "={{ $json.content }}",
              "scheduledAt": "={{ $json.scheduledAt }}",
              "socialMediaId": "={{ $json.socialMediaId }}",
              "mediaInputMode": "json",
              "mediaItemsJson": "={{ $json.mediaItemsJson }}"
            }
          ]
        },
        "controls": {}
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "Get Upload URL": {
      "main": [
        [
          {
            "node": "Generate Post Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Uploads": {
      "main": [
        [
          {
            "node": "Upload Image to S3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Image to S3": {
      "main": [
        [
          {
            "node": "Build Posts per Account",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Post Image": {
      "main": [
        [
          {
            "node": "Prepare Uploads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Brand and Style": {
      "main": [
        [
          {
            "node": "Fetch PostFast Accounts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Target Accounts": {
      "main": [
        [
          {
            "node": "Write Caption and Image Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Posts per Account": {
      "main": [
        [
          {
            "node": "PostFast Scheduler",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch PostFast Accounts": {
      "main": [
        [
          {
            "node": "Filter Target Accounts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Mon Wed Fri at 7am": {
      "main": [
        [
          {
            "node": "Set Brand and Style",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Write Caption and Image Prompt": {
      "main": [
        [
          {
            "node": "Get Upload URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

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

About this workflow

This workflow runs three times a week to generate a branded social caption and AI image with OpenAI, then uploads the image via PostFast pre-signed URLs and schedules the post across selected connected social accounts. Runs on a schedule every Monday, Wednesday, and Friday at…

Source: https://n8n.io/workflows/16961/ — 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

AI Institutional Stock Valuation Engine with Risk Scoring & Scenario Targets

Google Sheets, XML, HTTP Request +3
AI & RAG

Overview This is a production-grade, fully automated stock analysis system built entirely in n8n. It combines institutional-level financial analysis, dual AI model consensus, and a self-improving back

Google Sheets, XML, HTTP Request +3
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

This automation is a complete end-to-end system designed to find, qualify, and contact B2B leads — fully automated and powered by AI. Searches for target companies on LinkedIn via Ghost Genius API, us

Google Sheets, HTTP Request, OpenAI +1
AI & RAG

This comprehensive n8n automation template orchestrates a complete end-to-end workflow for generating engaging short-form Point-of-View (POV) style videos using multiple AI services and automatically

HTTP Request, OpenAI, Google Drive +4