N8N Content Distribution Automation Workflow
N8N Content Distribution Automation Workflow
Purpose: Automate SAM campaign distribution across 8 channels
Source: Information Memorandum → Multi-Channel Posts
Agent: AI-powered content transformation and scheduling
🎯 Workflow Overview
[Information Memorandum]
→ [AI Content Processor]
→ [8 Parallel Distribution Paths]
→ [Platform-Specific APIs]
→ [Performance Tracking]
→ [Dashboard Reporting]
What This Workflow Does:
1. Monitors for new article memorandums
2. Extracts content per channel specifications
3. Transforms content to platform-specific formats
4. Schedules posts at optimal times
5. Tracks performance across all channels
6. Reports results in real-time
📋 N8N Workflow Structure
Main Workflow: SAM Campaign Distributor
workflow_name: "SAM_Campaign_Article_Distributor"
trigger: "File watcher"
nodes: 25
execution_mode: "Sequential with parallel branches"
🔄 Node-by-Node Breakdown
NODE 1: Trigger - File Watcher
node_type: "Webhook" or "Cron + File Check"
purpose: "Detect new article memorandum"
configuration:
watch_folder: "C:\Working With AI\Odoo Projects\custom-modules-v18\ai_automator_docs\docs\articles\"
file_pattern: "ARTICLE_*_MEMORANDUM.md"
trigger_on: "file_created"
output:
- file_path
- file_name
- created_timestamp
NODE 2: Read Article Memorandum
node_type: "Read File"
purpose: "Load full memorandum content"
configuration:
file_path: "{{ $json.file_path }}"
encoding: "utf8"
output:
- full_content (markdown)
- file_metadata
NODE 3: Parse YAML Metadata
node_type: "Code (Python/JavaScript)"
purpose: "Extract metadata and extraction points"
code: |
import yaml
import re
content = input_data['full_content']
# Extract YAML blocks
metadata_match = re.search(r'```yaml\n(.*?)\n```', content, re.DOTALL)
metadata = yaml.safe_load(metadata_match.group(1))
# Extract extraction points
extraction_match = re.search(r'## 🔍 EXTRACTION POINTS\n```yaml\n(.*?)\n```', content, re.DOTALL)
extraction_points = yaml.safe_load(extraction_match.group(1))
output = {
'metadata': metadata,
'extraction_points': extraction_points,
'full_article': content
}
output:
- metadata (dict)
- extraction_points (dict)
- full_article (text)
NODE 4: AI Content Analyzer
node_type: "HTTP Request" (Claude API)
purpose: "Analyze content and prepare transformations"
configuration:
method: "POST"
url: "https://api.anthropic.com/v1/messages"
headers:
x-api-key: "{{ $env.CLAUDE_API_KEY }}"
anthropic-version: "2023-06-01"
body:
model: "claude-3-5-sonnet-20241022"
max_tokens: 4000
messages:
- role: "user"
content: |
You are a content transformation specialist for the SAM AI campaign.
Article Content:
{{ $json.full_article }}
Extraction Points:
{{ $json.extraction_points }}
Task: Validate content quality and identify key transformation elements for 8 channels.
Return JSON with:
{
"quality_score": 1-10,
"primary_hooks": [],
"key_data_points": [],
"emotional_triggers": [],
"shareability_score": 1-10,
"recommendations": []
}
output:
- content_analysis (JSON)
NODE 5: Split into 8 Channels
node_type: "Split In Batches" or "Function"
purpose: "Create parallel processing paths"
configuration:
channels:
- linkedin
- twitter
- medium
- devto
- reddit
- hackernews
- instagram
- email
output: [Triggers 8 parallel sub-workflows]
📱 CHANNEL-SPECIFIC SUB-WORKFLOWS
SUB-WORKFLOW 1: LinkedIn Distributor
nodes:
- LinkedIn Content Transformer (AI)
- Character Count Validator
- Hashtag Generator
- Image Attacher
- LinkedIn API Post
- Tracking Logger
Node: LinkedIn Content Transformer
node_type: "HTTP Request" (Claude API)
purpose: "Transform to LinkedIn format"
prompt: |
Transform this article for LinkedIn (professional network).
Source Article:
{{ $node["Parse YAML Metadata"].json.full_article }}
Extraction Points:
{{ $node["Parse YAML Metadata"].json.extraction_points }}
Requirements:
- Length: 150-200 words
- Tone: Professional + authority
- Structure: Hook → Value → Question/CTA
- Include 1 data point
- End with engagement question
- Use 3-5 hashtags
- Optimal for 8 AM posting
Return only the LinkedIn post text, ready to publish.
output:
- linkedin_post (text, 150-200 words)
- hashtags (array)
Node: LinkedIn API Post
node_type: "HTTP Request"
purpose: "Post to LinkedIn"
configuration:
method: "POST"
url: "https://api.linkedin.com/v2/ugcPosts"
authentication: "OAuth2"
headers:
Authorization: "Bearer {{ $env.LINKEDIN_ACCESS_TOKEN }}"
body:
author: "urn:li:person:{{ $env.LINKEDIN_USER_ID }}"
lifecycleState: "PUBLISHED"
specificContent:
com.linkedin.ugc.ShareContent:
shareCommentary:
text: "{{ $json.linkedin_post }}"
shareMediaCategory: "NONE"
visibility:
com.linkedin.ugc.MemberNetworkVisibility: "PUBLIC"
output:
- post_id
- post_url
- timestamp
SUB-WORKFLOW 2: Twitter Thread Distributor
nodes:
- Thread Generator (AI)
- Tweet Validator (280 chars each)
- Thread Compiler
- Twitter API - Tweet 1
- Twitter API - Tweet 2-15 (loop)
- Thread URL Capture
- Tracking Logger
Node: Thread Generator
node_type: "HTTP Request" (Claude API)
prompt: |
Transform this article into a Twitter thread.
Source: {{ $node["Parse YAML Metadata"].json.full_article }}
Requirements:
- 15 tweets total
- Tweet 1: Maximum hook (controversial/surprising)
- Tweets 2-13: One key point per tweet
- Tweet 14: Summary
- Tweet 15: CTA with link
- Each tweet: Max 280 characters
- Use (1/15), (2/15) format
- Include 2-3 hashtags in tweet 1 and 15
- Numbered thread structure
Return JSON array of 15 tweets.
output:
- thread_tweets (array of 15 strings)
Node: Twitter API Loop
node_type: "Loop" with HTTP Request
purpose: "Post tweets sequentially with reply chain"
configuration:
for_each: "{{ $json.thread_tweets }}"
delay_between: "180000" # 3 minutes
tweet_request:
method: "POST"
url: "https://api.twitter.com/2/tweets"
headers:
Authorization: "Bearer {{ $env.TWITTER_BEARER_TOKEN }}"
body:
text: "{{ $item }}"
reply:
in_reply_to_tweet_id: "{{ $previousTweetId }}" # Chain tweets
output:
- thread_url (first tweet URL)
- all_tweet_ids (array)
SUB-WORKFLOW 3: Medium Article Publisher
nodes:
- Article Formatter (markdown to Medium)
- Image Uploader (hero + embedded)
- Pull Quote Extractor
- Medium API Post
- Canonical URL Setter
- Tracking Logger
Node: Medium API Post
node_type: "HTTP Request"
configuration:
method: "POST"
url: "https://api.medium.com/v1/users/{{ $env.MEDIUM_USER_ID }}/posts"
headers:
Authorization: "Bearer {{ $env.MEDIUM_ACCESS_TOKEN }}"
body:
title: "{{ $json.metadata.seo.seo_title }}"
contentFormat: "markdown"
content: "{{ $node['Article Formatter'].json.formatted_article }}"
tags: ["AI", "Productivity", "Technology", "Innovation", "SAM"]
publishStatus: "public"
canonicalUrl: "{{ $json.canonical_url }}"
output:
- post_url
- post_id
SUB-WORKFLOW 4: Dev.to Publisher
nodes:
- Technical Format Converter
- Code Block Highlighter
- Dev.to Frontmatter Generator
- Dev.to API Post
- Tag Validator
- Tracking Logger
Node: Dev.to API Post
node_type: "HTTP Request"
configuration:
method: "POST"
url: "https://dev.to/api/articles"
headers:
api-key: "{{ $env.DEVTO_API_KEY }}"
body:
article:
title: "{{ $json.metadata.seo.seo_title }}"
published: true
body_markdown: "{{ $node['Technical Format Converter'].json.devto_markdown }}"
tags: ["ai", "productivity", "javascript", "python"]
series: "SAM AI Campaign"
canonical_url: "{{ $json.canonical_url }}"
output:
- article_url
- article_id
SUB-WORKFLOW 5: Reddit Multi-Subreddit Poster
nodes:
- Reddit Format Converter
- Subreddit Selector (3 variants)
- Title Optimizer
- Reddit API - r/programming
- Reddit API - r/AI
- Reddit API - r/SaaS
- Comment Responder (monitors)
- Tracking Logger
Node: Reddit API Post
node_type: "HTTP Request" (execute 3 times for 3 subreddits)
configuration:
method: "POST"
url: "https://oauth.reddit.com/api/submit"
headers:
Authorization: "Bearer {{ $env.REDDIT_ACCESS_TOKEN }}"
body:
sr: "{{ $json.subreddit }}" # programming, AI, or SaaS
kind: "self"
title: "{{ $json.reddit_title }}"
text: "{{ $json.reddit_body }}"
sendreplies: true
output:
- post_url
- post_id
- subreddit
SUB-WORKFLOW 6: Hacker News Poster
nodes:
- HN Title Optimizer (factual, 80 chars)
- HN First Comment Generator
- HN API Submit
- Comment Poster
- Engagement Monitor
- Tracking Logger
Node: HN API Submit
node_type: "HTTP Request"
configuration:
method: "POST"
url: "https://news.ycombinator.com/submit"
form_data:
title: "{{ $json.hn_title }}"
url: "{{ $json.article_url }}"
# Or for text posts:
text: "{{ $json.hn_text }}"
# Note: HN doesn't have official API, may need Algolia HN API
output:
- hn_post_url
- hn_post_id
SUB-WORKFLOW 7: Instagram Carousel Publisher
nodes:
- Carousel Slide Generator (AI)
- Image Designer (Canva API or Bannerbear)
- Instagram Graph API Auth
- Container Creator
- Media Publisher
- Tracking Logger
Node: Carousel Creator
node_type: "HTTP Request" (Instagram Graph API)
configuration:
# Step 1: Create container for each slide
method: "POST"
url: "https://graph.facebook.com/v18.0/{{ $env.INSTAGRAM_BUSINESS_ID }}/media"
body:
image_url: "{{ $json.slide_images[0] }}"
is_carousel_item: true
# Step 2: Create carousel container
method: "POST"
url: "https://graph.facebook.com/v18.0/{{ $env.INSTAGRAM_BUSINESS_ID }}/media"
body:
media_type: "CAROUSEL"
children: "{{ $json.carousel_item_ids }}"
caption: "{{ $json.instagram_caption }}"
# Step 3: Publish
method: "POST"
url: "https://graph.facebook.com/v18.0/{{ $env.INSTAGRAM_BUSINESS_ID }}/media_publish"
body:
creation_id: "{{ $json.carousel_container_id }}"
output:
- instagram_post_url
- instagram_post_id
SUB-WORKFLOW 8: Email Newsletter Sender
nodes:
- Email Formatter
- Subject Line Optimizer
- Subscriber List Fetcher
- Mailchimp/SendGrid API
- Delivery Tracker
- Open Rate Monitor
- Click Tracker
Node: SendGrid Email Campaign
node_type: "HTTP Request"
configuration:
method: "POST"
url: "https://api.sendgrid.com/v3/mail/send"
headers:
Authorization: "Bearer {{ $env.SENDGRID_API_KEY }}"
body:
personalizations:
- to:
- email: "{{ $json.subscriber_email }}"
dynamic_template_data:
first_name: "{{ $json.subscriber_name }}"
article_content: "{{ $json.email_body }}"
cta_link: "{{ $json.cta_url }}"
from:
email: "[email protected]"
name: "SAM AI"
template_id: "{{ $env.EMAIL_TEMPLATE_ID }}"
tracking_settings:
click_tracking:
enable: true
open_tracking:
enable: true
output:
- message_id
- recipients_count
- send_timestamp
📊 AGGREGATION & TRACKING
NODE: Performance Aggregator
node_type: "Function"
purpose: "Collect all channel results"
inputs:
- linkedin_result
- twitter_result
- medium_result
- devto_result
- reddit_results (array)
- hn_result
- instagram_result
- email_result
code: |
const results = {
article_id: input_data.metadata.article_number,
publish_timestamp: new Date().toISOString(),
channels: {
linkedin: {
post_url: linkedin_result.post_url,
status: 'published'
},
twitter: {
thread_url: twitter_result.thread_url,
tweet_count: 15,
status: 'published'
},
// ... all channels
},
total_posts: 8,
estimated_reach: 10000
}
return results
output:
- distribution_summary (JSON)
NODE: Google Analytics Event
node_type: "HTTP Request"
purpose: "Log distribution event"
configuration:
method: "POST"
url: "https://www.google-analytics.com/mp/collect"
body:
client_id: "{{ $env.GA_CLIENT_ID }}"
events:
- name: "article_distributed"
params:
article_number: "{{ $json.metadata.article_number }}"
channels_count: 8
timestamp: "{{ $json.publish_timestamp }}"
output:
- tracking_confirmed
NODE: Airtable Logger
node_type: "HTTP Request"
purpose: "Log to campaign tracking database"
configuration:
method: "POST"
url: "https://api.airtable.com/v0/{{ $env.AIRTABLE_BASE_ID }}/Campaign%20Tracking"
headers:
Authorization: "Bearer {{ $env.AIRTABLE_API_KEY }}"
body:
records:
- fields:
Article: "{{ $json.metadata.social.headline }}"
Article_Number: "{{ $json.metadata.article_number }}"
Publish_Date: "{{ $json.publish_timestamp }}"
LinkedIn_URL: "{{ $json.channels.linkedin.post_url }}"
Twitter_URL: "{{ $json.channels.twitter.thread_url }}"
Medium_URL: "{{ $json.channels.medium.post_url }}"
DevTo_URL: "{{ $json.channels.devto.article_url }}"
Reddit_URLs: "{{ $json.channels.reddit.post_urls.join(', ') }}"
HN_URL: "{{ $json.channels.hn.post_url }}"
Instagram_URL: "{{ $json.channels.instagram.post_url }}"
Email_Sent: "{{ $json.channels.email.recipients_count }}"
Status: "Published"
Estimated_Reach: 10000
output:
- airtable_record_id
📈 PERFORMANCE MONITORING SUB-WORKFLOW
Continuous Monitoring (Runs every hour)
workflow_name: "SAM_Campaign_Performance_Monitor"
trigger: "Cron (every hour)"
nodes:
- Fetch All Published Posts
- LinkedIn Insights API
- Twitter Analytics API
- Medium Stats API
- Dev.to Analytics
- Reddit Scores
- Instagram Insights
- Email Open/Click Stats
- Aggregate Metrics
- Update Dashboard
- Alert on Milestones
Node: LinkedIn Insights
node_type: "HTTP Request"
configuration:
method: "GET"
url: "https://api.linkedin.com/v2/organizationalEntityShareStatistics"
params:
q: "organizationalEntity"
organizationalEntity: "{{ $env.LINKEDIN_PAGE_ID }}"
shares: "{{ $json.linkedin_post_id }}"
output:
- impressions
- clicks
- likes
- comments
- shares
Node: Alert on Milestones
node_type: "IF" + "Slack/Email Notification"
conditions:
- IF total_views > 1000: Alert "Viral threshold!"
- IF conversions > 10: Alert "Conversion spike!"
- IF engagement_rate > 10%: Alert "High engagement!"
slack_notification:
webhook_url: "{{ $env.SLACK_WEBHOOK }}"
message: |
🚀 SAM Campaign Alert!
Article: {{ $json.article_title }}
Milestone: {{ $json.milestone_type }}
📊 Stats:
• Views: {{ $json.total_views }}
• Engagement: {{ $json.engagement_rate }}%
• Conversions: {{ $json.conversions }}
🔗 Details: {{ $json.dashboard_url }}
🎛️ DASHBOARD INTEGRATION
Node: Update Real-Time Dashboard
node_type: "HTTP Request" (to dashboard API)
configuration:
method: "POST"
url: "{{ $env.DASHBOARD_API_URL }}/update"
body:
article_id: "{{ $json.article_number }}"
metrics:
total_views: "{{ $json.aggregated.total_views }}"
total_engagement: "{{ $json.aggregated.total_engagement }}"
total_clicks: "{{ $json.aggregated.total_clicks }}"
conversions: "{{ $json.aggregated.conversions }}"
by_channel:
linkedin:
views: "{{ $json.linkedin.impressions }}"
engagement: "{{ $json.linkedin.engagement }}"
twitter:
views: "{{ $json.twitter.impressions }}"
engagement: "{{ $json.twitter.engagement }}"
# ... all channels
output:
- dashboard_updated: true
🔧 ERROR HANDLING & RECOVERY
Global Error Handler Node
node_type: "Error Trigger"
purpose: "Catch and handle all workflow errors"
configuration:
on_error:
- Log error to Airtable
- Send alert to Slack
- Retry failed operation (3 attempts)
- Fallback to manual notification
error_logging:
table: "Error_Log"
fields:
- timestamp
- workflow_name
- failed_node
- error_message
- article_id
- channel_affected
- retry_count
slack_alert:
message: |
⚠️ SAM Campaign Error
Article: {{ $json.article_id }}
Failed: {{ $json.failed_node }}
Channel: {{ $json.channel }}
Error: {{ $json.error_message }}
Retry: {{ $json.retry_count }}/3
retry_logic:
max_attempts: 3
delay_between: 300000 # 5 minutes
exponential_backoff: true
📅 SCHEDULING WORKFLOW
Master Schedule Controller
workflow_name: "SAM_Campaign_Schedule_Master"
purpose: "Orchestrate timing across all articles"
configuration:
articles_schedule:
article_01:
launch_day: "Monday Week 1"
linkedin: "08:00"
twitter: "09:00"
medium: "10:00"
devto: "11:00"
reddit: "12:00"
hn: "14:00"
instagram: "18:00"
email: "06:00"
article_02:
launch_day: "Tuesday Week 1"
# ... timing
execution:
- FOR each article in schedule:
- Check if launch_day matches today
- FOR each channel:
- Wait until scheduled time
- Trigger channel-specific workflow
- Log execution
🚀 COMPLETE WORKFLOW EXECUTION FLOW
1. FILE WATCHER detects new memorandum
↓
2. READ & PARSE memorandum content
↓
3. AI ANALYZES and validates content
↓
4. SPLIT into 8 parallel paths:
Path A: LinkedIn
→ Transform content
→ Post to LinkedIn
→ Log tracking
Path B: Twitter
→ Generate thread
→ Post sequentially
→ Log tracking
Path C: Medium
→ Format article
→ Upload images
→ Publish
Path D: Dev.to
→ Convert to technical format
→ Add code blocks
→ Publish
Path E: Reddit
→ Create 3 subreddit posts
→ Monitor comments
→ Log tracking
Path F: Hacker News
→ Optimize title
→ Post article
→ Add first comment
Path G: Instagram
→ Generate carousel
→ Design slides
→ Publish
Path H: Email
→ Format newsletter
→ Send to list
→ Track opens/clicks
5. AGGREGATE all results
↓
6. LOG to tracking systems (Airtable, GA)
↓
7. UPDATE real-time dashboard
↓
8. MONITOR performance (hourly)
↓
9. ALERT on milestones
↓
10. REPORT to Slack/Email
📊 EXPECTED AUTOMATION RESULTS
Time Savings:
- Manual distribution time: 8 channels × 30 min = 4 hours
- Automated distribution time: 5 minutes (setup) + 0 minutes (execution)
- Savings: 3 hours 55 minutes per article
- Campaign total: 10 articles × 4 hours = 40 hours saved
Consistency:
- 100% adherence to platform best practices
- 0% human error in formatting
- Perfect timing across all channels
- Consistent brand voice maintained
Scale:
- 10 articles × 8 channels = 80 posts
- Executed in 10 days
- All tracking automated
- Real-time performance monitoring
✅ WORKFLOW SETUP CHECKLIST
API Keys Required:
- [ ] Claude API (Anthropic)
- [ ] LinkedIn API (OAuth2)
- [ ] Twitter API (Bearer token)
- [ ] Medium API
- [ ] Dev.to API
- [ ] Reddit API (OAuth2)
- [ ] Instagram Graph API
- [ ] SendGrid/Mailchimp API
- [ ] Google Analytics API
- [ ] Airtable API
- [ ] Slack Webhook
Configuration Needed:
- [ ] N8N instance running
- [ ] All API credentials stored in environment variables
- [ ] File watcher path configured
- [ ] Dashboard API endpoint set
- [ ] Error logging table created
- [ ] Performance tracking tables ready
- [ ] Slack/Email alerts configured
Testing Required:
- [ ] Test article memorandum created
- [ ] Each channel workflow tested individually
- [ ] Full end-to-end test completed
- [ ] Error handling tested
- [ ] Performance monitoring validated
- [ ] Dashboard updates confirmed
🎯 SUCCESS METRICS
Workflow Performance:
- Execution time: <5 minutes per article
- Success rate: >95% across all channels
- Error recovery: <30 minutes
- Tracking accuracy: 100%
Campaign Performance:
- Total posts: 80 (10 articles × 8 channels)
- Total reach: 100,000-150,000
- Total engagement: 5,000-10,000
- Total conversions: 50-100
This N8N workflow transforms one information memorandum into 8 optimized, platform-specific posts automatically, saving 40 hours across the campaign while maintaining perfect consistency and tracking.
Status: 🟢 Ready for Implementation
Next Steps:
1. Set up N8N instance
2. Configure all API credentials
3. Import workflow JSON
4. Test with Article 01
5. Launch campaign automation