AutomationFlowsAI & RAG › Create Weekly Social Visuals From Openai Ideas with Canvora and Slack

Create Weekly Social Visuals From Openai Ideas with Canvora and Slack

ByJonnie Lane @marketer-360 on n8n.io

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.…

Cron / scheduled trigger★★★★☆ complexityAI-powered20 nodesOpenAIHTTP RequestSlack
AI & RAG Trigger: Cron / scheduled Nodes: 20 Complexity: ★★★★☆ AI nodes: yes Added:

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 →

Download .json
{
  "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
          }
        ]
      ]
    }
  }
}
Pro

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 →

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

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

HTTP Request, OpenAI, Postgres +2
AI & RAG

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

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

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

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

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

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

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

HTTP Request, Google Gemini, Gmail +2