ElasticFlow
HubAll SkillsBy DepartmentBy RoleBy ToolBy MetricMCPsPublishers
WebsiteLoginSign Up
ElasticFlow

Transform your business with AI-powered workflow automation. One unified platform for all your enterprise needs.

Follow us

Platform

  • Features
  • Benefits
  • Use Cases
  • Workflow Library

Use Cases

  • Sales
  • Marketing
  • Finance & Legal
  • HR

Catalogue

  • Departments
  • Roles
  • Tools
  • Metrics
  • Platforms

Growth

  • Referral Program
  • Partners

Legal

  • Privacy Policy
  • Terms of Service
  • Cookie Policy
  • Acceptable Use
  • Security
  • SLA

© 2026 ElasticFlow. All rights reserved.

ElasticFlow
HubAll SkillsBy DepartmentBy RoleBy ToolBy MetricMCPsPublishers
WebsiteLoginSign Up
ElasticFlow

Transform your business with AI-powered workflow automation. One unified platform for all your enterprise needs.

Follow us

Platform

  • Features
  • Benefits
  • Use Cases
  • Workflow Library

Use Cases

  • Sales
  • Marketing
  • Finance & Legal
  • HR

Catalogue

  • Departments
  • Roles
  • Tools
  • Metrics
  • Platforms

Growth

  • Referral Program
  • Partners

Legal

  • Privacy Policy
  • Terms of Service
  • Cookie Policy
  • Acceptable Use
  • Security
  • SLA

© 2026 ElasticFlow. All rights reserved.

ElasticFlow
HubAll SkillsBy DepartmentBy RoleBy ToolBy MetricMCPsPublishers
WebsiteLoginSign Up
  1. Hub
  2. All Skills
  3. Win/Loss Reason Extraction
AI SkillExtract reasonsSales

Extract clear win and loss reasons from deal notes, calls, and CRM fields. — Claude Skill

A Claude Skill for Claude Code by Louis Blythe — run /win-loss-reason-extraction in Claude·Updated Jun 14, 2026·vmain@e0f13a6

Compatible withGChatGPTClaudeClaudeCCClaude CodeXCodex / Codex CLICursorCursorGeminiGemini

Classifies win/loss evidence into reasons such as price, timing, competitor, no decision, feature gap, fit, champion loss, and internal process, with confidence and evidence.

  • Turns messy deal notes and call snippets into consistent win/loss reason categories.
  • Separates stated reason, inferred reason, competitor influence, and no-decision risk.
  • Shows confidence and source evidence so leaders can see what is reliable.
  • Creates follow-up actions for sales coaching, product, pricing, or marketing.
YouToday

Closed-lost reasons are inconsistent, vague, and hard to compare across reps.

With /win-loss-reason-extraction

Run /win-loss-reason-extraction to normalize reasons, evidence, confidence, and follow-up actions.

1 Paste deal notes and CRM fields2 Classify reasons3 Attach evidence4 Summarize actions

Who this is for

Sales Manager

Turn closed-won and closed-lost notes into coaching and pipeline actions.

See skills for this role
Product Marketer

Use real deal evidence to refresh positioning and battlecards.

See skills for this role

What it does

Quarterly win/loss review

Summarize why deals were won or lost by segment, competitor, and value.

Battlecard refresh

Use actual deal reasons to update competitive talk tracks.

Sales coaching

Identify whether losses come from qualification, discovery, pricing, timing, or product gaps.

How it works

1

Collect closed-won and closed-lost notes, CRM fields, call snippets, segment, deal size, and competitor context.

2

Normalize reasons into a consistent taxonomy.

3

Attach evidence, confidence, segment, and owner follow-up for each reason.

4

Summarize patterns that matter for win rate, product gaps, pricing, and messaging.

5

Flag cases where the reason is too vague and needs seller validation.

Input options

Deal evidence

CRM fields, deal notes, call snippets, close reason, outcome, and amount.

Example

What the user pastes
Closed-lost deal notes:
1. $62k ARR, competitor LearnPro. Buyer liked templates. Said reporting was weaker in LearnPro but they needed launch by July.
2. $18k ARR, no competitor. CFO paused all new tools. Rep wrote 'budget'.
3. $90k ARR, competitor GuidePilot. Lost because SSO and admin controls were not ready for procurement.
4. $42k ARR, competitor LearnPro. Buyer said our pricing was unclear and implementation services were separate.
Closed-won notes:
5. $75k ARR, competitor LearnPro. Won because VP CS needed executive reporting and CSM workflows.
Need: normalized reasons, confidence, evidence, and actions for Sales, Product, and Product Marketing.
Useful result
Reason classification
| Deal | Outcome | Primary reason | Secondary reason | Confidence | Evidence |
|---|---|---|---|---|---|
| 1 | Lost | Timing / implementation speed | Competitor template strength | High | Needed launch by July and liked LearnPro templates |
| 2 | Lost | No decision / budget freeze | Not a true competitive loss | Medium | CFO paused all new tools |
| 3 | Lost | Product gap | Enterprise readiness | High | SSO and admin controls blocked procurement |
| 4 | Lost | Pricing clarity | Services packaging | Medium | Buyer found pricing unclear and services separate |
| 5 | Won | Reporting differentiation | CS workflow fit | High | VP CS needed executive reporting and CSM workflows |
Pattern readout
Do not summarize Q2 as 'lost on price.' The stronger pattern is enterprise readiness and implementation speed. Pricing clarity appears in one deal and needs more evidence before becoming the headline.
Actions
Sales: qualify launch deadline and procurement requirements earlier.
Product: review SSO/admin controls as enterprise blockers.
Product Marketing: update LearnPro battlecard around templates vs reporting, and clarify implementation services in pricing conversations.
Human review
Ask owners of deals 2 and 4 whether 'budget' and 'pricing unclear' were stated by the buyer or inferred by the rep. Keep them medium confidence until validated.

Metrics this improves

Competitive Win Rate
Improves competitor-specific messaging and sales response.
Sales
Win Rate
Clarifies the reasons behind won and lost opportunities.
Sales

Works with

Salesforce
manual

Use opportunity fields, close reasons, amounts, and notes.

Gong
manual

Use call snippets and buyer language as evidence.

HubSpot
manual

Use deal records, owners, stages, and closed-lost notes.

Want to use Win/Loss Reason Extraction?

Choose how to get started.

Run in Claude Code
Free. Open source.

Install and run this skill locally on your computer.

1
Install Claude Code

Open a terminal on your computer and paste this command:

2
Install the skill

This downloads the skill with all its files to your computer:

Add -g at the end to make it available in all your projects.

3
Run it

Start Claude Code, then type the command:

then
View source on GitHub
Use on ElasticFlow
Team and collaboration features

Run skills from your browser. Share results, manage access, collaborate with your team. No terminal needed.

Free 14-day trial. Cancel anytime.

View on GitHub

Win/Loss Reason Extraction

You are an expert in building sales bots that automatically extract and categorize reasons why deals are won or lost. Your goal is to help developers create systems that learn from outcomes to improve future performance.

Why Win/Loss Extraction Matters

The Knowledge Gap

Without extraction:
- "Why did we lose?" "I don't know"
- Same mistakes repeated
- No pattern visibility
- Intuition-based strategy

With extraction:
- Categorized loss reasons
- Trend identification
- Data-driven improvement
- Actionable insights

Win Reason Categories

Common Win Reasons

WIN_REASONS = {
    "product_fit": {
        "keywords": ["exactly what we need", "perfect fit", "solves our problem"],
        "indicators": ["feature_match_high", "use_case_alignment"]
    },
    "price_value": {
        "keywords": ["fair price", "good value", "worth it", "roi makes sense"],
        "indicators": ["price_accepted", "roi_discussed_positively"]
    },
    "trust_relationship": {
        "keywords": ["trust you", "great to work with", "understood us"],
        "indicators": ["high_engagement", "personal_connection"]
    },
    "competitive_advantage": {
        "keywords": ["better than", "chose you over", "differentiated"],
        "indicators": ["competitor_comparison_won", "unique_feature_mentioned"]
    },
    "timing": {
        "keywords": ["right time", "urgent need", "perfect timing"],
        "indicators": ["fast_sales_cycle", "urgency_expressed"]
    },
    "champion_advocacy": {
        "keywords": ["fought for", "convinced the team", "my recommendation"],
        "indicators": ["strong_champion", "internal_selling"]
    }
}

Loss Reason Categories

Common Loss Reasons

LOSS_REASONS = {
    "price": {
        "keywords": ["too expensive", "over budget", "cheaper option", "can't afford"],
        "indicators": ["price_objection", "budget_constraints"],
        "severity": "high"
    },
    "timing": {
        "keywords": ["bad timing", "not now", "maybe later", "next year"],
        "indicators": ["timing_objection", "delayed_decision"],
        "severity": "medium"
    },
    "competitor": {
        "keywords": ["went with", "chose", "competitor won", "using [competitor]"],
        "indicators": ["competitor_mentioned", "comparison_lost"],
        "severity": "high"
    },
    "no_decision": {
        "keywords": ["doing nothing", "staying with current", "not a priority"],
        "indicators": ["stalled", "no_urgency"],
        "severity": "medium"
    },
    "feature_gap": {
        "keywords": ["missing feature", "doesn't do", "need X that you don't have"],
        "indicators": ["feature_request_unmet", "requirement_gap"],
        "severity": "high"
    },
    "bad_fit": {
        "keywords": ["not right for us", "doesn't fit", "not what we need"],
        "indicators": ["poor_icp_match", "misaligned_use_case"],
        "severity": "medium"
    },
    "internal_issues": {
        "keywords": ["reorganizing", "merger", "budget freeze", "leadership change"],
        "indicators": ["external_factors", "company_change"],
        "severity": "low"
    },
    "lost_champion": {
        "keywords": ["contact left", "no longer there", "reporting changed"],
        "indicators": ["champion_departed", "stakeholder_change"],
        "severity": "medium"
    }
}

Extraction Methods

Conversation Analysis

def extract_reasons_from_conversation(conversation, outcome):
    reasons = []

    # Analyze final messages
    final_messages = conversation.messages[-5:]

    for message in final_messages:
        if message.sender == "prospect":
            # Check against reason keywords
            if outcome == "lost":
                matched_reasons = match_loss_reasons(message.text)
            else:
                matched_reasons = match_win_reasons(message.text)

            reasons.extend(matched_reasons)

    # Analyze conversation patterns
    pattern_reasons = extract_pattern_based_reasons(conversation, outcome)
    reasons.extend(pattern_reasons)

    # Dedupe and rank
    return rank_reasons(reasons)

def match_loss_reasons(text):
    matches = []
    for reason_code, config in LOSS_REASONS.items():
        for keyword in config["keywords"]:
            if keyword.lower() in text.lower():
                matches.append({
                    "reason": reason_code,
                    "confidence": 0.8,
                    "source": "keyword_match",
                    "evidence": text
                })
    return matches

Pattern-Based Extraction

def extract_pattern_based_reasons(conversation, outcome):
    reasons = []

    if outcome == "lost":
        # Price patterns
        if had_price_objection(conversation) and not resolved_price_objection(conversation):
            reasons.append({
                "reason": "price",
                "confidence": 0.7,
                "source": "pattern",
                "evidence": "Unresolved price objection"
            })

        # Competitor patterns
        competitor = detect_competitor_mention(conversation)
        if competitor and conversation.last_stage == "evaluation":
            reasons.append({
                "reason": "competitor",
                "confidence": 0.6,
                "source": "pattern",
                "evidence": f"Competitor {competitor} mentioned during evaluation"
            })

        # No decision patterns
        if conversation.days_stalled > 30:
            reasons.append({
                "reason": "no_decision",
                "confidence": 0.5,
                "source": "pattern",
                "evidence": f"Deal stalled {conversation.days_stalled} days"
            })

    return reasons

LLM-Based Extraction

def extract_reasons_with_llm(conversation, outcome):
    prompt = f"""
    Analyze this {outcome} sales conversation and identify the primary
    reason for the outcome.

    Conversation:
    {format_conversation(conversation)}

    Provide:
    1. Primary reason (select from: {list_reasons(outcome)})
    2. Secondary reason (if applicable)
    3. Confidence level (high/medium/low)
    4. Evidence from conversation

    Format as JSON.
    """

    response = llm.generate(prompt)
    return parse_reason_response(response)

Reason Validation

Confidence Scoring

def calculate_reason_confidence(reason, conversation):
    confidence = 0.5  # Base confidence

    # Direct statement bonus
    if reason["source"] == "explicit_statement":
        confidence += 0.3

    # Multiple signals bonus
    signal_count = count_supporting_signals(reason, conversation)
    confidence += min(signal_count * 0.1, 0.3)

    # Recency bonus (recent statements more reliable)
    if reason.get("message_index") and reason["message_index"] >= len(conversation.messages) - 3:
        confidence += 0.1

    # Cross-reference with outcome survey
    if matches_survey_response(reason, conversation.deal_id):
        confidence += 0.2

    return min(confidence, 1.0)

Human Validation Loop

def queue_for_validation(deal_id, extracted_reasons):
    """Flag uncertain extractions for human review"""

    needs_review = []
    for reason in extracted_reasons:
        if reason["confidence"] < 0.7:
            needs_review.append(reason)

    if needs_review:
        create_review_task(
            deal_id=deal_id,
            reasons=needs_review,
            priority="medium" if len(needs_review) == 1 else "high"
        )

Aggregation & Analysis

Trend Analysis

def analyze_loss_trends(time_period):
    losses = get_lost_deals(time_period)

    # Count by reason
    reason_counts = Counter()
    for deal in losses:
        for reason in deal.loss_reasons:
            reason_counts[reason["reason"]] += 1

    # Calculate percentages
    total = len(losses)
    reason_pcts = {r: c/total for r, c in reason_counts.items()}

    # Compare to previous period
    prev_period = get_previous_period(time_period)
    prev_reason_pcts = analyze_loss_trends(prev_period)

    # Identify significant changes
    trends = {}
    for reason, pct in reason_pcts.items():
        prev_pct = prev_reason_pcts.get(reason, 0)
        change = pct - prev_pct
        if abs(change) > 0.05:  # >5% change
            trends[reason] = {
                "current": pct,
                "previous": prev_pct,
                "change": change,
                "direction": "increasing" if change > 0 else "decreasing"
            }

    return {
        "distribution": reason_pcts,
        "trends": trends,
        "top_reasons": sorted(reason_pcts.items(), key=lambda x: -x[1])[:5]
    }

Segmented Analysis

def analyze_by_segment(time_period):
    """Analyze win/loss reasons by segment"""

    segments = ["smb", "mid_market", "enterprise"]
    analysis = {}

    for segment in segments:
        deals = get_deals(time_period, segment=segment)
        won = [d for d in deals if d.outcome == "won"]
        lost = [d for d in deals if d.outcome == "lost"]

        analysis[segment] = {
            "win_rate": len(won) / len(deals) if deals else 0,
            "top_win_reasons": get_top_reasons(won, "win"),
            "top_loss_reasons": get_top_reasons(lost, "loss"),
            "deal_count": len(deals)
        }

    return analysis

Competitor Analysis

def analyze_competitor_losses(time_period):
    """Analyze losses to specific competitors"""

    competitor_losses = get_deals(
        time_period,
        outcome="lost",
        reason="competitor"
    )

    by_competitor = {}
    for deal in competitor_losses:
        competitor = deal.competitor_name
        if competitor not in by_competitor:
            by_competitor[competitor] = {
                "count": 0,
                "reasons": [],
                "deal_sizes": []
            }

        by_competitor[competitor]["count"] += 1
        by_competitor[competitor]["reasons"].extend(deal.loss_details)
        by_competitor[competitor]["deal_sizes"].append(deal.deal_size)

    # Summarize
    for competitor in by_competitor:
        by_competitor[competitor]["avg_deal_size"] = mean(
            by_competitor[competitor]["deal_sizes"]
        )
        by_competitor[competitor]["common_reasons"] = Counter(
            by_competitor[competitor]["reasons"]
        ).most_common(3)

    return by_competitor

Actionable Insights

Insight Generation

def generate_win_loss_insights(analysis):
    insights = []

    # Price insight
    if analysis["top_loss_reasons"][0][0] == "price":
        insights.append({
            "type": "alert",
            "topic": "pricing",
            "insight": f"Price is top loss reason at {analysis['top_loss_reasons'][0][1]:.0%}",
            "recommendation": "Review pricing strategy or value communication"
        })

    # Competitor insight
    competitor_losses = [r for r in analysis["distribution"] if r.startswith("competitor_")]
    if sum(analysis["distribution"].get(r, 0) for r in competitor_losses) > 0.3:
        insights.append({
            "type": "alert",
            "topic": "competitive",
            "insight": "Over 30% of losses to competitors",
            "recommendation": "Update competitive positioning and battlecards"
        })

    # Win insight
    if analysis.get("win_analysis", {}).get("top_win_reasons", []):
        top_win = analysis["win_analysis"]["top_win_reasons"][0]
        insights.append({
            "type": "positive",
            "topic": "winning",
            "insight": f"Primary win driver: {top_win[0]} ({top_win[1]:.0%})",
            "recommendation": f"Emphasize {top_win[0]} in messaging"
        })

    return insights

Integration

CRM Updates

def update_crm_with_reasons(deal_id, reasons):
    """Update CRM with extracted reasons"""

    primary_reason = reasons[0] if reasons else None
    secondary_reason = reasons[1] if len(reasons) > 1 else None

    crm_update = {
        "Loss_Reason__c": primary_reason["reason"] if primary_reason else None,
        "Loss_Reason_Secondary__c": secondary_reason["reason"] if secondary_reason else None,
        "Loss_Details__c": "; ".join([r["evidence"] for r in reasons]),
        "Competitor_Lost_To__c": extract_competitor(reasons),
        "Reason_Confidence__c": primary_reason["confidence"] if primary_reason else None
    }

    crm_client.update_opportunity(deal_id, crm_update)

Reference documents


name: win-loss-reason-extraction description: When the user wants to build or improve a sales bot's ability to automatically categorize why deals closed or died. Also use when the user mentions "win/loss analysis," "deal outcome," "loss reason," "closed reason," or "deal categorization."

Win/Loss Reason Extraction

You are an expert in building sales bots that automatically extract and categorize reasons why deals are won or lost. Your goal is to help developers create systems that learn from outcomes to improve future performance.

Why Win/Loss Extraction Matters

The Knowledge Gap

Without extraction:
- "Why did we lose?" "I don't know"
- Same mistakes repeated
- No pattern visibility
- Intuition-based strategy

With extraction:
- Categorized loss reasons
- Trend identification
- Data-driven improvement
- Actionable insights

Win Reason Categories

Common Win Reasons

WIN_REASONS = {
    "product_fit": {
        "keywords": ["exactly what we need", "perfect fit", "solves our problem"],
        "indicators": ["feature_match_high", "use_case_alignment"]
    },
    "price_value": {
        "keywords": ["fair price", "good value", "worth it", "roi makes sense"],
        "indicators": ["price_accepted", "roi_discussed_positively"]
    },
    "trust_relationship": {
        "keywords": ["trust you", "great to work with", "understood us"],
        "indicators": ["high_engagement", "personal_connection"]
    },
    "competitive_advantage": {
        "keywords": ["better than", "chose you over", "differentiated"],
        "indicators": ["competitor_comparison_won", "unique_feature_mentioned"]
    },
    "timing": {
        "keywords": ["right time", "urgent need", "perfect timing"],
        "indicators": ["fast_sales_cycle", "urgency_expressed"]
    },
    "champion_advocacy": {
        "keywords": ["fought for", "convinced the team", "my recommendation"],
        "indicators": ["strong_champion", "internal_selling"]
    }
}

Loss Reason Categories

Common Loss Reasons

LOSS_REASONS = {
    "price": {
        "keywords": ["too expensive", "over budget", "cheaper option", "can't afford"],
        "indicators": ["price_objection", "budget_constraints"],
        "severity": "high"
    },
    "timing": {
        "keywords": ["bad timing", "not now", "maybe later", "next year"],
        "indicators": ["timing_objection", "delayed_decision"],
        "severity": "medium"
    },
    "competitor": {
        "keywords": ["went with", "chose", "competitor won", "using [competitor]"],
        "indicators": ["competitor_mentioned", "comparison_lost"],
        "severity": "high"
    },
    "no_decision": {
        "keywords": ["doing nothing", "staying with current", "not a priority"],
        "indicators": ["stalled", "no_urgency"],
        "severity": "medium"
    },
    "feature_gap": {
        "keywords": ["missing feature", "doesn't do", "need X that you don't have"],
        "indicators": ["feature_request_unmet", "requirement_gap"],
        "severity": "high"
    },
    "bad_fit": {
        "keywords": ["not right for us", "doesn't fit", "not what we need"],
        "indicators": ["poor_icp_match", "misaligned_use_case"],
        "severity": "medium"
    },
    "internal_issues": {
        "keywords": ["reorganizing", "merger", "budget freeze", "leadership change"],
        "indicators": ["external_factors", "company_change"],
        "severity": "low"
    },
    "lost_champion": {
        "keywords": ["contact left", "no longer there", "reporting changed"],
        "indicators": ["champion_departed", "stakeholder_change"],
        "severity": "medium"
    }
}

Extraction Methods

Conversation Analysis

def extract_reasons_from_conversation(conversation, outcome):
    reasons = []

    # Analyze final messages
    final_messages = conversation.messages[-5:]

    for message in final_messages:
        if message.sender == "prospect":
            # Check against reason keywords
            if outcome == "lost":
                matched_reasons = match_loss_reasons(message.text)
            else:
                matched_reasons = match_win_reasons(message.text)

            reasons.extend(matched_reasons)

    # Analyze conversation patterns
    pattern_reasons = extract_pattern_based_reasons(conversation, outcome)
    reasons.extend(pattern_reasons)

    # Dedupe and rank
    return rank_reasons(reasons)

def match_loss_reasons(text):
    matches = []
    for reason_code, config in LOSS_REASONS.items():
        for keyword in config["keywords"]:
            if keyword.lower() in text.lower():
                matches.append({
                    "reason": reason_code,
                    "confidence": 0.8,
                    "source": "keyword_match",
                    "evidence": text
                })
    return matches

Pattern-Based Extraction

def extract_pattern_based_reasons(conversation, outcome):
    reasons = []

    if outcome == "lost":
        # Price patterns
        if had_price_objection(conversation) and not resolved_price_objection(conversation):
            reasons.append({
                "reason": "price",
                "confidence": 0.7,
                "source": "pattern",
                "evidence": "Unresolved price objection"
            })

        # Competitor patterns
        competitor = detect_competitor_mention(conversation)
        if competitor and conversation.last_stage == "evaluation":
            reasons.append({
                "reason": "competitor",
                "confidence": 0.6,
                "source": "pattern",
                "evidence": f"Competitor {competitor} mentioned during evaluation"
            })

        # No decision patterns
        if conversation.days_stalled > 30:
            reasons.append({
                "reason": "no_decision",
                "confidence": 0.5,
                "source": "pattern",
                "evidence": f"Deal stalled {conversation.days_stalled} days"
            })

    return reasons

LLM-Based Extraction

def extract_reasons_with_llm(conversation, outcome):
    prompt = f"""
    Analyze this {outcome} sales conversation and identify the primary
    reason for the outcome.

    Conversation:
    {format_conversation(conversation)}

    Provide:
    1. Primary reason (select from: {list_reasons(outcome)})
    2. Secondary reason (if applicable)
    3. Confidence level (high/medium/low)
    4. Evidence from conversation

    Format as JSON.
    """

    response = llm.generate(prompt)
    return parse_reason_response(response)

Reason Validation

Confidence Scoring

def calculate_reason_confidence(reason, conversation):
    confidence = 0.5  # Base confidence

    # Direct statement bonus
    if reason["source"] == "explicit_statement":
        confidence += 0.3

    # Multiple signals bonus
    signal_count = count_supporting_signals(reason, conversation)
    confidence += min(signal_count * 0.1, 0.3)

    # Recency bonus (recent statements more reliable)
    if reason.get("message_index") and reason["message_index"] >= len(conversation.messages) - 3:
        confidence += 0.1

    # Cross-reference with outcome survey
    if matches_survey_response(reason, conversation.deal_id):
        confidence += 0.2

    return min(confidence, 1.0)

Human Validation Loop

def queue_for_validation(deal_id, extracted_reasons):
    """Flag uncertain extractions for human review"""

    needs_review = []
    for reason in extracted_reasons:
        if reason["confidence"] < 0.7:
            needs_review.append(reason)

    if needs_review:
        create_review_task(
            deal_id=deal_id,
            reasons=needs_review,
            priority="medium" if len(needs_review) == 1 else "high"
        )

Aggregation & Analysis

Trend Analysis

def analyze_loss_trends(time_period):
    losses = get_lost_deals(time_period)

    # Count by reason
    reason_counts = Counter()
    for deal in losses:
        for reason in deal.loss_reasons:
            reason_counts[reason["reason"]] += 1

    # Calculate percentages
    total = len(losses)
    reason_pcts = {r: c/total for r, c in reason_counts.items()}

    # Compare to previous period
    prev_period = get_previous_period(time_period)
    prev_reason_pcts = analyze_loss_trends(prev_period)

    # Identify significant changes
    trends = {}
    for reason, pct in reason_pcts.items():
        prev_pct = prev_reason_pcts.get(reason, 0)
        change = pct - prev_pct
        if abs(change) > 0.05:  # >5% change
            trends[reason] = {
                "current": pct,
                "previous": prev_pct,
                "change": change,
                "direction": "increasing" if change > 0 else "decreasing"
            }

    return {
        "distribution": reason_pcts,
        "trends": trends,
        "top_reasons": sorted(reason_pcts.items(), key=lambda x: -x[1])[:5]
    }

Segmented Analysis

def analyze_by_segment(time_period):
    """Analyze win/loss reasons by segment"""

    segments = ["smb", "mid_market", "enterprise"]
    analysis = {}

    for segment in segments:
        deals = get_deals(time_period, segment=segment)
        won = [d for d in deals if d.outcome == "won"]
        lost = [d for d in deals if d.outcome == "lost"]

        analysis[segment] = {
            "win_rate": len(won) / len(deals) if deals else 0,
            "top_win_reasons": get_top_reasons(won, "win"),
            "top_loss_reasons": get_top_reasons(lost, "loss"),
            "deal_count": len(deals)
        }

    return analysis

Competitor Analysis

def analyze_competitor_losses(time_period):
    """Analyze losses to specific competitors"""

    competitor_losses = get_deals(
        time_period,
        outcome="lost",
        reason="competitor"
    )

    by_competitor = {}
    for deal in competitor_losses:
        competitor = deal.competitor_name
        if competitor not in by_competitor:
            by_competitor[competitor] = {
                "count": 0,
                "reasons": [],
                "deal_sizes": []
            }

        by_competitor[competitor]["count"] += 1
        by_competitor[competitor]["reasons"].extend(deal.loss_details)
        by_competitor[competitor]["deal_sizes"].append(deal.deal_size)

    # Summarize
    for competitor in by_competitor:
        by_competitor[competitor]["avg_deal_size"] = mean(
            by_competitor[competitor]["deal_sizes"]
        )
        by_competitor[competitor]["common_reasons"] = Counter(
            by_competitor[competitor]["reasons"]
        ).most_common(3)

    return by_competitor

Actionable Insights

Insight Generation

def generate_win_loss_insights(analysis):
    insights = []

    # Price insight
    if analysis["top_loss_reasons"][0][0] == "price":
        insights.append({
            "type": "alert",
            "topic": "pricing",
            "insight": f"Price is top loss reason at {analysis['top_loss_reasons'][0][1]:.0%}",
            "recommendation": "Review pricing strategy or value communication"
        })

    # Competitor insight
    competitor_losses = [r for r in analysis["distribution"] if r.startswith("competitor_")]
    if sum(analysis["distribution"].get(r, 0) for r in competitor_losses) > 0.3:
        insights.append({
            "type": "alert",
            "topic": "competitive",
            "insight": "Over 30% of losses to competitors",
            "recommendation": "Update competitive positioning and battlecards"
        })

    # Win insight
    if analysis.get("win_analysis", {}).get("top_win_reasons", []):
        top_win = analysis["win_analysis"]["top_win_reasons"][0]
        insights.append({
            "type": "positive",
            "topic": "winning",
            "insight": f"Primary win driver: {top_win[0]} ({top_win[1]:.0%})",
            "recommendation": f"Emphasize {top_win[0]} in messaging"
        })

    return insights

Integration

CRM Updates

def update_crm_with_reasons(deal_id, reasons):
    """Update CRM with extracted reasons"""

    primary_reason = reasons[0] if reasons else None
    secondary_reason = reasons[1] if len(reasons) > 1 else None

    crm_update = {
        "Loss_Reason__c": primary_reason["reason"] if primary_reason else None,
        "Loss_Reason_Secondary__c": secondary_reason["reason"] if secondary_reason else None,
        "Loss_Details__c": "; ".join([r["evidence"] for r in reasons]),
        "Competitor_Lost_To__c": extract_competitor(reasons),
        "Reason_Confidence__c": primary_reason["confidence"] if primary_reason else None
    }

    crm_client.update_opportunity(deal_id, crm_update)
ElasticFlow

Transform your business with AI-powered workflow automation. One unified platform for all your enterprise needs.

Follow us

Platform

  • Features
  • Benefits
  • Use Cases
  • Workflow Library

Use Cases

  • Sales
  • Marketing
  • Finance & Legal
  • HR

Catalogue

  • Departments
  • Roles
  • Tools
  • Metrics
  • Platforms

Growth

  • Referral Program
  • Partners

Legal

  • Privacy Policy
  • Terms of Service
  • Cookie Policy
  • Acceptable Use
  • Security
  • SLA

© 2026 ElasticFlow. All rights reserved.