This workflow corresponds to n8n.io template #17854 — we link there as the canonical source.
This workflow follows the Chainllm → Gmail 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": "Track marketing revenue attribution with Google Sheets and Claude",
"nodes": [
{
"id": "727e8f6a-05ba-4c38-83f9-f1adedcd2617",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-368,
-32
],
"parameters": {
"width": 480,
"height": 896,
"content": "## Track marketing revenue attribution with Google Sheets and Claude\n\n### How it works\n\nThis workflow tracks marketing revenue attribution across three paths: lead capture, deal-won attribution, and monthly reporting. New leads are normalized, mapped to an acquisition channel, and logged to Google Sheets; won deals are matched back to the original lead channel and recorded as attributed revenue. On a monthly schedule, it aggregates revenue by channel, asks Claude to write an executive summary, and emails the report.\n\n### Setup steps\n\n- Configure both webhook URLs in the source systems: one for new lead capture and one for deal-won events from your CRM or sales platform.\n- Connect Google Sheets credentials and select the spreadsheet/tabs used for the lead ledger and attributed revenue ledger.\n- Configure the normalization and attribution code nodes to match your lead identifiers, UTM/channel rules, revenue fields, and sheet column names.\n- Add Anthropic credentials for the Claude chat model used by the LLM chain.\n- Connect Gmail credentials and set the report recipient value in the Set Report Recipient node.\n- Verify the monthly schedule trigger timezone and run a test with sample lead and deal data before activating the workflow.\n\n### Customization\n\nYou can adjust the channel-mapping rules, attribution model, monthly schedule, Claude prompt, report recipient, and Google Sheets schema to match your marketing operations process."
},
"typeVersion": 1
},
{
"id": "a484e1d5-0c8d-4ca2-90a9-2bbbf8090498",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
192,
-32
],
"parameters": {
"color": 7,
"width": 864,
"height": 304,
"content": "## Capture and log leads\n\nReceives incoming lead submissions, normalizes the payload, maps each lead to a marketing channel, writes the lead to the Google Sheets ledger, and returns a webhook response."
},
"typeVersion": 1
},
{
"id": "7adf3719-39ba-43cc-bc9d-887742116471",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
192,
304
],
"parameters": {
"color": 7,
"width": 640,
"height": 320,
"content": "## Match won deals\n\nHandles deal-won webhook events, looks up the original lead in Google Sheets, and calculates which marketing channel should receive revenue credit."
},
"typeVersion": 1
},
{
"id": "3c09c60a-f7dc-4b85-8bbc-527f61ad2fb0",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
864,
304
],
"parameters": {
"color": 7,
"width": 416,
"height": 320,
"content": "## Record attributed revenue\n\nLogs the attributed won-deal revenue to Google Sheets and sends the webhook response confirming the attribution was recorded."
},
"typeVersion": 1
},
{
"id": "8b747ca6-e2f3-4fe5-9a59-f69cdc7f17a7",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
192,
656
],
"parameters": {
"color": 7,
"width": 640,
"height": 432,
"content": "## Prepare monthly data\n\nStarts the monthly reporting run, sets the report recipient, reads the revenue ledger, and aggregates revenue totals by marketing channel."
},
"typeVersion": 1
},
{
"id": "202d002f-c469-4461-8105-cc67f49b87fd",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
864,
656
],
"parameters": {
"color": 7,
"width": 352,
"height": 544,
"content": "## Generate executive summary\n\nUses the Claude LLM chain and its Anthropic chat model to turn the aggregated channel revenue data into a concise executive summary."
},
"typeVersion": 1
},
{
"id": "f08373bb-4ec9-4637-9fb0-bf7f9c567e86",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
1312,
448
],
"parameters": {
"color": 7,
"width": 240,
"height": 368,
"content": "## Email monthly report\n\nSends the completed monthly marketing revenue attribution report through Gmail to the configured recipient."
},
"typeVersion": 1
},
{
"id": "f37ae713-6bbb-49ad-83b0-0ad52873f08b",
"name": "When Lead Captured",
"type": "n8n-nodes-base.webhook",
"position": [
240,
96
],
"parameters": {
"path": "lead-capture",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2.1
},
{
"id": "9b0fa5ec-1315-46dd-8ba7-ba9ffb96ad5e",
"name": "Transform Lead Data",
"type": "n8n-nodes-base.code",
"position": [
464,
96
],
"parameters": {
"jsCode": "const raw = $input.first().json;\nconst b = raw.body || raw;\nconst src = (b.utm_source || '').toLowerCase().trim();\nconst med = (b.utm_medium || '').toLowerCase().trim();\n\nlet channel = 'Direct / Other';\nif (src.includes('google') && ['cpc', 'ppc', 'paid'].includes(med)) channel = 'Google Ads';\nelse if (src.includes('google')) channel = 'Google Organic';\nelse if (src.includes('bing')) channel = 'Bing';\nelse if (['facebook', 'instagram', 'meta', 'fb', 'ig'].some(s => src.includes(s))) {\n channel = ['cpc', 'ppc', 'paid'].includes(med) ? 'Meta Ads' : 'Meta Organic';\n}\nelse if (src.includes('linkedin')) channel = 'LinkedIn';\nelse if (med === 'email' || src.includes('newsletter')) channel = 'Email';\nelse if (med === 'referral') channel = 'Referral';\nelse if (src) channel = b.utm_source;\n\nreturn [{\n json: {\n timestamp: $now.toISO(),\n email: (b.email || '').toLowerCase().trim(),\n name: b.name || '',\n channel,\n utm_source: b.utm_source || '',\n utm_medium: b.utm_medium || '',\n utm_campaign: b.utm_campaign || ''\n }\n}];"
},
"typeVersion": 2
},
{
"id": "dd688d70-092f-484d-af57-9cd31bc339c6",
"name": "Append Lead to Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
688,
96
],
"parameters": {
"columns": {
"value": {
"name": "={{ $json.name }}",
"email": "={{ $json.email }}",
"channel": "={{ $json.channel }}",
"timestamp": "={{ $json.timestamp }}",
"utm_medium": "={{ $json.utm_medium }}",
"utm_source": "={{ $json.utm_source }}",
"utm_campaign": "={{ $json.utm_campaign }}"
},
"schema": [
{
"id": "timestamp",
"type": "string",
"display": true,
"required": false,
"displayName": "timestamp",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "email",
"type": "string",
"display": true,
"required": false,
"displayName": "email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "name",
"type": "string",
"display": true,
"required": false,
"displayName": "name",
"defaultMatch": false,
"canBeUsedToMatch": false
},
{
"id": "channel",
"type": "string",
"display": true,
"required": false,
"displayName": "channel",
"defaultMatch": false,
"canBeUsedToMatch": false
},
{
"id": "utm_source",
"type": "string",
"display": true,
"required": false,
"displayName": "utm_source",
"defaultMatch": false,
"canBeUsedToMatch": false
},
{
"id": "utm_medium",
"type": "string",
"display": true,
"required": false,
"displayName": "utm_medium",
"defaultMatch": false,
"canBeUsedToMatch": false
},
{
"id": "utm_campaign",
"type": "string",
"display": true,
"required": false,
"displayName": "utm_campaign",
"defaultMatch": false,
"canBeUsedToMatch": false
}
],
"mappingMode": "defineBelow"
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Leads"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"typeVersion": 4.7
},
{
"id": "d05ddf29-7ef7-4d4f-aaf7-50075ce6e2c5",
"name": "Confirm Lead Logged",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
912,
96
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={{ { \"ok\": true, \"channel\": $('Transform Lead Data').item.json.channel } }}"
},
"typeVersion": 1.5
},
{
"id": "242769f0-58da-4b21-a329-2c8451ccb39b",
"name": "When Deal Won",
"type": "n8n-nodes-base.webhook",
"position": [
240,
464
],
"parameters": {
"path": "deal-won",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2.1
},
{
"id": "d66dbd5d-ae5a-4814-8352-dcf620c359e4",
"name": "Search Lead Source in Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
464,
464
],
"parameters": {
"options": {},
"filtersUI": {
"values": [
{
"lookupValue": "={{ ($json.body?.email ?? $json.email ?? \"\").toLowerCase().trim() }}",
"lookupColumn": "email"
}
]
},
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Leads"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"typeVersion": 4.7,
"alwaysOutputData": true
},
{
"id": "40bc9062-7c9d-47b1-8dc0-78d3087306f8",
"name": "Calculate Channel Revenue",
"type": "n8n-nodes-base.code",
"position": [
688,
464
],
"parameters": {
"jsCode": "const raw = $('When Deal Won').first().json;\nconst deal = raw.body || raw;\nconst leadRows = $input.all().filter(i => i.json && i.json.email);\nconst lead = leadRows.length ? leadRows[0].json : null;\n\nreturn [{\n json: {\n date: $now.toISO().slice(0, 10),\n email: (deal.email || '').toLowerCase().trim(),\n deal_value: parseFloat(deal.deal_value) || 0,\n channel: lead ? lead.channel : 'Unknown / untracked',\n utm_campaign: lead ? (lead.utm_campaign || '') : ''\n }\n}];"
},
"typeVersion": 2
},
{
"id": "d545fc64-390b-485f-9cb1-32767ed7e04a",
"name": "Append Revenue to Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
912,
464
],
"parameters": {
"columns": {
"value": {
"date": "={{ $json.date }}",
"email": "={{ $json.email }}",
"channel": "={{ $json.channel }}",
"deal_value": "={{ $json.deal_value }}",
"utm_campaign": "={{ $json.utm_campaign }}"
},
"schema": [
{
"id": "date",
"type": "string",
"display": true,
"required": false,
"displayName": "date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "email",
"type": "string",
"display": true,
"required": false,
"displayName": "email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "deal_value",
"type": "number",
"display": true,
"required": false,
"displayName": "deal_value",
"defaultMatch": false,
"canBeUsedToMatch": false
},
{
"id": "channel",
"type": "string",
"display": true,
"required": false,
"displayName": "channel",
"defaultMatch": false,
"canBeUsedToMatch": false
},
{
"id": "utm_campaign",
"type": "string",
"display": true,
"required": false,
"displayName": "utm_campaign",
"defaultMatch": false,
"canBeUsedToMatch": false
}
],
"mappingMode": "defineBelow"
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Revenue"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"typeVersion": 4.7
},
{
"id": "33153e42-6889-48ef-be82-cc6bde4f23fe",
"name": "Confirm Revenue Logged",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1136,
464
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={{ { \"ok\": true, \"attributed_to\": $('Calculate Channel Revenue').item.json.channel } }}"
},
"typeVersion": 1.5
},
{
"id": "4d51366c-a38c-4138-a9a5-677c4322b4b1",
"name": "On 1st Month at 08:00 AM",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
240,
928
],
"parameters": {
"rule": {
"interval": [
{
"field": "months",
"triggerAtHour": 8
}
]
}
},
"typeVersion": 1.3
},
{
"id": "075cc89e-1ec6-4929-9a5d-a9eb79c1e4fa",
"name": "Read Revenue from Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
464,
928
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Revenue"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"typeVersion": 4.7
},
{
"id": "a8488d79-f34e-4fa2-bb53-93dfe69297ba",
"name": "Summarize Revenue by Channel",
"type": "n8n-nodes-base.code",
"position": [
688,
928
],
"parameters": {
"jsCode": "const now = new Date();\nconst firstOfThisMonth = new Date(now.getFullYear(), now.getMonth(), 1);\nconst firstOfLastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);\nconst monthLabel = firstOfLastMonth.toLocaleString('en', { month: 'long', year: 'numeric' });\n\nconst rows = $input.all().map(i => i.json).filter(r => {\n const d = new Date(r.date);\n return d >= firstOfLastMonth && d < firstOfThisMonth;\n});\n\nconst byChannel = {};\nfor (const r of rows) {\n const ch = r.channel || 'Unknown / untracked';\n if (!byChannel[ch]) byChannel[ch] = { revenue: 0, deals: 0 };\n byChannel[ch].revenue += parseFloat(r.deal_value) || 0;\n byChannel[ch].deals += 1;\n}\n\nconst total = Object.values(byChannel).reduce((s, c) => s + c.revenue, 0);\nconst sorted = Object.entries(byChannel).sort((a, b) => b[1].revenue - a[1].revenue);\n\nconst tableLines = sorted.map(([ch, v]) =>\n ch + ': ' + v.revenue.toFixed(2) + ' (' + v.deals + (v.deals === 1 ? ' deal' : ' deals') + ', ' + (total ? ((v.revenue / total) * 100).toFixed(1) : 0) + '%)'\n);\n\nreturn [{\n json: {\n monthLabel,\n totalRevenue: total,\n totalDeals: rows.length,\n tableText: tableLines.join('\\n') || 'No attributed revenue recorded last month.'\n }\n}];"
},
"typeVersion": 2
},
{
"id": "1abb31e9-b650-43ab-8b32-07459b3f1f0c",
"name": "Generate Exec Summary",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
912,
832
],
"parameters": {
"text": "=You are a sharp, plain-spoken marketing analyst. Write a short monthly attribution summary for a business owner.\n\nMonth: {{ $json.monthLabel }}\nTotal attributed revenue: {{ $json.totalRevenue }}\nTotal won deals: {{ $json.totalDeals }}\nRevenue by channel:\n{{ $json.tableText }}\n\nWrite 4 to 6 sentences. Lead with the single most important takeaway. Name the best and worst performing channels. If a lot of revenue is Unknown / untracked, say plainly that tracking coverage needs fixing. No bullet points, no hype, no jargon.",
"batching": {},
"promptType": "define"
},
"typeVersion": 1.9
},
{
"id": "ed5ee470-a84e-4b30-b2da-0ca71b151efb",
"name": "Claude Sonnet Model",
"type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
"position": [
992,
1232
],
"parameters": {
"model": {
"__rl": true,
"mode": "id",
"value": "claude-sonnet-4-6",
"cachedResultName": "Claude Sonnet 4.6"
},
"options": {
"effort": "low",
"thinkingMode": "adaptive",
"maxTokensToSample": 1024
}
},
"typeVersion": 1.5
},
{
"id": "960306ef-1d87-4084-9a63-382b5259c433",
"name": "Send Monthly Report via Email",
"type": "n8n-nodes-base.gmail",
"position": [
1360,
656
],
"parameters": {
"sendTo": "={{ $('Specify Report Recipient').item.json.reportRecipient }}",
"message": "={{ $json.text }}\n\nThe numbers:\n{{ $('Summarize Revenue by Channel').item.json.tableText }}\n\nTotal attributed revenue: {{ $('Summarize Revenue by Channel').item.json.totalRevenue }} across {{ $('Summarize Revenue by Channel').item.json.totalDeals }} deals.",
"options": {},
"subject": "=Marketing attribution report: {{ $('Summarize Revenue by Channel').item.json.monthLabel }}",
"resource": "message",
"emailType": "text",
"operation": "send"
},
"typeVersion": 2.2
},
{
"id": "set-report-recipient-0001",
"name": "Specify Report Recipient",
"type": "n8n-nodes-base.set",
"position": [
352,
800
],
"parameters": {
"mode": "manual",
"assignments": {
"assignments": [
{
"id": "report-recipient",
"name": "reportRecipient",
"type": "string",
"value": "user@example.com"
}
]
},
"includeOtherFields": false
},
"typeVersion": 3.4
}
],
"settings": {
"executionOrder": "v1"
},
"connections": {
"When Deal Won": {
"main": [
[
{
"node": "Search Lead Source in Sheets",
"type": "main",
"index": 0
}
]
]
},
"When Lead Captured": {
"main": [
[
{
"node": "Transform Lead Data",
"type": "main",
"index": 0
}
]
]
},
"Claude Sonnet Model": {
"ai_languageModel": [
[
{
"node": "Generate Exec Summary",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Transform Lead Data": {
"main": [
[
{
"node": "Append Lead to Sheets",
"type": "main",
"index": 0
}
]
]
},
"Append Lead to Sheets": {
"main": [
[
{
"node": "Confirm Lead Logged",
"type": "main",
"index": 0
}
]
]
},
"Generate Exec Summary": {
"main": [
[
{
"node": "Send Monthly Report via Email",
"type": "main",
"index": 0
}
]
]
},
"Append Revenue to Sheets": {
"main": [
[
{
"node": "Confirm Revenue Logged",
"type": "main",
"index": 0
}
]
]
},
"On 1st Month at 08:00 AM": {
"main": [
[
{
"node": "Specify Report Recipient",
"type": "main",
"index": 0
}
]
]
},
"Read Revenue from Sheets": {
"main": [
[
{
"node": "Summarize Revenue by Channel",
"type": "main",
"index": 0
}
]
]
},
"Specify Report Recipient": {
"main": [
[
{
"node": "Read Revenue from Sheets",
"type": "main",
"index": 0
}
]
]
},
"Calculate Channel Revenue": {
"main": [
[
{
"node": "Append Revenue to Sheets",
"type": "main",
"index": 0
}
]
]
},
"Search Lead Source in Sheets": {
"main": [
[
{
"node": "Calculate Channel Revenue",
"type": "main",
"index": 0
}
]
]
},
"Summarize Revenue by Channel": {
"main": [
[
{
"node": "Generate Exec Summary",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow captures leads via webhooks, logs UTM-based channel attribution to Google Sheets, attributes won-deal revenue back to the original channel, and sends a monthly executive summary written by Anthropic Claude via Gmail. Receives a POST request on a lead-capture…
Source: https://n8n.io/workflows/17854/ — 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.
Lead Follow-Up Email Automation. Uses googleSheets, chainLlm, lmChatGroq, gmail. Webhook trigger; 20 nodes.
NTF 02 Lead Enrichment. Uses lmChatAnthropic, chainLlm, googleSheets, slack. Webhook trigger; 9 nodes.
This workflow makes it easier to prepare for meetings and calls by researching your lead right before the call and creates a high-level meeting prep that is sent to your email. This removes the extra
Turn raw marketing data into actionable insights with this n8n Source/UTM Attribution and Reporting workflow! It automatically aggregates lead submissions, calculates Cost Per Lead (CPL) per channel,
This automated n8n workflow streamlines lead qualification by taking structured lead data from Tally forms, enriching it with Qwen-3’s AI analysis, and promptly notifying your sales or delivery teams.