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:
- Navigate to Settings > Properties > Deals
- Find the “Deal stage” property
- Customize stages to match your sales methodology (e.g., Qualification, Needs Analysis, Proposal, Negotiation, Closed Won/Lost)
Step 2: Create the Base Workflow
- Navigate to Automation > Workflows in your HubSpot account
- Click “Create workflow” > “From scratch”
- Select “Deal-based” workflow
- Name it “Deal Stage Progression Automation.”
Step 3: Set Up Stage Transition Triggers
For each stage transition, create enrollment triggers and actions:
- Set enrollment trigger: “Deal stage is known” AND “Deal stage equals [Current Stage]”
- 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:
- Add a delay action (e.g., check every 1 day)
- 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)
- If criteria are met, add “Set property value” action to update deal stage
- 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:
- For each stage, add a parallel branch after the stage is set
- Add a delay based on your expected stage duration (e.g., 14 days for Proposal stage)
- Add an “If/then branch” to check if the deal is still in that stage
- 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:
- Add a branch specifically for “Deal stage equals Closed Won”
- 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:
- 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
- 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
- Align with your sales methodology: Ensure your deal stages reflect your actual sales process.
- Define clear exit criteria: Establish objective requirements for moving to the next stage.
- Balance automation with flexibility: Allow sales reps to override automation when necessary
- Focus on value-adding activities: Automate administrative tasks so reps can focus on selling.
- Integrate with sales coaching: Use automation to identify coaching opportunities.
- 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.