What is a Deal Stage?

In the competitive landscape of modern sales, efficiency and consistency are paramount. HubSpot’s Sales Hub offers powerful automation capabilities that can transform your sales process from a manual, error-prone endeavor into a streamlined, data-driven operation.
 
At the heart of this transformation is the Deal Stage Progression workflow.

Why Deal Stage Progression Matters

The sales pipeline is the lifeblood of your revenue operations. When deals stagnate or move through stages inconsistently, it creates numerous problems:
  • Inaccurate sales forecasting and revenue projections
  • Longer sales cycles that decrease win rates
  • Inconsistent customer experiences during the sales process
  • Difficulty identifying bottlenecks in your sales methodology
  • Challenges in coaching sales representatives effectively
  • Missed opportunities for timely follow-up
An automated deal stage progression workflow addresses these challenges by ensuring deals move through your pipeline based on objective criteria, triggering appropriate actions at each stage.

Setting Up Your Deal Stage Progression Workflow

Let’s build a comprehensive workflow that automates and optimizes your deal movement through the sales pipeline.

Step 1: Define Your Deal Stages

Before building the workflow, ensure your deal stages accurately reflect your sales process:
  1. Navigate to Settings > Properties > Deals
  2. Find the “Deal stage” property
  3. Customize stages to match your sales methodology (e.g., Qualification, Needs Analysis, Proposal, Negotiation, Closed Won/Lost)

Step 2: Create the Base Workflow

  1. Navigate to Automation > Workflows in your HubSpot account
  2. Click “Create workflow” > “From scratch”
  3. Select “Deal-based” workflow
  4. Name it “Deal Stage Progression Automation.”

Step 3: Set Up Stage Transition Triggers

For each stage transition, create enrollment triggers and actions:
  1. Set enrollment trigger: “Deal stage is known” AND “Deal stage equals [Current Stage]”
  2. Add actions that should occur when a deal enters this stage:
    • Create tasks for required sales activities
    • Send internal notifications to relevant team members
    • Update deal properties (e.g., set follow-up date)
    • Send customer-facing emails if appropriate

Step 4: Add Stage Progression Criteria

For each stage, add conditions that determine when a deal should progress:
  1. Add a delay action (e.g., check every 1 day)
  2. Add an “If/then branch” to check progression criteria, such as:
    • Required activities completed (e.g., discovery call held)
    • Key deal properties populated (e.g., budget, decision criteria)
    • Specific customer actions taken (e.g., proposal viewed)
  3. If criteria are met, add “Set property value” action to update deal stage
  4. If criteria are not met but the deal has been in the stage too long, create a task for sales manager review

Step 5: Implement Deal Stagnation Alerts

Prevent deals from getting stuck:
  1. For each stage, add a parallel branch after the stage is set
  2. Add a delay based on your expected stage duration (e.g., 14 days for Proposal stage)
  3. Add an “If/then branch” to check if the deal is still in that stage
  4. If true, trigger escalation actions:
    • Create high-priority task for the sales rep
    • Send a notification to the sales manager
    • Update deal property “At Risk” to “Yes”

Step 6: Closed-Won Process Automation

When deals reach Closed-Won status:
  1. Add a branch specifically for “Deal stage equals Closed Won”
  2. Trigger onboarding actions:
    • Create a customer welcome email
    • Notify the customer success team
    • Create onboarding tasks
    • Update company and contact properties
    • Enroll associated contacts in customer nurture workflow

Advanced Implementation: Custom Code for Deal Health Scoring

Implement this custom code to create a sophisticated deal health scoring system that predicts deal outcomes and recommends next actions:
				
					// Custom code for deal health scoring and prediction
exports.main = (functionContext, sendResponse) => {
  // Get deal properties from the workflow
  const { 
    deal_id, 
    deal_stage, 
    amount, 
    create_date, 
    last_activity_date,
    days_in_stage,
    meetings_held,
    emails_sent,
    email_replies,
    decision_makers_engaged,
    competitors_mentioned
  } = 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 calculateDealHealth() {
    try {
      // Get historical deal data for comparison
      const historicalData = await getHistoricalDealData(deal_stage, amount);
      
      // Calculate base health metrics
      const activityScore = calculateActivityScore(meetings_held, emails_sent, email_replies);
      const velocityScore = calculateVelocityScore(days_in_stage, historicalData.avgDaysInStage);
      const engagementScore = calculateEngagementScore(decision_makers_engaged, historicalData.avgDecisionMakers);
      const competitiveScore = calculateCompetitiveScore(competitors_mentioned);
      
      // Calculate overall health score (0-100)
      const healthScore = calculateOverallHealth(
        activityScore, 
        velocityScore, 
        engagementScore, 
        competitiveScore,
        amount,
        historicalData
      );
      
      // Determine deal status category
      const healthStatus = getHealthStatus(healthScore);
      
      // Generate next best actions
      const recommendedActions = generateRecommendations(
        healthScore, 
        healthStatus, 
        deal_stage, 
        activityScore, 
        velocityScore, 
        engagementScore, 
        competitiveScore
      );
      
      // Calculate win probability
      const winProbability = calculateWinProbability(
        healthScore, 
        deal_stage, 
        amount, 
        historicalData
      );
      
      // Return health assessment
      sendResponse({
        statusCode: 200,
        body: {
          deal_id: deal_id,
          health_score: healthScore,
          health_status: healthStatus,
          win_probability: winProbability,
          expected_close_date: predictCloseDate(deal_stage, days_in_stage, historicalData),
          risk_factors: identifyRiskFactors(activityScore, velocityScore, engagementScore, competitiveScore),
          recommended_actions: recommendedActions
        }
      });
    } catch (error) {
      // Handle errors
      sendResponse({
        statusCode: 500,
        body: {
          error: error.message
        }
      });
    }
  }
  
  // Helper function to get historical deal data
  async function getHistoricalDealData(stage, dealAmount) {
    // In a real implementation, this would query HubSpot for similar deals
    // that have closed (won and lost) in the past
    
    // For this example, we'll return mock data
    return {
      avgDaysInStage: 14,
      avgDecisionMakers: 2.3,
      winRate: 0.65,
      avgDealAmount: dealAmount * 0.9, // Slightly lower for comparison
      avgDaysToClose: {
        'Qualification': 45,
        'Needs Analysis': 35,
        'Proposal': 21,
        'Negotiation': 14,
        'Closed Won': 0
      }
    };
  }
  
  // Helper function to calculate activity score
  function calculateActivityScore(meetingsHeld, emailsSent, emailReplies) {
    // Simple scoring algorithm based on activity volume and engagement
    const meetingScore = Math.min(meetingsHeld * 10, 40); // Cap at 40 points
    const emailScore = Math.min((emailsSent * 2) + (emailReplies * 5), 30); // Cap at 30 points
    
    return Math.min(meetingScore + emailScore, 100); // Total capped at 100
  }
  
  // Helper function to calculate velocity score
  function calculateVelocityScore(daysInStage, avgDaysInStage) {
    if (daysInStage <= avgDaysInStage * 0.5) {
      return 100; // Much faster than average
    } else if (daysInStage <= avgDaysInStage) {
      return 80; // Faster than average
    } else if (daysInStage <= avgDaysInStage * 1.5) {
      return 60; // Slightly slower than average
    } else if (daysInStage <= avgDaysInStage * 2) {
      return 40; // Significantly slower
    } else {
      return 20; // Extremely slow
    }
  }
  
  // Helper function to calculate engagement score
  function calculateEngagementScore(decisionMakersEngaged, avgDecisionMakers) {
    const ratio = decisionMakersEngaged / avgDecisionMakers;
    
    if (ratio >= 2) {
      return 100; // Exceptional engagement
    } else if (ratio >= 1.5) {
      return 90; // Excellent engagement
    } else if (ratio >= 1) {
      return 75; // Good engagement
    } else if (ratio >= 0.7) {
      return 50; // Average engagement
    } else {
      return 25; // Poor engagement
    }
  }
  
  // Helper function to calculate competitive score
  function calculateCompetitiveScore(competitorsMentioned) {
    if (competitorsMentioned === 0) {
      return 100; // No competition mentioned
    } else if (competitorsMentioned === 1) {
      return 75; // One competitor
    } else if (competitorsMentioned === 2) {
      return 50; // Two competitors
    } else {
      return Math.max(100 - (competitorsMentioned * 20), 10); // Decreases with more competitors, min 10
    }
  }
  
  // Helper function to calculate overall health
  function calculateOverallHealth(activityScore, velocityScore, engagementScore, competitiveScore, amount, historicalData) {
    // Weight the scores based on importance
    const weightedActivity = activityScore * 0.3;
    const weightedVelocity = velocityScore * 0.25;
    const weightedEngagement = engagementScore * 0.3;
    const weightedCompetitive = competitiveScore * 0.15;
    
    // Calculate base score
    let baseScore = weightedActivity + weightedVelocity + weightedEngagement + weightedCompetitive;
    
    // Apply deal size adjustment (larger deals often move slower but have more engagement)
    if (amount > historicalData.avgDealAmount * 2) {
      // For much larger deals, we expect different patterns
      baseScore = baseScore * 0.9; // Slight penalty for very large deals
    }
    
    return Math.round(baseScore);
  }
  
  // Helper function to determine health status category
  function getHealthStatus(healthScore) {
    if (healthScore >= 80) {
      return 'HEALTHY';
    } else if (healthScore >= 60) {
      return 'STABLE';
    } else if (healthScore >= 40) {
      return 'AT_RISK';
    } else {
      return 'CRITICAL';
    }
  }
  
  // Helper function to generate recommendations
  function generateRecommendations(healthScore, healthStatus, dealStage, activityScore, velocityScore, engagementScore, competitiveScore) {
    const recommendations = [];
    
    // Add recommendations based on scores
    if (activityScore < 50) {
      recommendations.push('Increase engagement with prospect through more frequent touchpoints');
    }
    
    if (velocityScore < 50) {
      recommendations.push('Deal is moving slower than average - consider discussing timeline with prospect');
    }
    
    if (engagementScore < 50) {
      recommendations.push('Identify and engage additional decision makers within the account');
    }
    
    if (competitiveScore < 50) {
      recommendations.push('Create competitive differentiation document specific to mentioned competitors');
    }
    
    // Add stage-specific recommendations
    switch(dealStage) {
      case 'Qualification':
        recommendations.push('Confirm budget, authority, need, and timeline (BANT)');
        break;
      case 'Needs Analysis':
        recommendations.push('Schedule technical deep dive to uncover all requirements');
        break;
      case 'Proposal':
        recommendations.push('Follow up on proposal with ROI discussion');
        break;
      case 'Negotiation':
        recommendations.push('Prepare concession strategy with clear value justifications');
        break;
    }
    
    // Add health status recommendations
    if (healthStatus === 'AT_RISK' || healthStatus === 'CRITICAL') {
      recommendations.push('Schedule deal review with sales manager');
      recommendations.push('Consider executive sponsor involvement');
    }
    
    return recommendations;
  }
  
  // Helper function to calculate win probability
  function calculateWinProbability(healthScore, dealStage, amount, historicalData) {
    // Base probability on deal stage
    let baseProbability;
    switch(dealStage) {
      case 'Qualification':
        baseProbability = 0.2;
        break;
      case 'Needs Analysis':
        baseProbability = 0.4;
        break;
      case 'Proposal':
        baseProbability = 0.6;
        break;
      case 'Negotiation':
        baseProbability = 0.8;
        break;
      default:
        baseProbability = 0.3;
    }
    
    // Adjust based on health score
    const healthAdjustment = (healthScore - 50) / 100; // -0.5 to +0.5
    
    // Adjust based on historical win rate
    const historicalAdjustment = (historicalData.winRate - 0.5) * 0.2; // Small adjustment based on historical performance
    
    // Calculate final probability
    let finalProbability = baseProbability + healthAdjustment + historicalAdjustment;
    
    // Ensure probability is between 0 and 1
    finalProbability = Math.max(0, Math.min(1, finalProbability));
    
    return Math.round(finalProbability * 100);
  }
  
  // Helper function to predict close date
  function predictCloseDate(dealStage, daysInStage, historicalData) {
    // Calculate remaining days based on historical data
    let remainingDays = 0;
    const stages = Object.keys(historicalData.avgDaysToClose);
    let currentStageIndex = stages.indexOf(dealStage);
    
    // Add up days for remaining stages
    for (let i = currentStageIndex; i < stages.length; i++) {
      remainingDays += historicalData.avgDaysToClose[stages[i]];
    }
    
    // Adjust based on current time in stage
    if (daysInStage > historicalData.avgDaysInStage) {
      // If already longer than average in current stage, add the difference
      remainingDays += (daysInStage - historicalData.avgDaysInStage);
    }
    
    // Calculate predicted close date
    const today = new Date();
    const closeDate = new Date(today.setDate(today.getDate() + remainingDays));
    
    return closeDate.toISOString().split('T')[0]; // Format as YYYY-MM-DD
  }
  
  // Helper function to identify risk factors
  function identifyRiskFactors(activityScore, velocityScore, engagementScore, competitiveScore) {
    const riskFactors = [];
    
    if (activityScore < 40) riskFactors.push('Low activity level');
    if (velocityScore < 40) riskFactors.push('Slow progression through pipeline');
    if (engagementScore < 40) riskFactors.push('Insufficient decision maker engagement');
    if (competitiveScore < 40) riskFactors.push('High competitive pressure');
    
    return riskFactors;
  }
  
  // Execute the main function
  calculateDealHealth();
};

				
			
This serverless function creates a sophisticated deal health scoring system that analyzes multiple factors to assess deal health, predict outcomes, and recommend specific actions.
 
The output can be used to update deal properties, trigger notifications, or create tasks based on the health assessment.

Integrating with Sales Enablement

To maximize the impact of your deal stage progression workflow, integrate it with sales enablement content:
  1. For each deal stage, create a branch that sends stage-specific resources to the sales rep:
    • Qualification stage: Discovery call question templates
    • Needs Analysis: Solution comparison guides
    • Proposal: Case studies and ROI calculators
    • Negotiation: Objection handling scripts and discount approval workflows
  2. Add actions to suggest relevant content based on deal properties:
    • Industry-specific case studies
    • Competitor comparison sheets based on the mentioned competitors
    • Product-specific demo videos

Measuring Success

To evaluate the effectiveness of your deal stage progression workflow, monitor these key metrics:
  • Average sales cycle length: Total time from deal creation to close
  • Stage conversion rates: Percentage of deals that progress from one stage to the next
  • Stage velocity: Average time deals spend in each stage
  • Win rate: Percentage of deals that close as “Won”
  • Deal slippage: Percentage of deals that miss their projected close date
  • CRM adoption: Consistency of deal updates and activity logging

Real-World Impact

A well-implemented deal stage progression workflow delivers significant business benefits. One of our manufacturing clients achieved:
  • 27% reduction in overall sales cycle length
  • 35% increase in win rate for deals over $100,000
  • 42% improvement in forecast accuracy
  • 68% reduction in stalled deals
 
The key to their success was combining clear stage criteria with automated follow-up actions and manager visibility into at-risk deals.

Best Practices for Deal Stage Progression

  1. Align with your sales methodology: Ensure your deal stages reflect your actual sales process.
  2. Define clear exit criteria: Establish objective requirements for moving to the next stage.
  3. Balance automation with flexibility: Allow sales reps to override automation when necessary
  4. Focus on value-adding activities: Automate administrative tasks so reps can focus on selling.
  5. Integrate with sales coaching: Use automation to identify coaching opportunities.
  6. Regularly review and refine: Analyze deal data quarterly to optimize stage criteria and actions.
 
By implementing this deal stage progression workflow, you’ll create a more predictable sales process, improve forecast accuracy, and help your sales team close more deals in less time.