{
  "id": "amCkThATp8WSOqFz",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Revenue Attribution & ROI Calculator",
  "tags": [],
  "nodes": [
    {
      "id": "15341b2a-14b2-4341-8b18-8cd1fc5c79f1",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -400,
        144
      ],
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "639b483f-1347-4361-990a-499d7a985398",
      "name": "Search Lead Source",
      "type": "n8n-nodes-base.airtable",
      "position": [
        176,
        144
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appeCsmVosYvWkVM3",
          "cachedResultUrl": "https://airtable.com/appeCsmVosYvWkVM3",
          "cachedResultName": "Contacts"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tblPecJqyJn6lmSDj",
          "cachedResultUrl": "https://airtable.com/appeCsmVosYvWkVM3/tblPecJqyJn6lmSDj",
          "cachedResultName": "Lead Sources"
        },
        "options": {},
        "operation": "search"
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "d2904611-761a-4c85-8445-b2f60caf5672",
      "name": "Code in JavaScript",
      "type": "n8n-nodes-base.code",
      "position": [
        400,
        144
      ],
      "parameters": {
        "jsCode": "// @ts-nocheck\n\n// Access data from specific nodes by their names\nconst dealsData = $('Deals').all();\nconst leadSourcesData = $('Search Lead Source').all();\n\n// Create a map for lead sources\nconst sourceMap = new Map();\n\nleadSourcesData.forEach((item) => {\n  const fields = item.json.fields || item.json;\n  const sourceName = fields['Source Name'] || fields['Lead Source'];\n  \n  sourceMap.set(sourceName, {\n    sourceType: fields['Source Type'],\n    cost: parseFloat(fields['Cost per Lead']) || 0,\n    totalLeads: parseInt(fields['Total Leads Generated']) || 0\n  });\n});\n\nconst sourceRevenue = new Map();\n\ndealsData.forEach((item) => {\n  const fields = item.json.fields || item.json;\n  const source = fields['Deal Source'] || fields['Lead Source'];\n  const revenue = parseFloat(fields['Deal Value']) || 0;\n  \n  if (!sourceRevenue.has(source)) {\n    sourceRevenue.set(source, {\n      totalRevenue: 0,\n      dealCount: 0,\n      source: source\n    });\n  }\n  \n  const current = sourceRevenue.get(source);\n  current.totalRevenue += revenue;\n  current.dealCount += 1;\n});\n\n// Calculate ROI for each source\n/** @type {any[]} */\nconst results = [];\n\nsourceRevenue.forEach((data, sourceName) => {\n  const sourceInfo = sourceMap.get(sourceName) || { cost: 0, totalLeads: 0 };\n  \n  const totalCost = sourceInfo.cost * sourceInfo.totalLeads;\n  const roi = totalCost > 0 ? ((data.totalRevenue - totalCost) / totalCost) * 100 : 0;\n  const avgDealSize = data.dealCount > 0 ? data.totalRevenue / data.dealCount : 0;\n  const conversionRate = sourceInfo.totalLeads > 0 ? (data.dealCount / sourceInfo.totalLeads) * 100 : 0;\n  \n  results.push({\n    json: {\n      source: sourceName,\n      totalRevenue: data.totalRevenue,\n      dealCount: data.dealCount,\n      avgDealSize: avgDealSize,\n      totalCost: totalCost,\n      roi: parseFloat(roi.toFixed(2)),\n      conversionRate: parseFloat(conversionRate.toFixed(2)),\n      updated_at: new Date().toISOString()\n    }\n  });\n});\n\n// Sort by ROI descending\nresults.sort((a, b) => b.json.roi - a.json.roi);\n\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "0542491a-d85d-4431-8ac0-fa8278a58088",
      "name": "Deals",
      "type": "n8n-nodes-base.airtable",
      "position": [
        -80,
        144
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appeCsmVosYvWkVM3",
          "cachedResultUrl": "https://airtable.com/appeCsmVosYvWkVM3",
          "cachedResultName": "Contacts"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tblJlUYIVVJrYrUZl",
          "cachedResultUrl": "https://airtable.com/appeCsmVosYvWkVM3/tblJlUYIVVJrYrUZl",
          "cachedResultName": "Deals"
        },
        "options": {},
        "operation": "search",
        "filterByFormula": "=AND({Stage} = \"Closed Won\", IS_AFTER({Actual Close Date}, DATEADD(NOW(), -30, 'days')))"
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "e92d2b3c-16ac-4979-8368-4208b533bab0",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        624,
        144
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "fbe7af8c-9d26-4f68-a78b-f35ece1c958e",
      "name": "Search records",
      "type": "n8n-nodes-base.airtable",
      "position": [
        832,
        160
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appeCsmVosYvWkVM3",
          "cachedResultUrl": "https://airtable.com/appeCsmVosYvWkVM3",
          "cachedResultName": "Contacts"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tblPecJqyJn6lmSDj",
          "cachedResultUrl": "https://airtable.com/appeCsmVosYvWkVM3/tblPecJqyJn6lmSDj",
          "cachedResultName": "Lead Sources"
        },
        "options": {},
        "operation": "search",
        "filterByFormula": "={Source Type} = \"{{ $json.source }}\""
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "85233782-5af1-44e7-80e6-4385c5c0f279",
      "name": "Update record",
      "type": "n8n-nodes-base.airtable",
      "position": [
        1040,
        160
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appeCsmVosYvWkVM3",
          "cachedResultUrl": "https://airtable.com/appeCsmVosYvWkVM3",
          "cachedResultName": "Contacts"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_AIRTABLE_VALUE",
          "cachedResultUrl": "https://airtable.com/appeCsmVosYvWkVM3/tblPecJqyJn6lmSDj",
          "cachedResultName": "Lead Sources"
        },
        "columns": {
          "value": {
            "id": "={{ $json.id }}",
            "Cost per Lead": "={{ $json['Cost per Lead'] }}",
            "Conversion rate": "={{ $json['Conversion rate'] }}",
            "Total Leads Generated": "={{ $json['Total Leads Generated'] }}",
            "Total Revenue Attributed": "={{ $json['Total Revenue Attributed'] }}"
          },
          "schema": [
            {
              "id": "id",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "id",
              "defaultMatch": true
            },
            {
              "id": "Source Name",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Source Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Source Type",
              "type": "options",
              "display": true,
              "options": [
                {
                  "name": "Paid Advertising",
                  "value": "Paid Advertising"
                },
                {
                  "name": "Organic Search",
                  "value": "Organic Search"
                },
                {
                  "name": "Referral ",
                  "value": "Referral "
                },
                {
                  "name": "Direct Traffic",
                  "value": "Direct Traffic"
                },
                {
                  "name": "Social Media",
                  "value": "Social Media"
                },
                {
                  "name": "Email Marketing",
                  "value": "Email Marketing"
                },
                {
                  "name": "Content Marketing",
                  "value": "Content Marketing"
                },
                {
                  "name": "Events/Trade Shows",
                  "value": "Events/Trade Shows"
                }
              ],
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Source Type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Cost per Lead",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Cost per Lead",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Conversion rate",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Conversion rate",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Total Leads Generated",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Total Leads Generated",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Total Revenue Attributed",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Total Revenue Attributed",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ROI",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "ROI",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update"
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "3e7d6497-552d-4639-a911-23ce6412ee90",
      "name": "Send a message",
      "type": "n8n-nodes-base.slack",
      "position": [
        1264,
        160
      ],
      "parameters": {
        "text": "=\ud83d\udcca Weekly Revenue Attribution Report - {{ new Date().toLocaleDateString() }} \n\nSource: {{ $json.fields['Source Type'] }}\nRevenue: ${{ $json.fields['Total Revenue Attributed'] }}\nROI: {{ $json.fields.ROI }}% \nConversion: {{ $json.fields['Conversion rate'] }}%",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09JRB0G41J",
          "cachedResultName": "sales-analytics"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "9eb3a37c-bb4b-4c82-967a-d0c365456eea",
      "name": "Send a message1",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1472,
        160
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "=Hi Management,<br><br> \n\nIncase you missed it, an update was sent to the sales-analytics Channel.<br><br>\n\nRegards.",
        "options": {
          "appendAttribution": false
        },
        "subject": "Sales Analytics Update"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "b9f57a73-014e-4364-ac84-e51895d8f7d7",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1072,
        -784
      ],
      "parameters": {
        "color": 2,
        "width": 752,
        "height": 752,
        "content": "# \ud83d\udcca REVENUE ATTRIBUTION & ROI CALCULATOR\n\nThis workflow calculates which lead sources generate actual revenue.\n\nRuns: Daily at midnight\nDuration: ~2-3 minutes\nPurpose: Update Lead Sources table with ROI metrics\n\n## Requirements:\n\u2705 Airtable base with Deals & Lead Sources tables\n\u2705 Closed Won deals from last 30 days\n\u2705 Cost data for each marketing source\n\n## What gets calculated:\n- Total revenue per source\n- ROI percentage\n- Conversion rates\n- Average deal size\n\n## \ud83d\ude80 Getting Started Checklist\n\n- [ ] **Gmail account** with API access enabled\n- [ ] **Airtable Account [https://airtable.com/]**\n- [ ] **N8N instance** running\n- [ ] **Slack workspace [https://slack.com/intl/en-gb/]** \n- [ ] **Import this workflow** and configure credentials"
      },
      "typeVersion": 1
    },
    {
      "id": "68282373-6e14-4b27-8e7c-17caf82c1dc9",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -192,
        -352
      ],
      "parameters": {
        "width": 512,
        "height": 592,
        "content": "## \ud83d\udce5 STEP 1 & 2: COLLECT DATA FROM AIRTABLE\n\nNODE 1 - Fetch Closed Won Deals:\nGets deals from last 30 days where Stage = \"Closed Won\"\n\nRequired fields:\n- Stage (Single select)\n- Deal Value (Currency)\n- Deal Source (Single select)\n- Actual Close Date (Date)\n\nNODE 2 - Fetch Lead Sources:\nGets all marketing sources with cost data\n\nRequired fields:\n- Source Name (Single line text)\n- Cost per Lead (Currency)\n- Total Leads Generated (Number)\n\n\u26a0\ufe0f Deal Source values must EXACTLY match Source Name\n   (case-sensitive, no extra spaces)"
      },
      "typeVersion": 1
    },
    {
      "id": "91441ad1-38f5-4b5b-a103-66f5b769d3d4",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        352,
        -336
      ],
      "parameters": {
        "color": 4,
        "width": 432,
        "height": 560,
        "content": "## \ud83e\uddee STEP 3 & 4: CALCULATE ROI & PROCESS\n\nNODE 3 - Code: Calculate Metrics\nFor each source, calculates:\n- Total Revenue = Sum of Deal Values\n- Total Cost = Cost per Lead \u00d7 Total Leads  \n- ROI = ((Revenue - Cost) / Cost) \u00d7 100\n- Conversion Rate = (Deals / Leads) \u00d7 100\n- Avg Deal Size = Revenue / Deal Count\n\nNODE 4 - Loop: Process Each Source\nSplits results array to update sources individually\n\n\ud83d\udd27 CUSTOMIZATION:\n- Edit ROI formula in Code node (line 35-42)\n- Change sorting logic (line 50-55)\n- Adjust date range in Deals node filter\n\n\ud83d\udca1 Add // @ts-nocheck at top of Code if errors appear"
      },
      "typeVersion": 1
    },
    {
      "id": "f992e5a9-3a68-4b07-86a2-12b07ea3e417",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        816,
        -272
      ],
      "parameters": {
        "color": 6,
        "width": 800,
        "height": 464,
        "content": "## UPDATE DATA & SEND REPORT\n\nSearches to ensure the lead source exists, if it doesn't exist, it gets skipped to the next. \nUpdate record node updates the existing lead source record with the calculated metrics.\n\nFields updated:\n- Total Revenue Attributed\n- Conversion Rate\n- ROI\n\nNotifications (OPTIONAL):\nSlack sends report of top 3 performing sources to the #sales-analytics channel.\nGmail sends a personal mail to the CEO, incase the slack analytics report was missed.\n\n\n\u26a0\ufe0f Field names must match Airtable EXACTLY\n   (case-sensitive)\n\n\ud83d\udca1 Remove Slack and email nodes if not needed."
      },
      "typeVersion": 1
    },
    {
      "id": "77f9cd46-e398-4151-a549-4d929979a703",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1664,
        -496
      ],
      "parameters": {
        "width": 432,
        "height": 640,
        "content": "## \u26a0\ufe0f TROUBLESHOOTING GUIDE\n\nISSUE: No deals returned from first node\nFIX: \n- Check date filter (last 30 days)\n- Verify \"Closed Won\" spelling in Stage\n- Ensure Actual Close Date is filled\n\nISSUE: Code node errors\nFIX:\n- Add // @ts-nocheck at top of code\n- Check field names match Airtable (case-sensitive)\n- Verify numeric fields contain numbers not text\n\nISSUE: Updates not writing\nFIX:\n- Check Airtable API permissions\n- Verify Record ID mapping in Update node\n- Confirm field names match exactly\n\nISSUE: Wrong calculations\nFIX:\n- Verify Cost per Lead and Total Leads have data\n- Check Deal Source matches Source Name exactly\n- Review Code node logic (line 35-55)\n\n\ud83d\udce7 Need help? Full docs: https://www.notion.so/RevOps-Intelligence-Hub-Complete-Documentation-282ca81d5bda804a9ccdfc974b6f089d?source=copy_link"
      },
      "typeVersion": 1
    },
    {
      "id": "3265038a-842d-47fc-bb2b-8c604709f009",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -496,
        80
      ],
      "parameters": {
        "color": 3,
        "width": 256,
        "height": 224,
        "content": "## Start Here"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "08aa22ad-e397-4ad5-9cb3-03743078508f",
  "connections": {
    "Deals": {
      "main": [
        [
          {
            "node": "Search Lead Source",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update record": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search records": {
      "main": [
        [
          {
            "node": "Update record",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send a message": {
      "main": [
        [
          {
            "node": "Send a message1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [
          {
            "node": "Search records",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Deals",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Lead Source": {
      "main": [
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}