AutomationFlowsAI & RAG › Ai-powered Lead Enrichment From Typeform & Calendly to Hubspot CRM

Ai-powered Lead Enrichment From Typeform & Calendly to Hubspot CRM

ByAvkash Kakdiya @itechnotion on n8n.io

This workflow starts whenever a new lead comes in through Typeform (form submission) or Calendly (meeting booking). It captures the lead’s information, standardizes it into a clean format, and checks the email domain. If it’s a business domain, the workflow uses AI to enrich the…

Event trigger★★★★☆ complexityAI-powered13 nodesTypeform TriggerCalendly TriggerAgentOpenAI ChatHubSpot
AI & RAG Trigger: Event Nodes: 13 Complexity: ★★★★☆ AI nodes: yes Added:

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

This workflow follows the Agent → HubSpot 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
{
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "name": "12 - Intelligent Prospect Enricher",
  "tags": [],
  "nodes": [
    {
      "id": "4dcd168b-82b8-46d1-93b8-b8939e13af53",
      "name": "\ud83e\uddfe Typeform Trigger",
      "type": "n8n-nodes-base.typeformTrigger",
      "position": [
        -280,
        0
      ],
      "parameters": {
        "formId": "YOUR_TYPEFORM_ID"
      },
      "credentials": {
        "typeformApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "e1d060c4-8025-4418-a560-63a79c0b2788",
      "name": "\ud83d\udcc5 Calendly Trigger",
      "type": "n8n-nodes-base.calendlyTrigger",
      "position": [
        -280,
        200
      ],
      "parameters": {
        "events": [
          "invitee.created"
        ]
      },
      "credentials": {
        "calendlyApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "a063356b-8248-4d72-ac54-2069653a788c",
      "name": "\ud83d\udd00 Merge Lead Sources",
      "type": "n8n-nodes-base.merge",
      "position": [
        -60,
        100
      ],
      "parameters": {},
      "typeVersion": 2
    },
    {
      "id": "00b454c8-a16c-4697-9567-3bbb5725188b",
      "name": "\ud83d\udee0\ufe0f Standardize Lead Data",
      "type": "n8n-nodes-base.code",
      "position": [
        160,
        100
      ],
      "parameters": {
        "jsCode": "let input = items[0].json;\nlet output = {};\n\n// Utility: Extract domain from email\nfunction extractDomain(email) {\n  return typeof email === 'string' && email.includes('@')\n    ? email.split('@')[1].toLowerCase()\n    : null;\n}\n\n// Case 1: Direct object (Typeform-style)\nif (input?.Name && input?.Email) {\n  output = {\n    name: input[\"Name\"] || null,\n    email: input[\"Email\"] || null,\n    phone: input[\"Phone Number\"] || null,\n    message: input[\"Message\"] || null,\n    domain: extractDomain(input[\"Email\"]),\n    source: \"Typeform\"\n  };\n}\n\n// Case 2: Array of one object (Typeform-style)\nelse if (Array.isArray(input) && input[0]?.Name && input[0]?.Email) {\n  const data = input[0];\n  output = {\n    name: data[\"Name\"] || null,\n    email: data[\"Email\"] || null,\n    phone: data[\"Phone Number\"] || null,\n    message: data[\"Message\"] || null,\n    domain: extractDomain(data[\"Email\"]),\n    source: \"Typeform\"\n  };\n}\n\n// Case 3: Calendly payload\nelse if (input?.event === \"invitee.created\" && input?.payload) {\n  const payload = input.payload;\n  const email = payload.email || null;\n  output = {\n    name: payload.name || `${payload.first_name || \"\"} ${payload.last_name || \"\"}`.trim(),\n    email: email,\n    phone: payload.text_reminder_number || null,\n    message: payload.questions_and_answers?.[0]?.answer || null,\n    domain: extractDomain(email),\n    source: \"Calendly\"\n  };\n}\n\n// Else: unsupported\nelse {\n  output = {\n    error: \"Unsupported input format\",\n    inputType: typeof input,\n    preview: JSON.stringify(input)\n  };\n}\n\nreturn [{ json: output }];"
      },
      "typeVersion": 2
    },
    {
      "id": "9720f9d6-047c-46bc-b703-9f47acddd5c2",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -380,
        -200
      ],
      "parameters": {
        "color": 6,
        "width": 700,
        "height": 700,
        "content": "## Lead Intake & Standardization\n\n*Captures leads from multiple sources (form + calendar).\n\nAll incoming data is merged and standardized into a consistent format:\nName, Email, Phone, Message, and Email Domain.*"
      },
      "typeVersion": 1
    },
    {
      "id": "31a89eaf-a2e2-49c4-b688-231e53d137d5",
      "name": "\u2696\ufe0f Email Domain Filter",
      "type": "n8n-nodes-base.if",
      "position": [
        380,
        100
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "412e4bc6-7d47-4786-bafc-85e68b898b22",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.domain }}",
              "rightValue": "gmail.com"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "cab2469b-cdb9-4d8d-8193-bfa8225b56a9",
      "name": "\ud83e\udd16 AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        600,
        100
      ],
      "parameters": {
        "text": "=You are a company research assistant. I will give you a domain name.\n\nHere's the domain: {{ $json.domain }}\n\nYour task is to return a JSON object with the following details about the company:\n\ncompany_name: Full name of the company\n\nindustry: The primary industry the company belongs to\n\nheadquarters: City and country of the company headquarters\n\nemployee_count: Approximate number of employees (numeric or range)\n\nwebsite: Official website URL\n\nlinkedin: LinkedIn profile link (if available)\n\ndescription: 1-2 sentence summary of the company and what it does\n\nOnly use verified, credible information. Respond only in JSON. ",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 2
    },
    {
      "id": "12b43543-207a-46d0-93fd-400d8729b493",
      "name": "\ud83d\udcac LLM (OpenAI / Claude)",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        688,
        320
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "0202fe5f-f6d1-4a05-9c2e-f077aef85a29",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        340,
        -200
      ],
      "parameters": {
        "width": 540,
        "height": 700,
        "content": "## Domain Check & AI Enrichment\n\n*Filters out free/public email domains (e.g., Gmail, Yahoo).\nIf it's a business domain, AI is used to enrich the data with:\n\nCompany Name, Industry, HQ, Website, LinkedIn\n\nCompany Size, and a Short Description\n\nThe AI is instructed to return data in structured JSON.*"
      },
      "typeVersion": 1
    },
    {
      "id": "4da58027-c7ca-4fe5-8236-210d66f206bb",
      "name": "\ud83e\uddec Combine Lead + AI Output",
      "type": "n8n-nodes-base.code",
      "position": [
        976,
        100
      ],
      "parameters": {
        "jsCode": "// Access data from previous nodes using correct n8n syntax\n// Replace \"If\" and \"AI Agent\" with your actual node names\n\n// Method 1: Try accessing by node name (replace with your actual node names)\nlet leadData = {};\nlet aiEnrichment = {};\n\ntry {\n  // Try to get data from IF node - replace \"If\" with your actual node name\n  const ifNodeData = $node[\"\u2696\ufe0f Email Domain Filter\"].json;\n  if (Array.isArray(ifNodeData)) {\n    leadData = ifNodeData[0];\n  } else {\n    leadData = ifNodeData;\n  }\n  console.log('Lead data from If node:', leadData);\n} catch (error) {\n  console.log('Could not access If node:', error.message);\n}\n\ntry {\n  // Try to get data from AI Agent node - replace \"AI Agent\" with your actual node name  \n  const aiNodeData = $node[\"\ud83e\udd16 AI Agent\"].json;\n  let aiOutput = '';\n  \n  if (Array.isArray(aiNodeData)) {\n    aiOutput = aiNodeData[0].output || aiNodeData[0];\n  } else {\n    aiOutput = aiNodeData.output || aiNodeData;\n  }\n  \n  console.log('AI output:', aiOutput);\n  \n  // Parse AI output if it's in JSON format\n  if (typeof aiOutput === 'string') {\n    const jsonMatch = aiOutput.match(/```json\\n([\\s\\S]*?)\\n```/);\n    if (jsonMatch && jsonMatch[1]) {\n      aiEnrichment = JSON.parse(jsonMatch[1]);\n    } else {\n      try {\n        aiEnrichment = JSON.parse(aiOutput);\n      } catch (parseError) {\n        aiEnrichment = { raw_output: aiOutput };\n      }\n    }\n  } else {\n    aiEnrichment = aiOutput;\n  }\n  \n  console.log('Parsed AI enrichment:', aiEnrichment);\n} catch (error) {\n  console.log('Could not access AI Agent node:', error.message);\n  aiEnrichment = { error: 'Could not access AI data' };\n}\n\n// Combine the data (no duplication)\nconst enrichedLead = {\n  // Original lead data\n  name: leadData.name || '',\n  email: leadData.email || '',\n  phone: leadData.phone || '',\n  message: leadData.message || '',\n  domain: leadData.domain || '',\n  source: leadData.source || '',\n  \n  // AI enrichment data\n  company_name: aiEnrichment.company_name || 'Unknown',\n  industry: aiEnrichment.industry || 'Unknown',\n  headquarters: aiEnrichment.headquarters || '',\n  employee_count: aiEnrichment.employee_count || '',\n  website: aiEnrichment.website || '',\n  linkedin: aiEnrichment.linkedin || '',\n  company_description: aiEnrichment.description || '',\n  \n  // Metadata\n  enriched_at: new Date().toISOString(),\n  workflow_id: $workflow.id\n};\n\nconsole.log('Final enriched lead:', enrichedLead);\n\nreturn { json: enrichedLead };"
      },
      "typeVersion": 2
    },
    {
      "id": "b766d9b2-450a-465d-a047-2d0fa5bc18ce",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        900,
        -200
      ],
      "parameters": {
        "color": 4,
        "height": 700,
        "content": "## Merge Lead & Enrichment Data\n\n*Merges lead details with AI-generated insights.\nHandles both string and JSON outputs from AI.\nAdds metadata like timestamp and workflow ID for tracking.\nEnsures a single clean payload is ready for downstream use.*"
      },
      "typeVersion": 1
    },
    {
      "id": "bfb6c543-5b49-4a58-b51b-12afd6a10a1d",
      "name": "\ud83d\udcbc CRM Integration",
      "type": "n8n-nodes-base.hubspot",
      "position": [
        1196,
        100
      ],
      "parameters": {
        "email": "={{ $json.email }}",
        "options": {},
        "authentication": "appToken",
        "additionalFields": {
          "country": "={{ $json.headquarters }}",
          "message": "={{ $json.message }}",
          "industry": "={{ $json.industry }}",
          "firstName": "={{ $json.name }}",
          "websiteUrl": "={{ $json.website }}",
          "companyName": "={{ $json.company_name }}",
          "phoneNumber": "={{ $json.phone }}",
          "customPropertiesUi": {
            "customPropertiesValues": [
              {
                "value": "={{ $json.linkedin }}",
                "property": "company_s_linkedin"
              },
              {
                "value": "={{ $json.company_description }}",
                "property": "company_descreption"
              }
            ]
          }
        }
      },
      "credentials": {
        "hubspotAppToken": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1,
      "alwaysOutputData": false
    },
    {
      "id": "b055d32e-692a-49b1-9a8b-31ff95d9cc66",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1160,
        -200
      ],
      "parameters": {
        "color": 3,
        "width": 220,
        "height": 700,
        "content": "## CRM Sync (e.g., HubSpot)\n\n*Enriched lead data is synced into a CRM platform.\nFields include contact info and enriched company details.\nCustom fields are mapped using dynamic expressions.*"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "TEMPLATE_VERSION_ID",
  "connections": {
    "\ud83e\udd16 AI Agent": {
      "main": [
        [
          {
            "node": "\ud83e\uddec Combine Lead + AI Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcbc CRM Integration": {
      "main": [
        []
      ]
    },
    "\ud83d\udcc5 Calendly Trigger": {
      "main": [
        [
          {
            "node": "\ud83d\udd00 Merge Lead Sources",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "\ud83e\uddfe Typeform Trigger": {
      "main": [
        [
          {
            "node": "\ud83d\udd00 Merge Lead Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd00 Merge Lead Sources": {
      "main": [
        [
          {
            "node": "\ud83d\udee0\ufe0f Standardize Lead Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2696\ufe0f Email Domain Filter": {
      "main": [
        [
          {
            "node": "\ud83e\udd16 AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcac LLM (OpenAI / Claude)": {
      "ai_languageModel": [
        [
          {
            "node": "\ud83e\udd16 AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udee0\ufe0f Standardize Lead Data": {
      "main": [
        [
          {
            "node": "\u2696\ufe0f Email Domain Filter",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83e\uddec Combine Lead + AI Output": {
      "main": [
        [
          {
            "node": "\ud83d\udcbc CRM Integration",
            "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 starts whenever a new lead comes in through Typeform (form submission) or Calendly (meeting booking). It captures the lead’s information, standardizes it into a clean format, and checks the email domain. If it’s a business domain, the workflow uses AI to enrich the…

Source: https://n8n.io/workflows/7537/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

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

AI & RAG

This workflow starts whenever a new lead is submitted through Typeform. It cleans and stores the raw lead data, checks if the email is business-related (not Gmail), and then uses AI to enrich the lead

OpenAI Chat, Typeform Trigger, Airtable +3
AI & RAG

Typeform IA - YT. Uses typeformTrigger, agent, lmChatOpenAi, toolWorkflow. Event-driven trigger; 75 nodes.

Typeform Trigger, Agent, OpenAI Chat +7
AI & RAG

This end-to-end AI-powered recruitment automation workflow helps HR and talent acquisition teams automate the complete hiring pipeline—from resume intake and parsing to GPT-4-based evaluation, TA appr

Form Trigger, Output Parser Structured, Google Drive +10
AI & RAG

This template enables natural-language-driven automation using Bright Data's MCP tools, triggered directly by new leads in HubSpot. It dynamically extracts and executes the right tool based on lead co

Google Sheets Trigger, Output Parser Structured, Output Parser Autofixing +7
AI & RAG

⚠️ DISCLAIMER: This workflow uses the AnySite LinkedIn community node, which is only available on self-hosted n8n instances. It will not work on n8n.cloud.

OpenAI Chat, Pipedrive Tool, HubSpot Trigger +5