Marketing analytics reports with Google Analytics, Sheets, Slides & email alerts
Pull contacts, verify each address with BillionVerify, and continue to Google Analytics — only deliverable addresses get through.
Why verify before the send
Sending to invalid, risky, catch-all, or disposable addresses spikes your bounce rate and erodes sender reputation. A verification gate before the Google Analytics step removes that risk automatically — only deliverable addresses continue, the rest are flagged.
The workflow
BillionVerify — verification sits right before the send.
Node by node
- 1Weekly/Monthly ScheduleTrigger· n8n
Starts the workflow — on a schedule, a webhook, or manually while you test.
- 2Workflow ConfigurationSource· n8n
Provides or transforms the contact data flowing through the workflow.
- 3Get Google Analytics DataSource· n8n
Provides or transforms the contact data flowing through the workflow.
- 4Get Ad Platform DataSource· n8n
Provides or transforms the contact data flowing through the workflow.
- 5Get CRM DataSource· n8n
Provides or transforms the contact data flowing through the workflow.
- 6Calculate KPIs (MAU, LTV, CAC)Source· n8n
Provides or transforms the contact data flowing through the workflow.
- 7Write Data to Google SheetsSource· n8n
Provides or transforms the contact data flowing through the workflow.
- 8Create Google Slides ReportSource· n8n
Provides or transforms the contact data flowing through the workflow.
- 9Check for ErrorsLogic· n8n
Branches on the verification result: only deliverable addresses continue to the send; the rest are skipped and flagged.
- 10Verify Email (BillionVerify)Verify· billionverify
The BillionVerify node verifies the address — status (valid / invalid / risky / catch-all / role / disposable), is_deliverable, and a confidence score — before anything is sent.
- 11Send Error NotificationSource· n8n
Provides or transforms the contact data flowing through the workflow.
- 12IF deliverableLogic· n8n
Branches on the verification result: only deliverable addresses continue to the send; the rest are skipped and flagged.
- 13Send Report EmailSend· n8n
Sends only to verified, deliverable addresses. Swap in your own provider node if you send elsewhere.
Workflow JSON
Copy or download this workflow, then import it in n8n (Workflows → Import from File / Paste). Install the BillionVerify community node first, then add your API key credential.
{
"name": "Marketing analytics reports with Google Analytics, Sheets, Slides & email alerts + BillionVerify",
"nodes": [
{
"id": "43a2a58a-a76f-4b86-bbfc-2274c8a9b2dc",
"name": "Weekly/Monthly Schedule",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-448,
192
],
"parameters": {
"rule": {
"interval": [
{
"field": "weeks",
"triggerAtDay": [
1
],
"triggerAtHour": 9
},
{
"field": "months",
"triggerAtHour": 9
}
]
}
},
"typeVersion": 1.2
},
{
"id": "3770762f-89b8-4575-9ccd-c2b636651eee",
"name": "Workflow Configuration",
"type": "n8n-nodes-base.set",
"position": [
-224,
192
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "id-1",
"name": "gaPropertyId",
"type": "string",
"value": "<__PLACEHOLDER_VALUE__Google Analytics Property ID__>"
},
{
"id": "id-2",
"name": "adPlatformApiUrl",
"type": "string",
"value": "<__PLACEHOLDER_VALUE__Ad Platform API endpoint URL__>"
},
{
"id": "id-3",
"name": "crmApiUrl",
"type": "string",
"value": "<__PLACEHOLDER_VALUE__CRM API endpoint URL__>"
},
{
"id": "id-4",
"name": "reportSpreadsheetId",
"type": "string",
"value": "<__PLACEHOLDER_VALUE__Google Sheets ID for report data__>"
},
{
"id": "id-5",
"name": "slidesTemplateId",
"type": "string",
"value": "<__PLACEHOLDER_VALUE__Google Slides template ID__>"
},
{
"id": "id-6",
"name": "reportRecipients",
"type": "string",
"value": "<__PLACEHOLDER_VALUE__Comma-separated email addresses__>"
},
{
"id": "id-7",
"name": "slackChannel",
"type": "string",
"value": "<__PLACEHOLDER_VALUE__Slack channel ID for error notifications__>"
},
{
"id": "id-8",
"name": "reportPeriodDays",
"type": "number",
"value": 7
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "7f1e4958-8b6e-4575-ac41-adb6581c3d9a",
"name": "Get Google Analytics Data",
"type": "n8n-nodes-base.googleAnalytics",
"position": [
0,
0
],
"parameters": {
"endDate": "={{ $now.minus({ days: 1 }).toFormat('yyyy-MM-dd') }}",
"dateRange": "custom",
"startDate": "={{ $now.minus({ days: $('Workflow Configuration').first().json.reportPeriodDays }).toFormat('yyyy-MM-dd') }}",
"metricsGA4": {
"metricValues": [
{
"listName": "active1DayUsers"
},
{
"listName": "sessions"
},
{
"listName": "screenPageViews"
},
{
"name": "conversions",
"listName": "other"
}
]
},
"propertyId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Workflow Configuration').first().json.gaPropertyId }}"
},
"dimensionsGA4": {
"dimensionValues": [
{}
]
},
"additionalFields": {}
},
"typeVersion": 2
},
{
"id": "9f9cc333-a289-406e-a632-49642d764f1a",
"name": "Get Ad Platform Data",
"type": "n8n-nodes-base.httpRequest",
"position": [
0,
192
],
"parameters": {
"url": "={{ $('Workflow Configuration').first().json.adPlatformApiUrl }}",
"options": {},
"sendQuery": true,
"authentication": "predefinedCredentialType",
"queryParameters": {
"parameters": [
{
"name": "start_date",
"value": "={{ $now.minus({ days: $('Workflow Configuration').first().json.reportPeriodDays }).toFormat('yyyy-MM-dd') }}"
},
{
"name": "end_date",
"value": "={{ $now.minus({ days: 1 }).toFormat('yyyy-MM-dd') }}"
}
]
}
},
"typeVersion": 4.3
},
{
"id": "0ad1b7a4-e29f-4206-b5e3-a2a516560422",
"name": "Get CRM Data",
"type": "n8n-nodes-base.httpRequest",
"position": [
0,
384
],
"parameters": {
"url": "={{ $('Workflow Configuration').first().json.crmApiUrl }}",
"options": {},
"sendQuery": true,
"authentication": "predefinedCredentialType",
"queryParameters": {
"parameters": [
{
"name": "start_date",
"value": "={{ $now.minus({ days: $('Workflow Configuration').first().json.reportPeriodDays }).toFormat('yyyy-MM-dd') }}"
},
{
"name": "end_date",
"value": "={{ $now.minus({ days: 1 }).toFormat('yyyy-MM-dd') }}"
}
]
}
},
"typeVersion": 4.3
},
{
"id": "bd9aae8e-6342-4efa-a349-2ad4e645b9f8",
"name": "Calculate KPIs (MAU, LTV, CAC)",
"type": "n8n-nodes-base.code",
"position": [
256,
192
],
"parameters": {
"jsCode": "const gaData = $input.first().json;\nconst adData = $input.all()[1]?.json || {};\nconst crmData = $input.all()[2]?.json || {};\nconst mau = gaData.activeUsers || 0;\nconst totalAdSpend = adData.totalSpend || 0;\nconst newCustomers = crmData.newCustomers || 1;\nconst cac = totalAdSpend / newCustomers;\nconst totalRevenue = crmData.totalRevenue || 0;\nconst totalCustomers = crmData.totalCustomers || 1;\nconst ltv = totalRevenue / totalCustomers;\nconst sessions = gaData.sessions || 0;\nconst conversions = gaData.conversions || 0;\nconst conversionRate = sessions > 0 ? (conversions / sessions * 100).toFixed(2) : 0;\nreturn [{\n json: {\n reportDate: new Date().toISOString(),\n periodStart: $now.minus({ days: $('Workflow Configuration').first().json.reportPeriodDays }).toFormat('yyyy-MM-dd'),\n periodEnd: $now.minus({ days: 1 }).toFormat('yyyy-MM-dd'),\n mau: mau,\n cac: parseFloat(cac.toFixed(2)),\n ltv: parseFloat(ltv.toFixed(2)),\n ltvCacRatio: parseFloat((ltv / cac).toFixed(2)),\n sessions: sessions,\n conversions: conversions,\n conversionRate: parseFloat(conversionRate),\n totalAdSpend: totalAdSpend,\n newCustomers: newCustomers,\n totalRevenue: totalRevenue\n }\n}];"
},
"typeVersion": 2
},
{
"id": "a88d2469-2882-4db4-86e1-018abaaf0f32",
"name": "Write Data to Google Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
496,
192
],
"parameters": {
"columns": {
"value": {},
"schema": [],
"mappingMode": "autoMapInputData",
"matchingColumns": [
"reportDate"
]
},
"options": {},
"operation": "appendOrUpdate",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "KPI Data"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Workflow Configuration').first().json.reportSpreadsheetId }}"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "credential-id",
"name": "googleSheetsOAuth2Api Credential"
}
},
"typeVersion": 4.7
},
{
"id": "3fa10a78-3c7f-46e4-a693-10c9fd6fb376",
"name": "Create Google Slides Report",
"type": "n8n-nodes-base.googleSlides",
"position": [
736,
192
],
"parameters": {
"title": "={{ 'Analytics Report - ' + $now.toFormat('yyyy-MM-dd') }}"
},
"typeVersion": 2
},
{
"id": "c976799f-8feb-4b1f-ad17-ce11f9393a5a",
"name": "Check for Errors",
"type": "n8n-nodes-base.if",
"position": [
960,
192
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "id-1",
"operator": {
"type": "string",
"operation": "notExists"
},
"leftValue": "={{ $json.error }}"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "1c52b591-2f9e-43e6-a07d-e4be6578d9c2",
"name": "Send Report Email",
"type": "n8n-nodes-base.gmail",
"position": [
1216,
64
],
"webhookId": "6ca702a2-e5bd-4ef3-94f8-b50afa6cf12b",
"parameters": {
"sendTo": "={{ $('Workflow Configuration').first().json.reportRecipients }}",
"message": "=<h2>Analytics Report</h2>\n<p><strong>Period:</strong> {{ $json.periodStart }} to {{ $json.periodEnd }}</p>\n\n<h3>Key Performance Indicators</h3>\n<ul>\n <li><strong>MAU (Monthly Active Users):</strong> {{ $json.mau }}</li>\n <li><strong>CAC (Customer Acquisition Cost):</strong> ${{ $json.cac }}</li>\n <li><strong>LTV (Lifetime Value):</strong> ${{ $json.ltv }}</li>\n <li><strong>LTV:CAC Ratio:</strong> {{ $json.ltvCacRatio }}</li>\n <li><strong>Conversion Rate:</strong> {{ $json.conversionRate }}%</li>\n</ul>\n\n<h3>Additional Metrics</h3>\n<ul>\n <li><strong>Sessions:</strong> {{ $json.sessions }}</li>\n <li><strong>Conversions:</strong> {{ $json.conversions }}</li>\n <li><strong>New Customers:</strong> {{ $json.newCustomers }}</li>\n <li><strong>Total Ad Spend:</strong> ${{ $json.totalAdSpend }}</li>\n <li><strong>Total Revenue:</strong> ${{ $json.totalRevenue }}</li>\n</ul>\n\n<p>View the full report in <a href=\"https://docs.google.com/presentation/d/{{ $('Create Google Slides Report').first().json.presentationId }}\">Google Slides</a></p>",
"options": {
"attachmentsUi": {
"attachmentsBinary": [
{}
]
}
},
"subject": "={{ 'Weekly/Monthly Analytics Report - ' + $now.toFormat('yyyy-MM-dd') }}"
},
"credentials": {
"gmailOAuth2": {
"id": "credential-id",
"name": "gmailOAuth2 Credential"
}
},
"typeVersion": 2.1
},
{
"id": "05820f46-c77c-43a6-b5a6-02829ba141ed",
"name": "Send Error Notification",
"type": "n8n-nodes-base.slack",
"position": [
1200,
432
],
"webhookId": "46ac626d-f7a4-47fd-9f7c-5a72530a897b",
"parameters": {
"text": "={{ '🚨 Analytics Report Generation Failed\\n\\n*Error:* ' + ($json.error || 'Unknown error occurred') + '\\n*Time:* ' + $now.toISO() }}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Workflow Configuration').first().json.slackChannel }}"
},
"otherOptions": {},
"authentication": "oAuth2"
},
"credentials": {
"slackOAuth2Api": {
"id": "credential-id",
"name": "slackOAuth2Api Credential"
}
},
"typeVersion": 2.3
},
{
"id": "03a6ec52-853c-4d50-a644-750e1c5e25b7",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1296,
-288
],
"parameters": {
"width": 768,
"height": 1056,
"content": "## Automate and Distribute Weekly and Monthly Marketing Analytics Reports\n\n### Who's it for?\nThis workflow is designed for marketing teams, data analysts, and business owners who need to consistently track key performance indicators (KPIs). It saves hours of manual data collection and reporting, ensuring stakeholders receive timely updates automatically.\n\n### What it does\nThis workflow automates the entire process of creating and distributing regular analytics reports. On a recurring weekly and monthly schedule, it:\n1. **Fetches Data:** Gathers the latest data from Google Analytics (users, sessions, conversions), your advertising platform (ad spend), and your CRM (new customers, revenue).\n2. **Calculates KPIs:** Processes the raw data to calculate essential business metrics, including Monthly Active Users (MAU), Customer Acquisition Cost (CAC), Lifetime Value (LTV), LTV:CAC ratio, and conversion rates.\n3. **Logs Historical Data:** Appends the newly calculated KPIs to a Google Sheet, creating a historical record for trend analysis.\n4. **Generates a Report:** Creates a new Google Slides presentation to serve as the main report.\n5. **Distributes the Report:** Emails a summary of the key metrics to a predefined list of recipients, including a link to the full Google Slides report.\n6. **Provides Error Alerts:** If any step in the process fails, it sends an immediate notification to a designated Slack channel so you can address the issue quickly.\n\n### How to set up\n1. **Configure Credentials:** Add your credentials for Google Analytics, Google Sheets, Google Slides, Gmail, Slack, and the HTTP Request nodes (for your ad platform and CRM).\n2. **Fill in Placeholders:** In the \"Workflow Configuration\" node, replace all placeholder values with your specific information:\n * `gaPropertyId`: Your Google Analytics Property ID.\n * `adPlatformApiUrl`: The API endpoint for your advertising platform.\n * `crmApiUrl`: The API endpoint for your CRM.\n * `reportSpreadsheetId`: The ID of the Google Sheet where data will be stored.\n * `slidesTemplateId`: The ID of your Google Slides report template.\n * `reportRecipients`: A comma-separated list of email addresses for report distribution.\n * `slackChannel`: The Slack channel ID for error notifications.\n3. **Activate Workflow:** Turn the workflow on. It will now run based on the schedule defined in the \"Weekly/Monthly Schedule\" trigger node.\n\n### How to customize the workflow\n* **Adjust Schedule:** Modify the \"Weekly/Monthly Schedule\" node to change the reporting frequency (e.g., daily, bi-weekly).\n* **Change Reporting Period:** In the \"Workflow Configuration\" node, change the `reportPeriodDays` value to adjust the lookback window (e.g., set to `30` for a monthly report).\n* **Customize KPIs:** Edit the Javascript in the \"Calculate KPIs (MAU, LTV, CAC)\" node to add or modify metrics.\n* **Enhance the Report:** Expand the \"Create Google Slides Report\" node to dynamically add charts and data from the calculated KPIs into the slides."
},
"typeVersion": 1
},
{
"id": "464e2f71-bf7f-4b0c-a372-658644e63da3",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
1136,
288
],
"parameters": {
"color": 6,
"height": 304,
"content": " **Provides Error Alerts:** If any step in the process fails, it sends an immediate notification to a designated Slack channel so you can address the issue quickly."
},
"typeVersion": 1
},
{
"id": "9c4b764d-0985-414a-9b9d-c2fc5f9291b4",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1152,
-64
],
"parameters": {
"color": 5,
"height": 288,
"content": " **Distributes the Report:** Emails a summary of the key metrics to a predefined list of recipients, including a link to the full Google Slides report."
},
"typeVersion": 1
},
{
"id": "f003852c-edb9-4446-98d6-264b3ca254ce",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
672,
16
],
"parameters": {
"color": 4,
"height": 352,
"content": " **Generates a Report:** Creates a new Google Slides presentation to serve as the main report."
},
"typeVersion": 1
},
{
"id": "8fe51cfa-4897-4d79-bc78-f03afc5c6011",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-64,
-160
],
"parameters": {
"color": 3,
"height": 688,
"content": " **Fetches Data:** Gathers the latest data from Google Analytics (users, sessions, conversions), your advertising platform (ad spend), and your CRM (new customers, revenue)."
},
"typeVersion": 1
},
{
"id": "5b686967-717b-40f9-80fb-e8c7935245d2",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
192,
16
],
"parameters": {
"color": 7,
"height": 352,
"content": " **Calculates KPIs:** Processes the raw data to calculate essential business metrics, including Monthly Active Users (MAU), Customer Acquisition Cost (CAC), Lifetime Value (LTV), LTV:CAC ratio, and conversion rates."
},
"typeVersion": 1
},
{
"id": "646db3f3-eda5-4b58-94fb-611e5426ae0f",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
432,
16
],
"parameters": {
"color": 6,
"height": 352,
"content": " **Logs Historical Data:** Appends the newly calculated KPIs to a Google Sheet, creating a historical record for trend analysis."
},
"typeVersion": 1
},
{
"parameters": {
"operation": "verify",
"email": "={{ $('Workflow Configuration').first().json.reportRecipients }}",
"additionalOptions": {}
},
"type": "n8n-nodes-billionverify.billionVerify",
"typeVersion": 1,
"position": [
856,
64
],
"name": "Verify Email (BillionVerify)",
"credentials": {
"billionVerifyApi": {
"id": "",
"name": "BillionVerify account"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "is-deliverable",
"leftValue": "={{ $json.is_deliverable }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
]
}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1036,
64
],
"name": "IF deliverable"
}
],
"connections": {
"Get CRM Data": {
"main": [
[
{
"node": "Calculate KPIs (MAU, LTV, CAC)",
"type": "main",
"index": 0
}
]
]
},
"Check for Errors": {
"main": [
[
{
"node": "Verify Email (BillionVerify)",
"type": "main",
"index": 0
}
],
[
{
"node": "Send Error Notification",
"type": "main",
"index": 0
}
]
]
},
"Get Ad Platform Data": {
"main": [
[
{
"node": "Calculate KPIs (MAU, LTV, CAC)",
"type": "main",
"index": 0
}
]
]
},
"Workflow Configuration": {
"main": [
[
{
"node": "Get Google Analytics Data",
"type": "main",
"index": 0
},
{
"node": "Get Ad Platform Data",
"type": "main",
"index": 0
},
{
"node": "Get CRM Data",
"type": "main",
"index": 0
}
]
]
},
"Weekly/Monthly Schedule": {
"main": [
[
{
"node": "Workflow Configuration",
"type": "main",
"index": 0
}
]
]
},
"Get Google Analytics Data": {
"main": [
[
{
"node": "Calculate KPIs (MAU, LTV, CAC)",
"type": "main",
"index": 0
}
]
]
},
"Create Google Slides Report": {
"main": [
[
{
"node": "Check for Errors",
"type": "main",
"index": 0
}
]
]
},
"Write Data to Google Sheets": {
"main": [
[
{
"node": "Create Google Slides Report",
"type": "main",
"index": 0
}
]
]
},
"Calculate KPIs (MAU, LTV, CAC)": {
"main": [
[
{
"node": "Write Data to Google Sheets",
"type": "main",
"index": 0
}
]
]
},
"Verify Email (BillionVerify)": {
"main": [
[
{
"node": "IF deliverable",
"type": "main",
"index": 0
}
]
]
},
"IF deliverable": {
"main": [
[
{
"node": "Send Report Email",
"type": "main",
"index": 0
}
],
[]
]
}
},
"settings": {
"executionOrder": "v1"
}
}When to use this
- Cleaning a list before a Google Analytics send or sync.
- Protecting Google Analytics deliverability and sender reputation.
- Keeping bounce rates low so your sending is never throttled.
FAQ
Why verify before sending in Google Analytics?
Verifying first keeps your bounce rate low, which protects your sender reputation and your results.
How do I import this workflow?
Download the JSON, then in n8n go to Workflows → Import from File (or paste it). Install the BillionVerify community node and add your API key credential.
What happens to risky or catch-all addresses?
They are routed to the false branch and excluded from the send. You decide whether to retry, review, or drop them.
Add verification to your workflow
Create a free account, grab your API key, and stop bounces before they happen.
Get started free