This workflow corresponds to n8n.io template #8254 — 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 →
{
"id": "dCbbcwU9DY7iPdic",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Your topic in a blog",
"tags": [],
"nodes": [
{
"id": "04822337-291d-4cdf-a7dc-7066989e1a29",
"name": "If",
"type": "n8n-nodes-base.if",
"position": [
176,
560
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "1efd6c6f-5e54-4cc8-9bdb-a9e21ed735b5",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "=={{ $json.need_expand }}",
"rightValue": true
}
]
},
"looseTypeValidation": true
},
"typeVersion": 2.2
},
{
"id": "8d82dec9-6718-488d-888c-480cea5e8b0a",
"name": "When clicking \u2018Execute workflow\u2019",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-1632,
560
],
"parameters": {},
"typeVersion": 1
},
{
"id": "707a30f3-29f0-49e0-8d3f-6b2062351d11",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1440,
512
],
"parameters": {
"content": "Insert topic off choice."
},
"typeVersion": 1
},
{
"id": "49ef4968-63c7-44d0-adaa-d5df28a49255",
"name": "Style",
"type": "n8n-nodes-base.code",
"position": [
-1168,
560
],
"parameters": {
"jsCode": "const base = $input.first().json || {};\nconst style_guide = [\n \"Use first person a few times (\u201cI\u201d), friendly and practical.\",\n \"Medium\u2013low formality. No fluff or buzzwords.\",\n \"Start with a concrete hook tied to one headline.\",\n \"Explain what happened, why it matters, who it affects, and what to do next.\",\n \"Embed 4\u20138 inline links to reputable external sources using descriptive anchor text.\",\n \"Name the source in the sentence when you cite a fact (e.g., Reuters, WHO, CISA).\",\n \"Clean HTML only: <p>, <h2>, <ul>, <ol>, <li>, <strong>, <em>, <a>.\",\n \"Short paragraphs (2\u20134 sentences).\",\n \"Include sections: What happened; Why it matters; Winners/losers or risks; Actionable plan; FAQ.\"\n].join(\"\\n\");\n\nconst do_list = [\n \"Pick one headline as the lead and summarize it in 2\u20133 sentences with a link.\",\n \"Pull 1\u20132 supporting links from trusted domains.\",\n \"Add a 4\u2013step action plan tailored to operators/founders.\",\n \"End with a grounded takeaway.\"\n];\n\nconst dont_list = [\n \"Don\u2019t speculate without saying it\u2019s an opinion.\",\n \"Don\u2019t add links to random low-quality blogs.\",\n \"Don\u2019t use clickbait.\"\n];\n\nreturn [{\n json: {\n ...base,\n style_guide,\n dos: do_list,\n donts: dont_list\n }\n}];\n\n"
},
"typeVersion": 2
},
{
"id": "ed3f5aec-1b65-4904-9d75-6ee9ef07f040",
"name": "Extract Topic",
"type": "n8n-nodes-base.code",
"position": [
-1392,
560
],
"parameters": {
"jsCode": "\n// Ultra-robust topic extraction for chat/Telegram/webhook\n\nfunction firstString(arr) {\n for (const v of arr) {\n if (typeof v === 'string' && v.trim()) return v.trim();\n }\n return '';\n}\n\n// If using n8n Chat Trigger, often it's in messages[]\nconst msgs = Array.isArray($json.messages)\n ? $json.messages.map(m => m?.content).filter(Boolean).join('\\n')\n : '';\n\nconst text = firstString([\n $json.text,\n $json.message?.text,\n $json.message?.content,\n $json.prompt,\n $json.query,\n $json.input,\n $json.body?.text,\n $json.body?.message,\n $json.payload?.text,\n $json.content,\n msgs,\n (typeof $json === 'string' ? $json : '')\n]);\n\nlet topic = String(text || '').trim();\n\n// Allow `/topic \u2026` or `topic: \u2026` style commands\ntopic = topic.replace(/^\\/topic\\s+/i, '').replace(/^topic:\\s*/i, '').trim();\n\nif (!topic) {\n return [{\n json: {\n topic: 'Scientists Develop Plant-Based Plastic That Is Self-Repairing and Antimicrobial - Technology Networks',\n help: 'No topic detected. Send a message with your topic, or use \"/topic <subject>\".'\n }\n }];\n}\n\nreturn [{ json: { topic } }];\n"
},
"typeVersion": 2
},
{
"id": "9a3b8a63-2ee2-4786-9884-7d907f2130a2",
"name": "Build Chat Research Body",
"type": "n8n-nodes-base.code",
"position": [
-944,
560
],
"parameters": {
"jsCode": "\n// Build Chat Research Body with resilient topic sourcing\nfunction pickTopic() {\n // Primary: from Extract Topic\n if ($node[\"Extract Topic\"]?.json?.topic) return String($node[\"Extract Topic\"].json.topic);\n\n // Fallbacks: read raw trigger payloads\n const trig = $node[\"When chat message received\"]?.json || {};\n const msgs = Array.isArray(trig.messages) ? trig.messages.map(m => m?.content).filter(Boolean).join('\\n') : '';\n\n const candidates = [\n $json.topic,\n trig.text,\n trig.message?.text,\n trig.message?.content,\n trig.prompt,\n trig.query,\n trig.input,\n trig.body?.text,\n trig.body?.message,\n trig.payload?.text,\n trig.content,\n msgs\n ].filter(v => typeof v === 'string' && v.trim());\n\n if (candidates.length) {\n const t = candidates[0].replace(/^\\/topic\\s+/i, '').replace(/^topic:\\s*/i, '').trim();\n if (t) return t;\n }\n return '';\n}\n\nconst topic = pickTopic();\nconst style = $node[\"Style\"]?.json?.style_guide || \"Human, practical, medium\u2013low formality.\";\nconst dos = ($node[\"Style\"]?.json?.dos || []).map(s => \"\u2022 \" + s).join(\"\\n\");\nconst donts = ($node[\"Style\"]?.json?.donts || []).map(s => \"\u2022 \" + s).join(\"\\n\");\n\nif (!topic) {\n throw new Error(\"No topic provided. Send a message to the bot with the topic you want researched (e.g., '/topic WA renewable energy in mining').\");\n}\n\nconst body = {\n model: \"gpt-4o-mini\",\n response_format: { type: \"json_object\" },\n temperature: 0.6,\n messages: [\n {\n role: \"system\",\n content:\n \"You are a research writer creating long-form, well-cited blog posts for a general audience. \" +\n \"Always include descriptive, trustworthy external links (with full URLs) to support claims. \" +\n \"Write between 1700\u20131800 words. Use only clean HTML tags: <p>, <h2>, <ul>, <ol>, <li>, <strong>, <em>, <a>. \" +\n \"Sections to include: What happened/Context; Why it matters; Opportunities/risks; Action plan; FAQ.\"\n },\n {\n role: \"user\",\n content:\n`TOPIC:\n${topic}\n\nSTYLE GUIDE:\n${style}\n\nDO:\n${dos}\n\nDON'T:\n${donts}\n\nReturn ONLY one JSON object exactly in this shape:\n{\n \"title\": \"<compelling title for the post>\",\n \"content\": \"<HTML content using only allowed tags>\"\n}`\n }\n ]\n};\n\nreturn [{ json: body }];\n"
},
"typeVersion": 2
},
{
"id": "24a2a816-17ac-4fcd-924b-3db16d45bc59",
"name": "Research Topic \u2013 GPT",
"type": "n8n-nodes-base.httpRequest",
"position": [
-720,
560
],
"parameters": {
"url": "https://api.openai.com/v1/chat/completions",
"method": "POST",
"options": {},
"jsonBody": "={{$json}}",
"sendBody": true,
"specifyBody": "json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "openAiApi"
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "592cbdfa-7669-4194-8621-f8dd302f80e7",
"name": "Build Tone Request",
"type": "n8n-nodes-base.code",
"position": [
-496,
560
],
"parameters": {
"jsCode": "\nconst style = $node[\"Style\"]?.json?.style_guide || \"Human, practical, medium\u2013low formality.\";\nconst dos = ($node[\"Style\"]?.json?.dos || []).join(\"\\n\");\nconst donts = ($node[\"Style\"]?.json?.donts || []).join(\"\\n\");\n\nconst article = $json || $node[\"Build Chat Research Body\"]?.json_output || {};\n\nreturn [{\n json: {\n model: \"gpt-4o-mini\",\n temperature: 0.4,\n response_format: { type: \"json_object\" },\n messages: [\n {\n role: \"system\",\n content:\n \"You are an editor. Improve clarity/flow only. Do NOT change topic, claims, links, or overall structure. \" +\n \"Preserve HTML tags (<p>, <h2>, <ul>, <ol>, <li>, <strong>, <em>, <a>). \" +\n \"Return a JSON object with keys: title, content.\"\n },\n {\n role: \"user\",\n content:\n \"STYLE GUIDE:\\n\" + style +\n \"\\n\\nDO:\\n\" + dos +\n \"\\n\\nDON'T:\\n\" + donts +\n \"\\n\\nARTICLE JSON TO POLISH (keep same keys & structure):\\n\" + JSON.stringify(article)\n }\n ]\n }\n}];\n"
},
"typeVersion": 2
},
{
"id": "1e0cf457-c663-49ee-ad90-2086c15b92ed",
"name": "Tone & Format Revision \u2013 GPT",
"type": "n8n-nodes-base.httpRequest",
"position": [
-272,
560
],
"parameters": {
"url": "https://api.openai.com/v1/chat/completions",
"method": "POST",
"options": {},
"jsonBody": "={{ $json }}",
"sendBody": true,
"specifyBody": "=json",
"authentication": "predefinedCredentialType",
"bodyParameters": {
"parameters": [
{}
]
},
"nodeCredentialType": "openAiApi"
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"retryOnFail": true,
"typeVersion": 4.2,
"alwaysOutputData": true
},
{
"id": "bdc9694f-bb30-4390-b776-fa248ce362db",
"name": "Word Count Guard",
"type": "n8n-nodes-base.code",
"position": [
-48,
560
],
"parameters": {
"jsCode": "// Word Count Guard (after Tone & Format)\nfunction strip(html) { return (html || '').replace(/<[^>]+>/g, ' '); }\nconst text = strip($json.content || '');\nconst words = text.trim().split(/\\s+/).filter(Boolean).length;\n\nconst MIN = 1600;\nif (words >= MIN) {\n return [{ json: { ...$json, word_count: words } }];\n}\n\n// too short \u2192 ask GPT to expand\nreturn [{\n json: {\n draft: $json,\n word_count: words,\n need_expand: true\n }\n}];\n"
},
"typeVersion": 2
},
{
"id": "88a3bc72-6e94-4ca9-a32d-85fc874d992d",
"name": "Expand Draft",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
400,
480
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1",
"cachedResultName": "GPT-4.1"
},
"options": {},
"messages": {
"values": [
{
"role": "system",
"content": "=Expand this article to exceed 1,800 words by deepening \u201cWhy it matters\u201d,\n\u201cWinners/risks\u201d, and a more detailed \u201cAction plan\u201d. Keep the same voice\nand existing links intact.\n\n{{ JSON.stringify($json.draft || $json) }}\n\n"
}
]
},
"simplify": false,
"jsonOutput": true
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.8
},
{
"id": "e7c71c62-9cbc-4f80-bb68-b7d26ee6d933",
"name": "Get Title, Content, and Image FileName",
"type": "n8n-nodes-base.code",
"position": [
800,
560
],
"parameters": {
"jsCode": "// ---- helpers ----\nfunction slugify(s) {\n return String(s || '')\n .toLowerCase()\n .normalize('NFD').replace(/[\\u0300-\\u036f]/g, '')\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 60);\n}\n\nfunction extractSources(html) {\n if (!html) return '';\n if (/<h2[^>]*>Sources<\\/h2>/i.test(html)) return html;\n\n const urls = Array.from(new Set(\n (html.match(/https?:\\/\\/[^\\s\"')<>]+/g) || []).map(u => u.replace(/[.,)\\]]+$/,''))\n ));\n\n if (!urls.length) return html;\n\n const items = urls.map(u => {\n try {\n const host = new URL(u).hostname.replace(/^www\\./,'');\n return `<li><a href=\"${u}\">${host}</a></li>`;\n } catch {\n return `<li><a href=\"${u}\">${u}</a></li>`;\n }\n }).join('');\n\n return html + `\\n\\n<h2>Sources</h2><ul>${items}</ul>`;\n}\n\n// Detect object vs string safely and only JSON.parse when it's a real string\nfunction coerceObject(j) {\n // 1) Common places where the node already gives an OBJECT\n if (j?.message?.parsed && typeof j.message.parsed === 'object') return j.message.parsed;\n if (j?.choices?.[0]?.message?.parsed && typeof j.choices[0].message.parsed === 'object') {\n return j.choices[0].message.parsed;\n }\n if (j?.message?.content && typeof j.message.content === 'object') return j.message.content;\n\n // 2) Candidate content (could be string or object)\n let cand =\n j?.choices?.[0]?.message?.content ??\n j?.message?.content ??\n j?.content ??\n '';\n\n // If it's already an OBJECT, just use it\n if (cand && typeof cand === 'object' && !Array.isArray(cand)) return cand;\n\n // 3) If it's a STRING, clean & parse\n if (typeof cand === 'string') {\n let s = cand.trim();\n\n // strip ```json fences if present\n s = s.replace(/^```(?:json)?\\s*/i, '').replace(/```$/i, '');\n\n // isolate outermost JSON object (in case there's prose around it)\n const start = s.indexOf('{');\n const end = s.lastIndexOf('}');\n if (start !== -1 && end !== -1) s = s.slice(start, end + 1);\n\n try {\n return JSON.parse(s);\n } catch (e) {\n throw new Error(\n 'Could not parse model JSON. ' +\n 'Type=' + typeof cand + ' Preview:\\n' + s.slice(0, 400)\n );\n }\n }\n\n // 4) Nothing usable\n throw new Error('No usable payload: got ' + typeof cand);\n}\n\n// ---- MAIN ----\nconst obj = coerceObject($json); // <- ONLY parses when needed\n\nconst title = String(obj.title || 'Untitled').trim();\nlet content = String(obj.content || '');\ncontent = extractSources(content);\n\nconst image_filename = `${slugify(title)}.jpg`;\n\nreturn [{ json: { title, content, image_filename } }];\n"
},
"typeVersion": 2
},
{
"id": "bc7ce808-9364-490c-ba57-7eec034a84d3",
"name": "Message a model",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
1024,
560
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini",
"cachedResultName": "GPT-4.1-MINI"
},
"options": {},
"messages": {
"values": [
{
"content": "=Generate a single-line English description of an editorial-style image to visually represent the following article.\n\nThe image must follow these rules:\n- Cinematic, editorial look (not cartoonish or abstract).\n- No visible text or logos.\n- Must work for a blog post featured image in Google News and Discover (ideal size: 1200x628 px).\n- The article title is: \"{{ $json.title }}\"\n- Article content: {{ $json.content }}.\n\nOutput just the English prompt, without quotes or formatting."
},
{
"role": "system",
"content": "You are an expert in crafting AI image generation prompts for editorial and news images. You help generate cinematic, editorial-style prompts for blog images that perform well in Google News and Google Discover. Avoid any text or logos in the result."
}
]
}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.8
},
{
"id": "b740df0f-147d-4558-ba9a-ba8ea8d34b5c",
"name": "Leonardo: Create Post Image",
"type": "n8n-nodes-base.httpRequest",
"position": [
1424,
560
],
"parameters": {
"url": "https://cloud.leonardo.ai/api/rest/v1/generations",
"method": "POST",
"options": {},
"jsonBody": "={\n \"prompt\": \"{{ $json.message.content }}\",\n \"modelId\": \"6bef9f1b-29cb-40c7-b9df-32b51c1f67d3\",\n \"width\": 1280,\n \"height\": 720,\n \"sd_version\": \"v2\",\n \"num_images\": 1,\n \"promptMagic\": true,\n \"promptMagicStrength\": 0.5,\n \"public\": false,\n \"scheduler\": \"LEONARDO\",\n \"guidance_scale\": 7\n}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpBearerAuth",
"headerParameters": {
"parameters": [
{
"name": "accept",
"value": "application/json"
}
]
}
},
"credentials": {
"httpBearerAuth": {
"name": "<your credential>"
}
},
"retryOnFail": true,
"typeVersion": 4.2
},
{
"id": "49474905-0c91-452f-9f22-d665a64cf99d",
"name": "Code",
"type": "n8n-nodes-base.code",
"position": [
1648,
560
],
"parameters": {
"jsCode": "// Node: Keep Generation Id\nconst id =\n $json.sdGenerationJob?.generationId ||\n $json.generationId ||\n $json.generations_by_pk?.id ||\n $json.id;\n\nif (!id) throw new Error('No generationId found in previous node output');\n\nreturn [{ json: { generationId: id } }];\n"
},
"typeVersion": 2
},
{
"id": "16c44420-52eb-4024-841b-504a7e3bfbd1",
"name": "Wait",
"type": "n8n-nodes-base.wait",
"position": [
1872,
560
],
"parameters": {
"amount": 30
},
"typeVersion": 1.1
},
{
"id": "f325dc6f-2318-4627-8d79-157668d032aa",
"name": "Get Leonardo Image Status",
"type": "n8n-nodes-base.httpRequest",
"position": [
2096,
560
],
"parameters": {
"url": "=https://cloud.leonardo.ai/api/rest/v1/generations/{{ $json.generationId }}\n",
"options": {},
"authentication": "genericCredentialType",
"genericAuthType": "httpBearerAuth"
},
"credentials": {
"httpBearerAuth": {
"name": "<your credential>"
}
},
"retryOnFail": true,
"typeVersion": 4.2
},
{
"id": "680da0a3-1068-407a-8d37-5685b635ffb1",
"name": "Get Leonardo Image",
"type": "n8n-nodes-base.httpRequest",
"position": [
2320,
560
],
"parameters": {
"url": "={{ $json.generations_by_pk.generated_images[0].url }}",
"options": {}
},
"retryOnFail": true,
"typeVersion": 4.2
},
{
"id": "bd5d579c-427a-4123-a55f-f8645e0729d0",
"name": "Upload Image to Wordpress",
"type": "n8n-nodes-base.httpRequest",
"position": [
2544,
560
],
"parameters": {
"url": "https://public-api.wordpress.com/wp/v2/sites/YOUR_SITE/media",
"method": "POST",
"options": {},
"sendBody": true,
"contentType": "binaryData",
"sendHeaders": true,
"authentication": "genericCredentialType",
"genericAuthType": "oAuth2Api",
"headerParameters": {
"parameters": [
{
"name": "Content-Disposition",
"value": "=attachment; filename=\"{{ $('Get Title, Content, and Image FileName').item.json.image_filename }}\""
},
{
"name": "Content-Type",
"value": "image/jpeg"
}
]
},
"inputDataFieldName": "data"
},
"credentials": {
"oAuth2Api": {
"name": "<your credential>"
},
"httpBasicAuth": {
"name": "<your credential>"
}
},
"retryOnFail": true,
"typeVersion": 4.2
},
{
"id": "157ebde6-f217-4780-b824-567a99357162",
"name": "Add ALT to Image",
"type": "n8n-nodes-base.httpRequest",
"position": [
2768,
560
],
"parameters": {
"url": "=https://public-api.wordpress.com/wp/v2/sites/deansfieldnotes.wordpress.com/media/{{ $json.id }}",
"method": "PUT",
"options": {},
"sendBody": true,
"authentication": "genericCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "alt_text",
"value": "={{ $('Message a model').item.json.message.content }}"
}
]
},
"genericAuthType": "oAuth2Api"
},
"credentials": {
"oAuth2Api": {
"name": "<your credential>"
},
"httpBasicAuth": {
"name": "<your credential>"
}
},
"retryOnFail": true,
"typeVersion": 4.2
},
{
"id": "16df3ae3-8030-4bc1-95e9-98a7097a1099",
"name": "Create WordPress Post",
"type": "n8n-nodes-base.httpRequest",
"position": [
2992,
560
],
"parameters": {
"url": "https://public-api.wordpress.com/wp/v2/sites/YOUR_SITE/posts",
"method": "POST",
"options": {},
"jsonBody": "={{ { \"title\": $(\"Get Title, Content, and Image FileName\").item.json.title, \"content\": $(\"Get Title, Content, and Image FileName\").item.json.content, \"status\": \"publish\", \"categories\": [916], \"featured_media\": $(\"Upload Image to Wordpress\").item.json.id } }}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "oAuth2Api",
"headerParameters": {
"parameters": [
{}
]
}
},
"credentials": {
"oAuth2Api": {
"name": "<your credential>"
},
"httpBasicAuth": {
"name": "<your credential>"
}
},
"retryOnFail": true,
"typeVersion": 4.2
},
{
"id": "4f03305d-122c-4743-aa8e-c7dd415b8035",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1184,
512
],
"parameters": {
"color": 3,
"width": 368,
"height": 176,
"content": "Builds the style of Blog and research body."
},
"typeVersion": 1
},
{
"id": "d30ee82f-6142-4958-a968-e6281c155ea0",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-784,
512
],
"parameters": {
"color": 4,
"width": 848,
"height": 192,
"content": "Reasearch's and revises the tone and format"
},
"typeVersion": 1
},
{
"id": "49295331-7963-4b0e-8dc1-f4d6dfde4439",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
144,
464
],
"parameters": {
"color": 3,
"width": 784,
"height": 224,
"content": "Ensures post is above 1600 words."
},
"typeVersion": 1
},
{
"id": "ccb499fa-a885-428d-88a9-330af66d30f6",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
976,
496
],
"parameters": {
"width": 1040,
"height": 208,
"content": "Creates prompt for image and creates image."
},
"typeVersion": 1
},
{
"id": "a880f3d1-3f8f-4963-b816-d306f538cc56",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
2032,
496
],
"parameters": {
"color": 6,
"width": 1104,
"height": 208,
"content": "Posts to your worspress."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "4bde5fb9-5e55-4c56-95b5-a552fd180cef",
"connections": {
"If": {
"main": [
[
{
"node": "Expand Draft",
"type": "main",
"index": 0
}
],
[
{
"node": "Get Title, Content, and Image FileName",
"type": "main",
"index": 0
}
]
]
},
"Code": {
"main": [
[
{
"node": "Wait",
"type": "main",
"index": 0
}
]
]
},
"Wait": {
"main": [
[
{
"node": "Get Leonardo Image Status",
"type": "main",
"index": 0
}
]
]
},
"Style": {
"main": [
[
{
"node": "Build Chat Research Body",
"type": "main",
"index": 0
}
]
]
},
"Expand Draft": {
"main": [
[
{
"node": "Get Title, Content, and Image FileName",
"type": "main",
"index": 0
}
]
]
},
"Extract Topic": {
"main": [
[
{
"node": "Style",
"type": "main",
"index": 0
}
]
]
},
"Message a model": {
"main": [
[
{
"node": "Leonardo: Create Post Image",
"type": "main",
"index": 0
}
]
]
},
"Add ALT to Image": {
"main": [
[
{
"node": "Create WordPress Post",
"type": "main",
"index": 0
}
]
]
},
"Word Count Guard": {
"main": [
[
{
"node": "If",
"type": "main",
"index": 0
}
]
]
},
"Build Tone Request": {
"main": [
[
{
"node": "Tone & Format Revision \u2013 GPT",
"type": "main",
"index": 0
}
]
]
},
"Get Leonardo Image": {
"main": [
[
{
"node": "Upload Image to Wordpress",
"type": "main",
"index": 0
}
]
]
},
"Create WordPress Post": {
"main": [
[]
]
},
"Research Topic \u2013 GPT": {
"main": [
[
{
"node": "Build Tone Request",
"type": "main",
"index": 0
}
]
]
},
"Build Chat Research Body": {
"main": [
[
{
"node": "Research Topic \u2013 GPT",
"type": "main",
"index": 0
}
]
]
},
"Get Leonardo Image Status": {
"main": [
[
{
"node": "Get Leonardo Image",
"type": "main",
"index": 0
}
]
]
},
"Upload Image to Wordpress": {
"main": [
[
{
"node": "Add ALT to Image",
"type": "main",
"index": 0
}
]
]
},
"Leonardo: Create Post Image": {
"main": [
[
{
"node": "Code",
"type": "main",
"index": 0
}
]
]
},
"Tone & Format Revision \u2013 GPT": {
"main": [
[
{
"node": "Word Count Guard",
"type": "main",
"index": 0
}
]
]
},
"When clicking \u2018Execute workflow\u2019": {
"main": [
[
{
"node": "Extract Topic",
"type": "main",
"index": 0
}
]
]
},
"Get Title, Content, and Image FileName": {
"main": [
[
{
"node": "Message a model",
"type": "main",
"index": 0
}
]
]
}
}
}
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.
httpBasicAuthhttpBearerAuthoAuth2ApiopenAiApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
The workflow starts manually or from a chat/Telegram/webhook input.
Source: https://n8n.io/workflows/8254/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
100% autonomous workflow that transforms YouTube videos into unique, high-quality, SEO-optimized blog articles and automatically publishes them to WordPress. No human intervention required: it uses Yo
The best content automation in the market! This advanced workflow not only creates and publishes SEO-optimized blog posts to your WordPress website but also backs up all content and images to a design
WP. Uses openAi, outputParserStructured, chainLlm, httpRequest. Event-driven trigger; 63 nodes.
This workflow automates the end-to-end process of creating, optimizing, and publishing content on WordPress.
This template is ideal for creators, bloggers, and automation enthusiasts who want to auto-generate blog posts from AI-generated content — without lifting a finger. Whether you're running a tech blog,