This workflow corresponds to n8n.io template #17278 — 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 →
{
"meta": {
"templateCredsSetupCompleted": false
},
"name": "AI Writes the Post, Canvora Designs It (Weekly Content on Autopilot)",
"nodes": [
{
"id": "st000000-0000-4000-8000-000000000000",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1088,
-160
],
"parameters": {
"width": 560,
"height": 992,
"content": "## AI Writes the Post, Canvora Designs It (Weekly Content on Autopilot)\n\n### How it works\n\nThis workflow runs every Monday morning to generate a weekly social post concept with AI, then sends it to Canvora to create branded designs. It polls Canvora until the generation completes, handles timeout or failure cases, and posts either a review-ready Slack message or a failure alert.\n\n### Setup steps\n\n- Configure the schedule trigger with the desired timezone and weekly run time.\n- Add OpenAI credentials in the OpenAI Write Post Concept node (or swap in Anthropic/Gemini \u2014 any text model works).\n- Create a free Canvora account at [canvora.ai](https://canvora.ai) \u2014 free credits included, no card required \u2014 and get an API key from canvora.ai \u2192 Integrations \u2192 API Keys (available on all plans).\n- In both Canvora HTTP Request nodes, use one shared Header Auth credential: name `X-API-Key`, value your `vd_...` key.\n- Set the campaign values in Set Campaign Parameters: topic, comma-separated formats, optional language, optional brand kit ID.\n- Connect Slack credentials and set the target channel for review messages and failure alerts.\n\n### Customization\n\n- Formats: 100+ available (carousels, quote_card, stat_card, blog_header, \u2026) \u2014 list them with GET https://api.canvora.ai/api/formats. Cost: 10 credits per visual, 15 per carousel slide.\n- Language: set e.g. `es` to run the whole pipeline in Spanish \u2014 Canvora generates natively in 150+ languages.\n- Brand kits: pass a brandId (GET /api/brands) so every visual uses your colors, fonts, and logo.\n- Adjust the polling interval, timeout logic, and Slack wording to fit your cadence."
},
"typeVersion": 1
},
{
"id": "st010000-0000-4000-8000-000000000000",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-528,
-80
],
"parameters": {
"color": 7,
"width": 416,
"height": 320,
"content": "## Schedule campaign inputs\n\nStarts the workflow on a weekly schedule and defines the campaign parameters that will guide the content and design generation."
},
"typeVersion": 1
},
{
"id": "st020000-0000-4000-8000-000000000000",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-80,
-80
],
"parameters": {
"color": 7,
"width": 416,
"height": 320,
"content": "## Generate post concept\n\nUses OpenAI to write the post concept, then extracts and structures the generated concept for downstream use."
},
"typeVersion": 1
},
{
"id": "st030000-0000-4000-8000-000000000000",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
384,
-160
],
"parameters": {
"color": 7,
"width": 240,
"height": 400,
"content": "## Start Canvora generation\n\nSubmits the prepared concept and campaign settings to the Canvora generation API to begin creating the visual assets."
},
"typeVersion": 1
},
{
"id": "st040000-0000-4000-8000-000000000000",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
656,
-80
],
"parameters": {
"color": 7,
"width": 640,
"height": 544,
"content": "## Poll generation status\n\nWaits between status checks, fetches the latest Canvora generation state, determines whether generation is complete, and loops until completion or timeout."
},
"typeVersion": 1
},
{
"id": "st050000-0000-4000-8000-000000000000",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
1328,
-160
],
"parameters": {
"color": 7,
"width": 656,
"height": 320,
"content": "## Prepare success review\n\nChecks whether the completed generation succeeded, sorts the output assets into the intended order, and sends a Slack review message for the finished content."
},
"typeVersion": 1
},
{
"id": "st060000-0000-4000-8000-000000000000",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
1488,
208
],
"parameters": {
"color": 7,
"width": 240,
"height": 336,
"content": "## Send failure alert\n\nPosts a Slack notification when the Canvora generation fails or exceeds the timeout threshold."
},
"typeVersion": 1
},
{
"id": "n00200000-0000-4000-8000-00000000000",
"name": "When Monday at 9am",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-480,
80
],
"parameters": {
"rule": {
"interval": [
{
"field": "weeks",
"triggerAtDay": [
1
],
"triggerAtHour": 9
}
]
}
},
"typeVersion": 1.2
},
{
"id": "n00300000-0000-4000-8000-00000000000",
"name": "Set Campaign Parameters",
"type": "n8n-nodes-base.set",
"position": [
-260,
80
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "s1",
"name": "topic",
"type": "string",
"value": "practical productivity systems for solo founders"
},
{
"id": "s2",
"name": "formats",
"type": "string",
"value": "instagram_post,linkedin_post"
},
{
"id": "s3",
"name": "language",
"type": "string",
"value": ""
},
{
"id": "s4",
"name": "brandId",
"type": "string",
"value": ""
}
]
}
},
"typeVersion": 3.4
},
{
"id": "n00400000-0000-4000-8000-00000000000",
"name": "OpenAI Write Post Concept",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
-40,
80
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-5-mini",
"cachedResultName": "gpt-5-mini"
},
"options": {},
"messages": {
"values": [
{
"content": "=You are a social content strategist. Write ONE specific, non-generic content concept (2-3 sentences) for a social media post about: {{ $('Set Campaign Parameters').item.json.topic }}. Use a concrete angle (a mistake, a number, a contrarian take, a how-to). Return ONLY the concept text, no preamble."
}
]
}
},
"typeVersion": 1.8
},
{
"id": "n00500000-0000-4000-8000-00000000000",
"name": "Extract Post Concept Code",
"type": "n8n-nodes-base.code",
"position": [
180,
80
],
"parameters": {
"jsCode": "const r = $input.first().json;\nconst idea = (r.message && r.message.content) || r.output || r.text || '';\nif (!String(idea).trim()) throw new Error('The AI step returned no concept text');\nreturn [{ json: { idea: String(idea).trim() } }];"
},
"typeVersion": 2
},
{
"id": "n00600000-0000-4000-8000-00000000000",
"name": "Post Concept to Canvora API",
"type": "n8n-nodes-base.httpRequest",
"position": [
432,
80
],
"parameters": {
"url": "https://api.canvora.ai/api/generations",
"method": "POST",
"options": {},
"jsonBody": "={{ JSON.stringify(Object.assign({\n inputType: 'idea',\n inputContent: $('Extract Post Concept Code').item.json.idea,\n title: 'AI autopilot: ' + $('Extract Post Concept Code').item.json.idea.slice(0, 60),\n outputFormats: $('Set Campaign Parameters').item.json.formats.split(',').map(s => s.trim()).filter(Boolean)\n},\n$('Set Campaign Parameters').item.json.language ? { language: $('Set Campaign Parameters').item.json.language.trim() } : {},\n$('Set Campaign Parameters').item.json.brandId ? { brandId: $('Set Campaign Parameters').item.json.brandId.trim() } : {}\n)) }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"typeVersion": 4.2
},
{
"id": "n00700000-0000-4000-8000-00000000000",
"name": "Wait 30 Seconds for Canvora",
"type": "n8n-nodes-base.wait",
"position": [
704,
80
],
"parameters": {
"amount": 30
},
"typeVersion": 1.1
},
{
"id": "n00800000-0000-4000-8000-00000000000",
"name": "Fetch Canvora Generation Status",
"type": "n8n-nodes-base.httpRequest",
"position": [
928,
80
],
"parameters": {
"url": "=https://api.canvora.ai/api/generations/{{ $('Post Concept to Canvora API').item.json.generation.id }}",
"options": {},
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"typeVersion": 4.2
},
{
"id": "n00900000-0000-4000-8000-00000000000",
"name": "If Generation Completed",
"type": "n8n-nodes-base.if",
"position": [
1152,
80
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "e-done",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ ['completed', 'failed', 'partial', 'cancelled'].includes($json.generation.status) }}",
"rightValue": true
}
]
}
},
"typeVersion": 2
},
{
"id": "n01000000-0000-4000-8000-00000000000",
"name": "If Generation Timeout",
"type": "n8n-nodes-base.if",
"position": [
1152,
304
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "e-timeout",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $runIndex >= 20 }}",
"rightValue": true
}
]
}
},
"typeVersion": 2
},
{
"id": "n01100000-0000-4000-8000-00000000000",
"name": "If Generation Successful",
"type": "n8n-nodes-base.if",
"position": [
1376,
0
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "e-success",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ ['completed', 'partial'].includes($json.generation.status) }}",
"rightValue": true
}
]
}
},
"typeVersion": 2
},
{
"id": "n01200000-0000-4000-8000-00000000000",
"name": "Send Slack Failure Notification",
"type": "n8n-nodes-base.slack",
"position": [
1540,
380
],
"parameters": {
"text": "=\u26a0\ufe0f Canvora generation didn't finish for this week's AI concept (status: {{ $json.generation ? $json.generation.status : 'timeout' }}). Failed generations refund credits automatically. Check https://canvora.ai/dashboard",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "name",
"value": "#content"
},
"otherOptions": {}
},
"typeVersion": 2.2
},
{
"id": "n01300000-0000-4000-8000-00000000000",
"name": "Sort Outputs by Order",
"type": "n8n-nodes-base.code",
"position": [
1616,
0
],
"parameters": {
"jsCode": "// Sort outputs by sortOrder (carousel slides can complete out of order)\nconst g = $input.first().json.generation;\nconst outputs = (g.outputs || [])\n .slice()\n .sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0));\n\nconst lines = outputs.map((o, i) => `${i + 1}. ${o.format || 'visual'} \u2014 ${o.fileUrl}`);\n\nconst text = [\n `\ud83c\udfa8 *New visuals ready* for \"${g.title || 'your content'}\"`,\n `${outputs.length} image(s)${g.status === 'partial' ? ' (some formats failed and were refunded)' : ''}:`,\n '',\n ...lines,\n '',\n `Review or edit: https://canvora.ai/generations/${g.id}`\n].join('\\n');\n\nreturn [{ json: { text, outputs, generationId: g.id } }];"
},
"typeVersion": 2
},
{
"id": "n01400000-0000-4000-8000-00000000000",
"name": "Post Review to Slack Channel",
"type": "n8n-nodes-base.slack",
"position": [
1824,
0
],
"parameters": {
"text": "={{ $json.text }}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "name",
"value": "#content"
},
"otherOptions": {}
},
"typeVersion": 2.2
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"connections": {
"When Monday at 9am": {
"main": [
[
{
"node": "Set Campaign Parameters",
"type": "main",
"index": 0
}
]
]
},
"If Generation Timeout": {
"main": [
[
{
"node": "Send Slack Failure Notification",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait 30 Seconds for Canvora",
"type": "main",
"index": 0
}
]
]
},
"Sort Outputs by Order": {
"main": [
[
{
"node": "Post Review to Slack Channel",
"type": "main",
"index": 0
}
]
]
},
"If Generation Completed": {
"main": [
[
{
"node": "If Generation Successful",
"type": "main",
"index": 0
}
],
[
{
"node": "If Generation Timeout",
"type": "main",
"index": 0
}
]
]
},
"Set Campaign Parameters": {
"main": [
[
{
"node": "OpenAI Write Post Concept",
"type": "main",
"index": 0
}
]
]
},
"If Generation Successful": {
"main": [
[
{
"node": "Sort Outputs by Order",
"type": "main",
"index": 0
}
],
[
{
"node": "Send Slack Failure Notification",
"type": "main",
"index": 0
}
]
]
},
"Extract Post Concept Code": {
"main": [
[
{
"node": "Post Concept to Canvora API",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Write Post Concept": {
"main": [
[
{
"node": "Extract Post Concept Code",
"type": "main",
"index": 0
}
]
]
},
"Post Concept to Canvora API": {
"main": [
[
{
"node": "Wait 30 Seconds for Canvora",
"type": "main",
"index": 0
}
]
]
},
"Wait 30 Seconds for Canvora": {
"main": [
[
{
"node": "Fetch Canvora Generation Status",
"type": "main",
"index": 0
}
]
]
},
"Fetch Canvora Generation Status": {
"main": [
[
{
"node": "If Generation Completed",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow runs every Monday and uses OpenAI to generate a social post concept, then sends it to the Canvora API to create on-brand visuals in selected formats and posts the finished image links to a Slack channel for review. Runs every Monday at 9am on a scheduled trigger.…
Source: https://n8n.io/workflows/17278/ — 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.
A scheduled process aggregates content from eight distinct data sources and standardizes all inputs into a unified format. AI models perform sentiment scoring, detect conspiracy or misinformation sign
Imagine a dedicated financial expert tirelessly working behind the scenes, sifting through every transaction, every investment move, and every accounting entry. That's exactly what this automated syst
News Automation - Main Workflow. Uses rssFeedRead, postgres, openAi, httpRequest. Scheduled trigger; 28 nodes.
Automate your social media content pipeline from idea to scheduled post. This workflow reads content ideas from a Google Sheet, uses OpenAI to generate platform-optimized posts for LinkedIn, X (Twitte
This scheduled workflow pulls the latest AI-related articles from NewsAPI, uses Google Gemini to filter and draft a LinkedIn post plus image prompt, generates an image with OpenAI, uploads the image a