In today’s complex buyer journeys, leads rarely follow a linear path from marketing to sales. Prospects interact with your brand across multiple channels and touchpoints, leaving valuable data scattered across different HubSpot Hubs. A Cross-Hub Lead Qualification workflow breaks down these silos, creating a unified view of each prospect and enabling more accurate qualification decisions based on the complete picture.

Why Cross-Hub Lead Qualification Matters

Traditional lead qualification often suffers from incomplete information. Marketing might qualify a lead based solely on form submissions and email engagement, while sales might focus exclusively on direct interactions, and service might hold valuable insights from previous customer relationships. A cross-hub approach addresses several critical business challenges:
  • Eliminates data silos: Combines insights from marketing, sales, and service interactions
  • Provides complete context: Gives teams visibility into the full prospect journey
  • Improves qualification accuracy: Uses multi-dimensional data for more precise scoring
  • Reduces redundant outreach: Prevents different teams from making disconnected touches
  • Accelerates sales cycles: Helps sales prioritize truly qualified leads
  • Enhances personalization: Enables tailored messaging based on comprehensive history
  • Improves resource allocation: Focuses team efforts on the most promising opportunities
Research shows that organizations with aligned marketing, sales, and service teams achieve 38% higher sales win rates and 36% higher customer retention.

Setting Up Your Cross-Hub Lead Qualification Workflow

Let’s build a comprehensive workflow that integrates data from Marketing, Sales, and Service Hubs to create a unified qualification framework.

Step 1: Define Cross-Hub Qualification Criteria

Before building the workflow, establish clear criteria that span all hubs:
  • Marketing criteria: Email engagement, content downloads, website behavior, form submissions
  • Sales criteria: Meeting attendance, call engagement, proposal interactions, objections raised
  • Service criteria: Previous support history, product usage, feedback provided, referral activity
  • Demographic criteria: Company size, industry, role, budget authority
  • Behavioral criteria: Engagement frequency, recency, and depth across all touchpoints

Step 2: Create the Base Workflow

  1. Navigate to Automation > Workflows in your HubSpot account
  2. Click “Create workflow” > “From scratch”
  3. Select “Contact-based” workflow
  4. Name it “Cross-Hub Lead Qualification”

Step 3: Set Up Marketing Hub Triggers and Actions

Begin with marketing engagement data:
  1. Set enrollment trigger: “Contact property” > “Marketing Qualified Lead” equals “Yes” OR specific high-value marketing activities
  2. Add a “Delay” action of 0 days (immediate execution)
  3. Add a custom code action to calculate a marketing engagement score (see advanced implementation below)
  4. Add a “Set property value” action to update “Marketing Engagement Score” property

Step 4: Implement Sales Hub Integration

Next, incorporate sales activity data:
  1. Add an “If/then branch” to check for sales activity
  2. If sales activity exists:
    • Add a custom code action to calculate a sales engagement score
    • Add a “Set property value” action to update “Sales Engagement Score” property
  3. If no sales activity:
    • Add a “Set property value” action to set “Sales Engagement Score” to 0

Step 5: Add Service Hub Data Points

Incorporate service and support history:
  1. Add an “If/then branch” to check for previous customer relationship
  2. If previous customer:
    • Add a custom code action to analyze support history and satisfaction
    • Add a “Set property value” action to update “Service Relationship Score” property
  3. If no previous relationship:
    • Add a “Set property value” action to set “Service Relationship Score” to neutral value

Step 6: Create Unified Scoring Model

Combine all hub scores into a unified qualification framework:
  1. Add a custom code action to calculate a “Cross-Hub Qualification Score” (see advanced implementation)
  2. Add a “Set property value” action to update “Cross-Hub Qualification Score” property
  3. Add a “Set property value” action to update “Cross-Hub Qualification Date” to current date

Step 7: Implement Qualification Logic

Create branches based on the unified score:
  1. Add an “If/then branch” based on “Cross-Hub Qualification Score”
  2. If score is above your SQL threshold (e.g., 80+):
    • Add a “Set property value” action to update “Lifecycle Stage” to “Sales Qualified Lead”
    • Add a “Create task” action for sales to follow up
    • Add a “Send email” action to notify the assigned sales rep with comprehensive context
  3. If score is in mid-range (e.g., 50-79):
    • Add a “Set property value” action to update “Nurture Track” to “High-Potential”
    • Add a “Enroll in another workflow” action for targeted nurturing
  4. If score is low (e.g., below 50):
    • Add a “Set property value” action to update “Nurture Track” to “Standard”
    • Add a “Enroll in another workflow” action for general nurturing

Step 8: Set Up Cross-Team Notifications

Ensure all teams have visibility into qualification decisions:
  1. For SQLs, add a “Send email” action to notify marketing of the qualification
  2. Add a “Create note” action on the contact record with qualification rationale
  3. Add a “Set property value” action to update “Qualification Source” to “Cross-Hub Workflow”
  4. Add a “Set property value” action to update “Qualification Factors” with key decision points

Advanced Implementation: Custom Code for Unified Qualification Scoring

Implement this custom code to create a sophisticated cross-hub qualification system that analyzes data from all HubSpot Hubs:
				
					// Custom code for cross-hub lead qualification scoring
exports.main = (functionContext, sendResponse) => {
  // Get contact properties from the workflow
  const { 
    contact_id,
    email,
    firstname,
    lastname,
    company,
    
    // Marketing properties
    first_conversion_date,
    recent_conversion_date,
    num_page_views,
    num_form_submissions,
    email_open_rate,
    email_click_rate,
    marketing_qualified_lead,
    lead_score,
    
    // Sales properties
    num_sales_activities,
    last_meeting_date,
    meeting_showed,
    deal_created,
    deal_stage,
    objections_raised,
    
    // Service properties
    customer_fit_score,
    previous_customer,
    support_ticket_count,
    average_csat,
    nps_score,
    
    // Company properties
    industry,
    company_size,
    annual_revenue,
    
    // Behavioral properties
    last_engagement_date,
    days_to_close
  } = functionContext.parameters;
  
  // Initialize HubSpot client
  const hubspot = require('@hubspot/api-client');
  const hubspotClient = new hubspot.Client({
    accessToken: process.env.PRIVATE_APP_ACCESS_TOKEN
  });
  
  async function calculateCrossHubScore() {
    try {
      // Get additional contact data
      const contactData = await getContactData(contact_id);
      
      // Calculate marketing engagement score
      const marketingScore = calculateMarketingScore({
        first_conversion_date,
        recent_conversion_date,
        num_page_views,
        num_form_submissions,
        email_open_rate,
        email_click_rate,
        marketing_qualified_lead,
        lead_score,
        content_downloads: contactData.content_downloads,
        high_value_page_visits: contactData.high_value_page_visits
      });
      
      // Calculate sales engagement score
      const salesScore = calculateSalesScore({
        num_sales_activities,
        last_meeting_date,
        meeting_showed,
        deal_created,
        deal_stage,
        objections_raised,
        sales_email_replies: contactData.sales_email_replies,
        sales_call_duration: contactData.sales_call_duration
      });
      
      // Calculate service relationship score
      const serviceScore = calculateServiceScore({
        previous_customer,
        support_ticket_count,
        average_csat,
        nps_score,
        days_as_customer: contactData.days_as_customer,
        product_usage_score: contactData.product_usage_score,
        expansion_revenue: contactData.expansion_revenue
      });
      
      // Calculate demographic fit score
      const demographicScore = calculateDemographicScore({
        industry,
        company_size,
        annual_revenue,
        job_title: contactData.job_title,
        job_function: contactData.job_function,
        decision_maker: contactData.decision_maker
      });
      
      // Calculate behavioral score
      const behavioralScore = calculateBehavioralScore({
        last_engagement_date,
        days_to_close,
        engagement_velocity: contactData.engagement_velocity,
        engagement_recency: contactData.engagement_recency,
        engagement_frequency: contactData.engagement_frequency
      });
      
      // Calculate unified cross-hub score
      const crossHubScore = calculateUnifiedScore(
        marketingScore,
        salesScore,
        serviceScore,
        demographicScore,
        behavioralScore
      );
      
      // Determine qualification status
      const qualificationStatus = determineQualificationStatus(crossHubScore);
      
      // Identify key qualification factors
      const qualificationFactors = identifyQualificationFactors(
        marketingScore,
        salesScore,
        serviceScore,
        demographicScore,
        behavioralScore
      );
      
      // Generate next best actions
      const nextBestActions = generateNextBestActions(
        crossHubScore,
        qualificationStatus,
        qualificationFactors
      );
      
      // Return the cross-hub qualification assessment
      sendResponse({
        statusCode: 200,
        body: {
          contact_id: contact_id,
          cross_hub_score: crossHubScore,
          qualification_status: qualificationStatus,
          component_scores: {
            marketing_score: marketingScore.score,
            sales_score: salesScore.score,
            service_score: serviceScore.score,
            demographic_score: demographicScore.score,
            behavioral_score: behavioralScore.score
          },
          qualification_factors: qualificationFactors,
          next_best_actions: nextBestActions,
          recommended_owner: determineRecommendedOwner(crossHubScore, qualificationStatus),
          recommended_content: recommendContent(qualificationFactors)
        }
      });
    } catch (error) {
      // Handle errors
      sendResponse({
        statusCode: 500,
        body: {
          error: error.message
        }
      });
    }
  }
  
  // Helper function to get additional contact data
  async function getContactData(contactId) {
    // In a real implementation, this would query HubSpot for additional
    // contact properties and calculated values
    
    // For this example, we'll return mock data
    return {
      content_downloads: 3,
      high_value_page_visits: 5,
      sales_email_replies: 2,
      sales_call_duration: 15, // minutes
      days_as_customer: previous_customer === 'Yes' ? 365 : 0,
      product_usage_score: 75,
      expansion_revenue: 0,
      job_title: 'Director of Marketing',
      job_function: 'Marketing',
      decision_maker: 'Yes',
      engagement_velocity: 0.8, // 0-1 scale
      engagement_recency: 3, // days since last engagement
      engagement_frequency: 0.7 // 0-1 scale
    };
  }
  
  // Helper function to calculate marketing score
  function calculateMarketingScore(data) {
    let score = 0;
    const factors = [];
    
    // Base score from lead score if available
    if (data.lead_score) {
      score += parseInt(data.lead_score) * 0.5; // Scale to 50 max points
      factors.push(`Lead score: ${data.lead_score}`);
    }
    
    // Add points for MQL status
    if (data.marketing_qualified_lead === 'Yes') {
      score += 20;
      factors.push('Marketing qualified lead');
    }
    
    // Add points for form submissions
    const formSubmissions = parseInt(data.num_form_submissions) || 0;
    if (formSubmissions > 0) {
      const formPoints = Math.min(formSubmissions * 5, 15);
      score += formPoints;
      factors.push(`${formSubmissions} form submissions`);
    }
    
    // Add points for content downloads
    const contentDownloads = parseInt(data.content_downloads) || 0;
    if (contentDownloads > 0) {
      const downloadPoints = Math.min(contentDownloads * 5, 15);
      score += downloadPoints;
      factors.push(`${contentDownloads} content downloads`);
    }
    
    // Add points for email engagement
    const openRate = parseFloat(data.email_open_rate) || 0;
    const clickRate = parseFloat(data.email_click_rate) || 0;
    
    if (openRate > 0.2) {
      score += 5;
      factors.push('Above average email open rate');
    }
    
    if (clickRate > 0.1) {
      score += 10;
      factors.push('Above average email click rate');
    }
    
    // Add points for high-value page visits
    const highValueVisits = parseInt(data.high_value_page_visits) || 0;
    if (highValueVisits > 0) {
      const visitPoints = Math.min(highValueVisits * 3, 15);
      score += visitPoints;
      factors.push(`${highValueVisits} high-value page visits`);
    }
    
    // Cap score at 100
    score = Math.min(score, 100);
    
    return {
      score,
      factors
    };
  }
  
  // Helper function to calculate sales score
  function calculateSalesScore(data) {
    let score = 0;
    const factors = [];
    
    // Add points for sales activities
    const salesActivities = parseInt(data.num_sales_activities) || 0;
    if (salesActivities > 0) {
      const activityPoints = Math.min(salesActivities * 5, 20);
      score += activityPoints;
      factors.push(`${salesActivities} sales activities`);
    }
    
    // Add points for meeting attendance
    if (data.meeting_showed === 'Yes') {
      score += 25;
      factors.push('Attended sales meeting');
    }
    
    // Add points for deal creation
    if (data.deal_created === 'Yes') {
      score += 20;
      factors.push('Deal created');
    }
    
    // Add points based on deal stage
    if (data.deal_stage) {
      let dealStagePoints = 0;
      
      switch(data.deal_stage) {
        case 'Appointment Scheduled':
          dealStagePoints = 5;
          break;
        case 'Qualified to Buy':
          dealStagePoints = 15;
          break;
        case 'Presentation Scheduled':
          dealStagePoints = 20;
          break;
        case 'Decision Maker Bought-In':
          dealStagePoints = 30;
          break;
        case 'Contract Sent':
          dealStagePoints = 40;
          break;
        case 'Closed Won':
          dealStagePoints = 50;
          break;
      }
      
      score += dealStagePoints;
      if (dealStagePoints > 0) {
        factors.push(`Deal stage: ${data.deal_stage}`);
      }
    }
    
    // Add points for sales email replies
    const emailReplies = parseInt(data.sales_email_replies) || 0;
    if (emailReplies > 0) {
      const replyPoints = Math.min(emailReplies * 5, 15);
      score += replyPoints;
      factors.push(`${emailReplies} replies to sales emails`);
    }
    
    // Add points for call duration
    const callDuration = parseInt(data.sales_call_duration) || 0;
    if (callDuration > 0) {
      let callPoints = 0;
      
      if (callDuration > 30) {
        callPoints = 20;
      } else if (callDuration > 15) {
        callPoints = 15;
      } else if (callDuration > 5) {
        callPoints = 10;
      } else {
        callPoints = 5;
      }
      
      score += callPoints;
      factors.push(`${callDuration} minute sales call`);
    }
    
    // Deduct points for objections
    const objections = parseInt(data.objections_raised) || 0;
    if (objections > 0) {
      const objectionPoints = Math.min(objections * 5, 15);
      score -= objectionPoints;
      factors.push(`${objections} objections raised`);
    }
    
    // Cap score between 0 and 100
    score = Math.max(0, Math.min(score, 100));
    
    return {
      score,
      factors
    };
  }
  
  // Helper function to calculate service score
  function calculateServiceScore(data) {
    let score = 50; // Start at neutral
    const factors = [];
    
    // If never a customer, return neutral score
    if (data.previous_customer !== 'Yes') {
      return {
        score,
        factors: ['No previous customer relationship']
      };
    }
    
    // Add points for customer tenure
    const daysAsCustomer = parseInt(data.days_as_customer) || 0;
    if (daysAsCustomer > 0) {
      let tenurePoints = 0;
      
      if (daysAsCustomer > 730) { // 2+ years
        tenurePoints = 20;
        factors.push('Long-term customer (2+ years)');
      } else if (daysAsCustomer > 365) { // 1+ year
        tenurePoints = 15;
        factors.push('Established customer (1+ year)');
      } else if (daysAsCustomer > 180) { // 6+ months
        tenurePoints = 10;
        factors.push('Established customer (6+ months)');
      } else if (daysAsCustomer > 90) { // 3+ months
        tenurePoints = 5;
        factors.push('Recent customer (3+ months)');
      } else {
        tenurePoints = 0;
        factors.push('New customer (< 3 months)');
      }
      
      score += tenurePoints;
    }
    
    // Add/deduct points based on CSAT
    const csat = parseFloat(data.average_csat) || 0;
    if (csat > 0) {
      let csatPoints = 0;
      
      if (csat >= 9) {
        csatPoints = 20;
        factors.push('Excellent CSAT (9+)');
      } else if (csat >= 8) {
        csatPoints = 15;
        factors.push('Good CSAT (8+)');
      } else if (csat >= 7) {
        csatPoints = 5;
        factors.push('Average CSAT (7+)');
      } else if (csat >= 5) {
        csatPoints = -10;
        factors.push('Below average CSAT (5-6)');
      } else {
        csatPoints = -20;
        factors.push('Poor CSAT (< 5)');
      }
      
      score += csatPoints;
    }
    
    // Add/deduct points based on NPS
    const nps = parseInt(data.nps_score) || 0;
    if (nps !== 0) {
      let npsPoints = 0;
      
      if (nps >= 9) {
        npsPoints = 20;
        factors.push('Promoter (NPS 9-10)');
      } else if (nps >= 7) {
        npsPoints = 10;
        factors.push('Passive (NPS 7-8)');
      } else {
        npsPoints = -15;
        factors.push('Detractor (NPS 0-6)');
      }
      
      score += npsPoints;
    }
    
    // Add/deduct points based on support ticket volume
    const ticketCount = parseInt(data.support_ticket_count) || 0;
    if (ticketCount > 0) {
      let ticketPoints = 0;
      
      if (ticketCount > 10) {
        ticketPoints = -15;
        factors.push('High support ticket volume (10+)');
      } else if (ticketCount > 5) {
        ticketPoints = -5;
        factors.push('Moderate support ticket volume (6-10)');
      } else if (ticketCount > 0) {
        ticketPoints = 0;
        factors.push('Low support ticket volume (1-5)');
      }
      
      score += ticketPoints;
    }
    
    // Add points for product usage
    const usageScore = parseInt(data.product_usage_score) || 0;
    if (usageScore > 0) {
      let usagePoints = 0;
      
      if (usageScore >= 80) {
        usagePoints = 20;
        factors.push('High product usage');
      } else if (usageScore >= 50) {
        usagePoints = 10;
        factors.push('Moderate product usage');
      } else {
        usagePoints = 0;
        factors.push('Low product usage');
      }
      
      score += usagePoints;
    }
    
    // Add points for expansion revenue
    const expansion = parseFloat(data.expansion_revenue) || 0;
    if (expansion > 0) {
      score += 15;
      factors.push('Previous expansion revenue');
    }
    
    // Cap score between 0 and 100
    score = Math.max(0, Math.min(score, 100));
    
    return {
      score,
      factors
    };
  }
  
  // Helper function to calculate demographic score
  function calculateDemographicScore(data) {
    let score = 0;
    const factors = [];
    
    // Score based on industry fit
    if (data.industry) {
      // Define target industries (would be customized for each business)
      const targetIndustries = ['Technology', 'Healthcare', 'Financial Services', 'Manufacturing', 'Retail'];
      const secondaryIndustries = ['Education', 'Professional Services', 'Media', 'Telecommunications'];
      
      if (targetIndustries.includes(data.industry)) {
        score += 25;
        factors.push(`Target industry: ${data.industry}`);
      } else if (secondaryIndustries.includes(data.industry)) {
        score += 15;
        factors.push(`Secondary industry: ${data.industry}`);
      } else {
        score += 5;
        factors.push(`Non-target industry: ${data.industry}`);
      }
    }
    
    // Score based on company size
    if (data.company_size) {
      let sizePoints = 0;
      
      // Example scoring - would be customized for target market
      switch(data.company_size) {
        case 'Enterprise (1000+ employees)':
          sizePoints = 25;
          break;
        case 'Mid-Market (101-999 employees)':
          sizePoints = 25;
          break;
        case 'Small Business (11-100 employees)':
          sizePoints = 15;
          break;
        case 'Startup (1-10 employees)':
          sizePoints = 10;
          break;
      }
      
      score += sizePoints;
      if (sizePoints > 0) {
        factors.push(`Company size: ${data.company_size}`);
      }
    }
    
    // Score based on annual revenue
    if (data.annual_revenue) {
      const revenue = parseFloat(data.annual_revenue) || 0;
      let revenuePoints = 0;
      
      if (revenue >= 100000000) { // $100M+
        revenuePoints = 25;
        factors.push('Enterprise revenue ($100M+)');
      } else if (revenue >= 10000000) { // $10M+
        revenuePoints = 20;
        factors.push('Mid-market revenue ($10M-$100M)');
      } else if (revenue >= 1000000) { // $1M+
        revenuePoints = 15;
        factors.push('SMB revenue ($1M-$10M)');
      } else {
        revenuePoints = 5;
        factors.push('Small business revenue (< $1M)');
      }
      
      score += revenuePoints;
    }
    
    // Score based on job title/function
    if (data.job_title || data.job_function) {
      // Define target roles (would be customized)
      const executiveRoles = ['CEO', 'CTO', 'CIO', 'CFO', 'COO', 'CMO', 'President', 'Owner', 'Founder'];
      const seniorRoles = ['VP', 'Director', 'Head', 'Senior Manager'];
      
      let rolePoints = 0;
      
      // Check for executive titles
      if (data.job_title && executiveRoles.some(role => data.job_title.includes(role))) {
        rolePoints = 25;
        factors.push(`Executive role: ${data.job_title}`);
      } 
      // Check for senior titles
      else if (data.job_title && seniorRoles.some(role => data.job_title.includes(role))) {
        rolePoints = 20;
        factors.push(`Senior role: ${data.job_title}`);
      }
      // Check for target functions
      else if (data.job_function && ['Marketing', 'Sales', 'IT', 'Operations'].includes(data.job_function)) {
        rolePoints = 15;
        factors.push(`Target function: ${data.job_function}`);
      }
      // Other roles
      else if (data.job_title) {
        rolePoints = 5;
        factors.push(`Other role: ${data.job_title}`);
      }
      
      score += rolePoints;
    }
    
    // Score based on decision maker status
    if (data.decision_maker === 'Yes') {
      score += 25;
      factors.push('Decision maker');
    }
    
    // Cap score at 100
    score = Math.min(score, 100);
    
    return {
      score,
      factors
    };
  }
  
  // Helper function to calculate behavioral score
  function calculateBehavioralScore(data) {
    let score = 0;
    const factors = [];
    
    // Score based on engagement recency
    const recencyDays = parseInt(data.engagement_recency) || 0;
    let recencyPoints = 0;
    
    if (recencyDays <= 1) {
      recencyPoints = 30;
      factors.push('Very recent engagement (today/yesterday)');
    } else if (recencyDays <= 7) {
      recencyPoints = 25;
      factors.push('Recent engagement (this week)');
    } else if (recencyDays <= 14) {
      recencyPoints = 20;
      factors.push('Recent engagement (last 2 weeks)');
    } else if (recencyDays <= 30) {
      recencyPoints = 15;
      factors.push('Recent engagement (this month)');
    } else if (recencyDays <= 90) {
      recencyPoints = 5;
      factors.push('Engagement within last 3 months');
    } else {
      recencyPoints = 0;
      factors.push('No recent engagement (3+ months)');
    }
    
    score += recencyPoints;
    
    // Score based on engagement frequency
    const frequency = parseFloat(data.engagement_frequency) || 0;
    let frequencyPoints = 0;
    
    if (frequency >= 0.8) {
      frequencyPoints = 30;
      factors.push('Very high engagement frequency');
    } else if (frequency >= 0.6) {
      frequencyPoints = 25;
      factors.push('High engagement frequency');
    } else if (frequency >= 0.4) {
      frequencyPoints = 15;
      factors.push('Moderate engagement frequency');
    } else if (frequency >= 0.2) {
      frequencyPoints = 10;
      factors.push('Low engagement frequency');
    } else {
      frequencyPoints = 0;
      factors.push('Very low engagement frequency');
    }
    
    score += frequencyPoints;
    
    // Score based on engagement velocity (trend)
    const velocity = parseFloat(data.engagement_velocity) || 0;
    let velocityPoints = 0;
    
    if (velocity >= 0.8) {
      velocityPoints = 40;
      factors.push('Rapidly increasing engagement');
    } else if (velocity >= 0.5) {
      velocityPoints = 30;
      factors.push('Increasing engagement');
    } else if (velocity >= 0.2) {
      velocityPoints = 20;
      factors.push('Slightly increasing engagement');
    } else if (velocity >= 0) {
      velocityPoints = 10;
      factors.push('Stable engagement');
    } else {
      velocityPoints = 0;
      factors.push('Decreasing engagement');
    }
    
    score += velocityPoints;
    
    // Cap score at 100
    score = Math.min(score, 100);
    
    return {
      score,
      factors
    };
  }
  
  // Helper function to calculate unified score
  function calculateUnifiedScore(marketingScore, salesScore, serviceScore, demographicScore, behavioralScore) {
    // Weight each component score
    // Weights would be customized based on business priorities
    const weightedMarketing = marketingScore.score * 0.25;
    const weightedSales = salesScore.score * 0.25;
    const weightedService = serviceScore.score * 0.15;
    const weightedDemographic = demographicScore.score * 0.15;
    const weightedBehavioral = behavioralScore.score * 0.20;
    
    // Calculate weighted average
    const unifiedScore = Math.round(
      weightedMarketing +
      weightedSales +
      weightedService +
      weightedDemographic +
      weightedBehavioral
    );
    
    return unifiedScore;
  }
  
  // Helper function to determine qualification status
  function determineQualificationStatus(score) {
    if (score >= 80) {
      return "SALES_QUALIFIED_LEAD";
    } else if (score >= 60) {
      return "MARKETING_QUALIFIED_LEAD";
    } else if (score >= 40) {
      return "LEAD";
    } else {
      return "SUBSCRIBER";
    }
  }
  
  // Helper function to identify qualification factors
  function identifyQualificationFactors(marketingScore, salesScore, serviceScore, demographicScore, behavioralScore) {
    const factors = [];
    
    // Add top factors from each category (limit to top 2 from each)
    if (marketingScore.factors.length > 0) {
      factors.push(...marketingScore.factors.slice(0, 2).map(f => `Marketing: ${f}`));
    }
    
    if (salesScore.factors.length > 0) {
      factors.push(...salesScore.factors.slice(0, 2).map(f => `Sales: ${f}`));
    }
    
    if (serviceScore.factors.length > 0) {
      factors.push(...serviceScore.factors.slice(0, 2).map(f => `Service: ${f}`));
    }
    
    if (demographicScore.factors.length > 0) {
      factors.push(...demographicScore.factors.slice(0, 2).map(f => `Demographic: ${f}`));
    }
    
    if (behavioralScore.factors.length > 0) {
      factors.push(...behavioralScore.factors.slice(0, 2).map(f => `Behavioral: ${f}`));
    }
    
    return factors;
  }
  
  // Helper function to generate next best actions
  function generateNextBestActions(score, status, factors) {
    const actions = [];
    
    switch(status) {
      case "SALES_QUALIFIED_LEAD":
        actions.push("Assign to sales representative");
        actions.push("Schedule discovery call");
        actions.push("Send personalized outreach email");
        break;
        
      case "MARKETING_QUALIFIED_LEAD":
        actions.push("Enroll in high-touch nurture campaign");
        actions.push("Invite to upcoming webinar");
        actions.push("Send case study relevant to industry");
        break;
        
      case "LEAD":
        actions.push("Enroll in educational nurture campaign");
        actions.push("Send industry-specific content");
        actions.push("Monitor for increased engagement");
        break;
        
      case "SUBSCRIBER":
        actions.push("Enroll in awareness nurture campaign");
        actions.push("Send top-of-funnel content");
        actions.push("Monitor for website activity");
        break;
    }
    
    // Add custom actions based on specific factors
    if (factors.some(f => f.includes("Decision maker"))) {
      actions.push("Prepare executive-focused messaging");
    }
    
    if (factors.some(f => f.includes("Previous customer"))) {
      actions.push("Reference previous relationship in outreach");
    }
    
    if (factors.some(f => f.includes("High product usage"))) {
      actions.push("Highlight new features relevant to their usage patterns");
    }
    
    return actions;
  }
  
  // Helper function to determine recommended owner
  function determineRecommendedOwner(score, status) {
    switch(status) {
      case "SALES_QUALIFIED_LEAD":
        return "Sales Representative";
      case "MARKETING_QUALIFIED_LEAD":
        return "Sales Development Representative";
      case "LEAD":
        return "Marketing Team";
      case "SUBSCRIBER":
        return "Marketing Automation";
      default:
        return "Marketing Team";
    }
  }
  
  // Helper function to recommend content
  function recommendContent(factors) {
    const recommendations = [];
    
    // Check for industry-specific recommendations
    if (factors.some(f => f.includes("industry: Technology"))) {
      recommendations.push("Technology Industry Solution Guide");
      recommendations.push("Tech ROI Calculator");
    } else if (factors.some(f => f.includes("industry: Healthcare"))) {
      recommendations.push("Healthcare Compliance Whitepaper");
      recommendations.push("Patient Experience Case Study");
    } else if (factors.some(f => f.includes("industry: Financial Services"))) {
      recommendations.push("Financial Services Security Brief");
      recommendations.push("Banking Digital Transformation Guide");
    }
    
    // Check for role-specific recommendations
    if (factors.some(f => f.includes("Executive role"))) {
      recommendations.push("Executive Business Impact Report");
      recommendations.push("Strategic Roadmap Template");
    } else if (factors.some(f => f.includes("Senior role"))) {
      recommendations.push("Department Leader's Playbook");
      recommendations.push("Team Efficiency Calculator");
    }
    
    // Check for behavior-specific recommendations
    if (factors.some(f => f.includes("High engagement"))) {
      recommendations.push("Product Deep Dive Webinar");
      recommendations.push("Advanced Features Guide");
    } else if (factors.some(f => f.includes("Low engagement"))) {
      recommendations.push("Industry Trends Report");
      recommendations.push("Quick Start Guide");
    }
    
    // If no specific recommendations, add general ones
    if (recommendations.length === 0) {
      recommendations.push("Product Overview");
      recommendations.push("Customer Success Stories");
      recommendations.push("Industry Benchmark Report");
    }
    
    return recommendations.slice(0, 3); // Return top 3 recommendations
  }
  
  // Execute the main function
  calculateCrossHubScore();
};

				
			
This serverless function creates a sophisticated cross-hub qualification system that analyzes data from Marketing, Sales, and Service Hubs to create a unified view of each prospect. The output can be used to make more accurate qualification decisions, prioritize follow-up activities, and personalize outreach based on the complete customer context.

Integrating with Personalization

To maximize the impact of your cross-hub qualification workflow, integrate it with personalization:
  1. Add a “Set property value” action to update “Personalization Segment” based on qualification factors
  2. Create dynamic content in emails based on the cross-hub qualification factors
  3. Set up smart content on your website that adapts based on qualification status
  4. Create personalized follow-up sequences based on the specific factors that influenced the qualification

Example personalization logic:

				
					// Example code snippet for personalization based on cross-hub qualification
function createPersonalizationRules(qualificationFactors, qualificationScore) {
  const personalizationRules = {
    contentFocus: [],
    messageTone: '',
    featuredCaseStudy: '',
    callToAction: ''
  };
  
  // Determine content focus based on qualification factors
  if (qualificationFactors.some(f => f.includes('Marketing:'))) {
    personalizationRules.contentFocus.push('marketing_automation');
  }
  
  if (qualificationFactors.some(f => f.includes('Sales:'))) {
    personalizationRules.contentFocus.push('sales_enablement');
  }
  
  if (qualificationFactors.some(f => f.includes('Service:'))) {
    personalizationRules.contentFocus.push('customer_service');
  }
  
  // Determine message tone based on qualification score
  if (qualificationScore >= 80) {
    personalizationRules.messageTone = 'direct_and_urgent';
    personalizationRules.callToAction = 'schedule_meeting';
  } else if (qualificationScore >= 60) {
    personalizationRules.messageTone = 'consultative';
    personalizationRules.callToAction = 'request_assessment';
  } else {
    personalizationRules.messageTone = 'educational';
    personalizationRules.callToAction = 'download_guide';
  }
  
  // Determine featured case study based on industry and size
  if (qualificationFactors.some(f => f.includes('industry: Healthcare'))) {
    personalizationRules.featuredCaseStudy = 'healthcare_provider_case_study';
  } else if (qualificationFactors.some(f => f.includes('industry: Financial'))) {
    personalizationRules.featuredCaseStudy = 'financial_services_case_study';
  } else if (qualificationFactors.some(f => f.includes('Enterprise'))) {
    personalizationRules.featuredCaseStudy = 'enterprise_case_study';
  } else {
    personalizationRules.featuredCaseStudy = 'general_case_study';
  }
  
  return personalizationRules;
}

				
			

Measuring Success

To evaluate the effectiveness of your cross-hub qualification workflow, monitor these key metrics:
 
  • Qualification accuracy: Percentage of qualified leads that convert to opportunities
  • Sales acceptance rate: Percentage of qualified leads accepted by sales
  • Conversion velocity: Speed at which leads move through the funnel
  • Revenue impact: Closed-won revenue from cross-hub qualified leads
  • Resource efficiency: Reduction in time spent on unqualified leads
  • Cross-team alignment: Improved collaboration between marketing, sales, and service

Real-World Impact

A well-implemented cross-hub qualification workflow delivers significant business benefits. One of our B2B software clients achieved:
 
  • 47% improvement in lead-to-opportunity conversion rate
  • 35% reduction in sales cycle length
  • 62% increase in average deal size
  • 28% higher win rate
  • 43% reduction in “lead ping-pong” between teams
 
The key to their success was breaking down data silos between teams and creating a unified qualification framework that considered the complete customer context.

Best Practices for Cross-Hub Lead Qualification

  1. Start with alignment: Ensure marketing, sales, and service teams agree on qualification criteria.
  2. Use progressive profiling: Build a more complete picture of leads over time.
  3. Weight factors appropriately: Assign different weights to different data points based on their predictive value.
  4. Implement regular reviews: Analyze qualification accuracy quarterly and refine criteria.
  5. Maintain feedback loops: Create mechanisms for sales to provide feedback on lead quality.
  6. Document the process: Ensure all teams understand how qualification decisions are made.
  7. Balance automation with human judgment: Use automation for initial scoring but allow for human override.
 
By implementing this cross-hub lead qualification workflow, you’ll create a more accurate, efficient qualification process that leverages all available data points across your HubSpot instance, ultimately driving higher conversion rates and better sales outcomes.