This workflow corresponds to n8n.io template #8963 — we link there as the canonical source.
This workflow follows the Gmail → Google Sheets 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 →
{
"id": "FTBefJJksAo6vP1K",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Automated Influencer Campaign Management System",
"tags": [],
"nodes": [
{
"id": "12f8177d-3695-45f1-badb-4fd24c4e149c",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"position": [
0,
0
],
"parameters": {
"path": "/YOUR_CUSTOM_WEBHOOK_PATH",
"options": {
"rawBody": false
},
"httpMethod": "POST"
},
"typeVersion": 2.1
},
{
"id": "bb59a3b4-d17d-4eb0-b2de-26df8ea7fd6f",
"name": "Data Sanitizer",
"type": "n8n-nodes-base.code",
"position": [
224,
0
],
"parameters": {
"jsCode": "// Debug: Log the incoming data first\nconsole.log('Incoming data:', JSON.stringify($input.all(), null, 2));\n\n// Get the data from webhook\nconst inputData = $input.first();\nconsole.log('Input item:', JSON.stringify(inputData, null, 2));\n\n// Extract JSON data (webhook data is nested differently)\nlet data;\nif (inputData.json.body) {\n // If data comes in request body\n data = inputData.json.body;\n} else if (inputData.json.query) {\n // If data comes as query parameters\n data = inputData.json.query;\n} else {\n // Direct JSON data\n data = inputData.json;\n}\n\nconsole.log('Processed data:', JSON.stringify(data, null, 2));\n\n// Handle case where data might be a string (needs parsing)\nif (typeof data === 'string') {\n try {\n data = JSON.parse(data);\n } catch (e) {\n throw new Error('Invalid JSON format in request');\n }\n}\n\n// Required fields validation\nconst required = ['name', 'email', 'social_handles', 'niche', 'country'];\nconst missing = required.filter(field => !data || !data[field]);\n\nif (missing.length > 0) {\n console.log('Available fields:', Object.keys(data || {}));\n throw new Error(`Missing required fields: ${missing.join(', ')}. Available fields: ${Object.keys(data || {}).join(', ')}`);\n}\n\n// Email format validation\nconst emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\nif (!emailRegex.test(data.email)) {\n throw new Error('Invalid email format');\n}\n\n// Clean social handles (remove @ symbols, extract usernames)\nconst cleanHandles = {};\nfor (const [platform, handle] of Object.entries(data.social_handles)) {\n if (handle) {\n cleanHandles[platform] = handle.replace(/[@\\/]/g, '').split('/').pop();\n }\n}\n\nreturn [{\n json: {\n ...data,\n social_handles: cleanHandles,\n created_at: new Date().toISOString(),\n status: 'pending_validation'\n }\n}];"
},
"typeVersion": 2
},
{
"id": "62f128f9-6a68-4d78-baae-908153106221",
"name": "Verifi Email",
"type": "n8n-nodes-verifiemail.verifiEmail",
"position": [
448,
0
],
"parameters": {
"email": "={{ $json.email }}"
},
"credentials": {
"verifiEmailApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "afbbc1e1-76c2-4ca1-a75f-a7fe0946f130",
"name": "Switch",
"type": "n8n-nodes-base.switch",
"position": [
624,
0
],
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "aea5e85b-3bb5-4db3-942b-7ada2f17ab2a",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.valid }}",
"rightValue": true
}
]
}
},
{
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "de79caea-c28e-47da-bc90-c2d6ca9a94e3",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.valid }}",
"rightValue": false
}
]
}
}
]
},
"options": {}
},
"typeVersion": 3.2
},
{
"id": "a01052ae-f470-44c9-bbd0-b9dc44e241c0",
"name": "Stop and Error",
"type": "n8n-nodes-base.stopAndError",
"position": [
496,
288
],
"parameters": {
"errorMessage": "invalid email"
},
"typeVersion": 1
},
{
"id": "9a885214-eb51-4f09-92b6-92f202a28245",
"name": "Instagram Profile Stats",
"type": "n8n-nodes-base.httpRequest",
"position": [
928,
-256
],
"parameters": {
"url": "https://instagram120.p.rapidapi.com/api/instagram/profile",
"method": "POST",
"options": {},
"sendBody": true,
"sendHeaders": true,
"bodyParameters": {
"parameters": [
{
"name": "username",
"value": "={{ $('Data Sanitizer').item.json.social_handles.instagram }}"
}
]
},
"headerParameters": {
"parameters": [
{
"name": "x-rapidapi-host",
"value": "instagram120.p.rapidapi.com"
},
{
"name": "x-rapidapi-key",
"value": "YOUR_RAPIDAPI_KEY_HERE"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "d77393c1-9fc2-45bc-9b13-b48f5c3593bf",
"name": "Parse Instagram Data",
"type": "n8n-nodes-base.code",
"position": [
1328,
-112
],
"parameters": {
"jsCode": "const instagramResponse = $input.first().json;\n\n// Get the user data from a previous node (you need to reference the correct node name)\nlet userData;\ntry {\n // Replace 'Data Sanitizer' with your actual previous node name that has user data\n userData = $('Data Sanitizer').first().json;\n} catch (e) {\n // Fallback if node reference fails\n userData = {\n name: 'Test User',\n email: 'test@example.com',\n niche: 'sports',\n country: 'US',\n rate_card: 1000,\n social_handles: { instagram: 'cristiano' }\n };\n}\n\nconsole.log('User Data:', JSON.stringify(userData, null, 2));\nconsole.log('Instagram Response:', JSON.stringify(instagramResponse, null, 2));\n\ntry {\n const profileData = instagramResponse.result;\n \n if (!profileData) {\n throw new Error('No Instagram profile data found');\n }\n\n // Parse the real Instagram data from RapidAPI\n const instagramData = {\n id: profileData.id,\n username: profileData.username,\n followers: profileData.edge_followed_by.count || 0,\n following: profileData.edge_follow.count || 0,\n posts_count: profileData.edge_owner_to_timeline_media.count || 0,\n is_private: profileData.is_private || false,\n verified: profileData.is_verified || false,\n biography: profileData.biography || '',\n full_name: profileData.full_name || '',\n profile_pic_url: profileData.profile_pic_url || '',\n profile_pic_url_hd: profileData.profile_pic_url_hd || ''\n };\n \n // Calculate realistic engagement for mega-influencer (Cristiano)\n let engagement_rate = 1.2; // Mega influencers typically have lower engagement rates\n if (instagramData.followers < 1000000) {\n engagement_rate = 3.5; // Regular influencers\n } else if (instagramData.followers < 100000000) {\n engagement_rate = 1.8; // Major influencers \n }\n \n const avg_engagement = Math.floor(instagramData.followers * (engagement_rate / 100));\n \n instagramData.engagement_rate = parseFloat(engagement_rate.toFixed(2));\n instagramData.avg_engagement = avg_engagement;\n instagramData.avg_likes = Math.floor(avg_engagement * 0.85);\n instagramData.avg_comments = Math.floor(avg_engagement * 0.15);\n\n return [{\n json: {\n ...userData,\n social_stats: {\n instagram: {\n ...instagramData,\n api_source: 'rapidapi_real',\n fetched_at: new Date().toISOString()\n }\n }\n }\n }];\n \n} catch (error) {\n console.log('Parse error:', error.message);\n \n return [{\n json: {\n ...userData,\n social_stats: {\n instagram: {\n followers: 0,\n following: 0,\n posts_count: 0,\n verified: false,\n biography: '',\n username: userData.social_handles?.instagram || 'unknown',\n avg_engagement: 0,\n engagement_rate: 0,\n api_source: 'parse_failed',\n error: error.message,\n fetched_at: new Date().toISOString()\n }\n }\n }\n }];\n}"
},
"typeVersion": 2
},
{
"id": "4a2298b1-e9a9-4667-8482-fd2580810c24",
"name": "YouTube Channel Stats",
"type": "n8n-nodes-base.httpRequest",
"position": [
928,
176
],
"parameters": {
"url": "https://youtube138.p.rapidapi.com/channel/details",
"options": {},
"sendQuery": true,
"sendHeaders": true,
"queryParameters": {
"parameters": [
{
"name": "id",
"value": "={{ $('Webhook').item.json.body.social_handles.youtube }}"
},
{
"name": "hl",
"value": "en"
},
{
"name": "gl",
"value": "US"
}
]
},
"headerParameters": {
"parameters": [
{
"name": "x-rapidapi-host",
"value": "youtube138.p.rapidapi.com"
},
{
"name": "x-rapidapi-key",
"value": "YOUR_RAPIDAPI_KEY_HERE"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "0f58c1a3-b441-4e80-bc9a-010e4da46d2d",
"name": "Parse YouTube Data",
"type": "n8n-nodes-base.code",
"position": [
1328,
80
],
"parameters": {
"jsCode": "const input = $input.first().json;\n\nconsole.log('Full YouTube input:', JSON.stringify(input, null, 2));\n\n// Try to get user data from previous nodes in the workflow\nlet userData;\ntry {\n // Try different possible node names - update these to match your actual node names\n userData = $('Parse Instagram Data').first().json || \n $('Data Sanitizer').first().json ||\n $('Process Email Validation').first().json ||\n $('Mock Instagram Data').first().json;\n \n console.log('Found user data from previous node:', JSON.stringify(userData, null, 2));\n} catch (e) {\n console.log('Could not find user data from previous nodes, using fallback');\n // Fallback user data for testing\n userData = {\n name: \"Sarah Johnson\",\n email: \"user@example.com\",\n niche: \"fitness\", \n country: \"US\",\n rate_card: 750,\n social_handles: {\n instagram: \"sarahfitlife\",\n youtube: \"SarahFitnessJourney\"\n },\n social_stats: {},\n created_at: new Date().toISOString(),\n status: 'processing'\n };\n}\n\n// Parse the YouTube channel data from the current input\nconst channelData = input;\n\ntry {\n if (!channelData.stats) {\n throw new Error('No YouTube stats found in response');\n }\n\n const youtubeData = {\n channel_id: channelData.channelId || '',\n username: channelData.username || '',\n title: channelData.title || '',\n subscribers: channelData.stats.subscribers || 0,\n subscribers_text: channelData.stats.subscribersText || '',\n total_views: channelData.stats.views || 0,\n video_count: channelData.stats.videos || 0,\n description: channelData.description || '',\n is_verified: channelData.isVerified || false,\n is_verified_artist: channelData.isVerifiedArtist || false,\n joined_date: channelData.joinedDate || '',\n joined_date_text: channelData.joinedDateText || '',\n country: channelData.country || '',\n has_business_email: channelData.hasBusinessEmail || false,\n avatar_url: channelData.avatar?.[0]?.url || '',\n banner_url: channelData.banner?.desktop?.[0]?.url || '',\n is_family_safe: channelData.isFamilySafe || true\n };\n\n // Calculate engagement metrics\n const avg_views_per_video = youtubeData.video_count > 0 ? \n Math.floor(youtubeData.total_views / youtubeData.video_count) : 0;\n \n // YouTube engagement rate: (avg views per video / subscribers) * 100\n const engagement_rate = youtubeData.subscribers > 0 ? \n parseFloat(((avg_views_per_video / youtubeData.subscribers) * 100).toFixed(2)) : 0;\n \n // Estimate social engagement based on YouTube norms\n const estimated_avg_likes = Math.floor(avg_views_per_video * 0.03); // ~3% of views\n const estimated_avg_comments = Math.floor(avg_views_per_video * 0.005); // ~0.5% of views\n const estimated_total_engagement = estimated_avg_likes + estimated_avg_comments;\n \n // Add calculated metrics\n youtubeData.avg_views_per_video = avg_views_per_video;\n youtubeData.engagement_rate = engagement_rate;\n youtubeData.estimated_avg_likes = estimated_avg_likes;\n youtubeData.estimated_avg_comments = estimated_avg_comments;\n youtubeData.estimated_total_engagement = estimated_total_engagement;\n\n // Channel age calculation (for scoring purposes)\n const joinedDate = new Date(channelData.joinedDate);\n const now = new Date();\n const monthsActive = Math.floor((now - joinedDate) / (1000 * 60 * 60 * 24 * 30));\n youtubeData.months_active = monthsActive;\n youtubeData.videos_per_month = monthsActive > 0 ? (youtubeData.video_count / monthsActive).toFixed(1) : 0;\n\n console.log('Successfully parsed YouTube data:', JSON.stringify(youtubeData, null, 2));\n\n return [{\n json: {\n ...userData,\n social_stats: {\n ...userData.social_stats,\n youtube: {\n ...youtubeData,\n api_source: 'rapidapi_real',\n fetched_at: new Date().toISOString()\n }\n }\n }\n }];\n \n} catch (error) {\n console.log('YouTube Parse Error:', error.message);\n \n return [{\n json: {\n ...userData,\n social_stats: {\n ...userData.social_stats,\n youtube: {\n channel_id: '',\n username: '',\n title: '',\n subscribers: 0,\n total_views: 0,\n video_count: 0,\n engagement_rate: 0,\n avg_views_per_video: 0,\n estimated_avg_likes: 0,\n estimated_avg_comments: 0,\n api_source: 'parse_failed',\n error: error.message,\n fetched_at: new Date().toISOString()\n }\n }\n }\n }];\n}"
},
"typeVersion": 2
},
{
"id": "bebd9b90-7868-4c26-9eec-18fa9a4754c5",
"name": "Merge",
"type": "n8n-nodes-base.merge",
"position": [
1536,
-16
],
"parameters": {},
"typeVersion": 3.2
},
{
"id": "867b3df2-09b1-459c-a619-12e30c753782",
"name": "Calculate Influencer Score",
"type": "n8n-nodes-base.code",
"position": [
1760,
-16
],
"parameters": {
"jsCode": "const allData = $input.all(); // Get all items from merge node\nconsole.log('Number of merge items:', allData.length);\n\n// Find the item with both Instagram and YouTube data\nlet data;\nif (allData.length === 2) {\n // Two separate items from merge - combine them\n const item1 = allData[0].json;\n const item2 = allData[1].json;\n \n data = {\n ...item1,\n social_stats: {\n ...item1.social_stats,\n ...item2.social_stats\n }\n };\n} else {\n // Single item with both platforms\n data = allData[0].json;\n}\n\nconst socialStats = data.social_stats || {};\n\nconsole.log('Combined data check:', {\n hasInstagram: !!socialStats.instagram,\n hasYoutube: !!socialStats.youtube,\n instagramFollowers: socialStats.instagram?.followers,\n youtubeSubscribers: socialStats.youtube?.subscribers\n});\n\n// Rest of scoring configuration (same as before)\nconst SCORING_CONFIG = {\n platform_weights: {\n instagram: 0.6,\n youtube: 0.4\n },\n follower_tiers: {\n nano: { min: 1000, max: 10000, score: 50 },\n micro: { min: 10000, max: 100000, score: 70 },\n macro: { min: 100000, max: 1000000, score: 90 },\n mega: { min: 1000000, max: Infinity, score: 100 }\n },\n engagement_tiers: {\n youtube: {\n excellent: { min: 10, score: 100 },\n good: { min: 5, score: 85 },\n average: { min: 2, score: 65 },\n poor: { min: 0, score: 40 }\n },\n instagram: {\n excellent: { min: 6, score: 100 },\n good: { min: 3, score: 85 },\n average: { min: 1.5, score: 65 },\n poor: { min: 0, score: 40 }\n }\n }\n};\n\nfunction calculatePlatformScore(platformData, platform) {\n if (!platformData || platformData.error) return null;\n \n const followers = platformData.followers || platformData.subscribers || 0;\n const engagementRate = platformData.engagement_rate || 0;\n \n let followerScore = 40;\n for (const [tier, config] of Object.entries(SCORING_CONFIG.follower_tiers)) {\n if (followers >= config.min && followers <= config.max) {\n followerScore = config.score;\n break;\n }\n }\n \n let engagementScore = 40;\n const thresholds = SCORING_CONFIG.engagement_tiers[platform] || SCORING_CONFIG.engagement_tiers.instagram;\n \n for (const [tier, config] of Object.entries(thresholds)) {\n if (engagementRate >= config.min) {\n engagementScore = config.score;\n break;\n }\n }\n \n let qualityBonus = 0;\n if (platformData.verified || platformData.is_verified) qualityBonus += 10;\n if (platformData.has_business_email) qualityBonus += 5;\n if (platform === 'youtube' && parseFloat(platformData.videos_per_month) > 4) qualityBonus += 5;\n if (platform === 'instagram' && !platformData.is_private) qualityBonus += 5;\n \n const totalScore = Math.min(100, Math.round(\n (followerScore * 0.4) + \n (engagementScore * 0.5) + \n qualityBonus\n ));\n \n return {\n total: totalScore,\n breakdown: {\n follower_score: followerScore,\n engagement_score: engagementScore,\n quality_bonus: qualityBonus,\n followers: followers,\n engagement_rate: engagementRate,\n tier: followers >= 1000000 ? 'mega' : followers >= 100000 ? 'macro' : followers >= 10000 ? 'micro' : 'nano'\n }\n };\n}\n\n// Calculate scores for each platform\nconst scores = {};\nlet weightedScore = 0;\nlet totalWeight = 0;\n\n// Instagram scoring\nif (socialStats.instagram && !socialStats.instagram.error) {\n scores.instagram = calculatePlatformScore(socialStats.instagram, 'instagram');\n if (scores.instagram) {\n weightedScore += scores.instagram.total * SCORING_CONFIG.platform_weights.instagram;\n totalWeight += SCORING_CONFIG.platform_weights.instagram;\n }\n}\n\n// YouTube scoring \nif (socialStats.youtube && !socialStats.youtube.error) {\n scores.youtube = calculatePlatformScore(socialStats.youtube, 'youtube');\n if (scores.youtube) {\n weightedScore += scores.youtube.total * SCORING_CONFIG.platform_weights.youtube;\n totalWeight += SCORING_CONFIG.platform_weights.youtube;\n }\n}\n\nconst overallScore = totalWeight > 0 ? Math.round(weightedScore / totalWeight) : 0;\n\n// Status determination\nconst totalFollowers = (socialStats.instagram?.followers || 0) + (socialStats.youtube?.subscribers || 0);\nconst followersPerDollar = totalFollowers / (data.rate_card || 1);\n\nlet status = 'rejected_low_score';\nlet rejection_reason = '';\n\nif (totalFollowers < 5000) {\n status = 'rejected_insufficient_reach';\n rejection_reason = 'Total audience below 5K minimum';\n} else if (data.rate_card > 2000 && followersPerDollar < 100) {\n status = 'rejected_overpriced';\n rejection_reason = 'Rate card too high for audience size';\n} else if (overallScore >= 80) {\n status = 'approved';\n} else if (overallScore >= 65) {\n status = 'pending_review';\n} else {\n status = 'rejected_low_score';\n rejection_reason = `Score ${overallScore}/100 below threshold`;\n}\n\nreturn [{\n json: {\n ...data,\n scoring: {\n platform_scores: scores,\n overall_score: overallScore,\n total_followers: totalFollowers,\n followers_per_dollar: Math.round(followersPerDollar),\n calculated_at: new Date().toISOString()\n },\n status: status,\n rejection_reason: rejection_reason || null\n }\n}];"
},
"typeVersion": 2
},
{
"id": "dc6b665b-58de-409b-9dde-e3c7fe442c70",
"name": "If",
"type": "n8n-nodes-base.if",
"position": [
2032,
-16
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "d48efcf5-0146-4bea-82e4-66164aefa314",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{$json.status}}",
"rightValue": "approved"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "f1ba8012-bf02-4dac-91f6-165425a57f06",
"name": "Stop and Error1",
"type": "n8n-nodes-base.stopAndError",
"position": [
2272,
144
],
"parameters": {
"errorMessage": "Engagement score doesn't meet the expectations"
},
"typeVersion": 1
},
{
"id": "0091470e-520c-4bb9-9172-fe66348f0137",
"name": "Add to Approved Database",
"type": "n8n-nodes-base.googleSheets",
"position": [
2272,
-128
],
"parameters": {
"columns": {
"value": {
"Name": "={{$json.name}}",
"Email": "={{$json.email}}",
"niche": "={{ $json.niche }}",
"Status": "={{$json.status}}",
"YouTube": "={{$json.social_stats.youtube.username}}",
"Instagram": "=@{{$json.social_stats.instagram.username}}",
"Rate Card": "=${{$json.rate_card}}",
"Overall Score": "={{$json.scoring.overall_score}}",
"Total followers": "={{ $json.scoring.total_followers }}",
"Instagram Followers": "={{$json.social_stats.instagram.followers}}",
"YouTube Subscribers": "={{$json.social_stats.youtube.subscribers}}"
},
"schema": [
{
"id": "Name",
"type": "string",
"display": true,
"required": false,
"displayName": "Name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Email",
"type": "string",
"display": true,
"required": false,
"displayName": "Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "niche",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "niche",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Instagram",
"type": "string",
"display": true,
"required": false,
"displayName": "Instagram",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Instagram Followers",
"type": "string",
"display": true,
"required": false,
"displayName": "Instagram Followers",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "YouTube",
"type": "string",
"display": true,
"required": false,
"displayName": "YouTube",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "YouTube Subscribers",
"type": "string",
"display": true,
"required": false,
"displayName": "YouTube Subscribers",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Total followers",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Total followers",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Overall Score",
"type": "string",
"display": true,
"required": false,
"displayName": "Overall Score",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Rate Card",
"type": "string",
"display": true,
"required": false,
"displayName": "Rate Card",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Status",
"type": "string",
"display": true,
"required": false,
"displayName": "Status",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1MoHJiDjjw3EJGBS4YgbOcgeQ0oDYyrVLE2aVxTte1GY/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "YOUR_GOOGLE_SHEETS_ID_HERE",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1MoHJiDjjw3EJGBS4YgbOcgeQ0oDYyrVLE2aVxTte1GY/edit?usp=drivesdk",
"cachedResultName": "Add to Approved Database"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "95394f91-7907-4e96-b1bb-e12e4fe241e8",
"name": "Send Welcome Email",
"type": "n8n-nodes-base.gmail",
"position": [
2480,
-128
],
"parameters": {
"sendTo": "={{ $json.Email }}",
"message": "=Hi {{ $json.Name }},\n\nCongratulations! Your influencer application has been approved with an outstanding score of {{ $json['Overall Score'] }}/100.\n\nHere's what impressed us about your profile:\n- Instagram: {{ $json.Instagram }} with {{ $json['Instagram Followers'] }} engaged followers\n- YouTube: {{ $json.YouTube }} with {{ $json['YouTube Subscribers'] }} subscribers\n- Combined audience reach: {{ $json['Total followers'] }} across platforms\n- Strong engagement in the {{ $json.niche }} space\n\nWHAT'S NEXT:\n1. Our partnerships team will contact you within 24-48 hours\n2. We'll discuss campaign opportunities that match your content style\n3. You'll receive our brand guidelines and content requirements\n4. First campaign opportunities typically start within 1-2 weeks\n\nCAMPAIGN TYPES WE OFFER:\n- Sponsored posts and stories\n- Product collaborations\n- Long-term brand partnerships\n- Exclusive launch campaigns\n\nYour rate card of {{ $json['Rate Card'] }} per post fits well within our budget range for {{$json.niche}} creators.\n\nIf you have any questions before our team reaches out, feel free to reply to this email.\n\nWelcome to the [Your Brand Name] family!\n\nBest regards,\nThe [Your Brand Name] Influencer Team\n\n---\nBusiness Contact: partnerships@yourbrand.com\nThis is an automated message from our influencer management system.",
"options": {},
"subject": "Welcome to YOUR_BRAND_NAME - You're Approved!",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "fda6ffae-e542-4c65-9b59-2ede839805c9",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-288,
-48
],
"parameters": {
"color": 7,
"width": 400,
"height": 400,
"content": "## WEBHOOK ENDPOINT\n\n**URL**: **/YOUR_CUSTOM_WEBHOOK_PATH**\nMethod: **POST**\nContent-Type: **application/json**\n\nTest payload:\n```{\n \"name\": \"Test User\",\n \"email\": \"test@example.com\", \n \"social_handles\": {\n \"instagram\": \"username\",\n \"youtube\": \"channelname\"\n },\n \"niche\": \"fitness\",\n \"country\": \"US\", \n \"rate_card\": 500\n}\n```"
},
"typeVersion": 1
},
{
"id": "7fe7b968-9b40-435c-9abc-aeeda1fc6c45",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
192,
-224
],
"parameters": {
"color": 7,
"width": 560,
"height": 384,
"content": "## REQUIRED FIELDS\n\n\u2713 name, email, social_handles\n\u2713 niche, country\n\u2713 Email format validation\n\u2713 Social handle cleanup\n\nMissing fields = **workflow stops**\n**Invalid email** = workflow stops"
},
"typeVersion": 1
},
{
"id": "f52e79de-92f2-44b7-8a6b-e0bfab2b1b15",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
848,
-272
],
"parameters": {
"color": 7,
"width": 304,
"height": 608,
"content": "\n\n\n\n\n\n\n\n\n\n\n\n\n## API SETUP REQUIRED\n\n\ud83d\udd11 **RapidAPI Key**: Update in both nodes\n\n\ud83d\udcca **Instagram120** API\n\ud83d\udcca **YouTube138** API\n\nRate limits:\n- Add **2-3 sec** delays for production\n- Monitor quota usage\n- Have backup mock data ready\n\n"
},
"typeVersion": 1
},
{
"id": "9cba5934-8d84-4a8a-ac14-ef738a18f653",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1664,
-32
],
"parameters": {
"color": 7,
"width": 304,
"height": 464,
"content": "\n\n\n\n\n\n\n\n\n\n\n\n## SCORING BREAKDOWN\n\n**Instagram**: 60% weight\n**YouTube**: 40% weight\n\nThresholds:\n- **80+** = Auto-approved\n- **65-79** = Manual review \n- **<65** = Auto-rejected\n\nAdditional checks:\n- **Min 5K** total followers\n- **Rate card** vs **audience ratio**"
},
"typeVersion": 1
},
{
"id": "0943217e-1983-4c7c-894c-53b7cf1801ad",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
2208,
-432
],
"parameters": {
"color": 7,
"width": 432,
"height": 480,
"content": "## APPROVED PATH ACTIONS\n\n1. Add to **Google Sheets** database\n2. Send **welcome email**\n3. Future: **Create Trello card**\n\n## PRODUCTION UPDATES NEEDED\n- **Replace YOUR_BRAND_NAME**\n- **Update Google Sheets ID**\n- **Set up email credentials**"
},
"typeVersion": 1
},
{
"id": "d6ae6ef3-ef78-46b4-86c2-9aad9d265b8e",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
2192,
128
],
"parameters": {
"color": 7,
"width": 288,
"height": 384,
"content": "\n\n\n\n\n\n\n\n\n\n\n\n## REJECTION REASONS\n- Low engagement score\n- Insufficient total reach (<5K)\n- Overpriced rate card\n- Invalid email\n- Missing required data\n\nAll rejections logged for analysis"
},
"typeVersion": 1
},
{
"id": "b0753d3d-365e-45a6-bc2e-037c1bbf65e8",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
1392,
512
],
"parameters": {
"color": 7,
"width": 304,
"height": 272,
"content": "## MONITORING CHECKLIST\n\n\u25a1 API quota usage tracking\n\u25a1 Webhook response times\n\u25a1 Email delivery rates\n\u25a1 Google Sheets write errors\n\u25a1 False positive/negative rates\n\nAdd Slack alerts for system failures\nWeekly performance review recommended"
},
"typeVersion": 1
},
{
"id": "4d1a7fc8-af75-4682-9567-a56b8c564e62",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
-272,
448
],
"parameters": {
"color": 7,
"width": 320,
"height": 224,
"content": "## CREDENTIALS TO SETUP\n\n\u25a1 **VerifiEmail** API key\n\u25a1 **RapidAPI** key **(Instagram/YouTube)**\n\u25a1 **Google Sheets OAuth2**\n\u25a1 **Gmail OAuth2**\n\n\u26a0\ufe0f Delete old credential IDs before import\n\u26a0\ufe0f Test with real data before production"
},
"typeVersion": 1
},
{
"id": "e95556ec-f5c8-402d-b101-905523dd11dd",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
-816,
-256
],
"parameters": {
"color": 7,
"width": 464,
"height": 672,
"content": "## AUTOMATED INFLUENCER CAMPAIGN MANAGEMENT SYSTEM\n\n**PURPOSE**: **Streamline influencer application processing** from form to approval\nFOR: **Marketing agencies, brands, influencer platforms with high application volume**\n\nWHAT IT DOES:\n- **Validates influencer applications automatically**\n- Fetches **real Instagram + YouTube** audience data \n- Scores **influencers using engagement + follower metrics**\n- Auto-approves qualified candidates\n- **Sends welcome emails** + adds to database\n- Rejects low-quality applications with reasons\n\nREQUIREMENTS:\n- **RapidAPI** subscription (Instagram/YouTube APIs)\n- **Google Sheets** for database\n- Email service (Gmail/SMTP)\n- n8n workflow platform\n\n## BUSINESS VALUE:\n- Saves **15-20 minutes** per application review\n- Consistent evaluation criteria\n- **24/7 automated processing**\n- Reduces manual screening workload by **80%**\n\n**CAPACITY**: Handles 100+ applications per day"
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "e2fa68ce-ab34-459c-b0a9-cfad3ace15a7",
"connections": {
"If": {
"main": [
[
{
"node": "Add to Approved Database",
"type": "main",
"index": 0
}
],
[
{
"node": "Stop and Error1",
"type": "main",
"index": 0
}
]
]
},
"Merge": {
"main": [
[
{
"node": "Calculate Influencer Score",
"type": "main",
"index": 0
}
]
]
},
"Switch": {
"main": [
[
{
"node": "Instagram Profile Stats",
"type": "main",
"index": 0
},
{
"node": "YouTube Channel Stats",
"type": "main",
"index": 0
}
],
[
{
"node": "Stop and Error",
"type": "main",
"index": 0
}
]
]
},
"Webhook": {
"main": [
[
{
"node": "Data Sanitizer",
"type": "main",
"index": 0
}
]
]
},
"Verifi Email": {
"main": [
[
{
"node": "Switch",
"type": "main",
"index": 0
}
]
]
},
"Data Sanitizer": {
"main": [
[
{
"node": "Verifi Email",
"type": "main",
"index": 0
}
]
]
},
"Parse YouTube Data": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"Parse Instagram Data": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"YouTube Channel Stats": {
"main": [
[
{
"node": "Parse YouTube Data",
"type": "main",
"index": 0
}
]
]
},
"Instagram Profile Stats": {
"main": [
[
{
"node": "Parse Instagram Data",
"type": "main",
"index": 0
}
]
]
},
"Add to Approved Database": {
"main": [
[
{
"node": "Send Welcome Email",
"type": "main",
"index": 0
}
]
]
},
"Calculate Influencer Score": {
"main": [
[
{
"node": "If",
"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.
gmailOAuth2googleSheetsOAuth2ApiverifiEmailApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
A comprehensive n8n workflow template for streamlining influencer application processing with real-time social media data validation, intelligent scoring algorithms, and automated onboarding workflows. Marketing agencies managing high-volume influencer applications Brand…
Source: https://n8n.io/workflows/8963/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This workflow acts as an instant SDR that replies to new inbound leads across multiple channels in real time. It first captures and normalizes all incoming lead data into a unified structure. The work
AI Lead Qualification & Follow-Up. Uses httpRequest, slack, googleSheets, gmail. Webhook trigger; 18 nodes.
Smart influencer discovery: Automatically finds and qualifies influencers based on your criteria and target audience Automated outreach: Sends personalized collaboration proposals with dynamic pricing
This workflow automates bulk email campaigns with built-in validation, deliverability protection, and smart send-time optimization.
Who is this for? Solo founders, sales teams, and event organizers who need email outreach without expensive tools but want full control from Telegram.