{
  "id": "19808wnGivsqaUOk",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Hubspot Lead Qualification",
  "tags": [],
  "nodes": [
    {
      "id": "5d75f208-1458-4c01-8382-6ace8d185de5",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        0,
        0
      ],
      "parameters": {
        "path": "hubspot-lead-qualification",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "b76025cb-d2d9-4999-aeee-bd061a947571",
      "name": "Edit Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        208,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "819b3eb4-ecd4-4b6b-a7eb-587365f98897",
              "name": "fullName",
              "type": "string",
              "value": "={{ $json.body.name }}"
            },
            {
              "id": "c5eeeaa0-5fd6-43ee-89aa-11c03392c889",
              "name": "email",
              "type": "string",
              "value": "={{ $json.body.email }}"
            },
            {
              "id": "28006c23-f68b-475b-bc79-b4da58b77319",
              "name": "service",
              "type": "string",
              "value": "={{ $json.body.service }}"
            },
            {
              "id": "456a03f4-e1e3-4993-bd98-c752ac044e9d",
              "name": "budgetRange",
              "type": "string",
              "value": "={{ $json.body.budget }}"
            },
            {
              "id": "f299aee3-828e-4e12-9689-53e18899118d",
              "name": "vision",
              "type": "string",
              "value": "={{ $json.body.message }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "df61d89d-b27e-4a5b-b1c8-287c8f39af9f",
      "name": "Message a model",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        416,
        0
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "id",
          "value": "gpt-4o-mini"
        },
        "options": {},
        "responses": {
          "values": [
            {
              "content": "=You are an AI lead qualification assistant for Avalency Studio.\n\nAvalency Studio builds AI automations, custom AI systems, web development, and mobile development for businesses.\n\nAnalyze this inbound website lead and return ONLY valid JSON. No markdown. No extra text.\n\nLead details:\nFull name: {{ $json.fullName }}\nEmail: {{ $json.email }}\nService interested in: {{ $json.serviceInterestedIn }}\nBudget range: {{ $json.budgetRange }}\nVision/message: {{ $json.vision }}\n\nReturn this exact JSON structure with realistic values:\n{\n  \"leadScore\": 0,\n  \"fitLevel\": \"Low\",\n  \"urgency\": \"Low\",\n  \"serviceCategory\": \"AI Automations\",\n  \"summary\": \"\",\n  \"whyThisScore\": \"\",\n  \"recommendedNextStep\": \"\",\n  \"followUpEmailDraft\": \"\"\n}\n\nRules:\n- leadScore must be a realistic number from 0 to 100.\n- Do not copy the example value of 0.\n- If fitLevel is \"High\", leadScore should usually be 75 or higher.\n- If fitLevel is \"Medium\", leadScore should usually be between 45 and 74.\n- If fitLevel is \"Low\", leadScore should usually be below 45.\n- A lead with a $10-20K budget and a clear AI automation need should usually score 80 or higher.\n- Higher budget means higher score.\n- Clear business problem means higher score.\n- AI Automations and Custom AI Systems are strong fits for Avalency Studio.\n- Web Development and Mobile Development are valid fits, but should score based on clarity, budget, and seriousness.\n- Vague, low-budget, or unclear requests should score lower.\n- urgency should be based on how immediate or painful the stated problem sounds.\n- serviceCategory must be one of: \"AI Automations\", \"Custom AI Systems\", \"Web Development\", \"Mobile Development\".\n- fitLevel must be one of: \"Low\", \"Medium\", \"High\".\n- urgency must be one of: \"Low\", \"Medium\", \"High\".\n- summary should be 1-2 sentences.\n- whyThisScore should be 1-2 sentences.\n- recommendedNextStep should be specific and actionable.\n- followUpEmailDraft should be short, professional, and written as Avalency Studio replying to the lead."
            }
          ]
        },
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "525d9a9c-64ba-43fa-b86f-a591c8c423b2",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [
        1184,
        0
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "420d5077-9bff-4b16-a137-68cb3859a1ad",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $json.total }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "b2cf98fd-0657-4740-89f6-f657128ad0b5",
      "name": "Lead Data + AI Analysis",
      "type": "n8n-nodes-base.code",
      "position": [
        768,
        0
      ],
      "parameters": {
        "jsCode": "const lead = $(\"Edit Fields\").first().json;\nconst input = $input.first().json;\n\nconst text = input.output[0].content[0].text;\n\nlet parsed;\n\ntry {\n  parsed = JSON.parse(text);\n} catch (error) {\n  throw new Error(`Failed to parse AI response as JSON: ${text}`);\n}\n\nreturn [\n  {\n    json: {\n      ...lead,\n      ...parsed,\n    },\n  },\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "a8b77023-d3a6-44df-b623-451dbf3a458b",
      "name": "Populate data with existing contact",
      "type": "n8n-nodes-base.code",
      "position": [
        1600,
        -16
      ],
      "parameters": {
        "jsCode": "const lead = $(\"Lead Data + AI Analysis\").first().json;\nconst searchResult = $input.first().json;\n\nconst contactId = searchResult.results?.[0]?.id;\n\nif (!contactId) {\n  throw new Error(\"Expected existing HubSpot contact, but no contact ID was found.\");\n}\n\nreturn [\n  {\n    json: {\n      ...lead,\n      hubspotContactId: contactId,\n      hubspotContactExists: true,\n    },\n  },\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "5b34a27f-ba9e-49e4-acf7-9276061b0c1c",
      "name": "Create HUBSPOT contact",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1456,
        192
      ],
      "parameters": {
        "url": "https://api.hubapi.com/crm/v3/objects/contacts",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"properties\": {\n    \"firstname\": \"{{ $('Lead Data + AI Analysis').first().json.fullName }}\",\n    \"email\": \"{{ $('Lead Data + AI Analysis').first().json.email }}\"\n  }\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Authorization",
              "value": "={{\"Bearer \" + $env.HUBSPOT_ACCESS_TOKEN}}"
            }
          ]
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "153ee230-a8bc-47db-9557-fea20d67b2be",
      "name": "Check for existing HUBSPOT contact",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1008,
        0
      ],
      "parameters": {
        "url": "https://api.hubapi.com/crm/v3/objects/contacts/search",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"filterGroups\": [\n    {\n      \"filters\": [\n        {\n          \"propertyName\": \"email\",\n          \"operator\": \"EQ\",\n          \"value\": \"{{ $json.email }}\"\n        }\n      ]\n    }\n  ],\n  \"properties\": [\"email\", \"firstname\", \"lastname\"],\n  \"limit\": 1\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Authorization",
              "value": "={{\"Bearer \" + $env.HUBSPOT_ACCESS_TOKEN}}"
            }
          ]
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "82042d54-bab1-4e29-aa0b-8505b2b17296",
      "name": "Use created contact",
      "type": "n8n-nodes-base.code",
      "position": [
        1776,
        192
      ],
      "parameters": {
        "jsCode": "const lead = $(\"Lead Data + AI Analysis\").first().json;\nconst createdContact = $input.first().json;\n\nconst contactId = createdContact.id;\n\nif (!contactId) {\n  throw new Error(\"HubSpot contact was created, but no contact ID was returned.\");\n}\n\nreturn [\n  {\n    json: {\n      ...lead,\n      hubspotContactId: contactId,\n      hubspotContactExists: false,\n    },\n  },\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "20700a21-e389-4054-a004-66e67819c021",
      "name": "Create HUBSPOT deal",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2592,
        0
      ],
      "parameters": {
        "url": "https://api.hubapi.com/crm/v3/objects/deals",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"properties\": {\n    \"dealname\": \"{{ $json.dealname }}\",\n    \"dealstage\": \"{{ $json.dealstage }}\",\n    \"amount\": \"{{ $json.amount }}\",\n    \"description\": \"{{ $json.description }}\",\n    \"ai_fit_level\": \"{{ $json.ai_fit_level }}\",\n    \"ai_followup_email_draft\": \"{{ $json.ai_followup_email_draft }}\",\n    \"ai_lead_score\": \"{{ $json.ai_lead_score }}\",\n    \"ai_summary\": \"{{ $json.ai_summary }}\",\n    \"ai_urgency\": \"{{ $json.ai_urgency }}\",\n    \"service_interested_in\": \"{{ $json.service_interested_in }}\",\n    \"recommended_next_step\": \"{{ $json.recommended_next_step }}\"\n  },\n  \"associations\": [\n    {\n      \"to\": {\n        \"id\": \"{{ $json.hubspotContactId }}\"\n      },\n      \"types\": [\n        {\n          \"associationCategory\": \"HUBSPOT_DEFINED\",\n          \"associationTypeId\": 3\n        }\n      ]\n    }\n  ]\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Authorization",
              "value": "={{\"Bearer \" + $env.HUBSPOT_ACCESS_TOKEN}}"
            }
          ]
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "51d560b2-54cc-4b29-a02a-5add45f69b7c",
      "name": "Prepare HUBSPOT deal payload",
      "type": "n8n-nodes-base.code",
      "position": [
        2240,
        0
      ],
      "parameters": {
        "jsCode": "const lead = $input.first().json;\n\nconst clean = (value) =>\n  String(value ?? \"\")\n    .replace(/\\r?\\n/g, \" \")\n    .replace(/\\s+/g, \" \")\n    .trim();\n\nconst service = lead.service || lead.serviceInterestedIn;\n\nconst amount =\n  lead.budgetRange === \"$5-10K\"\n    ? \"7500\"\n    : lead.budgetRange === \"$10-20K\"\n      ? \"15000\"\n      : lead.budgetRange === \"$20-50K\"\n        ? \"35000\"\n        : \"50000\";\n\nreturn [\n  {\n    json: {\n      hubspotContactId: lead.hubspotContactId,\n\n      dealname: `${clean(lead.fullName)} \u2014 ${clean(service)}`,\n      dealstage: \"5030000846\",\n      amount,\n\n      description: clean(\n        `Service: ${service} | Budget: ${lead.budgetRange} | Project Details: ${lead.vision}`\n      ),\n\n      ai_fit_level: clean(lead.fitLevel),\n      ai_followup_email_draft: clean(lead.followUpEmailDraft),\n      ai_lead_score: Number(lead.leadScore),\n      ai_summary: clean(lead.summary),\n      ai_urgency: clean(lead.urgency),\n      service_interested_in: clean(service),\n      recommended_next_step: clean(lead.recommendedNextStep),\n    },\n  },\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "c0d74fb2-a188-4518-ae7e-21a50b7eccdd",
      "name": "Send a message",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2800,
        0
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "=<p>You have a new qualified lead from the Avalency website workflow.</p>\n\n<hr />\n\n<p><strong>Deal Summary</strong></p>\n\n<p>\n<strong>Deal Name:</strong> {{ $json.properties.dealname || \"Not provided\" }}<br />\n<strong>Deal ID:</strong> {{ $json.id || \"Not provided\" }}<br />\n<strong>Deal Stage:</strong> Lead Captured<br />\n<strong>Estimated Amount:</strong> {{ $json.properties.amount ? \"$\" + $json.properties.amount : \"Not provided\" }}<br />\n<strong>Service Interested In:</strong> {{ $json.properties.service_interested_in || \"Not provided\" }}\n</p>\n\n<p>\n<strong>AI Fit Level:</strong> {{ $json.properties.ai_fit_level || \"Not provided\" }}<br />\n<strong>AI Lead Score:</strong> {{ $json.properties.ai_lead_score || \"Not provided\" }}<br />\n<strong>Urgency:</strong> {{ $json.properties.ai_urgency || \"Not provided\" }}\n</p>\n\n<hr />\n\n<p><strong>AI Summary</strong></p>\n\n<p>\n{{ $json.properties.ai_summary || \"No summary provided.\" }}\n</p>\n\n<p><strong>Recommended Next Step</strong></p>\n\n<p>\n{{ $json.properties.recommended_next_step || \"No next step provided.\" }}\n</p>\n\n<hr />\n\n<p><strong>Project Details</strong></p>\n\n<p>\n{{ $json.properties.description || \"No project details provided.\" }}\n</p>\n\n<hr />\n\n<p><strong>Suggested Follow-up Email</strong></p>\n\n<p>\n{{ $json.properties.ai_followup_email_draft || \"No follow-up email draft provided.\" }}\n</p>\n\n<hr />\n\n<p>\nNext step: Review the deal in HubSpot and follow up while the lead is still warm.\n</p>",
        "options": {},
        "subject": "=New Qualified Lead: {{ $json.properties.dealname || \"New Deal\" }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "ef68ab68-78d5-4940-891a-333d873ba62e",
      "name": "Add label to message",
      "type": "n8n-nodes-base.gmail",
      "position": [
        3008,
        0
      ],
      "parameters": {
        "labelIds": [
          "Label_4449832434809003725"
        ],
        "messageId": "={{ $json.id }}",
        "operation": "addLabels"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "c0010183-087e-4ab6-b0f2-81c96443676b",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        3216,
        0
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "{\n  \"success\": true,\n  \"message\": \"Lead received successfully.\",\n  \"status\": \"qualified_lead_created\"\n}"
      },
      "typeVersion": 1.5
    },
    {
      "id": "62dd7121-1ecb-425f-8c87-7d3a692c8762",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1104,
        -240
      ],
      "parameters": {
        "width": 704,
        "height": 752,
        "content": "# AI Lead Qualification for HubSpot CRM\n\nTurn website form submissions into qualified HubSpot deals using AI analysis, CRM automation, and internal email notifications.\n\n---\n\n## How it works\n\n1. A website form submits lead details to the workflow webhook.\n2. AI analyzes the lead and returns a score, urgency level, fit level, summary, and follow-up draft.\n3. HubSpot is searched by email to check if the contact already exists.\n4. If no contact is found, a new HubSpot contact is created.\n5. A HubSpot deal is created and associated with the contact.\n6. Gmail sends an internal notification with the lead details and AI qualification summary.\n7. The notification email can be organized with a `\"Deals\"` Gmail label.\n\n---\n\n## How to set up\n\n- [ ] Connect your OpenAI credentials.\n- [ ] Connect your HubSpot private app token.\n- [ ] Connect your Gmail OAuth credentials.\n- [ ] Create a Gmail label named `\"Deals\"` or update the Gmail label step to use your own label.\n- [ ] Create the required custom HubSpot deal properties.\n- [ ] Replace placeholder HubSpot pipeline and deal stage IDs with your own.\n- [ ] Update the webhook URL in your website form.\n- [ ] Test the workflow with a sample lead submission before activating.\n\n---\n\n## Customization\n\n- Adjust the AI prompt to match your sales qualification criteria.\n- Change the lead scoring rules based on your ideal customer profile.\n- Update the HubSpot deal properties to match your CRM setup.\n- Edit the Gmail notification subject and body for your team.\n- Change the Gmail label name if you do not want to use `\"Deals\"`.\n- Add or remove form fields depending on your website lead form.\n- Change the final webhook response for your frontend or form tool."
      },
      "typeVersion": 1
    },
    {
      "id": "4249f8b7-888f-49d8-a7d0-6e0fa07d1b29",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -48,
        -80
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 272,
        "content": "## 1. Receive and prepare lead"
      },
      "typeVersion": 1
    },
    {
      "id": "1ea283f3-b2a9-4c7a-9ab1-159c146d92ab",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        400,
        -80
      ],
      "parameters": {
        "color": 7,
        "width": 512,
        "height": 272,
        "content": "## 2. Qualify lead with AI"
      },
      "typeVersion": 1
    },
    {
      "id": "791141e5-227d-482e-b215-782cb9417480",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        944,
        -80
      ],
      "parameters": {
        "color": 7,
        "width": 1552,
        "height": 464,
        "content": "## 3. Find or create HubSpot contact and prepare deal data"
      },
      "typeVersion": 1
    },
    {
      "id": "5740c927-47e5-421e-a2fc-d580e73331d0",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2528,
        -80
      ],
      "parameters": {
        "color": 7,
        "width": 880,
        "height": 272,
        "content": "## 4. Create deal and notify team\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "cea35408-45fa-44ce-9980-717788a20e29",
  "connections": {
    "If": {
      "main": [
        [
          {
            "node": "Populate data with existing contact",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Create HUBSPOT contact",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "Message a model",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send a message": {
      "main": [
        [
          {
            "node": "Add label to message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Message a model": {
      "main": [
        [
          {
            "node": "Lead Data + AI Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create HUBSPOT deal": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Use created contact": {
      "main": [
        [
          {
            "node": "Prepare HUBSPOT deal payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add label to message": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create HUBSPOT contact": {
      "main": [
        [
          {
            "node": "Use created contact",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Lead Data + AI Analysis": {
      "main": [
        [
          {
            "node": "Check for existing HUBSPOT contact",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare HUBSPOT deal payload": {
      "main": [
        [
          {
            "node": "Create HUBSPOT deal",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check for existing HUBSPOT contact": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Populate data with existing contact": {
      "main": [
        [
          {
            "node": "Prepare HUBSPOT deal payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}