This workflow corresponds to n8n.io template #16933 — we link there as the canonical source.
This workflow follows the Agent → Emailsend 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": "8DUJ1ZRkEIWd8IMd",
"name": "Send weekly Vercel analytics digest emails with AI summaries from Groq",
"tags": [],
"nodes": [
{
"id": "sticky-main",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-160,
-16
],
"parameters": {
"width": 520,
"height": 1056,
"content": "## Send weekly Vercel analytics digest emails with AI summaries from Groq\n\n### Who's it for\n\nAnyone hosting a website on Vercel who wants a weekly analytics summary in their inbox instead of checking dashboards.\n\n### How it works\n\n1. Triggers every Monday at 9 AM.\n2. Fetches the last 7 days from Vercel Web Analytics \u2014 summary metrics, top pages, and referrer sources.\n3. A Code node consolidates the three API responses into a single report object.\n4. An AI agent (Groq Llama 3.3 70B) formats it into a clean HTML email digest.\n5. If the AI call ever fails, a fallback branch sends the raw statistics instead \u2014 you never miss a digest.\n6. The digest is delivered to your inbox via SMTP.\n\n### Setup steps\n\n- [ ] Create a Vercel API token: Vercel Dashboard \u2192 Settings \u2192 Tokens\n- [ ] In n8n, create a **Header Auth** credential: Name = `Authorization`, Value = `Bearer YOUR_TOKEN`\n- [ ] Enter your **Vercel Project ID**, **Team ID** (leave blank if personal), **site name**, and **email addresses** in the Set Config node\n- [ ] Connect your **Groq** credential (free tier works)\n- [ ] Connect your **SMTP** credential\n\n### Customisation\n\nAdjust the schedule, change the date range in Set Config, swap Groq for another LLM, or add country/device breakdowns by adding more Fetch nodes."
},
"typeVersion": 1
},
{
"id": "sticky-schedule",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
400,
48
],
"parameters": {
"color": 7,
"width": 256,
"height": 400,
"content": "## Schedule\n\nFires every Monday at 9 AM (your n8n instance timezone).\n\nAdjust the day and time in the trigger node to suit your preference."
},
"typeVersion": 1
},
{
"id": "sticky-configure",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
672,
48
],
"parameters": {
"color": 7,
"width": 256,
"height": 480,
"content": "## Configure\n\nThe only node you must edit. Set your Vercel **Project ID**, **Team ID** (blank if personal), **site name**, and **email addresses** here.\n\nFind Project ID in:\nVercel Dashboard \u2192 Project \u2192 Settings \u2192 General."
},
"typeVersion": 1
},
{
"id": "sticky-fetch",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
944,
48
],
"parameters": {
"color": 7,
"width": 672,
"height": 480,
"content": "## Fetch Vercel Analytics Data (Last 7 Days)\n\nThree sequential GET requests to the Vercel Web Analytics Query API:\n- **Summary** \u2014 `visits/count` \u2192 total page views + unique visitors\n- **Top Pages** \u2014 `visits/aggregate` grouped by `requestPath`, top 5\n- **Referrers** \u2014 `visits/aggregate` grouped by `referrerHostname`, top 5\n\n**Good to know:** these are the only endpoints the API exposes \u2014 there are no /summary, /pages or /referrers endpoints, and only pageviews + visitors metrics exist (no bounce rate or duration). All requests use millisecond timestamps (`since`/`until`) from Set Config. Auth via Header Auth credential (Bearer token)."
},
"typeVersion": 1
},
{
"id": "sticky-process",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1648,
48
],
"parameters": {
"color": 7,
"width": 768,
"height": 760,
"content": "## Process & Send\n\n**Build Report** \u2014 consolidates all three Vercel API responses into one clean JSON summary with defensive parsing (handles empty or missing data gracefully).\n\n**Digest Agent** (Groq Llama 3.3 70B) \u2014 formats the data into a structured HTML email with headline, key metrics, top pages, referrer sources, and one actionable insight.\n\n**Raw Stats Fallback** \u2014 if the AI call errors, this branch turns the report into a plain-stats email body, so the digest still arrives.\n\n**Send Digest Email** \u2014 wraps the output in a clean HTML template and delivers it via SMTP."
},
"typeVersion": 1
},
{
"id": "schedule-trigger",
"name": "Every Monday 9 AM",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
480,
368
],
"parameters": {
"rule": {
"interval": [
{
"field": "weeks",
"triggerAtDay": [
1
],
"triggerAtHour": 9
}
]
}
},
"typeVersion": 1.2
},
{
"id": "set-config",
"name": "Set Config",
"type": "n8n-nodes-base.set",
"position": [
720,
368
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "project-id",
"name": "projectId",
"type": "string",
"value": "YOUR_VERCEL_PROJECT_ID"
},
{
"id": "team-id",
"name": "teamId",
"type": "string",
"value": ""
},
{
"id": "site-name",
"name": "siteName",
"type": "string",
"value": "My Website"
},
{
"id": "recipient-email",
"name": "recipientEmail",
"type": "string",
"value": "you@example.com"
},
{
"id": "sender-email",
"name": "senderEmail",
"type": "string",
"value": "user@example.com"
},
{
"id": "from-ts",
"name": "since",
"type": "number",
"value": "={{ $now.minus({ days: 7 }).toMillis() }}"
},
{
"id": "to-ts",
"name": "until",
"type": "number",
"value": "={{ $now.toMillis() }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "fetch-summary",
"name": "Fetch Vercel Summary",
"type": "n8n-nodes-base.httpRequest",
"position": [
992,
368
],
"parameters": {
"url": "=https://api.vercel.com/v1/query/web-analytics/visits/count?projectId={{ $('Set Config').item.json.projectId }}{{ $('Set Config').item.json.teamId ? '&teamId=' + $('Set Config').item.json.teamId : '' }}&since={{ $('Set Config').item.json.since }}&until={{ $('Set Config').item.json.until }}",
"options": {},
"authentication": "predefinedCredentialType",
"nodeCredentialType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"retryOnFail": true,
"typeVersion": 4.2
},
{
"id": "fetch-pages",
"name": "Fetch Vercel Top Pages",
"type": "n8n-nodes-base.httpRequest",
"position": [
1200,
368
],
"parameters": {
"url": "=https://api.vercel.com/v1/query/web-analytics/visits/aggregate?projectId={{ $('Set Config').item.json.projectId }}{{ $('Set Config').item.json.teamId ? '&teamId=' + $('Set Config').item.json.teamId : '' }}&since={{ $('Set Config').item.json.since }}&until={{ $('Set Config').item.json.until }}&by=requestPath&limit=5",
"options": {},
"authentication": "predefinedCredentialType",
"nodeCredentialType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"retryOnFail": true,
"typeVersion": 4.2
},
{
"id": "fetch-referrers",
"name": "Fetch Vercel Referrers",
"type": "n8n-nodes-base.httpRequest",
"position": [
1440,
368
],
"parameters": {
"url": "=https://api.vercel.com/v1/query/web-analytics/visits/aggregate?projectId={{ $('Set Config').item.json.projectId }}{{ $('Set Config').item.json.teamId ? '&teamId=' + $('Set Config').item.json.teamId : '' }}&since={{ $('Set Config').item.json.since }}&until={{ $('Set Config').item.json.until }}&by=referrerHostname&limit=5",
"options": {},
"authentication": "predefinedCredentialType",
"nodeCredentialType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"retryOnFail": true,
"typeVersion": 4.2
},
{
"id": "build-report",
"name": "Build Report",
"type": "n8n-nodes-base.code",
"position": [
1680,
368
],
"parameters": {
"jsCode": "const summaryRaw = $('Fetch Vercel Summary').item.json;\nconst pagesRaw = $('Fetch Vercel Top Pages').item.json;\nconst referrersRaw = $('Fetch Vercel Referrers').item.json;\n\n// visits/count \u2192 { data: { pageviews, visitors } }\nconst s = summaryRaw.data || {};\nconst pageViews = s.pageviews || 0;\nconst visitors = s.visitors || 0;\nconst pagesPerVisit = visitors > 0 ? (pageViews / visitors).toFixed(2) : '0.00';\n\n// visits/aggregate by=requestPath \u2192 { data: [{ requestPath, pageviews, visitors }] }\nconst pagesArr = Array.isArray(pagesRaw.data) ? pagesRaw.data : [];\nconst topPages = pagesArr.slice(0, 5).map(p => ({\n page: p.requestPath || 'Unknown',\n views: p.pageviews || 0,\n visitors: p.visitors || 0\n}));\n\n// visits/aggregate by=referrerHostname \u2192 { data: [{ referrerHostname, pageviews, visitors }] }\nconst refArr = Array.isArray(referrersRaw.data) ? referrersRaw.data : [];\nconst referrers = refArr.slice(0, 5).map(r => ({\n source: r.referrerHostname || 'Direct / None',\n visits: r.visitors || r.pageviews || 0\n}));\n\nconst reportData = JSON.stringify({\n period: 'Last 7 days',\n overview: { pageViews, visitors, pagesPerVisit },\n topPages,\n referrers\n}, null, 2);\n\nreturn [{ json: { reportData } }];"
},
"typeVersion": 2
},
{
"id": "digest-agent",
"name": "Digest Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"onError": "continueErrorOutput",
"position": [
1920,
368
],
"parameters": {
"text": "={{ $json.reportData }}",
"options": {
"systemMessage": "You are a weekly analytics reporter for a website.\n\nAnalyse the Vercel Analytics JSON data provided and generate a concise weekly digest email body.\n\nOutput clean HTML only \u2014 no <html>, <head>, or <body> tags. Use:\n- <h3 style=\"color:#1a73c8;margin:20px 0 8px;\"> for section headers\n- <p style=\"margin:0 0 12px;\"> for paragraphs\n- <ul style=\"margin:0 0 12px;padding-left:20px;\"><li> for list items\n- <strong> for key numbers and metrics\n\nInclude exactly these sections:\n1. <h3>\ud83d\udcca Weekly Headline</h3> \u2014 one sentence highlighting the most notable stat (e.g. '1 200 page views this week')\n2. <h3>\ud83d\udcc8 Key Metrics</h3> \u2014 page views, unique visitors, pages per visit\n3. <h3>\ud83d\udd1d Top Pages</h3> \u2014 page path and view count for each\n4. <h3>\ud83d\udea6 Traffic Sources</h3> \u2014 referrer hostname and visit count ('Direct / None' means direct traffic)\n5. <h3>\ud83d\udca1 Insight</h3> \u2014 one specific, actionable observation the site owner should act on\n\nOnly report metrics present in the data \u2014 never invent numbers. Keep total under 300 words. Tone: professional, data-driven, and friendly."
},
"promptType": "define"
},
"typeVersion": 2.2
},
{
"id": "groq-model",
"name": "Groq Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGroq",
"position": [
1856,
640
],
"parameters": {
"model": "llama-3.3-70b-versatile",
"options": {}
},
"credentials": {
"groqApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "raw-stats-fallback",
"name": "Raw Stats Fallback",
"type": "n8n-nodes-base.code",
"position": [
2112,
560
],
"parameters": {
"jsCode": "// Runs only when the AI call fails \u2014 builds a plain-stats email body\n// with the same HTML styling and the same `output` field the email node reads.\nconst report = JSON.parse($('Build Report').first().json.reportData);\nconst o = report.overview || {};\n\nconst pageRows = (report.topPages || [])\n .map(p => `<li><strong>${p.page}</strong> \u2014 ${p.views} views (${p.visitors} visitors)</li>`)\n .join('');\nconst refRows = (report.referrers || [])\n .map(r => `<li><strong>${r.source}</strong> \u2014 ${r.visits} visits</li>`)\n .join('');\n\nconst output = `\n<p style=\"margin:0 0 12px;\">The AI summary was unavailable this week, so here are your raw numbers.</p>\n<h3 style=\"color:#1a73c8;margin:20px 0 8px;\">\ud83d\udcc8 Key Metrics</h3>\n<ul style=\"margin:0 0 12px;padding-left:20px;\">\n <li>Page views: <strong>${o.pageViews ?? 0}</strong></li>\n <li>Unique visitors: <strong>${o.visitors ?? 0}</strong></li>\n <li>Pages per visit: <strong>${o.pagesPerVisit ?? '0.00'}</strong></li>\n</ul>\n<h3 style=\"color:#1a73c8;margin:20px 0 8px;\">\ud83d\udd1d Top Pages</h3>\n<ul style=\"margin:0 0 12px;padding-left:20px;\">${pageRows || '<li>No data this week</li>'}</ul>\n<h3 style=\"color:#1a73c8;margin:20px 0 8px;\">\ud83d\udea6 Traffic Sources</h3>\n<ul style=\"margin:0 0 12px;padding-left:20px;\">${refRows || '<li>No data this week</li>'}</ul>`;\n\nreturn [{ json: { output } }];"
},
"typeVersion": 2
},
{
"id": "send-email",
"name": "Send Digest Email",
"type": "n8n-nodes-base.emailSend",
"position": [
2272,
368
],
"parameters": {
"html": "=<!DOCTYPE html>\n<html>\n<body style=\"margin:0;padding:0;background:#f0f4f8;font-family:'Segoe UI',Arial,sans-serif;\">\n<table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" style=\"background:#f0f4f8;padding:32px 16px;\">\n<tr><td align=\"center\">\n<table role=\"presentation\" width=\"600\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" style=\"background:#ffffff;border-radius:12px;overflow:hidden;border:1px solid #e2e8f0;\">\n <tr>\n <td style=\"background:linear-gradient(135deg,#0f4c81 0%,#1a73c8 100%);padding:24px 36px;\">\n <p style=\"margin:0;font-size:20px;font-weight:700;color:#ffffff;\">\ud83d\udcca Weekly Analytics Digest \u2014 {{ $('Set Config').item.json.siteName }}</p>\n <p style=\"margin:6px 0 0;font-size:13px;color:rgba(255,255,255,0.75);\">Week ending {{ $now.minus({days: 1}).toFormat('MMMM d, yyyy') }}</p>\n </td>\n </tr>\n <tr>\n <td style=\"padding:28px 36px;color:#374151;font-size:14px;line-height:1.7;\">\n {{ $json.output }}\n </td>\n </tr>\n <tr>\n <td style=\"background:#f8fafc;border-top:1px solid #e2e8f0;padding:14px 36px;text-align:center;font-size:11px;color:#9ca3af;\">\n Automated with n8n\n </td>\n </tr>\n</table>\n</td></tr>\n</table>\n</body>\n</html>",
"options": {},
"subject": "=Weekly analytics digest for {{ $('Set Config').item.json.siteName }} \u2014 w/e {{ $now.minus({days: 1}).toFormat('MMM d, yyyy') }}",
"toEmail": "={{ $('Set Config').item.json.recipientEmail }}",
"fromEmail": "={{ $('Set Config').item.json.senderEmail }}"
},
"credentials": {
"smtp": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "9664ab5f-30fc-4034-b6f5-c3426ff40a1d",
"nodeGroups": [],
"connections": {
"Groq Model": {
"ai_languageModel": [
[
{
"node": "Digest Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Set Config": {
"main": [
[
{
"node": "Fetch Vercel Summary",
"type": "main",
"index": 0
}
]
]
},
"Build Report": {
"main": [
[
{
"node": "Digest Agent",
"type": "main",
"index": 0
}
]
]
},
"Digest Agent": {
"main": [
[
{
"node": "Send Digest Email",
"type": "main",
"index": 0
}
],
[
{
"node": "Raw Stats Fallback",
"type": "main",
"index": 0
}
]
]
},
"Every Monday 9 AM": {
"main": [
[
{
"node": "Set Config",
"type": "main",
"index": 0
}
]
]
},
"Raw Stats Fallback": {
"main": [
[
{
"node": "Send Digest Email",
"type": "main",
"index": 0
}
]
]
},
"Fetch Vercel Summary": {
"main": [
[
{
"node": "Fetch Vercel Top Pages",
"type": "main",
"index": 0
}
]
]
},
"Fetch Vercel Referrers": {
"main": [
[
{
"node": "Build Report",
"type": "main",
"index": 0
}
]
]
},
"Fetch Vercel Top Pages": {
"main": [
[
{
"node": "Fetch Vercel Referrers",
"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.
groqApihttpHeaderAuthsmtp
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow runs every Monday, pulls the last 7 days of Vercel Web Analytics data, uses Groq (Llama 3.3 70B) to generate an HTML summary, and emails a weekly digest via SMTP, with an automatic raw-stats fallback if the AI step fails. Runs every Monday at 9:00 AM based on your…
Source: https://n8n.io/workflows/16933/ — 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.
Author: Nguyen Thieu Toan Category: Community & Knowledge Automation Tags: Telegram, Reddit, n8n Forum, AI Summarization, Gemini, Groq
This workflow is for beauty salons who want consistent, high‑quality social media content without writing every post manually. It also suits agencies and automation builders who manage multiple beauty
Online Marketing Weekly Report. Uses scheduleTrigger, lmChatOpenAi, toolWorkflow, executeWorkflowTrigger. Scheduled trigger; 51 nodes.
This workflow retrieves Online Marketing data (Google Analytics for several domains, Google Ads, Meta Ads) from the last 7 days and the same period in the previous year. The data is then prepared by A
Fully automated blog creation system using n8n + AI Agents + Image Generation