{
  "name": "SkoolFeed → Social Auto-Post",
  "meta": {
    "instanceId": "skoolfeed-social-v1"
  },
  "nodes": [
    {
      "parameters": {
        "content": "## SkoolFeed Social Auto-Post\n\n**Product:** SkoolFeed\n**URL:** https://skoolfeed.com\n\n**What this does:**\nAutomatically repurposes Skool community posts into platform-optimized social media content with AI-generated images.\n\n**Flow:**\nRSS → AI Content → AI Image → Post to Twitter, LinkedIn, Facebook → Log\n\n**Setup:**\n1. Replace YOUR_COMMUNITY_ID with your SkoolFeed RSS ID\n2. Configure all OAuth credentials\n3. Create Google Sheet for logging\n4. Activate workflow\n\n**Cost:** ~$0.06 per post (GPT-4o + DALL-E 3)",
        "height": 380,
        "width": 320,
        "color": 4
      },
      "id": "sticky-main",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [60, 40]
    },
    {
      "parameters": {
        "content": "## Credentials Needed\n\n- OpenAI API\n- Twitter OAuth2\n- LinkedIn OAuth2\n- Facebook Graph API\n- Google Sheets OAuth",
        "height": 180,
        "width": 200,
        "color": 6
      },
      "id": "sticky-creds",
      "name": "Sticky Note Credentials",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [60, 440]
    },
    {
      "parameters": {
        "content": "## Social Posting\n\nPosts to all 3 platforms simultaneously with the AI-generated image attached.\n\nEach node has error handling enabled - if one platform fails, others continue.",
        "height": 140,
        "width": 280,
        "color": 3
      },
      "id": "sticky-social",
      "name": "Sticky Note Social",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [2600, -50]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 4
            }
          ]
        }
      },
      "id": "schedule-trigger",
      "name": "Every 4 Hours",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.1,
      "position": [400, 300],
      "notes": "Runs automatically every 4 hours. Adjust interval as needed for your posting frequency."
    },
    {
      "parameters": {
        "url": "https://skoolfeed.com/feed/YOUR_COMMUNITY_ID",
        "options": {}
      },
      "id": "rss-read",
      "name": "Read SkoolFeed RSS",
      "type": "n8n-nodes-base.rssFeedRead",
      "typeVersion": 1,
      "position": [600, 300],
      "notes": "SkoolFeed RSS endpoint. Replace YOUR_COMMUNITY_ID with your actual community ID from skoolfeed.com"
    },
    {
      "parameters": {
        "jsCode": "// Deduplication: Skip already processed items\n// Prevents reposting the same content multiple times\nconst processedIds = $getWorkflowStaticData('global').processedIds || [];\nconst newItems = [];\n\nfor (const item of $input.all()) {\n  const itemId = item.json.guid || item.json.link;\n  \n  if (!processedIds.includes(itemId)) {\n    newItems.push(item);\n    processedIds.push(itemId);\n  }\n}\n\n// Keep only last 500 IDs to prevent memory bloat\nif (processedIds.length > 500) {\n  processedIds.splice(0, processedIds.length - 500);\n}\n\n$getWorkflowStaticData('global').processedIds = processedIds;\n\nif (newItems.length === 0) {\n  return [];\n}\n\nreturn newItems;"
      },
      "id": "dedupe",
      "name": "Deduplicate Posts",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [800, 300],
      "notes": "Tracks processed post IDs in workflow static data. Prevents duplicate posts if the same RSS item appears multiple times."
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "has-items",
              "leftValue": "={{ $json.title }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "check-new-posts",
      "name": "Has New Posts?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [1000, 300],
      "notes": "Only continues if there are new posts to process. Stops workflow early if RSS returned no new items."
    },
    {
      "parameters": {
        "batchSize": 1,
        "options": {
          "reset": false
        }
      },
      "id": "batch-process",
      "name": "Process One at a Time",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [1200, 250],
      "notes": "Processes posts one at a time to avoid API rate limits and ensure quality."
    },
    {
      "parameters": {
        "amount": 2,
        "unit": "seconds"
      },
      "id": "rate-limit",
      "name": "Rate Limit (2s)",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [1400, 250],
      "notes": "2 second delay between posts to respect API rate limits."
    },
    {
      "parameters": {
        "resource": "chat",
        "model": "gpt-4o",
        "messages": {
          "values": [
            {
              "content": "=You are a social media strategist. Transform this Skool community post into platform-optimized content.\n\n## Original Post\n**Title:** {{ $json.title }}\n**Content:** {{ ($json.contentSnippet || $json.content || '').substring(0, 2000) }}\n**Author:** {{ $json.creator || 'Community Member' }}\n**Link:** {{ $json.link }}\n\n## Requirements\n\nCreate content for each platform following these specs:\n\n### Twitter/X (280 chars max)\n- Lead with curiosity hook or bold statement\n- Use 2-3 relevant hashtags\n- End with soft CTA\n- Punchy, conversational\n\n### LinkedIn (1300 chars max)\n- Open with pattern-interrupt hook\n- Share insight or story arc\n- Use short paragraphs and line breaks\n- End with discussion question\n- 3-5 hashtags at bottom\n\n### Facebook (500 chars max)\n- Friendly, community tone\n- Light emoji use (1-2)\n- Relatable framing\n- Engagement-focused CTA\n\n### Image Prompt\nCreate a DALL-E 3 prompt (50-100 words) for a social media graphic:\n- Modern, clean design\n- Bold typography potential\n- Relevant visual metaphor\n- Professional color palette\n\n## Output Format\n\nRespond with valid JSON only:\n{\n  \"twitter\": \"tweet text here\",\n  \"linkedin\": \"linkedin post here\",\n  \"facebook\": \"facebook post here\",\n  \"image_prompt\": \"DALL-E prompt here\",\n  \"primary_hashtags\": [\"tag1\", \"tag2\", \"tag3\"]\n}"
            }
          ]
        },
        "options": {
          "temperature": 0.7,
          "maxTokens": 2000,
          "responseFormat": "json_object"
        }
      },
      "id": "ai-content",
      "name": "AI: Generate Social Content",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1.6,
      "position": [1600, 250],
      "credentials": {
        "openAiApi": {
          "id": "OPENAI_CREDENTIAL_ID",
          "name": "OpenAI API"
        }
      },
      "notes": "GPT-4o generates platform-specific content for Twitter (280 chars), LinkedIn (1300 chars), Facebook (500 chars), plus a DALL-E image prompt. Edit the prompt to customize brand voice."
    },
    {
      "parameters": {
        "jsCode": "// Parse and structure the AI response\nconst rssItem = $('Process One at a Time').first().json;\nlet aiResponse;\n\ntry {\n  aiResponse = JSON.parse($json.message.content);\n} catch (e) {\n  const match = $json.message.content.match(/```json?\\n?([\\s\\S]*?)\\n?```/);\n  if (match) {\n    aiResponse = JSON.parse(match[1]);\n  } else {\n    throw new Error('Failed to parse AI response as JSON');\n  }\n}\n\nreturn {\n  json: {\n    source: {\n      title: rssItem.title,\n      link: rssItem.link,\n      date: rssItem.pubDate || rssItem.isoDate,\n      author: rssItem.creator || 'Unknown'\n    },\n    twitter: aiResponse.twitter,\n    linkedin: aiResponse.linkedin,\n    facebook: aiResponse.facebook,\n    image_prompt: aiResponse.image_prompt,\n    hashtags: aiResponse.primary_hashtags || [],\n    generated_at: new Date().toISOString()\n  }\n};"
      },
      "id": "structure-data",
      "name": "Structure Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [1800, 250],
      "notes": "Parses the JSON response from GPT-4o and structures it for downstream nodes."
    },
    {
      "parameters": {
        "resource": "image",
        "operation": "generate",
        "model": "dall-e-3",
        "prompt": "={{ $json.image_prompt }}. Style: Modern social media graphic, clean composition, vibrant but professional colors, suitable for Twitter/LinkedIn/Facebook. No text in the image.",
        "options": {
          "size": "1024x1024",
          "quality": "standard",
          "style": "vivid"
        }
      },
      "id": "generate-image",
      "name": "AI: Generate Image",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1.6,
      "position": [2000, 250],
      "credentials": {
        "openAiApi": {
          "id": "OPENAI_CREDENTIAL_ID",
          "name": "OpenAI API"
        }
      },
      "onError": "continueRegularOutput",
      "notes": "DALL-E 3 generates a 1024x1024 image based on the AI-generated prompt. Cost: ~$0.04 per image. Continues on error so posts still go out without image if generation fails."
    },
    {
      "parameters": {
        "jsCode": "// Combine content data with image URL\nconst contentData = $('Structure Response').first().json;\nconst imageResult = $json;\n\nlet imageUrl = null;\nif (imageResult.data && imageResult.data[0]) {\n  imageUrl = imageResult.data[0].url;\n} else if (imageResult.url) {\n  imageUrl = imageResult.url;\n}\n\nreturn {\n  json: {\n    ...contentData,\n    image_url: imageUrl\n  }\n};"
      },
      "id": "combine-all",
      "name": "Combine Content + Image",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [2200, 250],
      "notes": "Merges the generated social content with the DALL-E image URL."
    },
    {
      "parameters": {
        "url": "={{ $json.image_url }}",
        "options": {}
      },
      "id": "download-image",
      "name": "Download Image",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [2400, 250],
      "onError": "continueRegularOutput",
      "notes": "Downloads the DALL-E image as binary data so it can be uploaded to social platforms."
    },
    {
      "parameters": {
        "text": "={{ $('Combine Content + Image').first().json.twitter }}",
        "additionalFields": {
          "media": {
            "media": [
              {
                "media_data": "={{ $binary.data }}"
              }
            ]
          }
        }
      },
      "id": "post-twitter",
      "name": "Post to Twitter/X",
      "type": "n8n-nodes-base.twitter",
      "typeVersion": 2,
      "position": [2650, 100],
      "credentials": {
        "twitterOAuth2Api": {
          "id": "TWITTER_CREDENTIAL_ID",
          "name": "Twitter OAuth2"
        }
      },
      "onError": "continueRegularOutput",
      "notes": "Posts to Twitter/X with image. Requires Twitter OAuth2 credentials. Free tier: 50 tweets/day limit."
    },
    {
      "parameters": {
        "person": "={{ $credentials.linkedInOAuth2Api.oauthTokenData.id }}",
        "text": "={{ $('Combine Content + Image').first().json.linkedin }}",
        "shareMediaCategory": "IMAGE",
        "binaryPropertyName": "data",
        "additionalFields": {}
      },
      "id": "post-linkedin",
      "name": "Post to LinkedIn",
      "type": "n8n-nodes-base.linkedIn",
      "typeVersion": 1,
      "position": [2650, 250],
      "credentials": {
        "linkedInOAuth2Api": {
          "id": "LINKEDIN_CREDENTIAL_ID",
          "name": "LinkedIn OAuth2"
        }
      },
      "onError": "continueRegularOutput",
      "notes": "Posts to LinkedIn with image. Requires LinkedIn OAuth2 with 'Share on LinkedIn' product enabled."
    },
    {
      "parameters": {
        "operation": "post",
        "pageId": {
          "__rl": true,
          "value": "YOUR_FACEBOOK_PAGE_ID",
          "mode": "id"
        },
        "message": "={{ $('Combine Content + Image').first().json.facebook }}",
        "additionalFields": {
          "link": "={{ $('Combine Content + Image').first().json.image_url }}"
        }
      },
      "id": "post-facebook",
      "name": "Post to Facebook",
      "type": "n8n-nodes-base.facebookGraphApi",
      "typeVersion": 1,
      "position": [2650, 400],
      "credentials": {
        "facebookGraphApi": {
          "id": "FACEBOOK_CREDENTIAL_ID",
          "name": "Facebook Graph API"
        }
      },
      "onError": "continueRegularOutput",
      "notes": "Posts to Facebook Page. Replace YOUR_FACEBOOK_PAGE_ID with your actual Page ID. Requires Page Access Token with pages_manage_posts permission."
    },
    {
      "parameters": {
        "jsCode": "// Collect results from all social posts\nconst content = $('Combine Content + Image').first().json;\n\nconst twitterResult = $('Post to Twitter/X').first()?.json || { error: 'Not posted' };\nconst linkedinResult = $('Post to LinkedIn').first()?.json || { error: 'Not posted' };\nconst facebookResult = $('Post to Facebook').first()?.json || { error: 'Not posted' };\n\nreturn {\n  json: {\n    source_title: content.source.title,\n    source_link: content.source.link,\n    posted_at: new Date().toISOString(),\n    twitter: {\n      content: content.twitter,\n      post_id: twitterResult.id || twitterResult.data?.id || null,\n      success: !!twitterResult.id || !!twitterResult.data?.id\n    },\n    linkedin: {\n      content: content.linkedin,\n      post_id: linkedinResult.id || null,\n      success: !!linkedinResult.id\n    },\n    facebook: {\n      content: content.facebook,\n      post_id: facebookResult.id || null,\n      success: !!facebookResult.id\n    },\n    image_url: content.image_url\n  }\n};"
      },
      "id": "collect-results",
      "name": "Collect Post Results",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [2900, 250],
      "notes": "Aggregates success/failure status from all social platforms for logging."
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "YOUR_GOOGLE_SHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Post Log",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Posted At": "={{ $json.posted_at }}",
            "Source Title": "={{ $json.source_title }}",
            "Source URL": "={{ $json.source_link }}",
            "Twitter Content": "={{ $json.twitter.content }}",
            "Twitter Success": "={{ $json.twitter.success }}",
            "Twitter Post ID": "={{ $json.twitter.post_id }}",
            "LinkedIn Content": "={{ $json.linkedin.content }}",
            "LinkedIn Success": "={{ $json.linkedin.success }}",
            "Facebook Content": "={{ $json.facebook.content }}",
            "Facebook Success": "={{ $json.facebook.success }}",
            "Image URL": "={{ $json.image_url }}"
          }
        },
        "options": {}
      },
      "id": "log-to-sheets",
      "name": "Log to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.4,
      "position": [3100, 250],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "GOOGLE_SHEETS_CREDENTIAL_ID",
          "name": "Google Sheets OAuth"
        }
      },
      "notes": "Logs all posts to Google Sheets for tracking. Create a sheet named 'Post Log' with columns matching the mapped fields."
    },
    {
      "parameters": {},
      "id": "no-new-posts",
      "name": "No New Posts",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [1200, 450],
      "notes": "Workflow ends here if no new posts found in RSS feed."
    }
  ],
  "connections": {
    "Every 4 Hours": {
      "main": [
        [
          {
            "node": "Read SkoolFeed RSS",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read SkoolFeed RSS": {
      "main": [
        [
          {
            "node": "Deduplicate Posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Deduplicate Posts": {
      "main": [
        [
          {
            "node": "Has New Posts?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has New Posts?": {
      "main": [
        [
          {
            "node": "Process One at a Time",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No New Posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process One at a Time": {
      "main": [
        [
          {
            "node": "Rate Limit (2s)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rate Limit (2s)": {
      "main": [
        [
          {
            "node": "AI: Generate Social Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI: Generate Social Content": {
      "main": [
        [
          {
            "node": "Structure Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structure Response": {
      "main": [
        [
          {
            "node": "AI: Generate Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI: Generate Image": {
      "main": [
        [
          {
            "node": "Combine Content + Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine Content + Image": {
      "main": [
        [
          {
            "node": "Download Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Image": {
      "main": [
        [
          {
            "node": "Post to Twitter/X",
            "type": "main",
            "index": 0
          },
          {
            "node": "Post to LinkedIn",
            "type": "main",
            "index": 0
          },
          {
            "node": "Post to Facebook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Post to Twitter/X": {
      "main": [
        [
          {
            "node": "Collect Post Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Post to LinkedIn": {
      "main": [
        [
          {
            "node": "Collect Post Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Post to Facebook": {
      "main": [
        [
          {
            "node": "Collect Post Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Collect Post Results": {
      "main": [
        [
          {
            "node": "Log to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "saveManualExecutions": true,
    "callerPolicy": "workflowsFromSameOwner"
  },
  "staticData": null,
  "tags": [
    { "name": "Ship12" },
    { "name": "SkoolFeed" },
    { "name": "Social Media" },
    { "name": "Auto-Post" }
  ]
}
