AutomationFlowsMarketing & Ads › Track Marketing Revenue Attribution with Google Sheets, Anthropic Claude,…

Track Marketing Revenue Attribution with Google Sheets, Anthropic Claude,…

Original n8n title: Track Marketing Revenue Attribution with Google Sheets, Anthropic Claude, and Gmail

ByNour Charaf Eddin @nour on n8n.io

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…

Webhook trigger★★★★☆ complexityAI-powered23 nodesGoogle SheetsChain LlmAnthropic ChatGmail
Marketing & Ads Trigger: Webhook Nodes: 23 Complexity: ★★★★☆ AI nodes: yes Added:

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 →

Download .json
{
  "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
          }
        ]
      ]
    }
  }
}
Pro

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 →

More Marketing & Ads workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Marketing & Ads

Lead Follow-Up Email Automation. Uses googleSheets, chainLlm, lmChatGroq, gmail. Webhook trigger; 20 nodes.

Google Sheets, Chain Llm, Groq Chat +2
Marketing & Ads

NTF 02 Lead Enrichment. Uses lmChatAnthropic, chainLlm, googleSheets, slack. Webhook trigger; 9 nodes.

Anthropic Chat, Chain Llm, Google Sheets +1
Marketing & Ads

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

OpenRouter Chat, Airtable, Chain Llm +2
Marketing & Ads

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,

Gmail, Google Sheets, Chain Llm +1
Marketing & Ads

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.

OpenRouter Chat, Gmail, Chain Llm