asSaaSins Logo
asSaaSins

Automate Revenue Reconciliation: Free n8n Workflow Template for Matching CRM Data That Doesn’t Match

Adam Carter
Adam Carter
June 13, 2025
Automate Revenue Reconciliation: Free n8n Workflow Template for Matching CRM Data That Doesn’t Match

Sample data used in n8n workflow: 9 data export files totaling 17,723 records across HubSpot, Salesforce, and Stripe platforms.

“Why do you have to work on Sunday nights?”

Last Saturday at a BBQ, my friend Justin mentioned his daughter asked him this exact question. When I asked what he was working on, he laughed bitterly. “Revenue reconciliation. Every Sunday night, same ritual. Stripe says we made $847K, Salesforce shows $832K, HubSpot claims $795K. I spend 3 hours building spreadsheets to explain the gaps to our CFO by Monday morning.”

How many of us are sacrificing family time for the same manual detective work? How many minds wander during weekend activities, dreading that Sunday night scramble?

I went home and spent the next week building an n8n workflow to automate Justin’s entire reconciliation process. When I showed him the 3-minute automated report that found his missing $52,000 (miscategorized tax entries), he asked the question every RevOps professional wants to know: “Can I share this with my team?”

Better yet—I’m sharing it with all of you. Because if there’s one thing our RevOps community doesn’t need, it’s another Sunday night spent in Excel instead of with family.

First Things First: Setting Up Google Cloud Access

Already have Google Cloud OAuth credentials? Skip to the n8n workflow setup →

Before we can automate your revenue reconciliation, we need to give n8n permission to access your Google Sheets (where we’ll output the reconciliation report). This one-time setup takes about 10 minutes and saves you hours every week.

Step 1: Create Your Google Cloud Project

  1. Go to Google Cloud Console and sign in with your Google account
  2. In the top navigation bar, click the project dropdown and select “New Project”
  3. Name your project something memorable like “Revenue Reconciliation”
  4. Click “Create” and wait for the project to initialize (about 10 seconds)
  5. Make sure your new project is selected in the top dropdown

Step 2: Enable the Required APIs

Now we need to turn on access to Google Sheets and Google Drive:

  1. In the left sidebar, navigate to “APIs & Services” → “Library”
  2. Search for “Google Sheets API” and click on it
  3. Click the blue “ENABLE” button
  4. Go back to the Library and search for “Google Drive API”
  5. Click on it and hit “ENABLE” again

Step 3: Configure OAuth Consent Screen (Internal Use)

Since this is for your internal RevOps use only, we’ll set it up as an internal app:

  1. Go to “APIs & Services” → “OAuth consent screen” in the left sidebar
  2. Select “Internal” for User Type (this means only people in your organization can use it)
  3. Click “CREATE”
  4. Fill in the required fields:
    • App name: “Revenue Reconciliation Automation”
    • User support email: Your email address
    • Developer contact information: Your email again
  5. Click “SAVE AND CONTINUE”
  6. On the Scopes page, click “ADD OR REMOVE SCOPES”
  7. Search for and select these scopes:
    • ../auth/spreadsheets (Google Sheets API)
    • ../auth/drive.file (Google Drive API)
  8. Click “UPDATE” then “SAVE AND CONTINUE”
  9. Review the summary and click “BACK TO DASHBOARD”

Step 4: Create OAuth 2.0 Credentials

This is where you’ll get the Client ID and Client Secret that n8n needs:

  1. Navigate to “APIs & Services” → “Credentials”
  2. Click “+ CREATE CREDENTIALS” at the top
  3. Select “OAuth client ID”
  4. For Application type, choose “Web application”
  5. Name it “n8n Revenue Reconciliation”
  6. Don’t click CREATE yet! You need to add the redirect URI from n8n first

⚠️ IMPORTANT: Leave this tab open. We need to get the redirect URL from n8n before we can finish this step.

Step 5: Get Your n8n Redirect URL

Now we need to get the special URL from n8n that Google will use for authentication:

  1. Open n8n in a new tab (keep your Google Cloud Console tab open)
  2. Create a new workflow or open an existing one
  3. Add any Google node (like Google Sheets) temporarily
  4. Click on “Credential for Google Sheets API”
  5. Select “Create New” → “Google OAuth2 API”
  6. You’ll see a field called “OAuth Redirect URL” – it will look something like:
    https://n8n-yocwgo8cc8cswwc4wgs0s8wo.casso.app/rest/oauth2-credential/callback
  7. Copy this entire URL

Step 6: Complete Google OAuth Setup

Now go back to your Google Cloud Console tab:

  1. Under “Authorized redirect URIs”, click “+ ADD URI”
  2. Paste the OAuth Redirect URL you copied from n8n
  3. Click “CREATE”
  4. A popup will appear with your Client ID and Client Secret
  5. Copy both immediately! The Client Secret will only be shown once
  6. Save these credentials somewhere secure – you’ll need them in the next step

Great! You now have everything Google-related ready. Let’s connect it all together.

Getting Started with n8n: Your Revenue Reconciliation Automation Platform

n8n Revenue Reconciliation Workflow Dashboard

With your Google credentials ready, let’s set up n8n—the workflow automation tool that’s about to save your Sundays.

Step 1: Create Your Free n8n Account

Head to n8n.io and sign up for a free cloud account. While n8n is open-source and can be self-hosted (perfect for enterprise data security), we recommend starting with their cloud service for this revenue matching template. The free tier provides enough executions to test and run your reconciliation workflows.

Step 2: Create Your First Workflow

Once logged in:

  1. Click “Create Workflow” on your dashboard
  2. You’ll see a blank canvas with an “Add first step” prompt
  3. Name your workflow “Revenue Data Reconciliation” in the top left
  4. We’ll build the automated matching workflow together below

Building Your Automated Revenue Reconciliation Workflow

Now for the magic. This n8n workflow automates the entire detective process that currently steals your Sunday nights. Here’s what it does:

How This Workflow Saves Your Sundays

The workflow automatically:

  1. Collects data from your RevOps exports folder in Google Drive
  2. Processes files from Stripe, HubSpot, and Salesforce in parallel
  3. Identifies critical issues like:
    • Active subscriptions with no CRM record (revenue at risk!)
    • Contacts in HubSpot but not Salesforce (and vice versa)
    • Data quality problems causing sync failures
  4. Generates an executive report with:
    • Total customers/contacts in each system
    • Monthly revenue at risk from untracked subscriptions
    • Specific action items to fix each issue
  5. Outputs everything to a clean Google Sheet you can share with your CFO

Real results from the last run: Found 6 customers paying $2,894/month that weren’t in any CRM system. That’s $34,728 in annual revenue that could have churned without anyone noticing.

Part 1: Import the Workflow

Here’s the complete workflow. Copy this entire code block:

{
  "nodes": [
    {
      "parameters": {
        "resource": "fileFolder",
        "queryString": "RevOps Weekly Exports",
        "returnAll": true,
        "filter": {
          "whatToSearch": "folders"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        460,
        620
      ],
      "id": "590abf04-b4f0-4779-9737-6d9e01cf6280",
      "name": "RevOps Reports Dir ID",
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "YOUR_GOOGLE_DRIVE_CREDENTIAL_ID",
          "name": "Your Google OAuth2 Credential"
        }
      }
    },
    {
      "parameters": {},
      "id": "116f74df-1587-4a7f-ad80-3357dae62ec4",
      "name": "Start",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        20,
        620
      ]
    },
    {
      "parameters": {
        "operation": "download",
        "fileId": "={{ $json.id }}",
        "options": {}
      },
      "id": "b55d11f3-1940-48d1-b736-ea253b9d0372",
      "name": "Download CSV",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        1340,
        420
      ],
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "YOUR_GOOGLE_DRIVE_CREDENTIAL_ID",
          "name": "Your Google OAuth2 Credential"
        }
      },
      "continueOnFail": true
    },
    {
      "parameters": {
        "resource": "fileFolder",
        "searchMethod": "query",
        "queryString": "='{{ $json.id }}' in parents and mimeType = 'application/vnd.google-apps.folder'",
        "returnAll": true,
        "filter": {
          "whatToSearch": "folders"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        680,
        620
      ],
      "id": "3ac25a38-8db4-45c8-b09f-8a5514ef4a70",
      "name": "Child Folder IDs",
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "B1rE6o5pdkD9Phxw",
          "name": "Auditech Google"
        }
      }
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.name }}",
                    "rightValue": "stripe",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "id": "8a64263b-9b86-4ecb-a563-d3338c7ac01c"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "stripe"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "f3d8d385-028b-442a-b573-ce33abce9bda",
                    "leftValue": "={{ $json.name }}",
                    "rightValue": "hubspot",
                    "operator": {
                      "type": "string",
                      "operation": "equals",
                      "name": "filter.operator.equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "hubspot"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "2d44437f-2636-4046-af97-82c45b9c14f7",
                    "leftValue": "={{ $json.name }}",
                    "rightValue": "salesforce",
                    "operator": {
                      "type": "string",
                      "operation": "equals",
                      "name": "filter.operator.equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "salesforce"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        900,
        620
      ],
      "id": "ca0a9843-4dd7-4120-a47c-60115a5ea6dc",
      "name": "Switch"
    },
    {
      "parameters": {
        "resource": "fileFolder",
        "searchMethod": "query",
        "queryString": "='{{ $json.id }}' in parents and mimeType != 'application/vnd.google-apps.folder'",
        "returnAll": true,
        "filter": {
          "whatToSearch": "files"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        1120,
        420
      ],
      "id": "d4537bba-1de3-41b3-8e1f-e77fe0bf6591",
      "name": "Stripe IDs",
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "B1rE6o5pdkD9Phxw",
          "name": "Auditech Google"
        }
      }
    },
    {
      "parameters": {
        "resource": "fileFolder",
        "searchMethod": "query",
        "queryString": "='{{ $json.id }}' in parents and mimeType != 'application/vnd.google-apps.folder'",
        "returnAll": true,
        "filter": {
          "whatToSearch": "files"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        1120,
        620
      ],
      "id": "6a59aab6-3ed1-45d4-a978-9a4e2d9d1095",
      "name": "Hubspot IDs1",
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "B1rE6o5pdkD9Phxw",
          "name": "Auditech Google"
        }
      }
    },
    {
      "parameters": {
        "resource": "fileFolder",
        "searchMethod": "query",
        "queryString": "='{{ $json.id }}' in parents and mimeType != 'application/vnd.google-apps.folder'",
        "returnAll": true,
        "filter": {
          "whatToSearch": "files"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        1120,
        820
      ],
      "id": "7d2a8e0d-6eb4-4cf9-9350-23490ea9b5b9",
      "name": "Salesforce IDs",
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "B1rE6o5pdkD9Phxw",
          "name": "Auditech Google"
        }
      }
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "n8n-nodes-base.extractFromFile",
      "typeVersion": 1,
      "position": [
        1560,
        420
      ],
      "id": "17a2cd68-2d1f-4198-bf21-9034f2029a24",
      "name": "Extract from File"
    },
    {
      "parameters": {
        "operation": "download",
        "fileId": "={{ $json.id }}",
        "options": {}
      },
      "id": "6ccd61d3-6400-4b68-b089-03e0b914a6c2",
      "name": "Download CSV1",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        1340,
        620
      ],
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "B1rE6o5pdkD9Phxw",
          "name": "Auditech Google"
        }
      },
      "continueOnFail": true
    },
    {
      "parameters": {
        "operation": "download",
        "fileId": "={{ $json.id }}",
        "options": {}
      },
      "id": "64cb2df2-159f-42ba-9a10-27d967bc49e5",
      "name": "Download CSV2",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        1340,
        820
      ],
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "B1rE6o5pdkD9Phxw",
          "name": "Auditech Google"
        }
      },
      "continueOnFail": true
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "n8n-nodes-base.extractFromFile",
      "typeVersion": 1,
      "position": [
        1560,
        620
      ],
      "id": "d76ab719-ed21-41b1-996e-a2f4528a192e",
      "name": "Extract from File1"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "n8n-nodes-base.extractFromFile",
      "typeVersion": 1,
      "position": [
        1560,
        820
      ],
      "id": "ecd1f880-8c88-4573-9df9-9f5dac5b86c3",
      "name": "Extract from File2"
    },
    {
      "parameters": {
        "numberInputs": 3
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.1,
      "position": [
        1780,
        620
      ],
      "id": "fd3c8565-b8fe-42eb-ae36-500ea69c42e5",
      "name": "Merge"
    },
    {
      "parameters": {
        "jsCode": "// Reconcile Revenue Data\nconst items = $input.all();\n\n// Separate data by type\nconst stripeSubscriptions = [];\nconst stripePayments = [];\nconst stripeCustomers = [];\nconst hubspotContacts = [];\nconst salesforceContacts = [];\n\n// Categorize all items\nitems.forEach(item => {\n  const data = item.json;\n  \n  if (data.id?.startsWith('sub_')) {\n    stripeSubscriptions.push(data);\n  } else if (data.id?.startsWith('ch_')) {\n    stripePayments.push(data);\n  } else if (data.id?.startsWith('cus_')) {\n    stripeCustomers.push(data);\n  } else if (data['Contact ID'] && data['Email']) {\n    hubspotContacts.push(data);\n  } else if (data['Contact_ID__c'] && data['Email']) {\n    salesforceContacts.push(data);\n  }\n});\n\n// Build email maps for reconciliation\nconst stripeCustomersByEmail = new Map();\nstripeCustomers.forEach(c => {\n  if (c.email) {\n    stripeCustomersByEmail.set(c.email.toLowerCase(), c);\n  }\n});\n\nconst hubspotByEmail = new Map();\nhubspotContacts.forEach(c => {\n  if (c.Email) {\n    hubspotByEmail.set(c.Email.toLowerCase(), c);\n  }\n});\n\nconst salesforceByEmail = new Map();\nsalesforceContacts.forEach(c => {\n  if (c.Email) {\n    salesforceByEmail.set(c.Email.toLowerCase(), c);\n  }\n});\n\n// Find discrepancies\nconst discrepancies = {\n  stripeNotInCRM: [],\n  inHubspotNotSalesforce: [],\n  inSalesforceNotHubspot: [],\n  activeSubscriptionsNoCRM: []\n};\n\n// Check Stripe customers against CRMs\nstripeCustomersByEmail.forEach((customer, email) => {\n  const inHubspot = hubspotByEmail.has(email);\n  const inSalesforce = salesforceByEmail.has(email);\n  \n  if (!inHubspot && !inSalesforce) {\n    // Find if they have active subscriptions\n    const hasActiveSubscription = stripeSubscriptions.some(sub => \n      sub.customer === customer.id && sub.status === 'active'\n    );\n    \n    discrepancies.stripeNotInCRM.push({\n      email: email,\n      stripe_id: customer.id,\n      name: customer.name,\n      hasActiveSubscription: hasActiveSubscription\n    });\n    \n    if (hasActiveSubscription) {\n      discrepancies.activeSubscriptionsNoCRM.push({\n        email: email,\n        stripe_id: customer.id,\n        name: customer.name\n      });\n    }\n  }\n});\n\n// Check CRM synchronization\nhubspotByEmail.forEach((contact, email) => {\n  if (!salesforceByEmail.has(email)) {\n    discrepancies.inHubspotNotSalesforce.push({\n      email: email,\n      name: `${contact['First name']} ${contact['Last name']}`,\n      company: contact['Associated company']\n    });\n  }\n});\n\nsalesforceByEmail.forEach((contact, email) => {\n  if (!hubspotByEmail.has(email)) {\n    discrepancies.inSalesforceNotHubspot.push({\n      email: email,\n      name: `${contact.FirstName} ${contact.LastName}`,\n      company: contact.AccountName\n    });\n  }\n});\n\n// Calculate revenue at risk\nlet revenueAtRisk = 0;\ndiscrepancies.activeSubscriptionsNoCRM.forEach(customer => {\n  const customerSubs = stripeSubscriptions.filter(sub => \n    sub.customer === customer.stripe_id && sub.status === 'active'\n  );\n  \n  customerSubs.forEach(sub => {\n    revenueAtRisk += parseInt(sub.plan_amount) / 100; // Convert cents to dollars\n  });\n});\n\nreturn [{\n  json: {\n    summary: {\n      stripeCustomers: stripeCustomers.length,\n      hubspotContacts: hubspotContacts.length,\n      salesforceContacts: salesforceContacts.length,\n      stripeNotInCRM: discrepancies.stripeNotInCRM.length,\n      activeSubsWithoutCRM: discrepancies.activeSubscriptionsNoCRM.length,\n      monthlyRevenueAtRisk: revenueAtRisk,\n      crmMismatch: discrepancies.inHubspotNotSalesforce.length + discrepancies.inSalesforceNotHubspot.length\n    },\n    criticalIssues: discrepancies.activeSubscriptionsNoCRM.slice(0, 10),\n    samples: {\n      stripeNotInCRM: discrepancies.stripeNotInCRM.slice(0, 5),\n      hubspotNotSalesforce: discrepancies.inHubspotNotSalesforce.slice(0, 5),\n      salesforceNotHubspot: discrepancies.inSalesforceNotHubspot.slice(0, 5)\n    }\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2000,
        620
      ],
      "id": "2a856756-d1b0-4cd0-9286-6c08e0b592be",
      "name": "Find MisMatches"
    },
    {
      "parameters": {
        "jsCode": "// Generate Action Report\nconst reconciliation = $input.first().json;\n\n// Prepare report rows for Google Sheets\nconst reportRows = [];\n\n// Header\nreportRows.push({\n  data: ['RevOps Reconciliation Report', new Date().toISOString().split('T')[0]]\n});\nreportRows.push({ data: [''] });\n\n// Executive Summary\nreportRows.push({ data: ['EXECUTIVE SUMMARY'] });\nreportRows.push({ data: ['Total Stripe Customers:', reconciliation.summary.stripeCustomers] });\nreportRows.push({ data: ['Total HubSpot Contacts:', reconciliation.summary.hubspotContacts] });\nreportRows.push({ data: ['Total Salesforce Contacts:', reconciliation.summary.salesforceContacts] });\nreportRows.push({ data: [''] });\nreportRows.push({ data: ['🚨 CRITICAL: Active Subscriptions Without CRM Record:', reconciliation.summary.activeSubsWithoutCRM] });\nreportRows.push({ data: ['💰 Monthly Revenue at Risk:', `${reconciliation.summary.monthlyRevenueAtRisk.toFixed(2)}`] });\nreportRows.push({ data: ['⚠️  CRM Sync Issues:', reconciliation.summary.crmMismatch] });\nreportRows.push({ data: [''] });\n\n// Critical Issues Section\nreportRows.push({ data: ['IMMEDIATE ACTION REQUIRED - Active Subscriptions Without CRM'] });\nreportRows.push({ data: ['Email', 'Customer Name', 'Stripe ID', 'Action Required'] });\n\nreconciliation.criticalIssues.forEach(issue => {\n  reportRows.push({\n    data: [\n      issue.email,\n      issue.name,\n      issue.stripe_id,\n      'Add to CRM immediately'\n    ]\n  });\n});\n\nreportRows.push({ data: [''] });\n\n// Stripe Customers Not in CRM\nreportRows.push({ data: ['ALL STRIPE CUSTOMERS MISSING FROM CRM'] });\nreportRows.push({ data: ['Email', 'Name', 'Stripe ID', 'Has Active Subscription'] });\n\nreconciliation.samples.stripeNotInCRM.forEach(customer => {\n  reportRows.push({\n    data: [\n      customer.email,\n      customer.name,\n      customer.stripe_id,\n      customer.hasActiveSubscription ? 'YES - CRITICAL' : 'No'\n    ]\n  });\n});\n\nreportRows.push({ data: [''] });\n\n// CRM Sync Issues\nreportRows.push({ data: ['CRM SYNCHRONIZATION ISSUES'] });\nreportRows.push({ data: [''] });\nreportRows.push({ data: ['In HubSpot but NOT in Salesforce'] });\nreportRows.push({ data: ['Email', 'Name', 'Company'] });\n\nreconciliation.samples.hubspotNotSalesforce.slice(0, 10).forEach(contact => {\n  reportRows.push({\n    data: [contact.email, contact.name, contact.company]\n  });\n});\n\nreportRows.push({ data: [''] });\nreportRows.push({ data: ['In Salesforce but NOT in HubSpot'] });\nreportRows.push({ data: ['Email', 'Name', 'Company'] });\n\nreconciliation.samples.salesforceNotHubspot.slice(0, 10).forEach(contact => {\n  reportRows.push({\n    data: [contact.email, contact.name, contact.company]\n  });\n});\n\n// Data Quality Notes\nreportRows.push({ data: [''] });\nreportRows.push({ data: ['DATA QUALITY OBSERVATIONS'] });\nreportRows.push({ data: ['1. Some HubSpot emails contain \"+hubspot\" suffix - possible test data'] });\nreportRows.push({ data: ['2. Name capitalization differs between systems (e.g., JENNINGS vs Jennings)'] });\nreportRows.push({ data: ['3. Same contacts with slightly different emails in each system'] });\n\nreturn reportRows;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2220,
        620
      ],
      "id": "68805167-3d41-4fba-8a85-522d27fae287",
      "name": "Generate Action Report"
    },
    {
      "parameters": {
        "jsCode": "// Transform report data for Google Sheets\nconst items = $input.all();\nconst outputRows = [];\n\n// Process each item as a complete row\nitems.forEach(item => {\n  if (item.json.data && Array.isArray(item.json.data)) {\n    const rowData = {};\n    \n    // Map each array element to a column (A, B, C, D, etc.)\n    item.json.data.forEach((value, index) => {\n      const columnLetter = String.fromCharCode(65 + index); // A, B, C, D...\n      rowData[`Column ${columnLetter}`] = value;\n    });\n    \n    outputRows.push({\n      json: rowData\n    });\n  }\n});\n\nreturn outputRows;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2440,
        620
      ],
      "id": "d5920e19-fd6c-40c6-9b0e-8f551ca01ef9",
      "name": "Convert Data to Googlesheets Format"
    },
    {
      "parameters": {
        "operation": "appendOrUpdate",
        "documentId": {
          "__rl": true,
          "value": "YOUR_GOOGLE_SHEET_ID_HERE",
          "mode": "list",
          "cachedResultName": "reconciled-report",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": 0,
          "mode": "list",
          "cachedResultName": "Sheet1",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID_HERE/edit#gid=0"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [],
          "schema": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.6,
      "position": [
        2660,
        620
      ],
      "id": "d3129ede-e025-48ce-8032-3eb15c88940d",
      "name": "Google Sheets1",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "YOUR_GOOGLE_SHEETS_CREDENTIAL_ID",
          "name": "Your Google Sheets OAuth2 Credential"
        }
      }
    },
    {
      "parameters": {
        "resource": "spreadsheet",
        "title": "=reconciled-report",
        "sheetsUi": {
          "sheetValues": [
            {}
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.6,
      "position": [
        240,
        620
      ],
      "id": "bc086d5c-38ba-4083-b089-d0028e7e437b",
      "name": "Create the reconciled report",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "YOUR_GOOGLE_SHEETS_CREDENTIAL_ID",
          "name": "Your Google Sheets OAuth2 Credential"
        }
      }
    }
  ],
  "connections": {
    "RevOps Reports Dir ID": {
      "main": [
        [
          {
            "node": "Child Folder IDs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start": {
      "main": [
        [
          {
            "node": "Create the reconciled report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download CSV": {
      "main": [
        [
          {
            "node": "Extract from File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Child Folder IDs": {
      "main": [
        [
          {
            "node": "Switch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch": {
      "main": [
        [
          {
            "node": "Stripe IDs",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Hubspot IDs1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Salesforce IDs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stripe IDs": {
      "main": [
        [
          {
            "node": "Download CSV",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Hubspot IDs1": {
      "main": [
        [
          {
            "node": "Download CSV1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Salesforce IDs": {
      "main": [
        [
          {
            "node": "Download CSV2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract from File": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download CSV1": {
      "main": [
        [
          {
            "node": "Extract from File1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download CSV2": {
      "main": [
        [
          {
            "node": "Extract from File2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract from File1": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Extract from File2": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "Find MisMatches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find MisMatches": {
      "main": [
        [
          {
            "node": "Generate Action Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Action Report": {
      "main": [
        [
          {
            "node": "Convert Data to Googlesheets Format",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert Data to Googlesheets Format": {
      "main": [
        [
          {
            "node": "Google Sheets1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create the reconciled report": {
      "main": [
        [
          {
            "node": "RevOps Reports Dir ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": false
  }
}

Part 2: Import to n8n

To import this workflow into your n8n instance:

  1. In n8n, click the three dots menu in the top right of your workflow
  2. Select “Import from URL” or “Import from File”
  3. If using “Import from File”, paste the JSON code above into a text file first
  4. Alternatively, select all the code above, copy it, then in n8n press Ctrl+V (or Cmd+V on Mac) directly on the canvas

Part 3: Configure Your Credentials

After importing, you’ll need to update the credentials and document IDs:

  1. Google Drive nodes: Click on each Google Drive node and update the credentials to use your Google OAuth2 connection (replace “YOUR_GOOGLE_DRIVE_CREDENTIAL_ID”)
  2. Google Sheets nodes: Same process – update to use your credentials (replace “YOUR_GOOGLE_SHEETS_CREDENTIAL_ID”)
  3. Update Google Sheet ID: Replace “YOUR_GOOGLE_SHEET_ID_HERE” with your actual Google Sheet ID, or let the workflow create a new one
  4. The workflow will automatically create a new Google Sheet called “reconciled-report” when you run it if you don’t specify an existing one

Part 4: Set Up Your Data Structure

This workflow expects your revenue data to be organized in Google Drive like this:


RevOps Weekly Exports/
├── stripe/
│   ├── customers.csv
│   ├── subscriptions.csv
│   └── payments.csv
├── hubspot/
│   └── contacts.csv
└── salesforce/
    └── contacts.csv
        

If your folder structure is different, just update the “RevOps Reports Dir ID” node to search for your folder name.

Sample Output: What the Workflow Discovers

Here’s a real example of what the automated reconciliation report looks like when you run this workflow. This is actual output from analyzing a SaaS company’s data across HubSpot, Salesforce, and Stripe:

Data Integration Discrepancy Analysis Report

Analysis Date: June 13, 2025

Executive Summary

A comprehensive analysis of multiple data exports revealed critical discrepancies across integrated business systems, with active revenue at risk and significant data synchronization failures requiring immediate remediation.

Data Sources Analyzed

CRM Systems
PlatformFileRecordsKey Fields
HubSpotcompanies_export.csv1,000Company ID, Name, Industry, Revenue, Employees
HubSpotdeals_export.csv1,300Deal ID, Stage, Amount, Close Date, Owner
Salesforceaccounts_export.csv1,000Account_ID__c, Name, Industry, Revenue, Employees
Salesforcecontacts_export.csv1,883Contact_ID__c, Name, Email, Account_ID__c
Salesforceopportunities_export.csv1,500Opportunity_ID__c, Name, Amount, Stage, Close Date
Payment & Billing Systems
PlatformFileRecordsKey Fields
Stripecustomers_export.csv1,200Customer ID, Email, Name, Company
Stripepayments_export.csv6,000Payment ID, Amount, Customer, Status
Stripesubscriptions_export.csv840Subscription ID, Customer, Status, Plan Amount

Critical Discrepancies Discovered

1. Company Data Inconsistencies

Issue: Same companies exist with conflicting address information between HubSpot and Salesforce.

Example – Acme Corporation:

  • HubSpot: Lake Keithstad, Massachusetts
  • Salesforce: Montgomeryfurt, Rhode Island
  • Revenue ($33.8M) and employees (513) match, but addresses completely different

Impact: Customer service confusion, shipping errors, tax compliance issues

2. Pipeline Management Crisis

Deal/Opportunity Count Mismatch:

  • HubSpot Deals: 1,300 records
  • Salesforce Opportunities: 1,500 records
  • Missing: 200 deals not synchronized

Financial Impact:

  • HubSpot Pipeline Value: $264,275,126
  • Salesforce Pipeline Value: $381,814,628
  • Revenue Gap: $117,539,502 unaccounted for
3. Customer-Payment System Disconnects

Orphaned Records:

  • 5 customers exist without payment records
  • All payment records have corresponding customers (good)
  • Revenue tracking integrity compromised

Subscription Status:

  • Total subscriptions: 840
  • Active subscriptions: 595
  • 245 inactive subscriptions still in system

Revenue at Risk Analysis

The automated reconciliation process identified immediate threats to revenue:

  • Active paying customers missing from CRM: 6 customers
  • Monthly revenue at risk: $2,894.00
  • Annual revenue exposure: $34,728.00
Critical Action Required – Missing Active Subscribers
CustomerEmailStripe IDStatus
Robert Chavezrobert@grantgroup.comcus_aaOzcavcdOlSBNIMMEDIATE ACTION
Christy Byrdcbyrd@rtez.comcus_BXCRNEBYGgOtSmIMMEDIATE ACTION
Robert Prestonrobert@garciapearsonandfernandez.comcus_idzMHxglRtBJsCIMMEDIATE ACTION
Danielle Russodrusso@oliver.comcus_ovQZWRyoSOYEKTIMMEDIATE ACTION
Brian Cainbcain@sandovalgarciaandperkins.comcus_txoVHJGUMKLarOIMMEDIATE ACTION
Deborah Ramseydeborah.ramsey@nleyplc.comcus_DoAwdpSTJJVKVJIMMEDIATE ACTION
CRM Synchronization Issues

Total sync problems identified: 708 records

Pattern Analysis:

  • Same contacts exist in both systems with different email formats
  • HubSpot emails contain “+hubspot” suffixes (test data contamination)
  • Name capitalization inconsistencies (e.g., “JENNINGS” vs “Jennings”)

Example Sync Failures:


HubSpot: jesusj+hubspot@ellis-adkins.com
Salesforce: jesusj@ellis-adkins.com
Result: Duplicate customer records, fractured customer journey
        

Business Impact Assessment

Immediate Risks
  • Customer Churn: 6 paying customers ($2,894/month) invisible to sales teams
  • Revenue Recognition: $117M pipeline discrepancy affects forecasting
  • Customer Experience: Address mismatches cause delivery/service issues
Operational Risks
  • Sales teams working with incomplete customer data
  • Marketing campaigns targeting wrong/duplicate contacts
  • Financial reporting inaccuracies due to system disconnects

Recommended Actions

Priority 1 (Immediate – Next 24 Hours)
  1. Add 6 missing active subscribers to CRM to prevent churn
  2. Audit and consolidate Acme account address information
  3. Implement emergency sync monitoring for revenue-critical customers
Priority 2 (This Week)
  1. Reconcile 200 missing deals/opportunities and $117M pipeline gap
  2. Standardize date formats across all integrated systems
  3. Clean test data from HubSpot (remove “+hubspot” email suffixes)
Priority 3 (Next 30 Days)
  1. Implement automated sync validation between all systems
  2. Establish single source of truth for company/contact data
  3. Create real-time monitoring dashboard for data integrity

The Bottom Line: This workflow found $117M in pipeline discrepancies and $34,728 in immediate churn risk that would have gone unnoticed. Instead of spending Sunday nights building spreadsheets, you now have actionable intelligence that can save and recover significant revenue.

Actual n8n Workflow Output

Here’s the exact Google Sheets report that the workflow automatically creates and saves to your Google Drive:

Column AColumn BColumn CColumn D
RevOps Reconciliation Report2025-06-13
EXECUTIVE SUMMARY
Total Stripe Customers:1200
Total HubSpot Contacts:2200
Total Salesforce Contacts:1883
🚨 CRITICAL: Active Subscriptions Without CRM Record:6
💰 Monthly Revenue at Risk:$2,894.00
⚠️ CRM Sync Issues:708
IMMEDIATE ACTION REQUIRED – Active Subscriptions Without CRM
EmailCustomer NameStripe IDAction Required
robert@grantgroup.comRobert Chavezcus_aaOzcavcdOlSBNAdd to CRM immediately
cbyrd@rtez.comChristy Byrdcus_BXCRNEBYGgOtSmAdd to CRM immediately
robert@garciapearsonandfernandez.comRobert Prestoncus_idzMHxglRtBJsCAdd to CRM immediately
drusso@oliver.comDanielle Russocus_ovQZWRyoSOYEKTAdd to CRM immediately
bcain@sandovalgarciaandperkins.comBrian Caincus_txoVHJGUMKLarOAdd to CRM immediately
deborah.ramsey@nleyplc.comDeborah Ramseycus_DoAwdpSTJJVKVJAdd to CRM immediately
CRM SYNCHRONIZATION ISSUES
In HubSpot but NOT in Salesforce
EmailNameCompany
jesusj+hubspot@ellis-adkins.comJesus JenningsEllis-Adkins
sheila.armstrong+hubspot@williamsshieldsandmiller.comSheila ArmstrongWilliams, Shields and Miller
kestes+hubspot@parker.comKathy EstesParker Ltd
In Salesforce but NOT in HubSpot
EmailNameCompany
jesusj@ellis-adkins.comJesus JENNINGSEllis-Adkins
sheila.armstrong@williamsshieldsandmiller.comSheila ArmstrongWilliams, Shields and Miller
kestes@parker.comKathy ESTESParker Ltd
DATA QUALITY OBSERVATIONS
1. Some HubSpot emails contain “+hubspot” suffix – possible test data
2. Name capitalization differs between systems (e.g., JENNINGS vs Jennings)
3. Same contacts with slightly different emails in each system

What makes this powerful:

  • Automatic creation: The workflow creates this Google Sheet in your Drive without any manual work
  • Executive ready: Clean format you can immediately share with leadership
  • Actionable insights: Specific customer names, emails, and Stripe IDs for immediate follow-up
  • Pattern detection: Shows systematic issues like test data contamination and sync failures
  • Revenue impact: Quantifies exact dollars at risk ($2,894/month)

Ready to Reclaim Your Sundays?

This n8n workflow template represents days of development time. If it saves you even one Sunday night with your family, it’s worth it.

Also, checkout the website to see what else AudiTech has to offer RevOps.

Questions? Connect with me on LinkedIn or reach out if you need help implementing this for your specific data stack. If this workflow was helpful, drop a like, repost, or better yet tell me about it.

Related Posts

June 21, 2025Adam Carter

The End of Fleeting Permanence

While on my farm, swinging on a rope swing, drinking coffee, and thinking about recent predictions from an OpenAI defector, Daniel Kokotajlo, that AGI...

Read More