AutomationFlowsAI & RAG › Create Daily Multi-platform Social Posts with Gemini, Nocodb, Cloudinary and…

Create Daily Multi-platform Social Posts with Gemini, Nocodb, Cloudinary and…

Original n8n title: Create Daily Multi-platform Social Posts with Gemini, Nocodb, Cloudinary and Telegram

ByJames Nunn @nunlimited on n8n.io

This workflow runs on a weekday evening schedule to generate tomorrow’s multi-platform social post using Google Gemini, stores the draft in NocoDB, generates a branded image, uploads it to Cloudinary, updates the NocoDB record with the image URL, and notifies a Telegram chat.…

Cron / scheduled trigger★★★★★ complexityAI-powered33 nodesNoco DbGoogle GeminiN8N Nodes CloudinaryHTTP Request
AI & RAG Trigger: Cron / scheduled Nodes: 33 Complexity: ★★★★★ AI nodes: yes Added:

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

This workflow follows the Googlegemini → 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
{
  "id": "YOUR_WF2_WORKFLOW_ID",
  "name": "Nnltd_WF2: Daily Content Generation",
  "tags": [],
  "nodes": [
    {
      "id": "b9ff1dfd-08fa-477a-92b9-9043f3b4c076",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -368,
        128
      ],
      "parameters": {
        "width": 480,
        "height": 896,
        "content": "## Nnltd_WF2: Daily Content Generation\n\n### How it works\n\nThis workflow runs on a daily schedule to generate and queue LinkedIn-style content for the next day. It first checks whether tomorrow already has an approved scheduled post, then gathers recent posts, content pillars, total post counts, and curated candidate material from NocoDB. Gemini is used to create post copy and matching image prompts, generated images are uploaded to Cloudinary, NocoDB records are updated, and James is notified via Telegram when all posts are prepared.\n\n### Setup steps\n\n- Configure the Daily Schedule trigger with the desired run time and timezone, especially if posting times should align with UK time.\n- Connect NocoDB credentials and verify the tables/fields used for recent posts, pillars, total post counts, curated candidates, scheduled posts, and image URL updates.\n- Configure Google Gemini credentials/models for both text generation and image generation nodes.\n- Connect Cloudinary credentials and set the target folder or upload options for generated images.\n- Update the Telegram HTTP request URL with a valid bot token and chat ID for James, replacing the placeholder in the Notify James node.\n- Review the custom code nodes for field names, date logic, approval status values, and posting-time assumptions so they match the database schema.\n\n### Customization\n\nAdjust the content pillar weighting, Gemini prompt structure, number of generated posts, LinkedIn posting slots, Cloudinary upload settings, and Telegram notification message to match the brand and publishing process."
      },
      "typeVersion": 1
    },
    {
      "id": "57fb6046-9389-493b-94c1-573edb663320",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        192,
        128
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 336,
        "content": "## Schedule and queue gate\n\nStarts the daily workflow and checks whether tomorrow already has an approved scheduled post, preventing duplicate generation when the queue is already filled."
      },
      "typeVersion": 1
    },
    {
      "id": "90fa3992-e81f-4369-a58a-12a544c6bb6b",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        640,
        144
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 320,
        "content": "## Gather content context\n\nFetches recent posts and pillar definitions from NocoDB, then normalizes the recent-post data so it can be safely used as context for generation."
      },
      "typeVersion": 1
    },
    {
      "id": "dd28ef63-284b-4288-8948-a050833f7527",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1312,
        144
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 320,
        "content": "## Count existing posts\n\nRetrieves the total post count from NocoDB and collapses the result into a safe, compact value for downstream pillar assignment logic."
      },
      "typeVersion": 1
    },
    {
      "id": "2cd0695b-edb2-4698-91ca-ae2559248261",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1760,
        144
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 320,
        "content": "## Select curated input\n\nFetches a curated candidate from NocoDB, sanitizes it, and assigns content pillars using the weighted round-robin logic before prompt building."
      },
      "typeVersion": 1
    },
    {
      "id": "dfef4318-7700-49af-94e3-4e4e2e02b1d1",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2432,
        176
      ],
      "parameters": {
        "color": 7,
        "width": 768,
        "height": 304,
        "content": "## Generate post copy\n\nBuilds the Gemini prompt, calls Gemini to generate the daily content, and parses the model response into structured post data."
      },
      "typeVersion": 1
    },
    {
      "id": "dd60dca1-fd0d-42af-b932-b366fd7ad220",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3232,
        144
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 320,
        "content": "## Schedule and save posts\n\nSplits the generated response into individual posts, assigns each one an expert-recommended schedule slot, and writes the new post records to NocoDB."
      },
      "typeVersion": 1
    },
    {
      "id": "c3313106-b041-4c1b-a0e9-afeb5247a49e",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3904,
        176
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 304,
        "content": "## Create and upload images\n\nBuilds an image prompt for each post, generates the image through Gemini, and uploads the result to Cloudinary."
      },
      "typeVersion": 1
    },
    {
      "id": "b748bf31-f0c9-4018-8819-6eaf3148114c",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4576,
        144
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 320,
        "content": "## Attach image URLs\n\nExtracts the hosted Cloudinary image URL and updates the corresponding NocoDB post record with the generated image information."
      },
      "typeVersion": 1
    },
    {
      "id": "10439db2-866c-487e-8ec6-63224bcfbfa5",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5024,
        144
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 320,
        "content": "## Aggregate and notify\n\nAggregates all completed post items and sends a final notification to James through the configured Telegram HTTP request."
      },
      "typeVersion": 1
    },
    {
      "id": "17ee7332-dfb2-41a1-b6a8-741fe8f65150",
      "name": "Schedule Daily Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        240,
        304
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 20 * * 0-4"
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "c3b7f6aa-d8ee-48f9-a130-6318086e66be",
      "name": "Get Recent Posts from NoCoDB",
      "type": "n8n-nodes-base.nocoDb",
      "position": [
        688,
        304
      ],
      "parameters": {
        "limit": 30,
        "table": "YOUR_CONTENT_CALENDAR_TABLE_ID",
        "options": {
          "sort": {
            "property": [
              {
                "field": "CreatedAt",
                "direction": "desc"
              }
            ]
          },
          "fields": [
            "Title",
            "Pillar",
            "Status"
          ]
        },
        "operation": "getAll",
        "projectId": "YOUR_NOCODB_PROJECT_ID",
        "workspaceId": "YOUR_NOCODB_WORKSPACE_ID",
        "authentication": "nocoDbApiToken"
      },
      "credentials": {
        "nocoDbApiToken": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "73e64b26-73ae-4d65-8541-ded2bea0d6e4",
      "name": "Validate Recent Posts",
      "type": "n8n-nodes-base.code",
      "position": [
        912,
        304
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst hasRealData = items && items.length > 0 && items[0].json && !items[0].json.error && items[0].json.Title;\nif (!hasRealData) {\n  return [{ json: { _empty: true } }];\n}\nreturn items;"
      },
      "typeVersion": 2
    },
    {
      "id": "afce2b8d-21e5-4986-ab36-a0b525c13688",
      "name": "Get Content Pillars",
      "type": "n8n-nodes-base.nocoDb",
      "position": [
        1136,
        304
      ],
      "parameters": {
        "table": "YOUR_CONTENT_PILLARS_TABLE_ID",
        "options": {
          "where": "(Active,eq,true)",
          "fields": [
            "Title",
            "Description",
            "Example Topics",
            "Target Frequency"
          ]
        },
        "operation": "getAll",
        "projectId": "YOUR_NOCODB_PROJECT_ID",
        "returnAll": true,
        "workspaceId": "YOUR_NOCODB_WORKSPACE_ID",
        "authentication": "nocoDbApiToken"
      },
      "credentials": {
        "nocoDbApiToken": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "a4fa992e-e344-47fa-b03e-1487f9521663",
      "name": "Create Gemini Prompt",
      "type": "n8n-nodes-base.code",
      "position": [
        2480,
        304
      ],
      "parameters": {
        "jsCode": "const upstream = $input.first().json;\nconst allPillarsText = upstream.allPillarsText;\nconst assignmentLines = upstream.assignmentLines;\nconst recentText = upstream.recentText;\nconst assignedPillars = upstream.assignedPillars;\nconst curatedArticle = upstream.curatedArticle;\n\n// Build curated article context if we have one\nlet curatedBlock = '';\nconst curatedIdx = assignedPillars.indexOf('Curated & Commentary');\nif (curatedIdx >= 0 && curatedArticle && !curatedArticle._empty) {\n  curatedBlock = '\\n\\nCURATED ARTICLE SEED FOR POST ' + (curatedIdx + 1) + ' (Curated & Commentary):\\nThe following real article should anchor Post ' + (curatedIdx + 1) + '. Zoe\\'s job is to ADD a distinct perspective.\\n\\nARTICLE TITLE: ' + curatedArticle.articleTitle + '\\nSOURCE: ' + curatedArticle.source + '\\nAUTHOR: ' + (curatedArticle.author || 'Unknown') + '\\nURL: ' + curatedArticle.url + '\\nEXCERPT: ' + curatedArticle.excerpt + '\\nSUGGESTED ANGLE: ' + curatedArticle.suggestedAngle + '\\n\\nRULES FOR CURATED COMMENTARY:\\n- Reference the article naturally\\n- Include the article URL in the LinkedIn version\\n- Take a DISTINCT position\\n- Never just summarise. Never just agree. Add something.\\n- The quiet confidence test still applies';\n}\n\nconst now = new Date();\nconst tomorrow = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1));\nconst tomorrowStr = tomorrow.toLocaleDateString('en-GB', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', timeZone: 'UTC' });\nconst DAY_SLOTS = { 1: '08:00', 2: '09:00', 3: '12:00', 4: '09:00', 5: '08:00' };\nconst slotTime = DAY_SLOTS[tomorrow.getUTCDay()] || '09:00';\n\nconst system_prompt = `You are Zoe, the social media voice of Nunlimited.\n\nABOUT Nunlimited:\nNunlimited is an AI-first content and business operating system. We serve content creators and entrepreneurs who want to build in public, systematise their creative output, and grow their brand without hiring large teams or paying enterprise prices.\n\nOur five content pillars:\n1. Build in Public \u2014 transparent documentation of the build journey\n2. Zo.E System \u2014 the AI content operating system powering Nunlimited\n3. Parent Brand \u2014 Nunlimited brand, values, and positioning\n4. Child Company Showcase \u2014 spotlighting ventures built within the Nunlimited ecosystem\n5. Community \u2014 engaging with and celebrating the audience and collaborators\n\nVOICE & TONE:\n\u2014 Quiet confidence. You know your stuff but don't need to prove it by being loud.\n\u2014 Humble and approachable. Never talk down. Never lecture.\n\u2014 Subtle wit and playfulness. A raised eyebrow, not a punchline.\n\u2014 Authentic and relaxed. Write like a real person having a conversation.\n\u2014 Professional but fun. We take building seriously. We don't take ourselves too seriously.\n\u2014 Use 'we' for Nunlimited. Never use 'I' \u2014 Zoe is not an individual human.\n\nWHAT ZOE NEVER DOES:\n\u2014 Never uses clickbait hooks\n\u2014 Never uses aggressive CTAs\n\u2014 Never uses empty superlatives (game-changing, revolutionary, ultimate)\n\u2014 Never uses LinkedIn-bro formatting (one-sentence paragraphs stacked for false drama)\n\u2014 Never uses excessive emojis (one or two max, only when natural)\n\u2014 Never uses hashtag walls (3-5 max, usually fewer)\n\u2014 Never posts engagement-bait\n\u2014 Never reveals proprietary Nunlimited framework details or IP\n\nGOLD STANDARD EXAMPLES:\n\nEXAMPLE 1 \u2014 LinkedIn (Build in Public):\nThere's a moment in most build journeys where the creator starts shipping before they've finished thinking. It's understandable. You see a problem you recognise, your brain lights up, and suddenly you're three features deep into a solution. But here's the thing \u2014 the audience wasn't done asking. And the problem they mentioned first is rarely the one that actually keeps them up at night. The best builders we've worked with have this almost frustrating ability to sit in the question a little longer than feels comfortable. They ask 'what else?' one more time. They let the silence breathe. It's not a trick. It's just patience dressed up as curiosity.\n\nEXAMPLE 2 \u2014 X/Twitter (Build in Public):\nMost projects don't stall because of complexity. They stall because someone on the team couldn't explain the vision internally when you weren't in the room. That's a different problem to solve.\n\nEXAMPLE 3 \u2014 Bluesky (Build in Public) [224 chars]:\nWeek 8 of building a content operating system with Claude, Gemini, and a lot of coffee. Today's win: got the content pipeline talking to five social platforms through a single API call. The future is weird and I'm here for it. nunlimited.com\n\nEXAMPLE 3B \u2014 Bluesky (Build in Public) [141 chars \u2014 short and complete is fine]:\nMost projects don't stall at launch. They stall three weeks earlier when nobody asked the hard question. The launch is just where you find out.\n\nEXAMPLE 3C \u2014 Bluesky (Zo.E System) [271 chars \u2014 near the limit, still complete]:\nThere's a version of content planning that's just collective wishful thinking. Creators narrate hope. Collaborators nod. Output stays the same. The fix isn't a better calendar. It's better questions. That's what we built the Zo.E System for. nunlimited.com\n\nKEY BLUESKY RULES: Complete thought always. No hashtags ever. Under 300 chars always. Read it back and count before including in JSON.\n\nEXAMPLE 4 \u2014 Instagram (Zo.E System):\nWhat if you could reclaim 30% of your creative time by getting better at saying no? Not 'no' to ideas. 'No' to the ones that were never going to land anyway. We call it intentional disqualification, and it might be the most counterintuitive thing we've built at Nunlimited.\n\nRespond ONLY with valid JSON. No markdown, no preamble, no explanation.`;\n\nconst user_prompt = `Generate 1 social media post for Nunlimited scheduled for tomorrow.\n\nTOMORROW'S DATE: ${tomorrowStr}\n\nIMPORTANT: Write content appropriate for ${tomorrowStr}. Do NOT reference today's day or write as if it is the current day. The post will be published tomorrow at ${slotTime}.\n\nACTIVE CONTENT PILLARS (reference \u2014 full brand context):\n${allPillarsText}\n\nPILLAR ASSIGNMENTS FOR THIS RUN \u2014 these are MANDATORY:\n${assignmentLines}\n\nYou MUST use exactly these pillars in this exact order. Do not substitute, swap, or reassign.${curatedBlock}\n\nRECENT POSTS (avoid repeating these topics):\n${recentText}\n\nOUTPUT FORMAT \u2014 respond ONLY with this exact JSON structure:\n{\n  \"posts\": [\n    {\n      \"title\": \"Short internal reference title (max 60 chars)\",\n      \"content_master\": \"The core idea in 1-2 sentences (platform-agnostic)\",\n      \"linkedin_version\": \"Full LinkedIn post (150-300 words) with 3-5 hashtags at end \u2014 MUST always include #ZoDotE and #Nunlimited in the hashtag block\",\n      \"x_version\": \"X/Twitter version under 280 chars\",\n      \"instagram_caption\": \"Instagram caption with up to 5 hashtags on separate line\",\n      \"facebook_version\": \"Facebook version, warm and conversational\",\n      \"bluesky_version\": \"Bluesky version \u2014 STRICT MAXIMUM 300 characters including spaces. This is a hard platform limit. Count every character carefully before responding. Must be a COMPLETE, self-contained thought \u2014 never truncated, never trailing off. Bluesky-native tone: direct, human, slightly wry. No hashtags. No em-dashes. Short sentences. Feels like a real person, not a brand post. Think: a good observation you'd share with a smart colleague.\",\n      \"threads_version\": \"Threads version under 500 chars, conversational and without hashtags\",\n      \"platforms\": [\"LinkedIn\", \"X\", \"Instagram\", \"Facebook\", \"Bluesky\", \"Threads\"],\n      \"pillar\": \"Exact pillar name from the list above\",\n      \"media_suggestion\": \"Description of an ideal accompanying visual for image generation\"\n    }\n  ]\n}\n\nRULES:\n- Generate exactly 1 post \u2014 it will be published at ${slotTime}\n- ALL platform versions including threads_version are required for every post \u2014 do not set any to null\n- Each platform version must feel NATIVE to that platform\n- Not every post needs nunlimited.com \u2014 only 1 in 3-4\n- The \"pillar\" field of each post MUST match the assigned pillar for its position\n- Never use clickbait, engagement bait, or aggressive CTAs\n- The quiet confidence test: if it sounds like it is trying to go viral, rewrite it\\n- Every platform version that uses hashtags (LinkedIn, X, Instagram, Facebook) MUST include both #ZoDotE and #Nunlimited \u2014 these are non-negotiable and must appear in every such post, every time\n- Do NOT make the content day-specific (no 'Happy Monday' etc) unless it adds genuine value`;\n\nreturn [{ json: { system_prompt, user_prompt, assignedPillars, curatedArticle } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "f6b281d4-6fd2-43a0-a818-c8a16824aca2",
      "name": "Execute Gemini Model",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "position": [
        2704,
        304
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "models/gemini-flash-latest",
          "cachedResultName": "models/gemini-flash-latest"
        },
        "options": {
          "temperature": 0.8,
          "systemMessage": "={{ $json.system_prompt }}",
          "maxOutputTokens": 4096
        },
        "messages": {
          "values": [
            {
              "content": "={{ $json.user_prompt }}"
            }
          ]
        },
        "jsonOutput": true,
        "builtInTools": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "dc24ad47-28a4-44d6-84dc-a76185e50818",
      "name": "Analyze Gemini Response",
      "type": "n8n-nodes-base.code",
      "position": [
        3056,
        304
      ],
      "parameters": {
        "jsCode": "const response = $input.first().json;\nlet parsed;\ntry {\n  const text = response?.content?.parts?.[0]?.text;\n  if (text) {\n    let cleaned = text.replace(/```json|```/g, '').trim();\n    // Try direct parse first\n    try {\n      parsed = JSON.parse(cleaned);\n    } catch (e1) {\n      // Gemini sometimes wraps JSON in extra braces or adds trailing content\n      // Try to extract the posts array directly\n      const postsMatch = cleaned.match(/\"posts\"\\s*:\\s*\\[/)\n      if (postsMatch) {\n        const startIdx = cleaned.indexOf('{');\n        // Find the matching closing brace for the outermost object\n        let depth = 0;\n        let endIdx = -1;\n        for (let i = startIdx; i < cleaned.length; i++) {\n          if (cleaned[i] === '{') depth++;\n          else if (cleaned[i] === '}') {\n            depth--;\n            if (depth === 0) { endIdx = i; break; }\n          }\n        }\n        if (endIdx > startIdx) {\n          parsed = JSON.parse(cleaned.substring(startIdx, endIdx + 1));\n        } else {\n          parsed = { posts: [] };\n        }\n      } else {\n        parsed = { posts: [] };\n      }\n    }\n  } else if (typeof response === 'object' && response.posts) {\n    parsed = response;\n  } else {\n    parsed = { posts: [] };\n  }\n} catch(e) {\n  parsed = { posts: [] };\n}\nif (!parsed || !parsed.posts) parsed = { posts: [] };\nif (parsed.posts.length === 0) {\n  // Log the raw text for debugging\n  const rawText = response?.content?.parts?.[0]?.text || 'NO TEXT FOUND';\n  return [{ json: { posts: [], _debug_rawLength: rawText.length, _debug_first200: rawText.substring(0, 200) } }];\n}\nreturn [{ json: parsed }];"
      },
      "typeVersion": 2
    },
    {
      "id": "09cf89b0-0689-46ef-891f-9cf747b8596d",
      "name": "Divide Posts",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        3280,
        304
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "posts"
      },
      "typeVersion": 1
    },
    {
      "id": "50484ed0-6827-489d-9639-6c8e595397cf",
      "name": "Allocate Posting Times",
      "type": "n8n-nodes-base.code",
      "position": [
        3504,
        304
      ],
      "parameters": {
        "jsCode": "// Expert-recommended LinkedIn posting times by day of week (UK timezone)\nconst DAY_SLOTS = {\n  1: '08:00', // Monday: 8 AM - start-of-week planning window\n  2: '09:00', // Tuesday: 9 AM - peak LinkedIn engagement\n  3: '12:00', // Wednesday: 12 PM - midweek lunchtime spike\n  4: '09:00', // Thursday: 9 AM - strong mid-week engagement\n  5: '08:00', // Friday: 8 AM - early, before weekend wind-down\n};\n\nconst now = new Date();\nconst tomorrow = new Date(Date.UTC(\n  now.getUTCFullYear(),\n  now.getUTCMonth(),\n  now.getUTCDate() + 1\n));\nconst yyyy = tomorrow.getUTCFullYear();\nconst mm = String(tomorrow.getUTCMonth() + 1).padStart(2, '0');\nconst dd = String(tomorrow.getUTCDate()).padStart(2, '0');\nconst dayOfWeek = tomorrow.getUTCDay();\n\nconst slotTime = DAY_SLOTS[dayOfWeek] || '09:00';\n\nreturn $input.all().map(item => ({\n  json: { ...item.json, scheduled_datetime: `${yyyy}-${mm}-${dd} ${slotTime}` }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "311feaf7-a049-489b-9909-9cce6852f9aa",
      "name": "Save to NoCoDB",
      "type": "n8n-nodes-base.nocoDb",
      "position": [
        3728,
        304
      ],
      "parameters": {
        "table": "YOUR_CONTENT_CALENDAR_TABLE_ID",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldName": "Title",
              "fieldValue": "={{ $json.title }}"
            },
            {
              "fieldName": "Content Master",
              "fieldValue": "={{ $json.content_master }}"
            },
            {
              "fieldName": "LinkedIn Version",
              "fieldValue": "={{ $json.linkedin_version }}"
            },
            {
              "fieldName": "X Version",
              "fieldValue": "={{ $json.x_version }}"
            },
            {
              "fieldName": "Instagram Caption",
              "fieldValue": "={{ $json.instagram_caption }}"
            },
            {
              "fieldName": "Facebook Version",
              "fieldValue": "={{ $json.facebook_version }}"
            },
            {
              "fieldName": "Bluesky Version",
              "fieldValue": "={{ $json.bluesky_version }}"
            },
            {
              "fieldName": "Threads Version",
              "fieldValue": "={{ $json.threads_version || $json.bluesky_version || $json.content_master }}"
            },
            {
              "fieldName": "Pillar",
              "fieldValue": "={{ $json.pillar }}"
            },
            {
              "fieldName": "Platforms",
              "fieldValue": "LinkedIn, X, Instagram, Facebook, Bluesky, Threads"
            },
            {
              "fieldName": "Status",
              "fieldValue": "Approved"
            },
            {
              "fieldName": "Source",
              "fieldValue": "AI-Generated"
            },
            {
              "fieldName": "Media Suggestion",
              "fieldValue": "={{ $json.media_suggestion }}"
            },
            {
              "fieldName": "Scheduled Datetime",
              "fieldValue": "={{ $json.scheduled_datetime }}"
            }
          ]
        },
        "operation": "create",
        "projectId": "YOUR_NOCODB_PROJECT_ID",
        "workspaceId": "YOUR_NOCODB_WORKSPACE_ID",
        "authentication": "nocoDbApiToken"
      },
      "credentials": {
        "nocoDbApiToken": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "09f0400c-8b9c-4085-aba6-f37264a71889",
      "name": "Formulate Image Prompt",
      "type": "n8n-nodes-base.code",
      "position": [
        3952,
        304
      ],
      "parameters": {
        "jsCode": "return $input.all().map(i => {\n  const it = i.json;\n  const mediaSuggestion = it['Media Suggestion'] || it.media_suggestion || 'Abstract minimal graphic with glowing accent on dark background';\n\n  const prompt = `Create a minimal graphic in the Nunlimited brand visual style.\n\nBRAND COLOURS (use exactly these hex values):\n- Background: #090b14 (Void Black) \u2014 fills ~70% of the canvas as pure empty space\n- Primary accent: #ff1f7a (Nunlimited Pink) \u2014 the dominant glow and focal element\n- Gradient: from #ff1f7a (Pink) to #ff9a6c (Coral) \u2014 use on the key focal element only\n- Secondary surface: #0f1220 (Deep Navy) \u2014 subtle depth layers only if needed\n- Text (only if any label appears): #e8eaf4 (Off-white), very small, JetBrains Mono style\n\nDESIGN PRINCIPLES:\n- Negative space is the design: ~70% of the canvas should be empty dark background\n- One focal point only \u2014 never compete for attention\n- Soft outer glow on the focal element: 40\u201380px pink (#ff1f7a) glow, never a hard edge\n- Restraint always wins \u2014 if in doubt, remove an element\n- Clean, modern, architectural tech aesthetic \u2014 think \"The Architect's Console\"\n- No watermarks. No text overlaid on the image.\n\nSUBJECT FOR THIS POST:\n${mediaSuggestion}\n\nSquare aspect ratio (1:1).`;\n\n  return { json: { ...it, imagePrompt: prompt } };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "5d57d1db-66de-422c-a600-6a4d42cbddd2",
      "name": "Produce Image with Gemini",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "position": [
        4176,
        304
      ],
      "parameters": {
        "prompt": "={{ $json.imagePrompt }}",
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "models/gemini-3.1-flash-image-preview",
          "cachedResultName": "models/gemini-3.1-flash-image-preview"
        },
        "options": {
          "binaryPropertyOutput": "data"
        },
        "resource": "image"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "0112af5b-896a-4b5b-8b53-610ac92a93b5",
      "name": "Send Image to Cloudinary",
      "type": "n8n-nodes-cloudinary.cloudinary",
      "position": [
        4400,
        304
      ],
      "parameters": {
        "operation": "uploadFile",
        "additionalFieldsFile": {
          "tags": "zoe,nnltd,nano-banana",
          "folder": "zoe/nnltd"
        }
      },
      "credentials": {
        "cloudinaryApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "cdb7bd08-a345-4b33-a522-6614d096060b",
      "name": "Get Cloudinary Link",
      "type": "n8n-nodes-base.code",
      "position": [
        4624,
        304
      ],
      "parameters": {
        "jsCode": "return $input.all().map((cloudinaryItem, index) => {\n  const cloudinaryResult = cloudinaryItem.json;\n  const postData = $('Formulate Image Prompt').all()[index]?.json || {};\n  const imageUrl = cloudinaryResult?.secure_url || cloudinaryResult?.url || null;\n  if (!imageUrl) {\n    return { json: { ...postData, cloudinarySuccess: false, error: 'No URL returned from Cloudinary' } };\n  }\n  return { json: { ...postData, cloudinarySuccess: true, imageUrl } };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "85cdf8a3-675a-427c-a020-0efa43b1c39c",
      "name": "Update Image in NoCoDB",
      "type": "n8n-nodes-base.code",
      "position": [
        4848,
        304
      ],
      "parameters": {
        "jsCode": "const results = [];\nfor (const item of $input.all()) {\n  const { Id: rowId, imageUrl, ...rest } = item.json;\n  if (!rowId) throw new Error('No row ID available');\n  if (!imageUrl) throw new Error('No image URL available for row ' + rowId);\n  const response = await this.helpers.httpRequest({\n    method: 'PATCH',\n    url: `https://YOUR_NOCODB_URL/api/v1/db/data/noco/YOUR_NOCODB_PROJECT_ID/YOUR_CONTENT_CALENDAR_TABLE_ID/${rowId}`,\n    headers: {\n      'xc-token': 'YOUR_NOCODB_API_TOKEN',\n      'Content-Type': 'application/json',\n    },\n    body: {\n      'Media Status': 'Generated',\n      'Media URLs': imageUrl,\n    },\n    json: true,\n  });\n  results.push({ json: { ...item.json, nocodbUpdated: true, response } });\n}\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "71657bab-87b5-48be-9534-367fecbb34d6",
      "name": "Send Telegram Notification",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        5296,
        304
      ],
      "parameters": {
        "url": "https://api.telegram.org/botYOUR_TELEGRAM_BOT_TOKEN/sendMessage",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ (() => {\n  const raw = $input.first().json;\n  // Aggregate node wraps items under 'data' array\n  const posts = Array.isArray(raw.data) ? raw.data : $input.all().map(i => i.json);\n  \n  const lines = posts.map((p, idx) => {\n    const title = p.title || p.Title || 'Untitled';\n    const pillar = p.pillar || p.Pillar || '';\n    const time = (p.scheduled_datetime || p['Scheduled Datetime'] || '').slice(11, 16);\n    const preview = (p.content_master || p['Content Master'] || '').substring(0, 120);\n    return `*${idx + 1}. ${title}*\\n${pillar} \u00b7 ${time}\\n${preview}\u2026`;\n  }).join('\\n\\n');\n  \n  const buttons = posts.map((p, idx) => ([\n    { text: `\u274c Reject #${idx + 1}`, callback_data: 'reject:' + (p.Id || p.id) }\n  ]));\n  \n  return JSON.stringify({\n    chat_id: YOUR_TELEGRAM_CHAT_ID,\n    text: `\ud83d\udccb *1 draft ready for tomorrow*\\n\\n${lines}\\n\\n_Tap to reject and regenerate if needed._`,\n    parse_mode: 'Markdown',\n    reply_markup: { inline_keyboard: buttons }\n  });\n})() }}",
        "sendBody": true,
        "specifyBody": "json"
      },
      "typeVersion": 4.2
    },
    {
      "id": "aggregate-posts",
      "name": "Combine All Posts",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        5072,
        304
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData"
      },
      "typeVersion": 1
    },
    {
      "id": "count-total-posts",
      "name": "Calculate Post Totals",
      "type": "n8n-nodes-base.nocoDb",
      "position": [
        1360,
        304
      ],
      "parameters": {
        "table": "YOUR_CONTENT_CALENDAR_TABLE_ID",
        "options": {
          "where": "(Status,neq,Rejected)",
          "fields": [
            "Id"
          ]
        },
        "operation": "getAll",
        "projectId": "YOUR_NOCODB_PROJECT_ID",
        "returnAll": true,
        "workspaceId": "YOUR_NOCODB_WORKSPACE_ID",
        "authentication": "nocoDbApiToken"
      },
      "credentials": {
        "nocoDbApiToken": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3,
      "continueOnFail": true
    },
    {
      "id": "fetch-curated-candidate",
      "name": "Retrieve Curated Post",
      "type": "n8n-nodes-base.nocoDb",
      "position": [
        1808,
        304
      ],
      "parameters": {
        "limit": 1,
        "table": "YOUR_CURATED_QUEUE_TABLE_ID",
        "options": {
          "sort": {
            "property": [
              {
                "field": "Relevance Score",
                "direction": "desc"
              },
              {
                "field": "Quality Score",
                "direction": "desc"
              }
            ]
          },
          "where": "(Status,eq,Approved)",
          "fields": [
            "Article Title",
            "URL",
            "Source",
            "Author",
            "Excerpt",
            "Suggested Angle",
            "Id"
          ]
        },
        "operation": "getAll",
        "projectId": "YOUR_NOCODB_PROJECT_ID",
        "workspaceId": "YOUR_NOCODB_WORKSPACE_ID",
        "authentication": "nocoDbApiToken"
      },
      "credentials": {
        "nocoDbApiToken": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3,
      "continueOnFail": true
    },
    {
      "id": "safe-curated",
      "name": "Validate Curated Candidate",
      "type": "n8n-nodes-base.code",
      "position": [
        2032,
        304
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst hasRealData = items && items.length > 0 && items[0].json && !items[0].json.error && items[0].json['Article Title'];\nif (!hasRealData) {\n  return [{ json: { _empty: true } }];\n}\nreturn items;"
      },
      "typeVersion": 2
    },
    {
      "id": "assign-pillars",
      "name": "Distribute Content Pillars",
      "type": "n8n-nodes-base.code",
      "position": [
        2256,
        304
      ],
      "parameters": {
        "jsCode": "// === Assign Pillars (Bresenham weighted round-robin) ===\n// CRITICAL: Deduplicate NocoDB data \u2014 returnAll produces duplicate rows\n\nconst rawPillars = $('Get Content Pillars').all().map(i => i.json);\nconst seenTitles = new Set();\nconst pillarsData = [];\nfor (const p of rawPillars) {\n  if (p.Title && !seenTitles.has(p.Title)) {\n    seenTitles.add(p.Title);\n    pillarsData.push(p);\n  }\n}\n\nconst recentData = $('Validate Recent Posts').all().filter(i => !i.json._empty).map(i => i.json);\n\n// Deduplicate post count by Id\nconst countData = $('Calculate Post Totals').all();\nconst seenIds = new Set();\nlet totalPosts = 0;\nfor (const item of countData) {\n  const id = item.json?.Id;\n  if (id && !seenIds.has(id)) {\n    seenIds.add(id);\n    totalPosts++;\n  }\n}\nif (totalPosts === 0) totalPosts = recentData.length;\n\nconst EPOCH_OFFSET = 65;\nconst bresenhamRun = Math.max(0, Math.floor((totalPosts - EPOCH_OFFSET) / 3));\n\nfunction parseFrequency(freqText) {\n  if (!freqText) return 0;\n  const txt = String(freqText).toLowerCase().trim();\n  if (txt.includes('as needed')) return 0;\n  const rangeMatch = txt.match(/(\\d+)\\s*-\\s*(\\d+)\\s*x\\s*\\/\\s*week/);\n  if (rangeMatch) return (parseInt(rangeMatch[1]) + parseInt(rangeMatch[2])) / 2;\n  const singleMatch = txt.match(/(\\d+(?:\\.\\d+)?)\\s*x\\s*\\/\\s*week/);\n  if (singleMatch) return parseFloat(singleMatch[1]);\n  return 0;\n}\n\nconst pillars = pillarsData\n  .map(p => ({ title: p.Title, weight: parseFrequency(p['Target Frequency']) }))\n  .filter(p => p.weight > 0);\n\nconst POSTS_PER_RUN = 1;\nconst totalWeight = pillars.reduce((s, p) => s + p.weight, 0);\nconst dailyShare = {};\npillars.forEach(p => { dailyShare[p.title] = (p.weight / totalWeight) * POSTS_PER_RUN; });\n\nconst credits = {};\npillars.forEach(p => { credits[p.title] = 0; });\n\nfor (let run = 0; run < bresenhamRun; run++) {\n  pillars.forEach(p => { credits[p.title] += dailyShare[p.title]; });\n  const sorted = pillars.slice().sort((a, b) => credits[b.title] - credits[a.title]);\n  sorted.slice(0, 1).forEach(p => { credits[p.title] -= 1; });\n}\n\npillars.forEach(p => { credits[p.title] += dailyShare[p.title]; });\nconst sorted = pillars.slice().sort((a, b) => credits[b.title] - credits[a.title]);\nconst assignedPillars = sorted.slice(0, 1).map(p => p.title);\n\nconst curatedIndex = assignedPillars.indexOf('Curated & Commentary');\nlet curatedArticle = null;\n\nif (curatedIndex >= 0) {\n  const candidateData = $('Retrieve Curated Post').all();\n  if (candidateData.length > 0 && candidateData[0].json && !candidateData[0].json._empty) {\n    const c = candidateData[0].json;\n    curatedArticle = {\n      articleTitle: c['Article Title'] || '',\n      url: c.URL || '',\n      source: c.Source || '',\n      author: c.Author || '',\n      excerpt: c.Excerpt || '',\n      suggestedAngle: c['Suggested Angle'] || '',\n      queueRowId: c.Id || null,\n    };\n  } else {\n    const substitute = sorted.find((p, i) => i >= 3);\n    if (substitute) {\n      assignedPillars[curatedIndex] = substitute.title;\n    }\n    curatedArticle = { _empty: true };\n  }\n}\n\nconst allPillarsText = pillarsData.map(item => {\n  return 'PILLAR: ' + item.Title +\n    '\\nDESCRIPTION: ' + (item.Description || '') +\n    '\\nEXAMPLE TOPICS: ' + (item['Example Topics'] || '') +\n    '\\nFREQUENCY: ' + (item['Target Frequency'] || '');\n}).join('\\n\\n');\n\nconst assignmentLines = assignedPillars.map((p, idx) =>\n  'Post ' + (idx + 1) + ' MUST be the \\\"' + p + '\\\" pillar.'\n).join('\\n');\n\nconst recentText = recentData.length > 0\n  ? recentData.slice(0, 15).map(item => '- ' + (item.Title || 'untitled') + ' [' + (item.Pillar || '?') + ']').join('\\n')\n  : 'No recent posts yet.';\n\nreturn [{\n  json: {\n    assignedPillars,\n    curatedArticle,\n    curatedQueueEmpty: curatedArticle && curatedArticle._empty,\n    allPillarsText,\n    assignmentLines,\n    recentText,\n    bresenhamRun,\n    totalPostsDeduplicated: totalPosts,\n    pillarCountRaw: rawPillars.length,\n    pillarCountDeduplicated: pillarsData.length,\n    creditState: Object.fromEntries(pillars.map(p => [p.title, credits[p.title].toFixed(3)])),\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "collapse-count",
      "name": "Condense Post Count",
      "type": "n8n-nodes-base.code",
      "position": [
        1584,
        304
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst seenIds = new Set();\nfor (const item of items) {\n  const id = item.json?.Id;\n  if (id) seenIds.add(id);\n}\nreturn [{ json: { totalPosts: seenIds.size } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "check-tomorrow-queue",
      "name": "Validate Tomorrow's Queue",
      "type": "n8n-nodes-base.code",
      "position": [
        464,
        304
      ],
      "parameters": {
        "jsCode": "// Gate: skip generation if tomorrow already has an Approved post scheduled\nconst now = new Date();\nconst tomorrow = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1));\nconst yyyy = tomorrow.getUTCFullYear();\nconst mm = String(tomorrow.getUTCMonth() + 1).padStart(2, '0');\nconst dd = String(tomorrow.getUTCDate()).padStart(2, '0');\nconst tomorrowDate = `${yyyy}-${mm}-${dd}`;\n\n// Build URL directly \u2014 avoids qs double-encoding the space in \"Scheduled Datetime\".\n// Use gte/lte instead of like \u2014 NocoDB does not support like on datetime fields (422).\nconst whereClause = `(Status,eq,Approved)~and(Scheduled%20Datetime,gte,${tomorrowDate}%2000:00:00)~and(Scheduled%20Datetime,lte,${tomorrowDate}%2023:59:59)`;\n\nconst response = await this.helpers.httpRequest({\n  method: 'GET',\n  url: `https://YOUR_NOCODB_URL/api/v1/db/data/noco/YOUR_NOCODB_PROJECT_ID/YOUR_CONTENT_CALENDAR_TABLE_ID?where=${whereClause}&fields=Id,Title&limit=1`,\n  headers: { 'xc-token': 'YOUR_NOCODB_API_TOKEN' },\n  json: true,\n});\n\nconst existing = response?.list || [];\nif (existing.length > 0) {\n  // Tomorrow already has content scheduled \u2014 skip generation silently\n  return [];\n}\n\n// Nothing scheduled for tomorrow \u2014 proceed with generation\nreturn [{ json: { proceed: true, date: tomorrowDate } }];"
      },
      "typeVersion": 2
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": true,
    "executionOrder": "v1"
  },
  "versionId": "531ec568-fd91-47eb-9b52-63fe19d83cc4",
  "connections": {
    "Divide Posts": {
      "main": [
        [
          {
            "node": "Allocate Posting Times",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save to NoCoDB": {
      "main": [
        [
          {
            "node": "Formulate Image Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine All Posts": {
      "main": [
        [
          {
            "node": "Send Telegram Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Condense Post Count": {
      "main": [
        [
          {
            "node": "Retrieve Curated Post",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Cloudinary Link": {
      "main": [
        [
          {
            "node": "Update Image in NoCoDB",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Content Pillars": {
      "main": [
        [
          {
            "node": "Calculate Post Totals",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Gemini Prompt": {
      "main": [
        [
          {
            "node": "Execute Gemini Model",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execute Gemini Model": {
      "main": [
        [
          {
            "node": "Analyze Gemini Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Post Totals": {
      "main": [
        [
          {
            "node": "Condense Post Count",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retrieve Curated Post": {
      "main": [
        [
          {
            "node": "Validate Curated Candidate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Recent Posts": {
      "main": [
        [
          {
            "node": "Get Content Pillars",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Allocate Posting Times": {
      "main": [
        [
          {
            "node": "Save to NoCoDB",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Formulate Image Prompt": {
      "main": [
        [
          {
            "node": "Produce Image with Gemini",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Daily Trigger": {
      "main": [
        [
          {
            "node": "Validate Tomorrow's Queue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Image in NoCoDB": {
      "main": [
        [
          {
            "node": "Combine All Posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze Gemini Response": {
      "main": [
        [
          {
            "node": "Divide Posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Image to Cloudinary": {
      "main": [
        [
          {
            "node": "Get Cloudinary Link",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Produce Image with Gemini": {
      "main": [
        [
          {
            "node": "Send Image to Cloudinary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Tomorrow's Queue": {
      "main": [
        [
          {
            "node": "Get Recent Posts from NoCoDB",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Distribute Content Pillars": {
      "main": [
        [
          {
            "node": "Create Gemini Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Curated Candidate": {
      "main": [
        [
          {
            "node": "Distribute Content Pillars",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Recent Posts from NoCoDB": {
      "main": [
        [
          {
            "node": "Validate Recent Posts",
            "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 runs on a weekday evening schedule to generate tomorrow’s multi-platform social post using Google Gemini, stores the draft in NocoDB, generates a branded image, uploads it to Cloudinary, updates the NocoDB record with the image URL, and notifies a Telegram chat.…

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

This workflow is a complete outbound automation system that discovers local businesses, extracts contact emails, generates personalized cold emails using AI, and runs a multi-step follow-up sequence —

Stop And Error, Google Sheets, HTTP Request +2
AI & RAG

A professional AI equity analysis automation built on n8n that transforms structured financial data and real-time news into disciplined, risk-adjusted price targets and actionable BUY/HOLD/SELL signal

Google Sheets, OpenAI, XML +3
AI & RAG

[](https://drive.google.com/file/d/1Cl0KwgRgcuBPVdGgL-nqAcheyvfVXttD/view) Click on the image to see the Example output in google drive

HTTP Request, Stop And Error, OpenAI +3