Integrating n8n with HubSpot: Complete CRM Automation Setup

HubSpot’s robust CRM capabilities combined with n8n’s flexible automation power create endless possibilities for streamlining your sales and marketing processes. This comprehensive guide will walk you through setting up key HubSpot integrations that can transform how you manage leads, contacts, and customer relationships.

Why Integrate n8n with HubSpot?

HubSpot excels at contact management and sales pipeline tracking, but it has limitations when it comes to complex multi-system workflows. n8n bridges this gap by connecting HubSpot to your other business tools and enabling sophisticated automation that goes beyond HubSpot’s native workflow capabilities.

Common integration scenarios include:

  • Syncing leads from multiple sources into HubSpot
  • Triggering external actions when deals move through pipeline stages
  • Enriching contact data from external APIs
  • Creating complex lead scoring based on multiple data sources
  • Automating follow-up sequences across multiple platforms

Setting Up Your HubSpot Connection

Prerequisites

  • Active HubSpot account (free tier works for basic integrations)
  • n8n instance with internet access
  • HubSpot API key or OAuth app credentials

Connecting n8n to HubSpot

Method 1: Using API Key (Simpler)

  1. In HubSpot, navigate to Settings → Integrations → API Key
  2. Generate your API key if you don’t have one
  3. In n8n, add a HubSpot node
  4. Select “API Key” as authentication method
  5. Paste your API key

Method 2: Using OAuth (More Secure)

  1. In HubSpot, go to Settings → Integrations → Connected Apps
  2. Create a new app with required scopes
  3. In n8n, select “OAuth2” authentication
  4. Enter your app credentials and authorize

Required HubSpot Scopes

For comprehensive automation, ensure your connection includes these scopes:

  • contacts – Read and write contact data
  • deals – Manage deals and pipeline information
  • companies – Access company records
  • tickets – Handle support tickets
  • timeline – Create timeline events
  • files – Upload and manage files

Essential HubSpot Automation Workflows

Workflow 1: Lead Capture and Enrichment

This workflow captures leads from multiple sources and enriches them before adding to HubSpot.

Trigger: Webhook (for form submissions, landing pages, etc.)

{
  "email": "prospect@company.com",
  "firstName": "Sarah",
  "lastName": "Johnson",
  "company": "Tech Solutions Inc",
  "source": "website_form"
}

Step 1: Data Enrichment Add an HTTP Request node to enrich the lead with company data:

  • URL: https://api.clearbit.com/v2/enrichment/find
  • Parameters: email={{$json.email}}
  • Headers: Authorization: Bearer YOUR_CLEARBIT_KEY

Step 2: Lead Scoring Use a Code node to calculate lead score:

const lead = $input.first().json;
let score = 0;

// Email domain scoring
const domain = lead.email.split('@')[1];
if (!['gmail.com', 'yahoo.com', 'hotmail.com'].includes(domain)) {
  score += 20; // Business email
}

// Company size scoring (from enrichment data)
if (lead.company_size > 100) score += 30;
else if (lead.company_size > 50) score += 20;
else if (lead.company_size > 10) score += 10;

// Industry scoring
const highValueIndustries = ['technology', 'healthcare', 'finance'];
if (highValueIndustries.includes(lead.industry)) {
  score += 25;
}

return [{
  json: {
    ...lead,
    leadScore: score,
    priority: score >= 50 ? 'high' : score >= 25 ? 'medium' : 'low'
  }
}];

Step 3: Create HubSpot Contact Add HubSpot node with “Create Contact” operation:

  • Email: {{$json.email}}
  • First Name: {{$json.firstName}}
  • Last Name: {{$json.lastName}}
  • Company: {{$json.company}}
  • Lead Score: {{$json.leadScore}}
  • Lead Source: {{$json.source}}

Step 4: Conditional Assignment Use IF node to assign high-priority leads to senior sales reps:

  • Condition: {{$json.priority}} equals high
  • True: Assign to senior sales rep and send immediate notification
  • False: Add to standard nurturing sequence

Workflow 2: Deal Stage Automation

Automate actions when deals move through your sales pipeline.

Trigger: HubSpot Trigger – Deal Updated Configure to trigger when deal stage changes.

Step 1: Stage Identification Use Switch node to handle different stages:

  • Case 1: “Qualified Lead” → Send welcome packet
  • Case 2: “Proposal Sent” → Set follow-up reminder
  • Case 3: “Closed Won” → Trigger onboarding workflow
  • Case 4: “Closed Lost” → Add to nurturing campaign

Step 2: Closed Won Actions For won deals, create a comprehensive onboarding sequence:

// Extract deal and contact information
const deal = $json;
const dealValue = deal.amount;
const contactId = deal.associations.contacts[0];

// Determine onboarding tier based on deal size
let onboardingTier = 'standard';
if (dealValue > 50000) onboardingTier = 'enterprise';
else if (dealValue > 10000) onboardingTier = 'professional';

// Set implementation timeline
const implementationWeeks = onboardingTier === 'enterprise' ? 8 : 
                           onboardingTier === 'professional' ? 4 : 2;

return [{
  json: {
    dealId: deal.id,
    contactId: contactId,
    dealValue: dealValue,
    onboardingTier: onboardingTier,
    implementationWeeks: implementationWeeks,
    kickoffDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString()
  }
}];

Step 3: Create Implementation Tasks Use HubSpot node to create tasks for the customer success team:

  • Task 1: Schedule kickoff call
  • Task 2: Prepare onboarding materials
  • Task 3: Set up customer accounts

Workflow 3: Contact Lifecycle Automation

Automatically move contacts through lifecycle stages based on their behavior and engagement.

Trigger: HubSpot Trigger – Contact Property Changed Monitor changes to key properties like email opens, website visits, or form submissions.

Step 1: Engagement Scoring

const contact = $json;
let engagementScore = 0;

// Email engagement
if (contact.hs_email_open_count > 10) engagementScore += 20;
if (contact.hs_email_click_count > 5) engagementScore += 15;

// Website activity
if (contact.num_pageviews > 20) engagementScore += 25;
if (contact.recent_conversion_date) engagementScore += 30;

// Determine lifecycle stage
let newStage = contact.lifecyclestage;
if (engagementScore >= 60 && contact.lifecyclestage === 'lead') {
  newStage = 'marketingqualifiedlead';
} else if (engagementScore >= 80 && contact.lifecyclestage === 'marketingqualifiedlead') {
  newStage = 'salesqualifiedlead';
}

return [{
  json: {
    contactId: contact.id,
    currentStage: contact.lifecyclestage,
    newStage: newStage,
    engagementScore: engagementScore,
    shouldUpdate: newStage !== contact.lifecyclestage
  }
}];

Step 2: Update Contact and Notify Team If lifecycle stage should change:

  • Update contact in HubSpot with new lifecycle stage
  • Create task for sales team if contact becomes SQL
  • Send notification to appropriate team members

Advanced Integration Patterns

Pattern 1: Bi-Directional Sync

Keep HubSpot in sync with external systems by creating workflows that work in both directions.

Example: Syncing with Customer Support System

  • When support ticket is created → Create HubSpot ticket
  • When HubSpot deal closes → Create customer account in support system
  • When contact updates in either system → Sync changes to the other

Pattern 2: Data Validation and Cleanup

Automatically clean and validate data as it enters HubSpot:

// Email validation and formatting
function validateEmail(email) {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(email);
}

// Phone number formatting
function formatPhone(phone) {
  const cleaned = phone.replace(/\D/g, '');
  if (cleaned.length === 10) {
    return `(${cleaned.slice(0,3)}) ${cleaned.slice(3,6)}-${cleaned.slice(6)}`;
  }
  return phone;
}

// Company name standardization
function standardizeCompany(company) {
  return company
    .replace(/\b(inc|llc|corp|ltd)\b\.?/gi, match => match.toUpperCase())
    .trim();
}

const contact = $input.first().json;
const cleanedContact = {
  ...contact,
  email: contact.email ? contact.email.toLowerCase().trim() : '',
  phone: contact.phone ? formatPhone(contact.phone) : '',
  company: contact.company ? standardizeCompany(contact.company) : '',
  isValidEmail: validateEmail(contact.email || '')
};

return [{ json: cleanedContact }];

Pattern 3: Multi-Touch Attribution

Track lead sources across multiple touchpoints:

// Attribution tracking
const contact = $json;
const newSource = $json.latest_source;
const existingSources = contact.all_lead_sources ? 
  contact.all_lead_sources.split(';') : [];

// Add new source if not already tracked
if (newSource && !existingSources.includes(newSource)) {
  existingSources.push(newSource);
}

// Calculate source weights for attribution
const sourceWeights = {
  'organic_search': 0.4,
  'paid_search': 0.3,
  'social_media': 0.2,
  'email': 0.1,
  'direct': 0.1
};

let attributionScore = 0;
existingSources.forEach(source => {
  attributionScore += sourceWeights[source] || 0.1;
});

return [{
  json: {
    contactId: contact.id,
    allLeadSources: existingSources.join(';'),
    attributionScore: Math.round(attributionScore * 100),
    primarySource: existingSources[0],
    latestSource: newSource
  }
}];

Error Handling and Best Practices

Handling API Rate Limits

HubSpot has API rate limits that you need to respect:

// Add delay between API calls for bulk operations
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));

// For bulk contact creation
const contacts = $input.all();
const results = [];

for (let i = 0; i < contacts.length; i++) {
  try {
    // Process contact
    const result = await processContact(contacts[i]);
    results.push(result);
    
    // Rate limiting - HubSpot allows 100 requests per 10 seconds
    if (i % 10 === 0 && i > 0) {
      await delay(1000); // Wait 1 second every 10 requests
    }
  } catch (error) {
    console.log(`Error processing contact ${i}:`, error.message);
    results.push({ error: error.message, contact: contacts[i] });
  }
}

return results.map(result => ({ json: result }));

Data Validation

Always validate data before sending to HubSpot:

function validateHubSpotContact(contact) {
  const errors = [];
  
  // Required fields
  if (!contact.email) errors.push('Email is required');
  if (!contact.firstname) errors.push('First name is required');
  
  // Data format validation
  if (contact.email && !validateEmail(contact.email)) {
    errors.push('Invalid email format');
  }
  
  // HubSpot-specific validation
  if (contact.phone && contact.phone.length > 20) {
    errors.push('Phone number too long for HubSpot');
  }
  
  return {
    isValid: errors.length === 0,
    errors: errors,
    contact: contact
  };
}

Monitoring and Logging

Add logging to track workflow performance:

// Comprehensive logging for HubSpot operations
const operation = 'create_contact';
const startTime = Date.now();

try {
  // Your HubSpot operation here
  const result = await hubspotOperation();
  
  const logData = {
    operation: operation,
    success: true,
    duration: Date.now() - startTime,
    recordsProcessed: 1,
    timestamp: new Date().toISOString()
  };
  
  console.log('HubSpot Operation Success:', JSON.stringify(logData));
  return [{ json: { ...result, log: logData } }];
  
} catch (error) {
  const errorLog = {
    operation: operation,
    success: false,
    error: error.message,
    duration: Date.now() - startTime,
    timestamp: new Date().toISOString()
  };
  
  console.error('HubSpot Operation Failed:', JSON.stringify(errorLog));
  throw error;
}

Testing Your HubSpot Integration

Test Data Strategy

Create test contacts and deals in HubSpot for safe testing:

// Test contact data generator
function generateTestContact() {
  const testDomains = ['test-company.com', 'example-corp.com'];
  const firstNames = ['Test', 'Demo', 'Sample'];
  const lastNames = ['Contact', 'Lead', 'Prospect'];
  
  return {
    email: `test+${Date.now()}@${testDomains[Math.floor(Math.random() * testDomains.length)]}`,
    firstname: firstNames[Math.floor(Math.random() * firstNames.length)],
    lastname: lastNames[Math.floor(Math.random() * lastNames.length)],
    company: 'Test Company Inc',
    phone: '555-0123',
    lead_source: 'API_TEST'
  };
}

Cleanup Procedures

Always clean up test data:

// Clean up test contacts after testing
const testContacts = await hubspot.contacts.search({
  filterGroups: [{
    filters: [{
      propertyName: 'lead_source',
      operator: 'EQ',
      value: 'API_TEST'
    }]
  }]
});

for (const contact of testContacts.results) {
  await hubspot.contacts.basicApi.archive(contact.id);
  console.log(`Cleaned up test contact: ${contact.id}`);
}

Scaling Your HubSpot Integration

As your automation grows, consider these optimization strategies:

Batch Processing: Group multiple operations together to reduce API calls and improve performance.

Caching: Store frequently accessed HubSpot data in n8n to avoid repeated API calls.

Queue Management: For high-volume operations, implement queuing to handle rate limits gracefully.

Monitoring: Set up alerts for failed operations and performance degradation.

The combination of HubSpot’s CRM capabilities and n8n’s automation flexibility creates powerful opportunities for streamlining your sales and marketing operations. Start with simple integrations and gradually build more sophisticated workflows as you identify opportunities for automation in your business processes.