AutomationFlowsCRM & Sales › Route and Score Inbound Leads with Openai, Hubspot, Slack, Gmail, and Google…

Route and Score Inbound Leads with Openai, Hubspot, Slack, Gmail, and Google…

Original n8n title: Route and Score Inbound Leads with Openai, Hubspot, Slack, Gmail, and Google Sheets

ByAvkash Kakdiya @itechnotion on n8n.io

This workflow receives inbound leads via webhook, validates and normalizes the data, scores and routes the lead to a sales rep, checks for duplicates and upserts the contact in HubSpot, drafts an outreach email with OpenAI, notifies the rep via Slack and Gmail, and logs the…

Webhook trigger★★★★☆ complexity23 nodesHubSpotSlackGmailGoogle SheetsHTTP Request
CRM & Sales Trigger: Webhook Nodes: 23 Complexity: ★★★★☆ Added:
Route and Score Inbound Leads with Openai, Hubspot, Slack, Gmail, and Google… — n8n workflow card showing HubSpot, Slack, Gmail integration

This workflow corresponds to n8n.io template #16079 — we link there as the canonical source.

This workflow follows the Gmail → Google Sheets 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
{
  "id": "rQcRMsm1eCFaPIwZ",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Sales Lead Auto-Router with Scoring & CRM Logging",
  "tags": [
    {
      "id": "4Of2LE0HSt4qmmUG",
      "name": "Ansh",
      "createdAt": "2026-04-20T10:32:01.726Z",
      "updatedAt": "2026-04-20T10:32:01.726Z"
    }
  ],
  "nodes": [
    {
      "id": "ee686c5e-09ab-4e5f-9591-c78485d406e8",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2896,
        -640
      ],
      "parameters": {
        "width": 556,
        "height": 724,
        "content": "## Sales Lead Auto-Router with Scoring & CRM Logging\nThis workflow automatically routes every new inbound lead to the right sales rep based on their territory, lead source, and calculated score. It eliminates manual triage so reps get notified instantly and no lead sits unassigned.\n\n### How it works\n\n\t\u2022\tA webhook captures new lead data the moment a form is submitted or a CRM event fires.\n\t\u2022\tThe workflow validates the payload and rejects missing or malformed data immediately.\n\t\u2022\tIt enriches and normalises the lead data (name, email, source, region).\n\t\u2022\tIt calculates a lead score based on source, company size, and intent signals.\n\t\u2022\tA duplicate check queries HubSpot to see if the contact already exists.\n\t\u2022\tAn assignment rule matches the lead to the correct sales rep by territory and score tier.\n\t\u2022\tOpenAI generates a personalised first-touch outreach email draft for the rep.\n\t\u2022\tThe assigned rep is notified via Slack and receives the draft via email.\n\t\u2022\tThe lead is created or updated in HubSpot.\n\t\u2022\tA confirmation row is logged to Google Sheets for reporting.\n\t\u2022\tAny processing errors are caught and routed to a dedicated Slack error alert.\n\n### Setup Steps\n\n\t1.\tConnect your form or CRM webhook to the Webhook node.\n\t2.\tUpdate the Code node scoring logic with your own scoring rules.\n\t3.\tUpdate the assignment map in the Set node with your reps and territories.\n\t4.\tConnect your HubSpot, Slack, Google Sheets, OpenAI, and Gmail credentials.\n\t5.\tReplace REPLACE_WITH_YOUR_SHEET_ID with your Google Sheets document ID.\n\t6.\tUpdate the Slack channel name in all Slack nodes.\n\t7.\tTurn on the workflow."
      },
      "typeVersion": 1
    },
    {
      "id": "49bdb5c8-6395-4f80-8e37-eac8e6b0a48b",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2320,
        -640
      ],
      "parameters": {
        "color": 7,
        "width": 848,
        "height": 720,
        "content": "## Step 1: Capture, Validate & Normalise the Lead\nThis step listens for new leads coming in from a form, landing page, or CRM event. It immediately validates that required fields are present and well-formed, rejecting bad payloads with a 400 response before any processing occurs. Valid data is then tidied up so everything is clean and ready to work with."
      },
      "typeVersion": 1
    },
    {
      "id": "d5163563-e69a-490c-b280-ac3ba0b24e72",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1456,
        -640
      ],
      "parameters": {
        "color": 7,
        "width": 1216,
        "height": 720,
        "content": "## Step 2: Score, Deduplicate & Match to a Rep\nThis step scores the lead based on source, company size, and email domain signals, then checks HubSpot to see if this contact already exists to avoid duplicate records. It then assigns the correct sales rep based on territory and score tier, and calls OpenAI to generate a personalised first-touch email draft ready for the rep to send."
      },
      "typeVersion": 1
    },
    {
      "id": "3c9e2f28-d19a-450c-8d1c-c25affea8e92",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -224,
        -640
      ],
      "parameters": {
        "color": 7,
        "width": 1072,
        "height": 720,
        "content": "## Step 3: Notify, Update CRM & Log\nThis step sends the assigned rep an instant Slack message with the lead details and the AI-generated outreach draft via email. It then creates or updates the contact in HubSpot and saves a summary row to Google Sheets so the team has a full record. A webhook response is returned to the caller confirming successful routing."
      },
      "typeVersion": 1
    },
    {
      "id": "f91eeb15-1247-40e9-9f35-8b98a3012cbc",
      "name": "Webhook \u2014 Inbound Lead",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -2272,
        -304
      ],
      "parameters": {
        "path": "inbound-lead",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "fd364ac4-1849-4f4e-b2c7-1754e6b9e511",
      "name": "Validate Payload",
      "type": "n8n-nodes-base.code",
      "position": [
        -2064,
        -304
      ],
      "parameters": {
        "jsCode": "// Payload Validation\nconst body = $input.first().json.body || {};\n\nconst errors = [];\n\n// Required field checks\nif (!body.email || typeof body.email !== 'string' || !body.email.includes('@')) {\n  errors.push('Missing or invalid email');\n}\nif (!body.first_name && !body.firstName) {\n  errors.push('Missing first name');\n}\nif (!body.last_name && !body.lastName) {\n  errors.push('Missing last name');\n}\n\n// Email format check\nconst emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\nif (body.email && !emailRegex.test(body.email.trim())) {\n  errors.push('Email format is invalid');\n}\n\nif (errors.length > 0) {\n  return [{\n    json: {\n      valid: false,\n      errors,\n      originalBody: body\n    }\n  }];\n}\n\nreturn [{\n  json: {\n    valid: true,\n    body\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "be92113c-1163-404f-b9e3-41a6db0bf8e4",
      "name": "Is Valid?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1856,
        -304
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "valid1",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.valid }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "b972b115-b53d-4446-8a84-a7e445dd180f",
      "name": "Respond 400 \u2014 Bad Request",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -1648,
        -208
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ status: 'error', message: 'Validation failed', errors: $json.errors }) }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "2f35551b-0d19-456a-8933-c4ac2662c5ce",
      "name": "Normalise Lead Data",
      "type": "n8n-nodes-base.set",
      "position": [
        -1648,
        -400
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "f1",
              "name": "firstName",
              "type": "string",
              "value": "={{ $json.body.first_name?.trim() || $json.body.firstName?.trim() || '' }}"
            },
            {
              "id": "f2",
              "name": "lastName",
              "type": "string",
              "value": "={{ $json.body.last_name?.trim() || $json.body.lastName?.trim() || '' }}"
            },
            {
              "id": "f3",
              "name": "email",
              "type": "string",
              "value": "={{ $json.body.email?.toLowerCase().trim() }}"
            },
            {
              "id": "f4",
              "name": "company",
              "type": "string",
              "value": "={{ $json.body.company?.trim() || 'Unknown' }}"
            },
            {
              "id": "f5",
              "name": "region",
              "type": "string",
              "value": "={{ $json.body.region?.toLowerCase().trim() || 'unassigned' }}"
            },
            {
              "id": "f6",
              "name": "leadSource",
              "type": "string",
              "value": "={{ $json.body.lead_source || $json.body.leadSource || 'organic' }}"
            },
            {
              "id": "f7",
              "name": "companySize",
              "type": "number",
              "value": "={{ parseInt($json.body.company_size || $json.body.companySize || '0') }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "78f42b11-9cfe-44ad-9e3c-820a9dbfd0dc",
      "name": "Score & Route Lead",
      "type": "n8n-nodes-base.code",
      "position": [
        -1424,
        -304
      ],
      "parameters": {
        "jsCode": "// Lead Scoring Logic\nconst item = $input.first().json;\n\nlet score = 0;\n\n// Source scoring\nconst sourceScores = {\n  'paid': 30,\n  'referral': 25,\n  'webinar': 20,\n  'organic': 15,\n  'social': 10,\n  'direct': 10,\n  'cold': 5\n};\nscore += sourceScores[item.leadSource] || 5;\n\n// Company size scoring\nif (item.companySize >= 500) score += 30;\nelse if (item.companySize >= 100) score += 20;\nelse if (item.companySize >= 20) score += 10;\nelse score += 5;\n\n// Email domain scoring (not free email = B2B)\nconst freeEmailDomains = ['gmail.com','yahoo.com','hotmail.com','outlook.com'];\nconst domain = item.email.split('@')[1] || '';\nif (!freeEmailDomains.includes(domain)) score += 20;\n\n// Score tier\nlet tier;\nif (score >= 60) tier = 'hot';\nelse if (score >= 35) tier = 'warm';\nelse tier = 'cold';\n\n// Territory to rep mapping\nconst repMap = {\n  'north': { hot: 'user@example.com', warm: 'user@example.com', cold: 'user@example.com' },\n  'south': { hot: 'user@example.com', warm: 'user@example.com', cold: 'user@example.com' },\n  'east':  { hot: 'user@example.com',   warm: 'user@example.com',   cold: 'user@example.com' },\n  'west':  { hot: 'user@example.com', warm: 'user@example.com', cold: 'user@example.com' },\n  'unassigned': { hot: 'user@example.com', warm: 'user@example.com', cold: 'user@example.com' }\n};\n\nconst region = item.region in repMap ? item.region : 'unassigned';\nconst assignedRep = repMap[region][tier];\n\nreturn [{\n  json: {\n    ...item,\n    leadScore: score,\n    scoreTier: tier,\n    assignedRep,\n    routedAt: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "811552fb-0832-4e21-8fed-0654554083a4",
      "name": "HubSpot \u2014 Check Duplicate",
      "type": "n8n-nodes-base.hubspot",
      "position": [
        -1216,
        -304
      ],
      "parameters": {
        "operation": "search",
        "authentication": "oAuth2",
        "additionalFields": {}
      },
      "typeVersion": 2
    },
    {
      "id": "9ff0e63e-10b3-4faf-ae52-52bb8c38c4b7",
      "name": "Merge Duplicate Flag",
      "type": "n8n-nodes-base.code",
      "position": [
        -1008,
        -304
      ],
      "parameters": {
        "jsCode": "// Merge duplicate check result back onto lead data\nconst leadData = $('Score & Route Lead').first().json;\nconst searchResult = $input.first().json;\n\n// HubSpot returns results array; if any results exist, contact already exists\nconst existingContacts = searchResult.results || [];\nconst isDuplicate = existingContacts.length > 0;\nconst existingContactId = isDuplicate ? existingContacts[0].id : null;\n\nreturn [{\n  json: {\n    ...leadData,\n    isDuplicate,\n    existingContactId\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "52686fe3-b656-4632-a5da-84690a4a9a83",
      "name": "Slack \u2014 Duplicate Warning",
      "type": "n8n-nodes-base.slack",
      "position": [
        -592,
        -208
      ],
      "parameters": {
        "resource": "chat",
        "authentication": "oAuth2"
      },
      "typeVersion": 2.3
    },
    {
      "id": "6e53846b-4088-4fb6-b132-7493b62a7ad7",
      "name": "Is New Lead?",
      "type": "n8n-nodes-base.if",
      "position": [
        -800,
        -304
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "dup1",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.isDuplicate }}",
              "rightValue": false
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "0c6255ea-f06f-4cf9-ae9e-67f356b300cf",
      "name": "Attach Draft to Lead",
      "type": "n8n-nodes-base.code",
      "position": [
        -384,
        -400
      ],
      "parameters": {
        "jsCode": "// Attach AI draft to lead data\nconst leadData = $('Is New Lead?').first().json;\nconst aiResponse = $input.first().json;\n\nconst emailDraft = aiResponse.message?.content || aiResponse.choices?.[0]?.message?.content || 'Draft unavailable \u2014 please write a personalised intro.';\n\nreturn [{\n  json: {\n    ...leadData,\n    emailDraft\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "af7924f0-8f23-45bb-9d9e-d413625f4230",
      "name": "Hot or Warm?",
      "type": "n8n-nodes-base.if",
      "position": [
        -192,
        -304
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond1",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.scoreTier }}",
              "rightValue": "cold"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "2d9b3f31-cbd8-44c8-bc11-96cf1003c658",
      "name": "HubSpot \u2014 Upsert Contact",
      "type": "n8n-nodes-base.hubspot",
      "position": [
        32,
        -400
      ],
      "parameters": {
        "email": "={{ $json.email }}",
        "options": {},
        "authentication": "oAuth2",
        "additionalFields": {
          "lastName": "={{ $json.lastName }}",
          "firstName": "={{ $json.firstName }}"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "75139778-efd5-4612-8c80-6bee73fe23fa",
      "name": "HubSpot \u2014 Log Cold Lead",
      "type": "n8n-nodes-base.hubspot",
      "position": [
        32,
        -208
      ],
      "parameters": {
        "email": "={{ $json.email }}",
        "options": {},
        "authentication": "oAuth2",
        "additionalFields": {
          "lastName": "={{ $json.lastName }}",
          "firstName": "={{ $json.firstName }}"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "901f40a0-b013-46b3-a57c-603cee6d03d9",
      "name": "Slack \u2014 Notify Rep",
      "type": "n8n-nodes-base.slack",
      "position": [
        240,
        -400
      ],
      "parameters": {
        "text": ":dart: *New {{ $json.scoreTier.toUpperCase() }} Lead Assigned to you!*\n\n*Name:* {{ $json.firstName }} {{ $json.lastName }}\n*Email:* {{ $json.email }}\n*Company:* {{ $json.company }} ({{ $json.companySize }} employees)\n*Region:* {{ $json.region }}\n*Source:* {{ $json.leadSource }}\n*Score:* {{ $json.leadScore }} \u2014 {{ $json.scoreTier }}\n*Routed at:* {{ $json.routedAt }}\n\nAssigned to: *{{ $json.assignedRep }}*\n\n:writing_hand: *AI Outreach Draft:*\n{{ $json.emailDraft }}",
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "typeVersion": 2.3
    },
    {
      "id": "5bbbfee5-db61-41c5-b42f-283e468bdd5a",
      "name": "Gmail \u2014 Email Rep with Draft",
      "type": "n8n-nodes-base.gmail",
      "position": [
        240,
        -208
      ],
      "parameters": {
        "sendTo": "={{ $json.assignedRep }}",
        "message": "Hi,\n\nA new {{ $json.scoreTier }} lead has been assigned to you.\n\nLead Details:\n- Name: {{ $json.firstName }} {{ $json.lastName }}\n- Email: {{ $json.email }}\n- Company: {{ $json.company }} ({{ $json.companySize }} employees)\n- Region: {{ $json.region }}\n- Source: {{ $json.leadSource }}\n- Score: {{ $json.leadScore }} ({{ $json.scoreTier }})\n- Routed at: {{ $json.routedAt }}\n\n---\nAI-GENERATED OUTREACH DRAFT\n(Review and personalise before sending)\n\n{{ $json.emailDraft }}\n---\n\nGood luck!\nYour Sales Ops Automation",
        "options": {},
        "subject": "[{{ $json.scoreTier.toUpperCase() }} Lead] {{ $json.firstName }} {{ $json.lastName }} from {{ $json.company }} \u2014 Score {{ $json.leadScore }}",
        "emailType": "text"
      },
      "typeVersion": 2.1
    },
    {
      "id": "e7ff948b-34f6-4a75-bd54-77064e464035",
      "name": "Google Sheets \u2014 Log Lead",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        448,
        -304
      ],
      "parameters": {
        "columns": {
          "value": {
            "Date": "={{ $json.routedAt }}",
            "Tier": "={{ $json.scoreTier }}",
            "Email": "={{ $json.email }}",
            "Score": "={{ $json.leadScore }}",
            "Region": "={{ $json.region }}",
            "Source": "={{ $json.leadSource }}",
            "Company": "={{ $json.company }}",
            "Duplicate": "={{ $json.isDuplicate }}",
            "Last Name": "={{ $json.lastName }}",
            "First Name": "={{ $json.firstName }}",
            "Assigned Rep": "={{ $json.assignedRep }}",
            "Existing Contact ID": "={{ $json.existingContactId || '' }}"
          },
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "REPLACE_WITH_YOUR_SHEET_ID"
        },
        "authentication": "serviceAccount"
      },
      "typeVersion": 4.5
    },
    {
      "id": "1be56b6c-15ba-44e5-9748-1b47c9ca03b1",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        656,
        -304
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ status: 'success', message: 'Lead routed', assignedTo: $('Score & Route Lead').first().json.assignedRep, tier: $('Score & Route Lead').first().json.scoreTier, isDuplicate: $('Merge Duplicate Flag').first().json.isDuplicate }) }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "77ecb2a8-e288-45b6-994d-68c1334b6fbe",
      "name": "HTTP \u2014 Draft Outreach Email",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -592,
        -400
      ],
      "parameters": {
        "url": "https://api.openai.com/v1/chat/completions",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "model",
              "value": "gpt-4o-mini"
            },
            {
              "name": "messages",
              "value": "={{ JSON.stringify([{ role: 'system', content: 'You are a helpful sales assistant. Write concise, friendly B2B outreach emails.' }, { role: 'user', content: 'Write a short first-touch outreach email to ' + $json.firstName + ' ' + $json.lastName + ' at ' + $json.company + '. They came via ' + $json.leadSource + ' and are in the ' + $json.region + ' region. Keep it under 100 words, no subject line.' }]) }}"
            },
            {
              "name": "max_tokens",
              "value": "300"
            }
          ]
        },
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.3
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "4ad66cf2-e190-4905-bbc2-4f11c2c0646b",
  "connections": {
    "Is Valid?": {
      "main": [
        [
          {
            "node": "Normalise Lead Data",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond 400 \u2014 Bad Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Hot or Warm?": {
      "main": [
        [
          {
            "node": "HubSpot \u2014 Upsert Contact",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "HubSpot \u2014 Log Cold Lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is New Lead?": {
      "main": [
        [
          {
            "node": "HTTP \u2014 Draft Outreach Email",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Slack \u2014 Duplicate Warning",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Payload": {
      "main": [
        [
          {
            "node": "Is Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Score & Route Lead": {
      "main": [
        [
          {
            "node": "HubSpot \u2014 Check Duplicate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalise Lead Data": {
      "main": [
        [
          {
            "node": "Score & Route Lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Attach Draft to Lead": {
      "main": [
        [
          {
            "node": "Hot or Warm?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Duplicate Flag": {
      "main": [
        [
          {
            "node": "Is New Lead?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack \u2014 Notify Rep": {
      "main": [
        [
          {
            "node": "Google Sheets \u2014 Log Lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook \u2014 Inbound Lead": {
      "main": [
        [
          {
            "node": "Validate Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HubSpot \u2014 Log Cold Lead": {
      "main": [
        [
          {
            "node": "Gmail \u2014 Email Rep with Draft",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets \u2014 Log Lead": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HubSpot \u2014 Upsert Contact": {
      "main": [
        [
          {
            "node": "Slack \u2014 Notify Rep",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HubSpot \u2014 Check Duplicate": {
      "main": [
        [
          {
            "node": "Merge Duplicate Flag",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack \u2014 Duplicate Warning": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP \u2014 Draft Outreach Email": {
      "main": [
        [
          {
            "node": "Attach Draft to Lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail \u2014 Email Rep with Draft": {
      "main": [
        [
          {
            "node": "Google Sheets \u2014 Log Lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

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

About this workflow

This workflow receives inbound leads via webhook, validates and normalizes the data, scores and routes the lead to a sales rep, checks for duplicates and upserts the contact in HubSpot, drafts an outreach email with OpenAI, notifies the rep via Slack and Gmail, and logs the…

Source: https://n8n.io/workflows/16079/ — 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

This is ideal for agencies, freelancers, SaaS founders, and small sales teams who want every lead recorded and followed up automatically within seconds. The workflow supports two storage options: HubS

HTTP Request, Google Sheets, Slack +1
CRM & Sales

This workflow receives inbound leads via webhook, uses Anthropic Claude (with website scraping and web search) to score and classify them, then updates HubSpot lifecycle stage, creates deals for hot l

HTTP Request, HubSpot, Slack +1
CRM & Sales

Real-Time Lead Processing - Captures and processes leads instantly from website forms with zero delay Intelligent Fit Scoring - Automatically scores leads 0-100 based on company size, seniority, and r

Clearbit, HubSpot, HTTP Request +3
CRM & Sales

This workflow receives B2B referral submissions via a webhook, validates the payload, emails the referrer and referred lead via Gmail, alerts the sales team in Slack, deduplicates and creates records

Gmail, Slack, HubSpot +1
CRM & Sales

SMB sales teams and SaaS companies who want to automatically prioritize and nurture new leads without manual qualification. Perfect for businesses getting 50+ leads per month who need to identify high

HubSpot Trigger, HTTP Request, Chain Llm +5