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 →
{
"name": "AEO / GEO Improvement Loop (measure, diagnose, fix, re-check)",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "weeks"
}
]
}
},
"name": "Weekly Monday 7am",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1,
"position": [
180,
300
]
},
{
"parameters": {
"jsCode": "// ============ EDIT THIS: the buyer questions you want to own in AI answers ============\nconst QUESTIONS = [\n 'best AI platform for GTM and demand generation',\n 'how to automate outbound sales with AI',\n 'tools to make my brand discoverable in AI search',\n 'best marketing automation platforms 2026',\n 'AI tools to enrich and route leads automatically'\n];\nconst BRAND = 'YourBrand'; // EDIT: your brand name\n// =====================================================================================\nreturn QUESTIONS.map(q => ({ json: { question: q, brand: BRAND } }));"
},
"name": "Target Buyer Questions",
"type": "n8n-nodes-base.code",
"typeVersion": 1,
"position": [
400,
300
]
},
{
"parameters": {
"jsCode": "// MEASURE. Runs credential-free on a built-in sample of answers so this imports and runs.\n// To go live, swap for HTTP nodes calling the ChatGPT, Perplexity and Gemini APIs per question,\n// then parse the brands named in each answer.\nconst ENGINES = ['ChatGPT','Perplexity','Gemini'];\nfunction sampleBrandsFor(q) {\n const pool = ['Clay','Instantly','HubSpot','Jasper','YourBrand','Copy.ai','Apollo','Writer'];\n const seed = q.length;\n const picks = [];\n for (let i = 0; i < 4; i++) picks.push(pool[(seed + i * 3) % pool.length]);\n return picks;\n}\nconst out = [];\nfor (const item of $input.all()) {\n const q = item.json.question, brand = item.json.brand;\n for (const engine of ENGINES) {\n const brands = sampleBrandsFor(q + engine);\n out.push({ json: { question: q, engine, brands_named: brands, brand_mentioned: brands.includes(brand), brand } });\n }\n}\nreturn out;"
},
"name": "Query ChatGPT / Perplexity / Gemini",
"type": "n8n-nodes-base.code",
"typeVersion": 1,
"position": [
620,
300
]
},
{
"parameters": {
"jsCode": "// SCORE SHARE OF VOICE and isolate the gap questions (brand named in zero engines).\nconst rows = $input.all().map(i => i.json);\nconst brand = rows[0].brand;\nconst byQ = {};\nrows.forEach(r => { byQ[r.question] = byQ[r.question] || 0; if (r.brand_mentioned) byQ[r.question]++; });\nconst gapQuestions = Object.keys(byQ).filter(q => byQ[q] === 0);\nreturn [{ json: { brand, gap_questions: gapQuestions } }];"
},
"name": "Score + Isolate Gaps",
"type": "n8n-nodes-base.code",
"typeVersion": 1,
"position": [
840,
300
]
},
{
"parameters": {
"jsCode": "// DIAGNOSE. Reads what's missing per gap question (owned content, schema, third-party mentions,\n// demand). In production this calls a site-crawl API + a web search; the demo samples deterministically.\nconst FIX_ACTIONS = {\n content: 'No owned page answers this question directly. Write an answer-shaped page/section.',\n schema: 'An owned page exists but carries no structured data. Add FAQPage/Article JSON-LD.',\n citation: 'Content and schema exist but no independent source corroborates it. Seed 2-3 third-party mentions.',\n authority: 'All layers exist but the brand still loses -- earn citations from higher-trust sources.'\n};\nconst RECHECK_DAYS = { content: 21, schema: 7, citation: 14, authority: 30 };\nfunction classify(sig) {\n if (!sig.has_owned_content) return 'content';\n if (!sig.has_schema_markup) return 'schema';\n if (sig.third_party_mentions === 0) return 'citation';\n return 'authority';\n}\nconst j = $json;\nconst fixes = j.gap_questions.map((q, i) => {\n const sig = { has_owned_content: i % 3 !== 0, has_schema_markup: i % 3 === 2, third_party_mentions: [0,1,0][i % 3], search_volume_proxy: [8,6,9][i % 3] };\n const fixType = classify(sig);\n const effortRank = { schema: 0, citation: 1, content: 2, authority: 3 }[fixType];\n const priority = sig.search_volume_proxy * 10 - effortRank;\n return { question: q, fix_type: fixType, action: FIX_ACTIONS[fixType], recheck_after_days: RECHECK_DAYS[fixType], priority };\n});\nfixes.sort((a, b) => b.priority - a.priority);\nreturn [{ json: { brand: j.brand, fixes } }];"
},
"name": "Diagnose + Prioritise Fixes",
"type": "n8n-nodes-base.code",
"typeVersion": 1,
"position": [
1060,
300
]
},
{
"parameters": {
"jsCode": "// BUILD THE REPORT.\nconst j = $json;\nconst lines = [\n '*AEO / GEO Improvement Plan - ' + j.brand + '*',\n '',\n j.fixes.length ? (j.fixes.length + ' gap question(s) to close, ranked by priority:') : 'No visibility gaps this run.',\n ...j.fixes.map(f => ' - [' + f.fix_type.toUpperCase() + '] ' + f.question + ' (re-check in ' + f.recheck_after_days + 'd)'),\n '',\n j.fixes[0] ? ('Start with: ' + j.fixes[0].action) : ''\n];\nreturn [{ json: { report: lines.join('\\n') } }];"
},
"name": "Build Improvement Report",
"type": "n8n-nodes-base.code",
"typeVersion": 1,
"position": [
1280,
300
]
},
{
"parameters": {
"values": {
"string": [
{
"name": "send_to",
"value": "#growth (Slack) or a content team task board"
},
{
"name": "message",
"value": "={{ $json.report }}"
}
]
},
"options": {}
},
"name": "Send Plan",
"type": "n8n-nodes-base.set",
"typeVersion": 1,
"position": [
1500,
300
]
}
],
"connections": {
"Weekly Monday 7am": {
"main": [
[
{
"node": "Target Buyer Questions",
"type": "main",
"index": 0
}
]
]
},
"Target Buyer Questions": {
"main": [
[
{
"node": "Query ChatGPT / Perplexity / Gemini",
"type": "main",
"index": 0
}
]
]
},
"Query ChatGPT / Perplexity / Gemini": {
"main": [
[
{
"node": "Score + Isolate Gaps",
"type": "main",
"index": 0
}
]
]
},
"Score + Isolate Gaps": {
"main": [
[
{
"node": "Diagnose + Prioritise Fixes",
"type": "main",
"index": 0
}
]
]
},
"Diagnose + Prioritise Fixes": {
"main": [
[
{
"node": "Build Improvement Report",
"type": "main",
"index": 0
}
]
]
},
"Build Improvement Report": {
"main": [
[
{
"node": "Send Plan",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
AEO / GEO Improvement Loop (measure, diagnose, fix, re-check). Scheduled trigger; 7 nodes.
Source: https://github.com/saurabhshuklagrowisto/saurabh-ai-systems/blob/main/pipeline-automation/aeo-geo-improvement-loop.json — 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 template is an interactive playground designed to help you master the most useful keyboard shortcuts in n8n and supercharge your building speed. Forget boring lists—this workflow gives you hands-
Workflow 2469. Uses moveBinaryData, googleDrive, itemLists, n8n. Scheduled trigger; 33 nodes.
Perfect for content publishing with organic scheduling patterns, social media automation, API systems that need to avoid rate limiting, or any automation requiring randomised timing control across mul
Complete backup solution that saves both workflows and credentials to local/server disk with optional FTP upload for off-site redundancy.
todoist automate. Uses todoist, executionData. Scheduled trigger; 27 nodes.