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 →
{
"name": "Grain White-Label Agency Dashboard v1",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "days",
"daysInterval": 1,
"triggerAtHour": 6
}
]
}
},
"id": "daily-report-trigger",
"name": "Daily Report Trigger (06:00)",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-600,
300
],
"typeVersion": 1.2
},
{
"parameters": {
"httpMethod": "GET",
"path": "agency-dashboard/{{ $parameter.client_id }}",
"responseMode": "responseNode",
"options": {}
},
"id": "dashboard-api",
"name": "Dashboard API Endpoint",
"type": "n8n-nodes-base.webhook",
"position": [
-600,
500
],
"typeVersion": 2
},
{
"parameters": {
"jsCode": "// Agency & Client Configuration\nconst config = {\n agency: {\n id: $vars.AGENCY_ID || 'agency_001',\n name: $vars.AGENCY_NAME || 'Digital Agency',\n logo_url: $vars.AGENCY_LOGO || '',\n primary_color: $vars.AGENCY_PRIMARY_COLOR || '#2563eb',\n secondary_color: $vars.AGENCY_SECONDARY_COLOR || '#1e40af',\n domain: $vars.AGENCY_DOMAIN || 'reports.agency.com',\n email_from: $vars.AGENCY_EMAIL || 'reports@agency.com'\n },\n \n clients: [\n {\n id: 'client_001',\n name: '\u00d6rnek \u0130\u015fletme A.\u015e.',\n industry: 'restaurant',\n google_analytics_id: '',\n google_ads_id: '',\n meta_ads_id: '',\n google_business_id: '',\n social_accounts: {\n instagram: '',\n facebook: '',\n linkedin: ''\n },\n contacts: [\n { email: 'owner@example.com', name: '\u0130\u015fletme Sahibi', send_reports: true }\n ],\n report_frequency: 'weekly', // daily, weekly, monthly\n kpis: ['traffic', 'conversions', 'reviews', 'social_engagement']\n }\n ],\n \n report_templates: {\n executive_summary: true,\n traffic_analytics: true,\n seo_performance: true,\n social_media: true,\n paid_ads: true,\n reviews_reputation: true,\n recommendations: true\n }\n};\n\nreturn config;"
},
"id": "load-config",
"name": "Load Agency Configuration",
"type": "n8n-nodes-base.code",
"position": [
-380,
400
],
"typeVersion": 2
},
{
"parameters": {
"batchSize": 1,
"options": {}
},
"id": "split-clients",
"name": "Process Each Client",
"type": "n8n-nodes-base.splitInBatches",
"position": [
-160,
400
],
"typeVersion": 3
},
{
"parameters": {
"url": "https://www.googleapis.com/analytics/v3/data/ga",
"authentication": "genericCredentialType",
"genericAuthType": "oAuth2Api",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "ids",
"value": "ga:{{ $json.clients[0].google_analytics_id }}"
},
{
"name": "start-date",
"value": "30daysAgo"
},
{
"name": "end-date",
"value": "today"
},
{
"name": "metrics",
"value": "ga:sessions,ga:users,ga:pageviews,ga:bounceRate,ga:avgSessionDuration,ga:goalCompletionsAll"
}
]
},
"options": {}
},
"id": "fetch-ga-data",
"name": "Fetch Google Analytics",
"type": "n8n-nodes-base.httpRequest",
"position": [
60,
200
],
"typeVersion": 4.2
},
{
"parameters": {
"url": "https://searchconsole.googleapis.com/webmasters/v3/sites/{{ encodeURIComponent($json.website) }}/searchAnalytics/query",
"authentication": "genericCredentialType",
"genericAuthType": "oAuth2Api",
"method": "POST",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "{\n \"startDate\": \"{{ $now.minus({days: 30}).toFormat('yyyy-MM-dd') }}\",\n \"endDate\": \"{{ $now.toFormat('yyyy-MM-dd') }}\",\n \"dimensions\": [\"query\", \"page\"],\n \"rowLimit\": 100\n}",
"options": {}
},
"id": "fetch-gsc-data",
"name": "Fetch Search Console",
"type": "n8n-nodes-base.httpRequest",
"position": [
60,
400
],
"typeVersion": 4.2
},
{
"parameters": {
"url": "https://graph.facebook.com/v18.0/{{ $json.social_accounts.instagram }}/insights",
"authentication": "genericCredentialType",
"genericAuthType": "oAuth2Api",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "metric",
"value": "impressions,reach,follower_count,profile_views"
},
{
"name": "period",
"value": "day"
}
]
},
"options": {}
},
"id": "fetch-instagram",
"name": "Fetch Instagram Insights",
"type": "n8n-nodes-base.httpRequest",
"position": [
60,
600
],
"typeVersion": 4.2
},
{
"parameters": {
"url": "https://mybusinessbusinessinformation.googleapis.com/v1/{{ $json.google_business_id }}",
"authentication": "genericCredentialType",
"genericAuthType": "oAuth2Api",
"options": {}
},
"id": "fetch-gbp-data",
"name": "Fetch Google Business Profile",
"type": "n8n-nodes-base.httpRequest",
"position": [
60,
800
],
"typeVersion": 4.2
},
{
"parameters": {
"jsCode": "// Aggregate all data sources\nconst config = $('Load Agency Configuration').first().json;\nconst gaData = $('Fetch Google Analytics').first().json || {};\nconst gscData = $('Fetch Search Console').first().json || {};\nconst instagramData = $('Fetch Instagram Insights').first().json || {};\nconst gbpData = $('Fetch Google Business Profile').first().json || {};\n\nconst client = config.clients[0];\nconst reportDate = new Date().toISOString().split('T')[0];\n\n// Calculate metrics\nconst metrics = {\n traffic: {\n sessions: gaData.totalsForAllResults?.['ga:sessions'] || 0,\n users: gaData.totalsForAllResults?.['ga:users'] || 0,\n pageviews: gaData.totalsForAllResults?.['ga:pageviews'] || 0,\n bounce_rate: parseFloat(gaData.totalsForAllResults?.['ga:bounceRate'] || 0).toFixed(2),\n avg_session_duration: gaData.totalsForAllResults?.['ga:avgSessionDuration'] || 0,\n conversions: gaData.totalsForAllResults?.['ga:goalCompletionsAll'] || 0\n },\n \n seo: {\n total_clicks: gscData.rows?.reduce((sum, row) => sum + row.clicks, 0) || 0,\n total_impressions: gscData.rows?.reduce((sum, row) => sum + row.impressions, 0) || 0,\n avg_position: gscData.rows?.length > 0 \n ? (gscData.rows.reduce((sum, row) => sum + row.position, 0) / gscData.rows.length).toFixed(1) \n : 0,\n top_queries: (gscData.rows || []).slice(0, 10).map(r => ({\n query: r.keys[0],\n clicks: r.clicks,\n impressions: r.impressions\n }))\n },\n \n social: {\n instagram: {\n followers: instagramData.data?.find(d => d.name === 'follower_count')?.values[0]?.value || 0,\n reach: instagramData.data?.find(d => d.name === 'reach')?.values[0]?.value || 0,\n impressions: instagramData.data?.find(d => d.name === 'impressions')?.values[0]?.value || 0\n }\n },\n \n reviews: {\n google_rating: gbpData.averageRating || 0,\n total_reviews: gbpData.totalReviewCount || 0\n }\n};\n\n// Calculate period comparisons (mock for now)\nconst comparisons = {\n traffic_change: '+12.5%',\n seo_change: '+8.3%',\n social_change: '+15.2%',\n reviews_change: '+3'\n};\n\nreturn {\n report_id: `report_${Date.now()}`,\n generated_at: new Date().toISOString(),\n period: {\n start: $now.minus({days: 30}).toFormat('yyyy-MM-dd'),\n end: $now.toFormat('yyyy-MM-dd')\n },\n agency: config.agency,\n client: client,\n metrics: metrics,\n comparisons: comparisons\n};"
},
"id": "aggregate-data",
"name": "Aggregate All Data",
"type": "n8n-nodes-base.code",
"position": [
280,
400
],
"typeVersion": 2
},
{
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4o"
},
"messages": {
"values": [
{
"content": "=Sen profesyonel bir dijital pazarlama analisti ve rapor yazar\u0131s\u0131n. A\u015fa\u011f\u0131daki verilere g\u00f6re m\u00fc\u015fteri i\u00e7in \u00f6zet ve \u00f6neriler olu\u015ftur.\n\nM\u00fc\u015fteri: {{ $json.client.name }}\nSekt\u00f6r: {{ $json.client.industry }}\nD\u00f6nem: {{ $json.period.start }} - {{ $json.period.end }}\n\nMetrikler:\n{{ JSON.stringify($json.metrics, null, 2) }}\n\nDe\u011fi\u015fimler:\n{{ JSON.stringify($json.comparisons, null, 2) }}\n\nJSON format\u0131nda yan\u0131t ver:\n{\n \"executive_summary\": \"2-3 c\u00fcmlelik genel de\u011ferlendirme\",\n \"highlights\": [\n { \"metric\": \"metrik ad\u0131\", \"value\": \"de\u011fer\", \"trend\": \"up|down|stable\", \"insight\": \"k\u0131sa yorum\" }\n ],\n \"recommendations\": [\n { \"priority\": \"high|medium|low\", \"category\": \"seo|social|ads|content\", \"action\": \"yap\u0131lmas\u0131 gereken\", \"expected_impact\": \"beklenen etki\" }\n ],\n \"next_month_focus\": \"Gelecek ay odaklan\u0131lmas\u0131 gereken alan\"\n}"
}
]
},
"options": {
"responseFormat": "json_object",
"maxTokens": 1500,
"temperature": 0.5
}
},
"id": "generate-insights",
"name": "Generate AI Insights",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
500,
400
],
"typeVersion": 1.8
},
{
"parameters": {
"jsCode": "// Generate White-Label HTML Report\nconst data = $('Aggregate All Data').first().json;\nconst insights = JSON.parse($('Generate AI Insights').first().json.message.content);\nconst agency = data.agency;\nconst client = data.client;\nconst metrics = data.metrics;\n\nconst html = `\n<!DOCTYPE html>\n<html lang=\"tr\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Performans Raporu - ${client.name}</title>\n <style>\n :root {\n --primary: ${agency.primary_color};\n --secondary: ${agency.secondary_color};\n }\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: 'Segoe UI', system-ui, sans-serif; background: #f8fafc; color: #1e293b; }\n .header { background: linear-gradient(135deg, var(--primary), var(--secondary)); color: white; padding: 40px; }\n .header img { height: 50px; margin-bottom: 20px; }\n .header h1 { font-size: 28px; margin-bottom: 8px; }\n .header p { opacity: 0.9; }\n .container { max-width: 1200px; margin: 0 auto; padding: 40px 20px; }\n .summary-card { background: white; border-radius: 12px; padding: 24px; margin-bottom: 24px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }\n .summary-card h2 { color: var(--primary); margin-bottom: 16px; font-size: 20px; }\n .metrics-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-bottom: 24px; }\n .metric-card { background: white; border-radius: 12px; padding: 20px; text-align: center; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }\n .metric-card .value { font-size: 32px; font-weight: bold; color: var(--primary); }\n .metric-card .label { color: #64748b; font-size: 14px; margin-top: 4px; }\n .metric-card .change { font-size: 14px; margin-top: 8px; }\n .change.up { color: #22c55e; }\n .change.down { color: #ef4444; }\n .recommendations { list-style: none; }\n .recommendations li { padding: 16px; border-left: 4px solid var(--primary); background: #f8fafc; margin-bottom: 12px; border-radius: 0 8px 8px 0; }\n .recommendations li.high { border-color: #ef4444; }\n .recommendations li.medium { border-color: #f59e0b; }\n .recommendations li.low { border-color: #22c55e; }\n .footer { text-align: center; padding: 40px; color: #64748b; font-size: 14px; }\n .footer a { color: var(--primary); }\n </style>\n</head>\n<body>\n <div class=\"header\">\n ${agency.logo_url ? `<img src=\"${agency.logo_url}\" alt=\"${agency.name}\">` : `<h3>${agency.name}</h3>`}\n <h1>Ayl\u0131k Performans Raporu</h1>\n <p>${client.name} \u2022 ${data.period.start} - ${data.period.end}</p>\n </div>\n \n <div class=\"container\">\n <div class=\"summary-card\">\n <h2>\ud83d\udcca Y\u00f6netici \u00d6zeti</h2>\n <p>${insights.executive_summary}</p>\n </div>\n \n <div class=\"metrics-grid\">\n <div class=\"metric-card\">\n <div class=\"value\">${metrics.traffic.sessions.toLocaleString()}</div>\n <div class=\"label\">Oturum</div>\n <div class=\"change up\">${data.comparisons.traffic_change}</div>\n </div>\n <div class=\"metric-card\">\n <div class=\"value\">${metrics.traffic.users.toLocaleString()}</div>\n <div class=\"label\">Kullan\u0131c\u0131</div>\n </div>\n <div class=\"metric-card\">\n <div class=\"value\">${metrics.seo.total_clicks.toLocaleString()}</div>\n <div class=\"label\">SEO T\u0131klama</div>\n <div class=\"change up\">${data.comparisons.seo_change}</div>\n </div>\n <div class=\"metric-card\">\n <div class=\"value\">${metrics.reviews.google_rating}</div>\n <div class=\"label\">Google Puan\u0131</div>\n </div>\n </div>\n \n <div class=\"summary-card\">\n <h2>\ud83d\udca1 \u00d6neriler</h2>\n <ul class=\"recommendations\">\n ${insights.recommendations.map(r => `\n <li class=\"${r.priority}\">\n <strong>${r.action}</strong><br>\n <small>Beklenen Etki: ${r.expected_impact}</small>\n </li>\n `).join('')}\n </ul>\n </div>\n \n <div class=\"summary-card\">\n <h2>\ud83c\udfaf Gelecek Ay Odak</h2>\n <p>${insights.next_month_focus}</p>\n </div>\n </div>\n \n <div class=\"footer\">\n <p>Bu rapor ${agency.name} taraf\u0131ndan haz\u0131rlanm\u0131\u015ft\u0131r.</p>\n <p><a href=\"https://${agency.domain}\">${agency.domain}</a></p>\n </div>\n</body>\n</html>\n`;\n\nreturn {\n html_report: html,\n report_data: data,\n insights: insights\n};"
},
"id": "generate-html-report",
"name": "Generate White-Label HTML Report",
"type": "n8n-nodes-base.code",
"position": [
720,
400
],
"typeVersion": 2
},
{
"parameters": {
"operation": "toJson",
"mode": "jsonToBinary",
"options": {}
},
"id": "create-pdf",
"name": "Convert to PDF (Optional)",
"type": "n8n-nodes-base.convertToFile",
"position": [
940,
300
],
"disabled": true,
"typeVersion": 1.1
},
{
"parameters": {
"fromEmail": "={{ $('Load Agency Configuration').first().json.agency.email_from }}",
"toEmail": "={{ $('Aggregate All Data').first().json.client.contacts.filter(c => c.send_reports).map(c => c.email).join(',') }}",
"subject": "=\ud83d\udcca {{ $('Aggregate All Data').first().json.client.name }} - Ayl\u0131k Performans Raporu",
"emailType": "html",
"message": "={{ $json.html_report }}",
"options": {}
},
"id": "send-email-report",
"name": "Send Email Report",
"type": "n8n-nodes-base.gmail",
"position": [
940,
500
],
"typeVersion": 2.1
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ success: true, report: $json.report_data, insights: $json.insights }) }}",
"options": {
"responseHeaders": {
"entries": [
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "Access-Control-Allow-Origin",
"value": "*"
}
]
}
}
},
"id": "api-response",
"name": "API Response",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
940,
700
],
"typeVersion": 1.1
},
{
"parameters": {
"resource": "spreadsheet",
"documentId": {
"__rl": true,
"mode": "list",
"value": ""
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "Reports Log"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"report_id": "={{ $json.report_data.report_id }}",
"client_name": "={{ $json.report_data.client.name }}",
"generated_at": "={{ $json.report_data.generated_at }}",
"sessions": "={{ $json.report_data.metrics.traffic.sessions }}",
"users": "={{ $json.report_data.metrics.traffic.users }}",
"seo_clicks": "={{ $json.report_data.metrics.seo.total_clicks }}"
}
},
"options": {}
},
"id": "log-to-sheets",
"name": "Log Report to Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
1160,
400
],
"disabled": true,
"typeVersion": 4.5
}
],
"connections": {
"Daily Report Trigger (06:00)": {
"main": [
[
{
"node": "Load Agency Configuration",
"type": "main",
"index": 0
}
]
]
},
"Dashboard API Endpoint": {
"main": [
[
{
"node": "Load Agency Configuration",
"type": "main",
"index": 0
}
]
]
},
"Load Agency Configuration": {
"main": [
[
{
"node": "Process Each Client",
"type": "main",
"index": 0
}
]
]
},
"Process Each Client": {
"main": [
[
{
"node": "Fetch Google Analytics",
"type": "main",
"index": 0
},
{
"node": "Fetch Search Console",
"type": "main",
"index": 0
},
{
"node": "Fetch Instagram Insights",
"type": "main",
"index": 0
},
{
"node": "Fetch Google Business Profile",
"type": "main",
"index": 0
}
]
]
},
"Fetch Google Analytics": {
"main": [
[
{
"node": "Aggregate All Data",
"type": "main",
"index": 0
}
]
]
},
"Fetch Search Console": {
"main": [
[
{
"node": "Aggregate All Data",
"type": "main",
"index": 0
}
]
]
},
"Fetch Instagram Insights": {
"main": [
[
{
"node": "Aggregate All Data",
"type": "main",
"index": 0
}
]
]
},
"Fetch Google Business Profile": {
"main": [
[
{
"node": "Aggregate All Data",
"type": "main",
"index": 0
}
]
]
},
"Aggregate All Data": {
"main": [
[
{
"node": "Generate AI Insights",
"type": "main",
"index": 0
}
]
]
},
"Generate AI Insights": {
"main": [
[
{
"node": "Generate White-Label HTML Report",
"type": "main",
"index": 0
}
]
]
},
"Generate White-Label HTML Report": {
"main": [
[
{
"node": "Send Email Report",
"type": "main",
"index": 0
},
{
"node": "API Response",
"type": "main",
"index": 0
}
]
]
},
"Send Email Report": {
"main": [
[
{
"node": "Log Report to Sheets",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"saveManualExecutions": true,
"callerPolicy": "workflowsFromSameOwner"
},
"staticData": null,
"tags": [
{
"name": "White-Label"
},
{
"name": "Agency"
},
{
"name": "Dashboard"
},
{
"name": "Reporting"
},
{
"name": "Grain"
}
],
"triggerCount": 2,
"updatedAt": "2026-01-09T00:00:00.000Z",
"versionId": "grain-whitelabel-dashboard-v1"
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Grain White-Label Agency Dashboard v1. Uses httpRequest, openAi, gmail, googleSheets. Scheduled trigger; 15 nodes.
Source: https://github.com/No3214/saas/blob/efb737b073e5ff52f402061f133591fe5209bcd6/templates/agency-revops/Grain_WhiteLabel_Agency_Dashboard_v1.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.
Stop wasting billable hours on manual time-tracking. AutoTimesheet Pro uses AI to collect emails, meetings, and GitHub work, then writes a clean timesheet straight into Google Sheets. Perfect for deve
Imagine a dedicated financial expert tirelessly working behind the scenes, sifting through every transaction, every investment move, and every accounting entry. That's exactly what this automated syst
Who is this for? AI creators, marketers, agencies, and researchers tracking YouTube trends who need weekly high-signal insights without 4+ hours manual research.
Automatically logs time to Jira every night from a Google Sheet. No manual worklog entries needed — just fill in your sheet and the workflow handles the rest at 10 PM.
This template automates overnight system health monitoring for DevOps and IT operations teams. It checks your internal services and APIs on a schedule, logs all results to Google Sheets, and sends AI-