AutomationFlowsCRM & Sales › Gtm Control Tower - Lead Routing

Gtm Control Tower - Lead Routing

GTM Control Tower - Lead Routing. Uses httpRequest, salesforce, googleBigQuery. Webhook trigger; 6 nodes.

Webhook trigger★★★★☆ complexity6 nodesHTTP RequestSalesforceGoogle BigQuery
CRM & Sales Trigger: Webhook Nodes: 6 Complexity: ★★★★☆ Added:

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
{
  "id": "gtmControlTower01",
  "name": "GTM Control Tower - Lead Routing",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "gtm-lead",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "0e6ad5f7-02c1-44d0-a86e-15d0ed5cb203",
      "name": "Receive CRM Signal",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        -700,
        80
      ]
    },
    {
      "parameters": {
        "jsCode": "const body = $json.body ?? $json;\nconst revenue = Number(body.annual_revenue ?? 0);\nconst employees = Number(body.employee_count ?? 0);\nconst score = Math.min(100, Math.round((revenue / 500000) + (employees / 20) + (body.intent_signal ? 25 : 0)));\nconst segment = revenue >= 20000000 ? 'Enterprise' : revenue >= 5000000 ? 'Mid-Market' : 'SMB';\nconst region = body.region ?? 'Central';\nconst owner = `${region.slice(0, 2).toUpperCase()}-${segment === 'Enterprise' ? 'ENT' : segment === 'Mid-Market' ? 'MM' : 'SMB'}`;\nconst eventTimestamp = new Date().toISOString();\nconst leadId = body.lead_id ?? `LEAD-${Date.now()}`;\nreturn [{ json: { ...body, lead_id: leadId, account_id: body.account_id ?? `ACC-${leadId}`, event_id: `${leadId}-${eventTimestamp}`, event_type: 'lead_routed', lifecycle_stage: score >= 70 ? 'mql' : 'lead', event_timestamp: eventTimestamp, score, segment, region, owner_id: owner, route_seconds: 0, is_duplicate: false } }];"
      },
      "id": "d7a6c42e-6637-4fc6-84ef-ae9bb3be1bbb",
      "name": "Normalize, Score & Route",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -450,
        80
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.hubapi.com/crm/objects/2026-03/contacts/batch/upsert",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "hubspotOAuth2Api",
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json",
        "jsonBody": "={{ { inputs: [{ id: $json.email, idProperty: 'email', objectWriteTraceId: $json.event_id, properties: { firstname: $json.first_name ?? '', lastname: $json.last_name ?? '', company: $json.company ?? '', annualrevenue: String($json.annual_revenue ?? 0), hs_lead_status: $json.score >= 70 ? 'IN_PROGRESS' : 'NEW', lifecyclestage: $json.lifecycle_stage === 'mql' ? 'marketingqualifiedlead' : 'lead', numemployees: $json.employee_count >= 1000 ? '1000+' : $json.employee_count >= 500 ? '500-1000' : $json.employee_count >= 100 ? '100-500' : $json.employee_count >= 50 ? '50-100' : $json.employee_count >= 25 ? '25-50' : $json.employee_count >= 5 ? '5-25' : '1-5' } }] } }}",
        "options": {}
      },
      "id": "49c76290-3a5f-4c33-8823-aac2fb997ee9",
      "name": "Upsert HubSpot Contact",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.5,
      "position": [
        -190,
        -120
      ],
      "notes": "Primary live CRM path. Uses HubSpot's current contacts batch-upsert API because the bundled HubSpot node still calls a legacy contacts endpoint."
    },
    {
      "parameters": {
        "authentication": "oAuth2",
        "operation": "upsert",
        "resource": "lead",
        "externalId": "GTM_Lead_Id__c",
        "externalIdValue": "={{ $json.lead_id }}",
        "company": "={{ $json.company ?? 'Unknown' }}",
        "lastname": "={{ $json.last_name ?? 'Unknown' }}",
        "additionalFields": {
          "email": "={{ $json.email }}",
          "annualRevenue": "={{ $json.annual_revenue }}",
          "numberOfEmployees": "={{ $json.employee_count }}",
          "leadSource": "={{ $json.source ?? 'Web' }}",
          "rating": "={{ $json.score >= 70 ? 'Hot' : 'Warm' }}",
          "customFieldsUi": {
            "customFieldsValues": [
              {
                "fieldId": "GTM_Score__c",
                "value": "={{ $json.score }}"
              },
              {
                "fieldId": "GTM_Segment__c",
                "value": "={{ $json.segment }}"
              },
              {
                "fieldId": "GTM_Routed_Owner__c",
                "value": "={{ $json.owner_id }}"
              },
              {
                "fieldId": "GTM_Last_Routed_At__c",
                "value": "={{ $json.event_timestamp }}"
              }
            ]
          }
        },
        "options": {}
      },
      "id": "c15b7f89-8319-47ec-b687-0306c7dd0d6c",
      "name": "Upsert Salesforce Lead",
      "type": "n8n-nodes-base.salesforce",
      "typeVersion": 1,
      "position": [
        -190,
        180
      ],
      "disabled": true,
      "notes": "Organization-specific custom-field example; intentionally disabled. The verified portable Salesforce path is the query-first CSV connector and does not require GTM_* custom fields."
    },
    {
      "parameters": {
        "authentication": "serviceAccount",
        "operation": "insert",
        "projectId": {
          "mode": "id",
          "value": "__GCP_PROJECT_ID__"
        },
        "datasetId": {
          "mode": "id",
          "value": "__BIGQUERY_SOURCE_DATASET__"
        },
        "tableId": {
          "mode": "id",
          "value": "raw_crm_events"
        },
        "dataMode": "autoMap",
        "options": {
          "ignoreUnknownValues": true
        }
      },
      "id": "75e1691f-11ee-477b-bf4a-7dcb77f1f1be",
      "name": "Append BigQuery Event",
      "type": "n8n-nodes-base.googleBigQuery",
      "typeVersion": 2.1,
      "position": [
        70,
        80
      ],
      "notes": "Bind your own least-privilege BigQuery credential after importing this workflow."
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ { accepted: true, lead_id: $json.lead_id ?? $('Normalize, Score & Route').item.json.lead_id, score: $('Normalize, Score & Route').item.json.score, owner_id: $('Normalize, Score & Route').item.json.owner_id } }}",
        "options": {
          "responseCode": 202
        }
      },
      "id": "848c1bd7-faf1-4ad2-88e0-63f0e39a6d2d",
      "name": "Return Routing Decision",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [
        330,
        80
      ]
    }
  ],
  "connections": {
    "Receive CRM Signal": {
      "main": [
        [
          {
            "node": "Normalize, Score & Route",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize, Score & Route": {
      "main": [
        [
          {
            "node": "Upsert HubSpot Contact",
            "type": "main",
            "index": 0
          },
          {
            "node": "Upsert Salesforce Lead",
            "type": "main",
            "index": 0
          },
          {
            "node": "Append BigQuery Event",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append BigQuery Event": {
      "main": [
        [
          {
            "node": "Return Routing Decision",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "saveManualExecutions": true
  },
  "versionId": "8455fc67-af18-4cfb-b76b-ec3d7f32b9ec",
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

GTM Control Tower - Lead Routing. Uses httpRequest, salesforce, googleBigQuery. Webhook trigger; 6 nodes.

Source: https://github.com/harrisonoconnorhover/gtm-control-tower/blob/main/integrations/n8n/lead-routing-workflow.json — original creator credit. Request a take-down →

More CRM & Sales workflows → · Browse all categories →

Related workflows

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

CRM & Sales

AI Lead Qualifier. Uses httpRequest, openAi, salesforce, slack. Webhook trigger; 8 nodes.

HTTP Request, OpenAI, Salesforce +2
CRM & Sales

Automate your lead qualification pipeline — capture Typeform Webhook leads, enrich with APIs, score intelligently, and route to HubSpot, Slack, and Sheets in real-time.

HTTP Request, HubSpot, Slack +1
CRM & Sales

For Temp_Smart Lead Routing & Qualification System (Typeform to HubSpot). Uses httpRequest, hubspot, slack, googleSheets. Webhook trigger; 27 nodes.

HTTP Request, HubSpot, Slack +1
CRM & Sales

This workflow runs on a schedule to fetch new, unsynced Salesforce leads, pulls account journey activity from the Factors.ai API based on the lead’s email domain, and updates the lead in Salesforce wi

Salesforce, HTTP Request
CRM & Sales

How it works This Lead Capture & Auto-Qualification workflow transforms raw leads into qualified prospects through intelligent automation. Here's the high-level flow: Lead Intake → Data Validation → E

Clearbit, HTTP Request, HubSpot +1