Skip to content
Back to skills

Saas Metrics Reporting

ASecurity

Use when tracking and reporting SaaS business metrics.

  • 2 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 10, 2026
businesspythongogit

Security analysis

A100/100

Scanned September 10, 2026

npx -y skills add LoopyLuci/Skills --skill saas-metrics-reporting --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Saas Metrics Reporting?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Saas Metrics Reporting
[![Security: A β€” Skills Directory](https://www.skillsdirectory.com/api/skills/loopyluci-saas-metrics-reporting/badge)](https://www.skillsdirectory.com/skills/loopyluci-saas-metrics-reporting)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: saas-metrics-reporting
description: "Use when tracking and reporting SaaS business metrics."
version: 1.0.0
author: Hermes Agent
license: MIT
metadata:
  hermes:
    tags: [saas, metrics, mrr, churn, LTV, CAC, subscription, reporting, board-deck]
    related_skills: [business-metrics-kpis, crm-sales-pipeline, customer-success-retention, digital-marketing-strategy]
---

# SaaS Metrics and Reporting

Tracking, analyzing, and reporting SaaS business metrics β€” from recurring revenue and churn through unit economics, cohort analysis, and board reporting.

## When to Use

- Building a SaaS metrics dashboard
- Preparing board decks and investor updates
- Analyzing subscription revenue and churn
- Calculating unit economics (LTV, CAC, payback)
- Running cohort analysis for retention

## Core SaaS Metrics

```python
from typing import Dict, List, Optional
from datetime import datetime, timedelta
from collections import defaultdict

class SaaS_Metrics:
    """Core SaaS metric calculations."""
    
    @staticmethod
    def calculate_mrr(subscriptions: List[Dict]) -> Dict:
        """Calculate Monthly Recurring Revenue."""
        new_mrr = 0
        churn_mrr = 0
        expansion_mrr = 0
        contraction_mrr = 0
        
        for sub in subscriptions:
            if sub.get('type') == 'new':
                new_mrr += sub['amount']
            elif sub.get('type') == 'churn':
                churn_mrr += sub['amount']
            elif sub.get('type') == 'upgrade':
                expansion_mrr += sub.get('delta', sub['amount'])
            elif sub.get('type') == 'downgrade':
                contraction_mrr += abs(sub.get('delta', sub['amount']))
        
        net_new_mrr = new_mrr + expansion_mrr - churn_mrr - contraction_mrr
        
        return {
            'new_mrr': new_mrr,
            'churn_mrr': churn_mrr,
            'expansion_mrr': expansion_mrr,
            'contraction_mrr': contraction_mrr,
            'net_new_mrr': net_new_mrr,
        }
    
    @staticmethod
    def churn_rates(customers: List[Dict], period_days: int = 30) -> Dict:
        """Calculate logo and revenue churn."""
        start_count = len(customers)
        start_mrr = sum(c.get('mrr', 0) for c in customers)
        
        churned = [c for c in customers if c.get('churned_in_period', False)]
        churn_count = len(churned)
        churn_mrr = sum(c.get('mrr', 0) for c in churned)
        
        logo_churn = churn_count / max(start_count, 1) * 100
        revenue_churn = churn_mrr / max(start_mrr, 1) * 100
        
        return {
            'logo_churn_rate': round(logo_churn, 2),
            'revenue_churn_rate': round(revenue_churn, 2),
            'logo_churn_count': churn_count,
            'revenue_churn_amount': churn_mrr,
            'annualized_logo_churn': round((1 - (1 - logo_churn/100) ** (365/period_days)) * 100, 2),
        }
    
    @staticmethod
    def unit_economics(cac_data: Dict, ltv_data: Dict) -> Dict:
        """Calculate unit economics."""
        cac = cac_data.get('total_sales_marketing_spend', 0) / max(cac_data.get('new_customers', 1), 1)
        
        avg_revenue_per_user = ltv_data.get('avg_monthly_revenue', 0)
        gross_margin = ltv_data.get('gross_margin_pct', 0.7)
        churn_rate = ltv_data.get('monthly_churn_rate', 0.05)
        
        # LTV = ARPU * Gross Margin / Monthly Churn
        ltv = (avg_revenue_per_user * gross_margin) / max(churn_rate, 0.01)
        ltv_cac = ltv / max(cac, 1)
        
        # Payback period = CAC / (ARPU * Gross Margin)
        monthly_contribution = avg_revenue_per_user * gross_margin
        payback_months = cac / max(monthly_contribution, 1)
        
        return {
            'cac': round(cac, 2),
            'ltv': round(ltv, 2),
            'ltv_cac_ratio': round(ltv_cac, 2),
            'payback_months': round(payback_months, 1),
            'health': 'Excellent' if ltv_cac >= 5 else 'Good' if ltv_cac >= 3 else 'Needs improvement' if ltv_cac >= 1 else 'Unhealthy',
        }
```

## Cohort Analysis

```python
class CohortAnalyzer:
    """Run retention cohort analysis."""
    
    @staticmethod
    def retention_cohorts(subscriptions: List[Dict]) -> Dict:
        """Calculate retention by monthly cohorts."""
        cohorts = defaultdict(lambda: {'total': 0, 'periods': {}})
        
        for sub in subscriptions:
            cohort_key = sub.get('signup_month', 'unknown')
            period = sub.get('month_since_signup', 0)
            
            cohorts[cohort_key]['total'] += 1
            if period not in cohorts[cohort_key]['periods']:
                cohorts[cohort_key]['periods'][period] = 0
            cohorts[cohort_key]['periods'][period] += 1
        
        # Convert to retention percentages
        cohort_table = {}
        for month, data in sorted(cohorts.items()):
            total = data['total']
            rates = {}
            for period in sorted(data['periods'].keys()):
                rates[period] = round(data['periods'][period] / max(total, 1) * 100, 1)
            cohort_table[month] = rates
        
        return cohort_table
```

## Board Deck Generator

```python
def generate_board_slide(metrics: Dict) -> str:
    """Generate a board update slide for a key metric."""
    slide = "πŸ“Š KPI: " + metrics.get('title', 'Metric') + "\n"
    slide += "=" * 40 + "\n"
    slide += f"Current: {metrics.get('current', 'N/A')}\n"
    slide += f"Previous: {metrics.get('previous', 'N/A')}\n"
    change = metrics.get('change_pct', 0)
    arrow = "β–²" if change > 0 else "β–Ό" if change < 0 else "β†’"
    slide += f"Change: {arrow} {abs(change)}%\n"
    slide += f"Target: {metrics.get('target', 'N/A')}\n"
    slide += f"Status: {'βœ… On track' if metrics.get('on_track', False) else '⚠️ Needs attention'}\n"
    slide += f"\n{metrics.get('commentary', '')}"
    return slide
```

## Common Pitfalls

1. **Ignoring contraction MRR** β€” expansions hide downgrades; track both
2. **Gross vs net revenue retention** β€” gross retention is what matters; net can be misleading
3. **Cohort analysis not segmented** β€” aggregate retention hides different behaviors by segment
4. **CAC payback period too long** β€” >18 months payback is risky for VC-backed SaaS
5. **nrr vs grr confusion** β€” Net Revenue Retention includes upsells; Gross does not
6. **Not using SaaS benchmarks** β€” a metric in isolation is meaningless; compare by stage and industry

## Verification Checklist

- [ ] MRR tracked (new, churn, expansion, contraction)
- [ ] Logo and revenue churn rates calculated
- [ ] LTV and CAC calculated with clear definitions
- [ ] LTV:CAC ratio β‰₯ 3
- [ ] Monthly cohort retention analyzed
- [ ] Board deck template with standard SaaS metrics
- [ ] SaaS benchmarks comparison
- [ ] Dashboard automated (not manual spreadsheets)

## See Also

- business-metrics-kpis β€” general business KPI framework
- crm-sales-pipeline β€” pipeline-to-revenue metrics
- customer-success-retention β€” churn reduction strategies
- digital-marketing-strategy β€” CAC optimization

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…