AutomationFlowsSocial Media › LinkedIn Post Creator with AI Captions

LinkedIn Post Creator with AI Captions

Original n8n title: Design and Post Linkedin Content with AI Captions and Custom Templates

ByGilbert Onyebuchi @gilbert-onyebuchi on n8n.io

This workflow leverages n8n to automate LinkedIn content creation from start to finish. Upload an image and quote through a web form, and get a professionally designed post with AI-generated captions, ready to publish in seconds. Randomly selects from 6 professional design…

Webhook trigger★★★★☆ complexity17 nodesHTTP RequestLinkedInEdit Image
Social Media Trigger: Webhook Nodes: 17 Complexity: ★★★★☆ Added:

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

This workflow follows the Editimage → HTTP Request 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": true
  },
  "nodes": [
    {
      "id": "79bad9c0-492e-4817-8c2b-fad534c6a0ea",
      "name": "Webhook - Receive Form",
      "type": "n8n-nodes-base.webhook",
      "position": [
        2352,
        848
      ],
      "parameters": {
        "path": "67adcc76-29c9-412a-b3ce-f34d2944a09f",
        "options": {
          "rawBody": true
        },
        "httpMethod": "POST",
        "responseMode": "lastNode"
      },
      "typeVersion": 1.1
    },
    {
      "id": "aa0b4dc2-2100-44d8-adad-9595a99037d0",
      "name": "Create HTML Design",
      "type": "n8n-nodes-base.code",
      "position": [
        2736,
        848
      ],
      "parameters": {
        "jsCode": "const quote = $json.quote;\nconst imageBase64 = $json.imageBase64;\nconst mimeType = $json.mimeType;\n\n// Better randomization using timestamp and execution ID\n// This ensures different designs even in quick succession\nconst timestamp = Date.now();\nconst executionId = $execution.id || 0;\nconst randomSeed = (timestamp + executionId) % 6;\nconst designIndex = Math.floor((Math.random() * 1000 + randomSeed) % 6);\n\nconsole.log(`Selected design: ${designIndex + 1} (seed: ${randomSeed}, time: ${timestamp})`);\n\nlet html = '';\n\n// Design 1 - Purple Gradient with Circles\nif (designIndex === 0) {\n  html = `\n<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"UTF-8\">\n  <style>\n    * {\n      margin: 0;\n      padding: 0;\n      box-sizing: border-box;\n    }\n    \n    html, body {\n      width: 2460px;\n      height: 2560px;\n      overflow: hidden;\n      margin: 0;\n      padding: 0;\n    }\n    \n    body {\n      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n      font-family: 'Arial', sans-serif;\n      position: relative;\n    }\n    \n    .deco-circle-1 {\n      position: absolute;\n      width: 700px;\n      height: 700px;\n      border: 6px solid rgba(255, 255, 255, 0.2);\n      border-radius: 50%;\n      top: -350px;\n      right: -350px;\n    }\n    \n    .deco-circle-2 {\n      position: absolute;\n      width: 900px;\n      height: 900px;\n      border: 6px solid rgba(255, 255, 255, 0.2);\n      border-radius: 50%;\n      bottom: -450px;\n      left: -450px;\n    }\n    \n    .image-circle {\n      width: 1600px;\n      height: 1600px;\n      border-radius: 50%;\n      overflow: hidden;\n      border: 18px solid white;\n      box-shadow: 0 20px 80px rgba(0,0,0,0.3);\n      position: absolute;\n      top: 230px;\n      left: 430px;\n      z-index: 2;\n    }\n    \n    .image-circle img {\n      width: 100%;\n      height: 100%;\n      object-fit: cover;\n      display: block;\n    }\n    \n    .quote-bottom {\n      color: white;\n      font-size: 90px;\n      font-weight: 700;\n      text-align: center;\n      line-height: 1.3;\n      text-shadow: 4px 4px 16px rgba(0,0,0,0.2);\n      position: absolute;\n      bottom: 300px;\n      left: 200px;\n      right: 200px;\n      z-index: 2;\n      overflow: hidden;\n      max-height: 350px;\n      word-wrap: break-word;\n      word-break: break-word;\n      white-space: normal;\n    }\n    \n    .dot {\n      width: 18px;\n      height: 18px;\n      background: white;\n      border-radius: 50%;\n      position: absolute;\n      z-index: 1;\n    }\n    \n    .dot-1 { top: 220px; left: 220px; opacity: 0.3; }\n    .dot-2 { top: 340px; right: 270px; opacity: 0.4; }\n    .dot-3 { bottom: 220px; left: 340px; opacity: 0.3; }\n    .dot-4 { bottom: 400px; right: 220px; opacity: 0.4; }\n  </style>\n</head>\n<body>\n  <div class=\"deco-circle-1\"></div>\n  <div class=\"deco-circle-2\"></div>\n  <div class=\"dot dot-1\"></div>\n  <div class=\"dot dot-2\"></div>\n  <div class=\"dot dot-3\"></div>\n  <div class=\"dot dot-4\"></div>\n  \n  <div class=\"image-circle\">\n    <img src=\"data:${mimeType};base64,${imageBase64}\" alt=\"Profile\">\n  </div>\n  \n  <div class=\"quote-bottom\">${quote}</div>\n</body>\n</html>\n`;\n}\n\n// Design 2 - Dark Elegant with Golden Border\nelse if (designIndex === 1) {\n  html = `\n<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"UTF-8\">\n  <style>\n    * {\n      margin: 0;\n      padding: 0;\n      box-sizing: border-box;\n    }\n    \n    html, body {\n      width: 2460px;\n      height: 2560px;\n      overflow: hidden;\n      margin: 0;\n      padding: 0;\n    }\n    \n    body {\n      background: #1a1a2e;\n      font-family: 'Georgia', serif;\n      position: relative;\n    }\n    \n    .image-circle {\n      width: 1600px;\n      height: 1600px;\n      border-radius: 50%;\n      overflow: hidden;\n      border: 14px solid #f39c12;\n      box-shadow: 0 40px 120px rgba(243, 156, 18, 0.3);\n      position: absolute;\n      top: 150px;\n      left: 430px;\n      z-index: 2;\n    }\n    \n    .image-circle img {\n      width: 100%;\n      height: 100%;\n      object-fit: cover;\n      display: block;\n    }\n    \n    .quote-bottom {\n      color: #ffffff;\n      font-size: 86px;\n      font-weight: 400;\n      text-align: center;\n      line-height: 1.4;\n      font-style: italic;\n      position: absolute;\n      bottom: 250px;\n      left: 200px;\n      right: 200px;\n      z-index: 2;\n      overflow: hidden;\n      max-height: 380px;\n      word-wrap: break-word;\n      word-break: break-word;\n      white-space: normal;\n    }\n    \n    .quotation-mark {\n      color: #f39c12;\n      font-size: 180px;\n      line-height: 0;\n      position: absolute;\n      bottom: 620px;\n      left: 50%;\n      transform: translateX(-50%);\n      z-index: 2;\n    }\n  </style>\n</head>\n<body>\n  <div class=\"image-circle\">\n    <img src=\"data:${mimeType};base64,${imageBase64}\" alt=\"Profile\">\n  </div>\n  <div class=\"quotation-mark\">\"</div>\n  <div class=\"quote-bottom\">${quote}</div>\n</body>\n</html>\n`;\n}\n\n// Design 3 - Pink Gradient Split\nelse if (designIndex === 2) {\n  html = `\n<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"UTF-8\">\n  <style>\n    * {\n      margin: 0;\n      padding: 0;\n      box-sizing: border-box;\n    }\n    \n    html, body {\n      width: 2460px;\n      height: 2560px;\n      overflow: hidden;\n      margin: 0;\n      padding: 0;\n    }\n    \n    body {\n      background: linear-gradient(180deg, #f093fb 0%, #f5576c 100%);\n      font-family: 'Arial', sans-serif;\n      position: relative;\n    }\n    \n    .image-circle {\n      width: 1600px;\n      height: 1600px;\n      border-radius: 50%;\n      overflow: hidden;\n      border: 22px solid white;\n      box-shadow: 0 30px 100px rgba(0,0,0,0.2);\n      position: absolute;\n      top: 150px;\n      left: 430px;\n      z-index: 2;\n    }\n    \n    .image-circle img {\n      width: 100%;\n      height: 100%;\n      object-fit: cover;\n      display: block;\n    }\n    \n    .quote-bottom {\n      color: white;\n      font-size: 82px;\n      font-weight: 700;\n      text-align: center;\n      line-height: 1.4;\n      text-shadow: 2px 2px 12px rgba(0,0,0,0.3);\n      position: absolute;\n      bottom: 280px;\n      left: 200px;\n      right: 200px;\n      z-index: 2;\n      overflow: hidden;\n      max-height: 360px;\n      word-wrap: break-word;\n      word-break: break-word;\n      white-space: normal;\n    }\n  </style>\n</head>\n<body>\n  <div class=\"image-circle\">\n    <img src=\"data:${mimeType};base64,${imageBase64}\" alt=\"Profile\">\n  </div>\n  <div class=\"quote-bottom\">${quote}</div>\n</body>\n</html>\n`;\n}\n\n// Design 4 - Ocean Blue Card\nelse if (designIndex === 3) {\n  html = `\n<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"UTF-8\">\n  <style>\n    * {\n      margin: 0;\n      padding: 0;\n      box-sizing: border-box;\n    }\n    \n    html, body {\n      width: 2460px;\n      height: 2560px;\n      overflow: hidden;\n      margin: 0;\n      padding: 0;\n    }\n    \n    body {\n      background: linear-gradient(to bottom, #0f2027, #203a43, #2c5364);\n      font-family: 'Arial', sans-serif;\n      position: relative;\n    }\n    \n    .card {\n      background: white;\n      border-radius: 70px;\n      position: absolute;\n      top: 140px;\n      left: 230px;\n      right: 230px;\n      bottom: 140px;\n      box-shadow: 0 60px 160px rgba(0,0,0,0.4);\n      z-index: 1;\n    }\n    \n    .image-circle {\n      width: 1400px;\n      height: 1400px;\n      border-radius: 50%;\n      overflow: hidden;\n      border: 18px solid #2c5364;\n      position: absolute;\n      top: 360px;\n      left: 530px;\n      z-index: 2;\n    }\n    \n    .image-circle img {\n      width: 100%;\n      height: 100%;\n      object-fit: cover;\n      display: block;\n    }\n    \n    .quote-bottom {\n      color: #2d3436;\n      font-size: 78px;\n      font-weight: 600;\n      text-align: center;\n      line-height: 1.5;\n      position: absolute;\n      bottom: 350px;\n      left: 280px;\n      right: 280px;\n      z-index: 2;\n      overflow: hidden;\n      max-height: 340px;\n      word-wrap: break-word;\n      word-break: break-word;\n      white-space: normal;\n    }\n  </style>\n</head>\n<body>\n  <div class=\"card\"></div>\n  <div class=\"image-circle\">\n    <img src=\"data:${mimeType};base64,${imageBase64}\" alt=\"Profile\">\n  </div>\n  <div class=\"quote-bottom\">${quote}</div>\n</body>\n</html>\n`;\n}\n\n// Design 5 - Teal Modern\nelse if (designIndex === 4) {\n  html = `\n<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"UTF-8\">\n  <style>\n    * {\n      margin: 0;\n      padding: 0;\n      box-sizing: border-box;\n    }\n    \n    html, body {\n      width: 2460px;\n      height: 2560px;\n      overflow: hidden;\n      margin: 0;\n      padding: 0;\n    }\n    \n    body {\n      background: linear-gradient(135deg, #06beb6 0%, #48b1bf 100%);\n      font-family: 'Arial', sans-serif;\n      position: relative;\n    }\n    \n    .image-circle {\n      width: 1600px;\n      height: 1600px;\n      border-radius: 50%;\n      overflow: hidden;\n      border: 20px solid rgba(255,255,255,0.9);\n      box-shadow: 0 25px 90px rgba(0,0,0,0.25);\n      position: absolute;\n      top: 250px;\n      left: 430px;\n      z-index: 2;\n    }\n    \n    .image-circle img {\n      width: 100%;\n      height: 100%;\n      object-fit: cover;\n      display: block;\n    }\n    \n    .quote-bottom {\n      color: white;\n      font-size: 88px;\n      font-weight: 700;\n      text-align: center;\n      line-height: 1.35;\n      text-shadow: 3px 3px 15px rgba(0,0,0,0.3);\n      position: absolute;\n      bottom: 300px;\n      left: 180px;\n      right: 180px;\n      z-index: 2;\n      overflow: hidden;\n      max-height: 370px;\n      word-wrap: break-word;\n      word-break: break-word;\n      white-space: normal;\n    }\n    \n    .corner-accent {\n      position: absolute;\n      width: 200px;\n      height: 200px;\n      border: 8px solid rgba(255,255,255,0.2);\n      z-index: 1;\n    }\n    \n    .corner-1 {\n      top: 0;\n      left: 0;\n      border-right: none;\n      border-bottom: none;\n    }\n    \n    .corner-2 {\n      bottom: 0;\n      right: 0;\n      border-left: none;\n      border-top: none;\n    }\n  </style>\n</head>\n<body>\n  <div class=\"corner-accent corner-1\"></div>\n  <div class=\"corner-accent corner-2\"></div>\n  <div class=\"image-circle\">\n    <img src=\"data:${mimeType};base64,${imageBase64}\" alt=\"Profile\">\n  </div>\n  <div class=\"quote-bottom\">${quote}</div>\n</body>\n</html>\n`;\n}\n\n// Design 6 - Sunset Orange\nelse if (designIndex === 5) {\n  html = `\n<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"UTF-8\">\n  <style>\n    * {\n      margin: 0;\n      padding: 0;\n      box-sizing: border-box;\n    }\n    \n    html, body {\n      width: 2460px;\n      height: 2560px;\n      overflow: hidden;\n      margin: 0;\n      padding: 0;\n    }\n    \n    body {\n      background: linear-gradient(135deg, #fc4a1a 0%, #f7b733 100%);\n      font-family: 'Arial', sans-serif;\n      position: relative;\n    }\n    \n    .image-circle {\n      width: 1600px;\n      height: 1600px;\n      border-radius: 50%;\n      overflow: hidden;\n      border: 16px solid white;\n      box-shadow: 0 30px 100px rgba(0,0,0,0.35);\n      position: absolute;\n      top: 250px;\n      left: 430px;\n      z-index: 2;\n    }\n    \n    .image-circle img {\n      width: 100%;\n      height: 100%;\n      object-fit: cover;\n      display: block;\n    }\n    \n    .quote-bottom {\n      color: white;\n      font-size: 92px;\n      font-weight: 800;\n      text-align: center;\n      line-height: 1.3;\n      text-shadow: 5px 5px 20px rgba(0,0,0,0.4);\n      position: absolute;\n      bottom: 300px;\n      left: 200px;\n      right: 200px;\n      z-index: 2;\n      overflow: hidden;\n      max-height: 380px;\n      word-wrap: break-word;\n      word-break: break-word;\n      white-space: normal;\n      letter-spacing: -1px;\n    }\n    \n    .triangle {\n      position: absolute;\n      width: 0;\n      height: 0;\n      border-style: solid;\n      opacity: 0.15;\n      z-index: 1;\n    }\n    \n    .triangle-1 {\n      border-width: 0 0 400px 400px;\n      border-color: transparent transparent white transparent;\n      top: 0;\n      right: 0;\n    }\n    \n    .triangle-2 {\n      border-width: 400px 400px 0 0;\n      border-color: white transparent transparent transparent;\n      bottom: 0;\n      left: 0;\n    }\n  </style>\n</head>\n<body>\n  <div class=\"triangle triangle-1\"></div>\n  <div class=\"triangle triangle-2\"></div>\n  <div class=\"image-circle\">\n    <img src=\"data:${mimeType};base64,${imageBase64}\" alt=\"Profile\">\n  </div>\n  <div class=\"quote-bottom\">${quote}</div>\n</body>\n</html>\n`;\n}\n\nreturn {\n  json: {\n    html: html,\n    quote: quote,\n    designUsed: designIndex + 1,\n    designName: ['Purple Circles', 'Dark Golden', 'Pink Gradient', 'Ocean Blue', 'Teal Modern', 'Sunset Orange'][designIndex],\n    timestamp: timestamp\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "c4af2d86-aa2c-45f8-8c27-64583b3ffe90",
      "name": "HTML to Image (HCTI)",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Sign up at htmlcsstoimage.com - Free 50 images/month",
      "position": [
        2944,
        848
      ],
      "parameters": {
        "url": "https://hcti.io/v1/image",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "html",
              "value": "={{ $json.html }}"
            },
            {
              "name": "viewport_width",
              "value": 1080
            },
            {
              "name": "viewport_height",
              "value": 1080
            }
          ]
        },
        "genericAuthType": "httpBasicAuth"
      },
      "credentials": {
        "httpBasicAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.1,
      "alwaysOutputData": true
    },
    {
      "id": "94ce69a3-f7b6-4f42-bdce-760d52bc1fe6",
      "name": "Download Image",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3152,
        848
      ],
      "parameters": {
        "url": "={{ $json.url }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "55c01d4c-d3d0-42fe-98eb-bf5366092a57",
      "name": "Prepare Image Data",
      "type": "n8n-nodes-base.code",
      "position": [
        3568,
        848
      ],
      "parameters": {
        "jsCode": "// Convert downloaded image to base64\nconst binaryData = $input.item.binary.data;\nconst base64Image = binaryData.data.toString('base64');\n\nconst quoteData = $('Parse Form Data').item.json;\n\nreturn {\n  json: {\n    quote: quoteData.quote,\n    designImageBase64: base64Image,\n    imageUrl: $('HTML to Image (HCTI)').item.json.url\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "8d510d0f-e601-40ed-baf1-ca2b2a206694",
      "name": "Prepare Final Response",
      "type": "n8n-nodes-base.code",
      "position": [
        3984,
        848
      ],
      "parameters": {
        "jsCode": "const captionResponse = $input.item.json;\nconst designData = $('Prepare Image Data').item.json;\n\n// Extract caption from ChatGPT\nlet caption = \"\";\ntry {\n  caption = captionResponse.choices[0].message.content.trim();\n} catch (error) {\n  caption = \"Caption generation failed. Please try again.\";\n}\n\n// Prepare response for the form\nconst response = {\n  success: true,\n  quote: designData.quote,\n  designImageBase64: designData.designImageBase64,\n  caption: caption,\n  timestamp: new Date().toISOString()\n};\n\nreturn {\n  json: response\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "845ef993-27a6-4f41-8ee7-4bf6e6812eca",
      "name": "Respond to Webhook1",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        4160,
        848
      ],
      "parameters": {
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "application/json"
              },
              {
                "name": "Access-Control-Allow-Origin",
                "value": "*"
              }
            ]
          }
        },
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}"
      },
      "typeVersion": 1
    },
    {
      "id": "a1bc71ff-c28c-4f74-837b-b4422221a4a3",
      "name": "Create a post",
      "type": "n8n-nodes-base.linkedIn",
      "position": [
        3808,
        1216
      ],
      "parameters": {
        "additionalFields": {}
      },
      "typeVersion": 1
    },
    {
      "id": "084329c5-4a75-4dee-84e6-10372a02c693",
      "name": "Webhook-LinkedIn-post",
      "type": "n8n-nodes-base.webhook",
      "position": [
        3584,
        1216
      ],
      "parameters": {
        "path": "087bc7d9-39b4-48c2-add3-2e73b082cdf4",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "3d79ded9-d0cc-4276-802f-3323ec499c23",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        4032,
        1216
      ],
      "parameters": {
        "options": {
          "responseCode": 200,
          "responseHeaders": {
            "entries": [
              {
                "name": "Access-Control-Allow-Origin",
                "value": "*"
              },
              {
                "name": "Access-Control-Allow-Methods",
                "value": "POST, OPTIONS"
              },
              {
                "name": "Access-Control-Allow-Headers",
                "value": "Content-Type"
              },
              {
                "name": "Content-Type",
                "value": "application/json"
              }
            ]
          }
        },
        "respondWith": "json",
        "responseBody": "{\n  \"success\": true,\n  \"message\": \"Post received and posted to LinkedIn\"\n}"
      },
      "typeVersion": 1.4
    },
    {
      "id": "e5c47950-4ce7-42e2-8443-c2008a033e04",
      "name": "Edit Image",
      "type": "n8n-nodes-base.editImage",
      "position": [
        3360,
        848
      ],
      "parameters": {
        "options": {},
        "operation": "resize"
      },
      "typeVersion": 1
    },
    {
      "id": "5c6c2021-4a73-4431-aabd-00ddadd9aae3",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3520,
        1120
      ],
      "parameters": {
        "color": 7,
        "width": 832,
        "height": 272,
        "content": "## 3. Confirm and post directly to LinkedIn"
      },
      "typeVersion": 1
    },
    {
      "id": "1857d7f7-1d3f-4fdc-aefb-90b12d4b089a",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1376,
        624
      ],
      "parameters": {
        "width": 816,
        "height": 864,
        "content": "## Automated LinkedIn Post Designer with N8N Integration\n\n## How it works\n- Upload your portrait and post quote in the webform - attached link to the webform below\n- Webhook receives the form data\n- Parse form Data, convert the image to base64 image\n- Create HTML Design: create the design from 6 ready templates, all in HTML\n- HTML to image: convert the code to an image and download\n- Reduce image size, generate captions using OpenAI API\n- Output response to view in webform\n- Edit captions in webform and post to LinkedIn directly\n\n## Setup\n1. Get the web form attached below\n2. Match both webhooks in the form to send data\n3. Get [html css to image](https://htmlcsstoimage.com/) API and match in HTML to image (HCTI) http request node - Basic Auth\n4. Get [OpenAI](https://platform.openai.com/settings/) API and insert into ChatGPT http request node - Header parameters\n5. Create [LinkedIn](https://www.linkedin.com/) credential and match the Create a post LinkedIn node\n\n## [Link to the webform](https://drive.google.com/file/d/1c2fuxuJXt-eV3qM4aW48H0CVebA1js1j/view?usp=sharing)"
      },
      "typeVersion": 1
    },
    {
      "id": "5819505b-aa46-44aa-a8da-b5d094341ed4",
      "name": "Parse Form Data",
      "type": "n8n-nodes-base.code",
      "position": [
        2544,
        848
      ],
      "parameters": {
        "jsCode": "// Extract form data\nconst body = $input.item.binary;\nconst params = $input.item.json.body;\n\n// Get quote text\nconst quote = params.quote || \"No quote provided\";\n\n// Get image binary data\nconst imageData = body.image;\n\nif (!imageData) {\n  throw new Error(\"No image uploaded\");\n}\n\n// Convert binary to base64 for HTML embedding\nconst base64Image = imageData.data.toString('base64');\nconst mimeType = imageData.mimeType || 'image/jpeg';\n\nreturn {\n  json: {\n    quote: quote,\n    imageBase64: base64Image,\n    mimeType: mimeType,\n    timestamp: new Date().toISOString()\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "6a4c4f5a-5fc5-4449-a56b-ca4ddfc2e308",
      "name": "ChatGPT - Generate Caption",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3776,
        848
      ],
      "parameters": {
        "url": "https://api.openai.com/v1/chat/completions",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "model",
              "value": "gpt-4o-mini"
            },
            {
              "name": "messages",
              "value": "={{ [\n  {\n    role: 'system',\n    content: 'You are an expert Linkedin caption writer. Write in an educative, inspirational, motivating, and informative tone. Never use em dashes. Keep it authentic, engaging, and conversion-focused.'\n  },\n  {\n    role: 'user',\n    content: 'Write a compelling linkedin caption for this quote: \"' + $json.quote + '\"\\n\\nRequirements:\\n- Tone: Educative, inspirational, motivating, informative\\n- NO em dash allowed anywhere\\n- Length: 100-150 words\\n- Start with an attention-grabbing hook\\n- Expand on the quote meaning or practical application\\n- End with a clear call-to-action or thought-provoking question\\n- Add 5-7 highly relevant hashtags\\n- Make it personal and relatable\\n- No fluff, just value\\n\\nFormat:\\n[Hook line]\\n\\n[Main content - 2-3 short paragraphs]\\n\\n[CTA or question]\\n\\n[Hashtags separated by spaces]'\n  }\n] }}"
            },
            {
              "name": "temperature",
              "value": 0.8
            },
            {
              "name": "max_tokens",
              "value": 400
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_TOKEN_HERE"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "d5dd0d46-94b4-40a3-b6ff-35f3b92f5aa9",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2288,
        736
      ],
      "parameters": {
        "color": 7,
        "width": 1200,
        "height": 656,
        "content": "## 1. Receive data from webform, process and generate design"
      },
      "typeVersion": 1
    },
    {
      "id": "c35b2f27-a007-4468-9ac1-fc28339a348a",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3520,
        736
      ],
      "parameters": {
        "color": 7,
        "width": 832,
        "height": 352,
        "content": "## 2. Generate caption and send response back to webform"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Edit Image": {
      "main": [
        [
          {
            "node": "Prepare Image Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create a post": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Image": {
      "main": [
        [
          {
            "node": "Edit Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Form Data": {
      "main": [
        [
          {
            "node": "Create HTML Design",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create HTML Design": {
      "main": [
        [
          {
            "node": "HTML to Image (HCTI)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Image Data": {
      "main": [
        [
          {
            "node": "ChatGPT - Generate Caption",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTML to Image (HCTI)": {
      "main": [
        [
          {
            "node": "Download Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook-LinkedIn-post": {
      "main": [
        [
          {
            "node": "Create a post",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Final Response": {
      "main": [
        [
          {
            "node": "Respond to Webhook1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook - Receive Form": {
      "main": [
        [
          {
            "node": "Parse Form Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ChatGPT - Generate Caption": {
      "main": [
        [
          {
            "node": "Prepare Final Response",
            "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.

Pro

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

About this workflow

This workflow leverages n8n to automate LinkedIn content creation from start to finish. Upload an image and quote through a web form, and get a professionally designed post with AI-generated captions, ready to publish in seconds. Randomly selects from 6 professional design…

Source: https://n8n.io/workflows/11620/ — original creator credit. Request a take-down →

More Social Media workflows → · Browse all categories →

Related workflows

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

Social Media

This n8n workflow automatically shares content from a Telegram Channel to multiple platforms like WordPress, Facebook, X/Twitter, and LinkedIn. It uses a Switch node to detect the type of content—text

Telegram Trigger, Telegram, WordPress +5
Social Media

This n8n workflow is designed for content curators, digital marketers, and social media managers who want to automate the process of discovering, translating, and publishing news content from multiple

Edit Image, Facebook Graph Api, WordPress +9
Social Media

This workflow automates the post-publish process for YouTube videos, combining advanced SEO optimization, cross-platform promotion, and analytics reporting. It is designed for creators, marketers, and

YouTube, HTTP Request, LinkedIn +6
Social Media

This workflow is designed for content creators, social media managers, digital marketers, and business owners who want to automate their content creation and distribution process across multiple platf

Google Drive, Google Drive Trigger, WordPress +13
Social Media

This n8n workflow is designed for Shopify store owners, e-commerce managers, and digital marketers who want to automate their product promotion across multiple platforms. The workflow automatically cr

WordPress, Edit Image, HTTP Request +10