Back to n8n Workflows

Post Articles to LinkedIn with Audience Score

Mike Taylor Mike Taylor
Concept Testing

Automatically transform blog articles into high-performing LinkedIn posts using AI insight extraction, Malcolm Gladwell-style storytelling framework, and Rally audience validation. Complete with human approval workflow and performance tracking.

Post Articles to LinkedIn with Audience Score n8n workflow diagram

Click to expand

Overview

This workflow automatically transforms your existing blog content into high-performing LinkedIn posts using AI-powered insight extraction and virtual audience validation. It selects random articles from your Pocketbase CMS, extracts compelling insights using GPT-4o, crafts engaging LinkedIn posts using the proven Bait-Hook-Reward-Payload framework, validates content with AI personas through Rally, and publishes with human approval - all while maintaining a complete audit trail in Google Sheets.

🎯 Pro Tips & Secret Sauce

The magic lies in the multi-layered AI validation system combined with the Bait-Hook-Reward-Payload framework. Rather than just repurposing content, this workflow:

  1. Extracts contrarian insights that would be commercially valuable to your audience using AI analysis
  2. Applies Malcolm Gladwell-style storytelling through the structured BHRP framework to maximize engagement
  3. Pre-validates content with AI personas via Rally's audience simulation before human review
  4. Provides quantified audience scores (e.g., "7 out of 10 personas would engage") to inform publishing decisions
  5. Maintains complete randomization to ensure content variety and prevent repetitive posting patterns

This creates LinkedIn posts that feel authentically human while being systematically optimized for engagement through AI-powered audience psychology.

πŸ“ Step-by-Step Instructions

  1. Schedule Trigger - Automatically runs every 8 hours at minute 27 to maintain consistent posting rhythm
  2. Fetch Article List - Retrieves all available articles from Pocketbase CMS (ID, title, excerpt only for efficiency)
  3. Extract Items Array - Isolates the articles data from the API response structure
  4. Split Articles - Converts the articles array into individual processable items
  5. Random Sort - Shuffles articles randomly to ensure content variety and prevent predictable patterns
  6. Limit Selection - Selects just one article for processing to maintain focused content creation
  7. Get Full Article - Fetches complete article content including body text from Pocketbase
  8. Clean Content - Removes base64 images and cleans HTML formatting for AI processing
  9. Extract Insights - Uses GPT-4 to identify commercially valuable, contrarian insights from the article
  10. Process Payload - Structures the extracted insight for the content framework
  11. Generate LinkedIn Post - Uses GPT-4o with Bait-Hook-Reward-Payload framework to create engaging social content
  12. Extract Response - Pulls the generated content from the AI response
  13. Parse Framework Elements - Separates the bait, hook, reward, and final post components for analysis
  14. Consolidate Data - Combines all elements (title, URL, insights, framework components) into structured format
  15. Log to Sheets - Records everything in Google Sheets for audit trail and performance analysis
  16. Validate with Rally - Sends the post to Rally's AI personas asking "Would you comment on this post?"
  17. Split Responses - Processes individual persona responses from Rally's audience simulation
  18. Calculate Engagement Score - Counts how many AI personas would engage vs. total responses
  19. Format for Approval - Combines the post with audience score for human review
  20. Request Human Approval - Sends post and engagement metrics to Slack for final go/no-go decision
  21. Check Approval Status - Evaluates whether the human reviewer approved publication
  22. Publish or Skip - Either publishes to LinkedIn (if approved) or takes no action (if rejected)

πŸ“‹ Requirements

Required Integrations

  • Pocketbase - Content management system for blog articles
  • OpenAI - GPT-4 and GPT-4o for insight extraction and content generation
  • Google Sheets - Data logging and audit trail
  • Rally API - AI persona audience validation service
  • Slack - Human approval workflow
  • LinkedIn - Social media publishing

Required Credentials

  • OpenAI API key with GPT-4 access
  • Pocketbase instance URL and authentication
  • Google Sheets OAuth2 credentials
  • Rally API Bearer token
  • Slack app with messaging permissions
  • LinkedIn OAuth2 for business page posting

Setup Prerequisites

  • Active Pocketbase instance with articles collection (fields: id, title, excerpt, content, slug)
  • Rally account with configured audience personas
  • Google Sheets document for logging
  • Slack workspace with workflow app installed
  • LinkedIn business page with API access

πŸš€ n8n Workflow Template

{
  "active": true,
  "connections": {
    "Code": {
      "main": [
        [
          {
            "index": 0,
            "node": "Edit Fields2",
            "type": "main"
          }
        ]
      ]
    },
    "Code1": {
      "main": [
        [
          {
            "index": 0,
            "node": "OpenAI",
            "type": "main"
          }
        ]
      ]
    },
    "Code2": {
      "main": [
        [
          {
            "index": 0,
            "node": "Edit Fields4",
            "type": "main"
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "index": 0,
            "node": "Split Out",
            "type": "main"
          }
        ]
      ]
    },
    "Edit Fields1": {
      "main": [
        [
          {
            "index": 0,
            "node": "Code",
            "type": "main"
          }
        ]
      ]
    },
    "Edit Fields2": {
      "main": [
        [
          {
            "index": 0,
            "node": "Google Sheets",
            "type": "main"
          }
        ]
      ]
    },
    "Edit Fields3": {
      "main": [
        [
          {
            "index": 0,
            "node": "OpenAI1",
            "type": "main"
          }
        ]
      ]
    },
    "Edit Fields4": {
      "main": [
        [
          {
            "index": 0,
            "node": "Slack",
            "type": "main"
          }
        ]
      ]
    },
    "Google Sheets": {
      "main": [
        [
          {
            "index": 0,
            "node": "HTTP Request2",
            "type": "main"
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        [
          {
            "index": 0,
            "node": "Code1",
            "type": "main"
          }
        ]
      ]
    },
    "HTTP Request1": {
      "main": [
        [
          {
            "index": 0,
            "node": "Edit Fields",
            "type": "main"
          }
        ]
      ]
    },
    "HTTP Request2": {
      "main": [
        [
          {
            "index": 0,
            "node": "Split Out1",
            "type": "main"
          }
        ]
      ]
    },
    "If": {
      "main": [
        [
          {
            "index": 0,
            "node": "LinkedIn",
            "type": "main"
          }
        ],
        [
          {
            "index": 0,
            "node": "No Operation, do nothing",
            "type": "main"
          }
        ]
      ]
    },
    "Limit": {
      "main": [
        [
          {
            "index": 0,
            "node": "HTTP Request",
            "type": "main"
          }
        ]
      ]
    },
    "OpenAI": {
      "main": [
        [
          {
            "index": 0,
            "node": "Edit Fields3",
            "type": "main"
          }
        ]
      ]
    },
    "OpenAI1": {
      "main": [
        [
          {
            "index": 0,
            "node": "Edit Fields1",
            "type": "main"
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "index": 0,
            "node": "HTTP Request1",
            "type": "main"
          }
        ]
      ]
    },
    "Slack": {
      "main": [
        [
          {
            "index": 0,
            "node": "If",
            "type": "main"
          }
        ]
      ]
    },
    "Sort": {
      "main": [
        [
          {
            "index": 0,
            "node": "Limit",
            "type": "main"
          }
        ]
      ]
    },
    "Split Out": {
      "main": [
        [
          {
            "index": 0,
            "node": "Sort",
            "type": "main"
          }
        ]
      ]
    },
    "Split Out1": {
      "main": [
        [
          {
            "index": 0,
            "node": "Code2",
            "type": "main"
          }
        ]
      ]
    }
  },
  "id": "6wmxSq4UjoCcrUL3",
  "meta": {
    "instanceId": "7921b3cd29c1121b3ec4f2177acf06fe1f1325838297f593db7db4e9563eb98d",
    "templateCredsSetupCompleted": true
  },
  "name": "LinkedIn Posting Articles",
  "nodes": [
    {
      "id": "92253aae-37f6-4783-a685-7677d95d7da9",
      "name": "Schedule Trigger",
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 8,
              "triggerAtMinute": "={{ 27 }}"
            }
          ]
        }
      },
      "position": [
        260,
        460
      ],
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2
    },
    {
      "id": "62ccf963-8e6c-482e-a051-5ad4950571e1",
      "name": "HTTP Request1",
      "parameters": {
        "options": {},
        "url": ""
      },
      "position": [
        580,
        460
      ],
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2
    },
    {
      "id": "cc8e850e-2cf0-4705-964c-cec01714fb20",
      "name": "Edit Fields",
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "83f54e7c-f561-46ea-8d8b-027658688ea8",
              "name": "items",
              "type": "array",
              "value": "={{ $json.items }}"
            }
          ]
        },
        "options": {}
      },
      "position": [
        800,
        460
      ],
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4
    },
    {
      "id": "05240d23-703e-43db-a93d-bcfefff2208d",
      "name": "Sort",
      "parameters": {
        "type": "random"
      },
      "position": [
        580,
        700
      ],
      "type": "n8n-nodes-base.sort",
      "typeVersion": 1
    },
    {
      "id": "b8d0cb31-a4ac-4656-88ef-c17d4eab1b0b",
      "name": "Limit",
      "parameters": {},
      "position": [
        800,
        700
      ],
      "type": "n8n-nodes-base.limit",
      "typeVersion": 1
    },
    {
      "credentials": {
        "openAiApi": {
          "id": "SjQqP3EQbG5JSddr",
          "name": "OpenAi account"
        }
      },
      "id": "e5eba671-24e4-4442-aebc-5d7745803b39",
      "name": "OpenAI",
      "parameters": {
        "jsonOutput": true,
        "messages": {
          "values": [
            {
              "content": "You review blog posts for Ask Rally, a virtual audience simulator for synthetic research with AI personas. Your job is to extract interesting and non-obvious insights based on contrarian opinions derived from experience from individual posts on our company blog. You will receive a post and return the extracted insight. Return JSON with the key \u0027payload\u0027, the value being the extracted insight.",
              "role": "system"
            },
            {
              "content": "=Extract an interesting insight, unique experience, or contrarian opinion that is in this post, that would be commercially valuable or good to know for customers of Ask Rally. \n\n# {{ $json.title }}\n{{ $json.content }}\n"
            }
          ]
        },
        "modelId": {
          "__rl": true,
          "cachedResultName": "GPT-4.1",
          "mode": "list",
          "value": "gpt-4.1"
        },
        "options": {},
        "simplify": false
      },
      "position": [
        1580,
        420
      ],
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1.8
    },
    {
      "id": "e572cd34-0004-4da5-9d59-213f0e5a67b0",
      "name": "Split Out",
      "parameters": {
        "fieldToSplitOut": "items",
        "options": {}
      },
      "position": [
        1020,
        460
      ],
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1
    },
    {
      "credentials": {
        "openAiApi": {
          "id": "SjQqP3EQbG5JSddr",
          "name": "OpenAi account"
        }
      },
      "id": "f16c0d2b-d457-4672-8d49-e2138d2eb4d4",
      "name": "OpenAI1",
      "parameters": {
        "messages": {
          "values": [
            {
              "content": "Write a social media post for LinkedIn, in the style of Malcom Gladwell, but with real meaning and substance. Use the Bait-Hook-Reward-Payload framework:\n\n- **Payload:** An interesting insight, unique experience, or contrarian opinion that you have, that would be commercially valuable to you for your audience to know.\n- **Bait**: Grab the attention of the person browsing social media. This could be a contrarian statement, appeal to identity, celebrity name, or anything else that stops them scrolling.\n- **Hook**: Keep their attention now you have it. Show this post is relevant to them, by alerting them to an issue, using familiar words, or providing social proof to keep them reading.\n- **Reward**: Compensate them for their attention to elicit reciprocation. Is there any useful information, a surprising statistic, or an interesting anecdote to reveal? Or a threat to make?\nPOST: the final LinkedIn post incorporating these elements.\n\nYOU MUST FOLLOW THIS FORMAT WHEN OUTPUTTING YOUR WRITING.\n\nInstructions:\nYou will be provided with a Payload. First decide on the Bait, Hook, and Reward, before writing a social post for LinkedIn that sounds authentically human and extremely detailed, without being salesy.\nTo maximize engagement, the content should be surprising, interesting, or practically useful, or induce high-arousal emotions such as anxiety, anger, awe, or surprise. Use actual statistics or quotes from the article where possible. The article was written by me or someone on my team, so refer to we as if it\u0027s something we did at Ask Rally. Don\u0027t mention the name of the company just assume our audience already knows who we are and what we do as they follow us.\nFollow best practices for posting engaging content on LinkedIn, but do not use emoji or hashtags. Follow the structure of the examples below:\n\n### LinkedIn Post:\n- **Payload**: Most people don\u2019t know that some species of whale are going extinct, so we\u2019re running the London marathon to aid the \"Save the Whales\" foundation.\n- **Bait**: People who care about whales or animals will pay attention when whales are mentioned.\n- **Hook**: If we refer to the whales as \u201cin danger\u201d people who care will want to know more.\n- **Reward**: Some species of whales are going extinct, and people would benefit from knowing.\nPOST: The whales are in danger of extinction. Many people aren\u0027t aware, which is why it\u0027s so important to bring awareness to this cause. Together with a few colleagues, we\u0027ll be running the London marathon next week in aid of the \"Save the Whales\" foundation, and any support would be appreciated.\n\n### Facebook Post:\n- **Payload**: Our hotel specializes in hiring smart people who need a temporary job to fund their interests.\n- **Bait**: Use the trope that smart people are so focused they fail to look after themselves.\n- **Hook**: Particle Physics is a subject that is universally associated with smart people.\n- **Reward**: Fantasizing about being somewhere remote where you can earn money while focusing on your work.\nPOST: Meet Jon. He has been working the night shift at our hotel for over a month already, and he says the best part is the peace and quiet. Particle physics, which is the topic of Jon\u2019s Masters Thesis, requires concentration. That\u2019s fine by us. So long as our guests can count on Jon, he can count all the particles he wants (or whatever it is that particle physicists do).\n\n### Instagram Post:\n- **Payload**: I believe there is no such thing as a bad experience traveling. Every disaster has a silver lining. For example when it rained in Kauai 2 years ago I ended up meeting my bestie @moniqueontour.\n- **Bait**: Mentioning a \u2018wasted trip\u2019 will get the attention of people who like to travel, and for whom wasting a trip would be upsetting.\n- **Hook**: Talk about a rainy day in a hot location like Kauai, which most people would complain about because they\u2019re wasting time inside.\n- **Reward**: An anecdote that reveals that good things can happen when you least expect them.\nPOST: *There\u2019s no such thing as a wasted trip! On this rainy day in Kauai 2 years ago I met my bestie @moniqueontour and we\u2019ve been on 4 amazing adventures together since.*\n\n### Twitter / X Post:\n- **Payload**: You don\u2019t have to do networking if you don\u2019t like it: I built a blog for my agency business that drove 60% of our leads.\n- **Bait**: Most self-help advice focuses on fixing weaknesses, but instead let\u2019s focus on how to reframe weaknesses into strengths.\n- **Hook**: We need to make it clear this post is for business owners, and that you are speaking from experience, establishing connection and credibility.\n- **Reward**: The fact that 60% of new business came from the blog is a statistic they can take away and use as evidence to support their own strategy, or to share with their wider network.\n\nPOST: Don\u2019t try to mold yourself into the person you think your business needs you to be, build your business around who you actually are. When I started my agency business, I hated networking. I\u2019m introverted by nature  so I wrote blog posts instead \u2013 eventually 60% of my agency\u2019s new business came from our blog.",
              "role": "system"
            },
            {
              "content": "=# {{ $(\u0027Code1\u0027).item.json.title }}\n\n{{ $(\u0027Code1\u0027).item.json.excerpt }}\n\n{{ $(\u0027Code1\u0027).item.json.content }}\n\n---\nMake sure you output the Bait, Hook, Reward, and POST. Do not be overly salesy, cheesy, or mention the name of our company. Just get right to the research and providing some insight of real substance.\n### LinkedIn Post:\n- **Payload**:  {{ $json.choices[0].message.content.payload }}\n\n"
            }
          ]
        },
        "modelId": {
          "__rl": true,
          "cachedResultName": "GPT-4O",
          "mode": "list",
          "value": "gpt-4o"
        },
        "options": {}
      },
      "position": [
        1400,
        720
      ],
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1.8
    },
    {
      "id": "b9ba6b6c-f28a-49c9-8eac-e07e52f9a750",
      "name": "Code",
      "parameters": {
        "jsCode": "for (const item of $input.all()) {\n  // Get the text and ensure it\u0027s a string\n  let text = item.json.message.content;\n  \n  // Convert to string if it\u0027s not already\n  if (typeof text !== \u0027string\u0027) {\n    text = String(text);\n  }\n  \n  // Initialize sections\n  let bait = \u0027\u0027;\n  let hook = \u0027\u0027;\n  let reward = \u0027\u0027;\n  let post = \u0027\u0027;\n  \n  if (text \u0026\u0026 text.length \u003e 0) {\n    // Simple approach - split by section markers and find sections\n    if (text.includes(\u0027**Bait**:\u0027)) {\n      const baitStart = text.indexOf(\u0027**Bait**:\u0027) + 9;\n      const baitEnd = text.indexOf(\u0027**Hook**:\u0027);\n      bait = text.substring(baitStart, baitEnd \u003e -1 ? baitEnd : text.length).trim();\n    }\n    \n    if (text.includes(\u0027**Hook**:\u0027)) {\n      const hookStart = text.indexOf(\u0027**Hook**:\u0027) + 9;\n      const hookEnd = text.indexOf(\u0027**Reward**:\u0027);\n      hook = text.substring(hookStart, hookEnd \u003e -1 ? hookEnd : text.length).trim();\n    }\n    \n    if (text.includes(\u0027**Reward**:\u0027)) {\n      const rewardStart = text.indexOf(\u0027**Reward**:\u0027) + 11;\n      const rewardEnd = text.indexOf(\u0027POST:\u0027);\n      reward = text.substring(rewardStart, rewardEnd \u003e -1 ? rewardEnd : text.length).trim();\n    }\n    \n    if (text.includes(\u0027POST:\u0027)) {\n      const postStart = text.indexOf(\u0027POST:\u0027) + 5;\n      post = text.substring(postStart).trim();\n    }\n  }\n  \n  // Clean up leading asterisks and newlines\n  function cleanSection(section) {\n    return section\n      .replace(/^\\*+\\s*/, \u0027\u0027)      // Remove leading asterisks and spaces\n      .replace(/^\\n+/, \u0027\u0027)         // Remove leading newlines\n      .replace(/^\\\\n+/, \u0027\u0027)        // Remove leading escaped newlines\n      .trim();                     // Final trim\n  }\n  \n  // Update the item with cleaned sections\n  item.json.bait = cleanSection(bait);\n  item.json.hook = cleanSection(hook);\n  item.json.reward = cleanSection(reward);\n  item.json.post = cleanSection(post);\n}\n\nreturn $input.all();"
      },
      "position": [
        1980,
        720
      ],
      "type": "n8n-nodes-base.code",
      "typeVersion": 2
    },
    {
      "id": "3b0ff419-6f4a-485d-854f-e9ea22cf7cef",
      "name": "HTTP Request",
      "parameters": {
        "options": {},
        "url": ""
      },
      "position": [
        1020,
        700
      ],
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2
    },
    {
      "id": "965a225e-08a5-486e-b6fd-ee555e7d3dd8",
      "name": "Code1",
      "parameters": {
        "jsCode": "// Get the input items\nconst items = $input.all();\n\n// Process each item\nconst processedItems = items.map(item =\u003e {\n  let content = item.json.content || \u0027\u0027;\n  \n  // Remove base64 images (data:image URLs)\n  // This regex matches: \u003cimg src=\"data:image/...;base64,...\" ... /\u003e\n  content = content.replace(/\u003cimg[^\u003e]*src=\"data:image\\/[^\"]*\"[^\u003e]*\\/?\u003e/gi, \u0027\u0027);\n  \n  // Alternative: Replace with placeholder text\n  // content = content.replace(/\u003cimg[^\u003e]*src=\"data:image\\/[^\"]*\"[^\u003e]*\\/?\u003e/gi, \u0027[Image removed]\u0027);\n  \n  // Clean up any empty paragraphs that might be left\n  content = content.replace(/\u003cp\u003e\\s*\u003c\\/p\u003e/gi, \u0027\u0027);\n  \n  // Clean up multiple consecutive line breaks\n  content = content.replace(/(\\r?\\n){3,}/g, \u0027\\n\\n\u0027);\n  \n  return {\n    ...item,\n    json: {\n      ...item.json,\n      content: content.trim()\n    }\n  };\n});\n\nreturn processedItems;"
      },
      "position": [
        1380,
        420
      ],
      "type": "n8n-nodes-base.code",
      "typeVersion": 2
    },
    {
      "id": "0d281b03-116e-4da8-80b5-7dbd3f1cd6d4",
      "name": "Edit Fields1",
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "f201926c-d151-445e-ac80-e05548525c90",
              "name": "message.content",
              "type": "string",
              "value": "={{ $json.message.content }}"
            }
          ]
        },
        "options": {}
      },
      "position": [
        1760,
        720
      ],
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4
    },
    {
      "id": "e84e6c05-88f4-4807-ae39-4743b1a1ad8e",
      "name": "Edit Fields2",
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "f53e4a81-d642-4d06-af44-701c986f56b7",
              "name": "title",
              "type": "string",
              "value": "={{ $(\u0027HTTP Request\u0027).item.json.title }}"
            },
            {
              "id": "c18a5ff8-2d6d-4437-abdf-c203c2a8dc90",
              "name": "url",
              "type": "string",
              "value": ""
            },
            {
              "id": "8412bccf-5edd-4c3f-99a0-3ae65718d268",
              "name": "excerpt",
              "type": "string",
              "value": "={{ $(\u0027HTTP Request\u0027).item.json.excerpt }}"
            },
            {
              "id": "dd7303a4-5e41-48f5-8235-fafcf87ba443",
              "name": "payload",
              "type": "string",
              "value": "={{ $(\u0027Edit Fields3\u0027).item.json.choices[0].message.content.payload }}"
            },
            {
              "id": "6f827f32-e955-4d7e-8e67-d5e192227e61",
              "name": "bait",
              "type": "string",
              "value": "={{ $json.bait }}"
            },
            {
              "id": "2c0f18a2-5e63-408e-9239-83d7436bcf0d",
              "name": "hook",
              "type": "string",
              "value": "={{ $json.hook }}"
            },
            {
              "id": "065c8dad-98f1-46cf-97f6-8a7c31ce8370",
              "name": "reward",
              "type": "string",
              "value": "={{ $json.reward }}"
            },
            {
              "id": "d2433381-dcf3-482e-8fee-5a88084e9aed",
              "name": "post",
              "type": "string",
              "value": "={{ $json.post }}"
            },
            {
              "id": "b006bf6d-1eac-4cae-b6e8-1a80ae02a932",
              "name": "debug.messages",
              "type": "string",
              "value": "={{ $(\u0027OpenAI1\u0027).item.json.message.content }}"
            }
          ]
        },
        "options": {}
      },
      "position": [
        540,
        1140
      ],
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4
    },
    {
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "SBVzbvbBhOfg6GpM",
          "name": "Google Sheets account"
        }
      },
      "id": "be3336a9-9c13-4200-b031-6ac21fbb1416",
      "name": "Google Sheets",
      "parameters": {
        "columns": {
          "attemptToConvertTypes": false,
          "convertFieldsToString": false,
          "mappingMode": "autoMapInputData",
          "matchingColumns": [],
          "schema": [
            {
              "canBeUsedToMatch": true,
              "defaultMatch": false,
              "display": true,
              "displayName": "title",
              "id": "title",
              "removed": false,
              "required": false,
              "type": "string"
            },
            {
              "canBeUsedToMatch": true,
              "defaultMatch": false,
              "display": true,
              "displayName": "url",
              "id": "url",
              "removed": false,
              "required": false,
              "type": "string"
            },
            {
              "canBeUsedToMatch": true,
              "defaultMatch": false,
              "display": true,
              "displayName": "excerpt",
              "id": "excerpt",
              "removed": false,
              "required": false,
              "type": "string"
            },
            {
              "canBeUsedToMatch": true,
              "defaultMatch": false,
              "display": true,
              "displayName": "payload",
              "id": "payload",
              "removed": false,
              "required": false,
              "type": "string"
            },
            {
              "canBeUsedToMatch": true,
              "defaultMatch": false,
              "display": true,
              "displayName": "bait",
              "id": "bait",
              "removed": false,
              "required": false,
              "type": "string"
            },
            {
              "canBeUsedToMatch": true,
              "defaultMatch": false,
              "display": true,
              "displayName": "hook",
              "id": "hook",
              "removed": false,
              "required": false,
              "type": "string"
            },
            {
              "canBeUsedToMatch": true,
              "defaultMatch": false,
              "display": true,
              "displayName": "reward",
              "id": "reward",
              "removed": false,
              "required": false,
              "type": "string"
            },
            {
              "canBeUsedToMatch": true,
              "defaultMatch": false,
              "display": true,
              "displayName": "post",
              "id": "post",
              "removed": false,
              "required": false,
              "type": "string"
            },
            {
              "canBeUsedToMatch": true,
              "defaultMatch": false,
              "display": true,
              "displayName": "debug",
              "id": "debug",
              "removed": false,
              "required": false,
              "type": "string"
            }
          ],
          "value": {}
        },
        "documentId": {
          "__rl": true,
          "cachedResultName": "",
          "cachedResultUrl": "",
          "mode": "list",
          "value": "11f20zmD6Icbms3rvzJK3yJLqmTdTY4oDdiXIjQsLe84"
        },
        "operation": "append",
        "options": {},
        "sheetName": {
          "__rl": true,
          "cachedResultName": "Sheet1",
          "cachedResultUrl": "",
          "mode": "list",
          "value": "gid=0"
        }
      },
      "position": [
        760,
        1140
      ],
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.6
    },
    {
      "id": "c64c42dc-83b2-4e08-8680-1f313ac725ea",
      "name": "Edit Fields3",
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "8e083b84-31bf-4550-ab03-2cfd14c03c63",
              "name": "choices[0].message.content.payload",
              "type": "string",
              "value": "={{ $json.choices[0].message.content.payload }}"
            }
          ]
        },
        "options": {}
      },
      "position": [
        1940,
        420
      ],
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4
    },
    {
      "credentials": {
        "slackApi": {
          "id": "EQYFzV9nZU6fyuKc",
          "name": "Slack account"
        }
      },
      "id": "ea6bf55e-4446-46b0-ad13-b2e9ec62b25b",
      "name": "Slack",
      "parameters": {
        "approvalOptions": {
          "values": {
            "approvalType": "double"
          }
        },
        "message": "={{ $json.post }}\n\n{{ $json.countA }}",
        "operation": "sendAndWait",
        "options": {
          "limitWaitTime": {
            "values": {
              "resumeUnit": "days"
            }
          }
        },
        "user": {
          "__rl": true,
          "cachedResultName": "mjt145",
          "mode": "list",
          "value": "U01773MS3FF"
        }
      },
      "position": [
        1600,
        1140
      ],
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.3,
      "webhookId": "bdd451cc-3892-4b31-b0ee-af37811268f4"
    },
    {
      "credentials": {
        "linkedInOAuth2Api": {
          "id": "THdkZz5Qe8rG3g5L",
          "name": "LinkedIn account"
        }
      },
      "id": "f447b858-78aa-4c7c-90a5-f5905ca6d0e4",
      "name": "LinkedIn",
      "parameters": {
        "additionalFields": {},
        "person": "YN0_AYTgLo",
        "text": "={{ $(\u0027Google Sheets\u0027).last().json.post }}"
      },
      "position": [
        2040,
        1040
      ],
      "type": "n8n-nodes-base.linkedIn",
      "typeVersion": 1
    },
    {
      "id": "20daed25-9f77-4273-81f2-65c201e49bc3",
      "name": "If",
      "parameters": {
        "conditions": {
          "combinator": "and",
          "conditions": [
            {
              "id": "737503a5-26ab-4701-830b-f5bb45274112",
              "leftValue": "={{ $json.data.approved }}",
              "operator": {
                "operation": "true",
                "singleValue": true,
                "type": "boolean"
              },
              "rightValue": "true"
            }
          ],
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          }
        },
        "options": {}
      },
      "position": [
        1800,
        1140
      ],
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2
    },
    {
      "id": "e081d12e-b286-4352-bc08-60f69b0fb5fe",
      "name": "No Operation, do nothing",
      "parameters": {},
      "position": [
        2040,
        1240
      ],
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1
    },
    {
      "id": "74fe86d6-05c7-41a9-8308-e96422dc2481",
      "name": "Sticky Note",
      "parameters": {
        "content": "## Select a Blog Post from Pocketbase at random",
        "height": 480,
        "width": 740
      },
      "position": [
        480,
        380
      ],
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1
    },
    {
      "id": "7059142d-8916-4ae0-b59f-f9928f0d6cd3",
      "name": "Sticky Note1",
      "parameters": {
        "color": 6,
        "content": "## Extract an insight",
        "height": 220,
        "width": 880
      },
      "position": [
        1300,
        360
      ],
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1
    },
    {
      "id": "7dcb44da-8b70-4d95-a2e4-a2fbba9e97ff",
      "name": "Sticky Note2",
      "parameters": {
        "color": 3,
        "content": "## Generate Bait, Hook, Reward, Post",
        "height": 300,
        "width": 880
      },
      "position": [
        1300,
        660
      ],
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1
    },
    {
      "id": "5b3fb6e6-b361-4251-aa24-80fc3d16f26b",
      "name": "Sticky Note3",
      "parameters": {
        "color": 4,
        "content": "## Save to GSheets",
        "height": 380,
        "width": 560
      },
      "position": [
        440,
        1000
      ],
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1
    },
    {
      "credentials": {
        "httpBearerAuth": {
          "id": "4WNRwxtlBVvuz6qV",
          "name": "Bearer Auth account"
        }
      },
      "id": "bc768d33-f863-4f2d-be39-20dee5d57e35",
      "name": "HTTP Request2",
      "parameters": {
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "smart",
              "value": "false"
            },
            {
              "name": "provider",
              "value": "openai"
            },
            {
              "name": "query",
              "value": "=Be critical \u2013 Would you comment on this post?\n\n{{ $json.post }}\n\n--- \n\na) yes\nb) no "
            },
            {
              "name": "audience_id",
              "value": "rb842b547c27640"
            },
            {
              "name": "voting_mode",
              "value": "true"
            }
          ]
        },
        "genericAuthType": "httpBearerAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "accept",
              "value": "application/json"
            }
          ]
        },
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "url": ""
      },
      "position": [
        600,
        1560
      ],
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2
    },
    {
      "id": "725187e0-32bf-407a-b172-f05c3714cff7",
      "name": "Sticky Note4",
      "parameters": {
        "color": 5,
        "content": "## Ask Rally",
        "height": 340,
        "width": 820
      },
      "position": [
        440,
        1440
      ],
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1
    },
    {
      "id": "5717a9d5-cce2-45e3-8807-605f8ef661fe",
      "name": "Split Out1",
      "parameters": {
        "fieldToSplitOut": "responses",
        "options": {}
      },
      "position": [
        820,
        1560
      ],
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1
    },
    {
      "id": "5f2e0e96-4b10-4a5f-9190-ae5acd119419",
      "name": "Code2",
      "parameters": {
        "jsCode": "// Count number of \u0027a\u0027 options out of total\nlet countA = 0;\nlet total = 0;\n\nfor (const item of $input.all()) {\n    try {\n        // Parse the response JSON\n        const responseData = JSON.parse(item.json.response);\n        \n        // Get the option value\n        const option = responseData.option;\n        \n        if (option) {\n            total++;\n            if (option === \"a\") {\n                countA++;\n            }\n        }\n    } catch (error) {\n        // Handle cases where response is not valid JSON\n        console.log(`Error parsing response for item ${item.json.persona_id}:`, error.message);\n    }\n}\n\n// Calculate percentage\nconst percentage = total \u003e 0 ? (countA / total * 100).toFixed(1) : 0;\n\n// Log the results\nconsole.log(`Count of \u0027a\u0027: ${countA}`);\nconsole.log(`Total responses: ${total}`);\nconsole.log(`Percentage of \u0027a\u0027: ${percentage}%`);\n\n// Return the results\nreturn {\n    countA: countA,\n    total: total,\n    percentage: parseFloat(percentage)\n};"
      },
      "position": [
        1020,
        1560
      ],
      "type": "n8n-nodes-base.code",
      "typeVersion": 2
    },
    {
      "id": "f4694330-1409-4f68-a10c-9d93775a53d8",
      "name": "Sticky Note5",
      "parameters": {
        "color": 7,
        "content": "## Get approval and publish",
        "height": 420,
        "width": 880
      },
      "position": [
        1300,
        1000
      ],
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1
    },
    {
      "id": "29563661-db92-4b2e-8ad8-27edd941b329",
      "name": "Edit Fields4",
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "f7890ebc-583f-4de5-83e7-4357c9a3edc1",
              "name": "countA",
              "type": "string",
              "value": "=Audience Score: {{ $json.countA }} / {{ $json.total }}"
            },
            {
              "id": "17040415-583c-4d70-9d2e-bb8a69b61eca",
              "name": "post",
              "type": "string",
              "value": "={{ $(\u0027Edit Fields2\u0027).last().json.post }}"
            }
          ]
        },
        "options": {}
      },
      "position": [
        1380,
        1140
      ],
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4
    }
  ],
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "tags": [],
  "versionId": "4f041156-5115-491a-a95a-234efe75d626"
}

About the Author

Mike Taylor

Mike Taylor

Mike Taylor is the CEO & Co-Founder of Rally. He previously co-founded a 50-person growth marketing agency called Ladder, created marketing & AI courses on LinkedIn, Vexpower, and Udemy taken by over 450,000 people, and published a book with O’Reilly on prompt engineering.