Menu
Zurück zum Blog
1 min read
Automatisierung

Customer Success Automation

Customer Success automatisieren. Health Scores, Lifecycle Emails, Churn Prediction und Proactive Outreach für SaaS.

Customer SuccessHealth ScoreChurn PredictionLifecycle MarketingCustomer RetentionAutomation
Customer Success Automation

Customer Success Automation

Meta-Description: Customer Success automatisieren. Health Scores, Lifecycle Emails, Churn Prediction und Proactive Outreach für SaaS.

Keywords: Customer Success, Health Score, Churn Prediction, Lifecycle Marketing, Customer Retention, Automation, NPS


Einführung

Customer Success ist der Schlüssel zu Retention und Expansion. Mit Automation können Health Scores berechnet, Risiko-Kunden identifiziert und proaktive Maßnahmen ergriffen werden – skalierbar für tausende Kunden.


Customer Success Overview

┌─────────────────────────────────────────────────────────────┐
│              CUSTOMER SUCCESS AUTOMATION                     │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Customer Health Score:                                     │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Score Components (0-100):                          │   │
│  │  ├── Product Usage (40%)                            │   │
│  │  │   ├── Login frequency                           │   │
│  │  │   ├── Feature adoption                          │   │
│  │  │   └── Active users/seats                        │   │
│  │  ├── Engagement (30%)                               │   │
│  │  │   ├── Support tickets                           │   │
│  │  │   ├── NPS score                                 │   │
│  │  │   └── Training completion                       │   │
│  │  └── Business Value (30%)                           │   │
│  │      ├── ROI achieved                              │   │
│  │      ├── Expansion potential                       │   │
│  │      └── Contract value                            │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  Lifecycle Stages:                                          │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Onboarding → Adoption → Growth → Renewal          │   │
│  │      │          │          │         │              │   │
│  │      ↓          ↓          ↓         ↓              │   │
│  │   Welcome    Training   Upsell   Review            │   │
│  │   Emails     Content    Offers   Campaign          │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  Risk Triggers:                                             │
│  ├── Login decrease > 50%                                  │
│  ├── Support tickets spike                                 │
│  ├── Key user departure                                    │
│  ├── Contract renewal in 60 days                          │
│  └── Low NPS score (< 7)                                   │
│                                                             │
│  Automated Actions:                                         │
│  ├── Email sequences                                       │
│  ├── In-app messages                                       │
│  ├── CSM alerts                                            │
│  ├── Scheduled calls                                       │
│  └── Personalized offers                                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Health Score Calculation

// lib/customer-success/health-score.ts
import { db } from '@/lib/db';
import { subDays, differenceInDays } from 'date-fns';

interface HealthScoreWeights {
  usage: number;
  engagement: number;
  business: number;
}

interface HealthScoreComponents {
  usage: {
    loginScore: number;
    featureAdoptionScore: number;
    activeUsersScore: number;
  };
  engagement: {
    supportScore: number;
    npsScore: number;
    trainingScore: number;
  };
  business: {
    contractValueScore: number;
    expansionScore: number;
    tenureScore: number;
  };
}

interface HealthScore {
  score: number;
  trend: 'up' | 'down' | 'stable';
  riskLevel: 'healthy' | 'attention' | 'at-risk' | 'critical';
  components: HealthScoreComponents;
  lastUpdated: Date;
}

const DEFAULT_WEIGHTS: HealthScoreWeights = {
  usage: 0.4,
  engagement: 0.3,
  business: 0.3
};

export async function calculateHealthScore(
  customerId: string,
  weights: HealthScoreWeights = DEFAULT_WEIGHTS
): Promise<HealthScore> {
  const now = new Date();
  const thirtyDaysAgo = subDays(now, 30);

  // Get customer data
  const customer = await db.customer.findUnique({
    where: { id: customerId },
    include: {
      subscription: true,
      users: true,
      supportTickets: {
        where: { createdAt: { gte: thirtyDaysAgo } }
      },
      npsResponses: {
        orderBy: { createdAt: 'desc' },
        take: 1
      }
    }
  });

  if (!customer) {
    throw new Error('Customer not found');
  }

  // Calculate usage scores
  const loginScore = await calculateLoginScore(customerId, thirtyDaysAgo);
  const featureAdoptionScore = await calculateFeatureAdoptionScore(customerId);
  const activeUsersScore = calculateActiveUsersScore(customer);

  const usageScore = (loginScore + featureAdoptionScore + activeUsersScore) / 3;

  // Calculate engagement scores
  const supportScore = calculateSupportScore(customer.supportTickets);
  const npsScore = calculateNPSHealthScore(customer.npsResponses[0]?.score);
  const trainingScore = await calculateTrainingScore(customerId);

  const engagementScore = (supportScore + npsScore + trainingScore) / 3;

  // Calculate business scores
  const contractValueScore = calculateContractValueScore(customer.subscription);
  const expansionScore = await calculateExpansionScore(customerId);
  const tenureScore = calculateTenureScore(customer.createdAt);

  const businessScore = (contractValueScore + expansionScore + tenureScore) / 3;

  // Weighted total
  const totalScore = Math.round(
    usageScore * weights.usage +
    engagementScore * weights.engagement +
    businessScore * weights.business
  );

  // Calculate trend
  const previousScore = await getPreviousHealthScore(customerId);
  const trend = getTrend(totalScore, previousScore);

  // Determine risk level
  const riskLevel = getRiskLevel(totalScore);

  const healthScore: HealthScore = {
    score: totalScore,
    trend,
    riskLevel,
    components: {
      usage: { loginScore, featureAdoptionScore, activeUsersScore },
      engagement: { supportScore, npsScore, trainingScore },
      business: { contractValueScore, expansionScore, tenureScore }
    },
    lastUpdated: now
  };

  // Store for history
  await db.healthScoreHistory.create({
    data: {
      customerId,
      score: totalScore,
      components: healthScore.components,
      calculatedAt: now
    }
  });

  return healthScore;
}

async function calculateLoginScore(
  customerId: string,
  since: Date
): Promise<number> {
  const logins = await db.userActivity.count({
    where: {
      user: { customerId },
      activityType: 'login',
      createdAt: { gte: since }
    }
  });

  // Expected: at least 20 logins per month for healthy usage
  return Math.min(100, (logins / 20) * 100);
}

async function calculateFeatureAdoptionScore(
  customerId: string
): Promise<number> {
  const totalFeatures = await db.feature.count({
    where: { isCore: true }
  });

  const usedFeatures = await db.featureUsage.groupBy({
    by: ['featureId'],
    where: {
      user: { customerId }
    }
  });

  return Math.round((usedFeatures.length / totalFeatures) * 100);
}

function calculateActiveUsersScore(customer: any): number {
  const totalSeats = customer.subscription?.seats || 1;
  const activeUsers = customer.users.filter((u: any) =>
    u.lastActiveAt && differenceInDays(new Date(), u.lastActiveAt) < 7
  ).length;

  return Math.round((activeUsers / totalSeats) * 100);
}

function calculateSupportScore(tickets: any[]): number {
  if (tickets.length === 0) return 100;

  // Fewer tickets = higher score, but some engagement is good
  const ticketCount = tickets.length;
  if (ticketCount <= 2) return 100;
  if (ticketCount <= 5) return 80;
  if (ticketCount <= 10) return 60;
  return 40;
}

function calculateNPSHealthScore(npsScore?: number): number {
  if (npsScore === undefined) return 50; // Neutral if no data
  if (npsScore >= 9) return 100;
  if (npsScore >= 7) return 75;
  if (npsScore >= 5) return 50;
  return 25;
}

function getRiskLevel(score: number): HealthScore['riskLevel'] {
  if (score >= 80) return 'healthy';
  if (score >= 60) return 'attention';
  if (score >= 40) return 'at-risk';
  return 'critical';
}

function getTrend(current: number, previous?: number): HealthScore['trend'] {
  if (!previous) return 'stable';
  const diff = current - previous;
  if (diff > 5) return 'up';
  if (diff < -5) return 'down';
  return 'stable';
}

Lifecycle Email Automation

// lib/customer-success/lifecycle.ts
import { db } from '@/lib/db';
import { addDays, differenceInDays } from 'date-fns';

interface LifecycleStage {
  id: string;
  name: string;
  triggers: TriggerCondition[];
  actions: Action[];
}

interface TriggerCondition {
  type: 'days_since_signup' | 'event' | 'health_score' | 'subscription';
  value: any;
}

interface Action {
  type: 'email' | 'in_app' | 'slack_notify' | 'task';
  template: string;
  delay?: number;
}

const lifecycleStages: LifecycleStage[] = [
  {
    id: 'onboarding',
    name: 'Onboarding',
    triggers: [{ type: 'days_since_signup', value: { min: 0, max: 14 } }],
    actions: [
      { type: 'email', template: 'welcome', delay: 0 },
      { type: 'email', template: 'getting-started', delay: 1 },
      { type: 'email', template: 'first-project', delay: 3 },
      { type: 'email', template: 'invite-team', delay: 7 },
      { type: 'email', template: 'onboarding-check-in', delay: 14 }
    ]
  },
  {
    id: 'adoption',
    name: 'Adoption',
    triggers: [
      { type: 'days_since_signup', value: { min: 14, max: 60 } },
      { type: 'event', value: 'onboarding_completed' }
    ],
    actions: [
      { type: 'email', template: 'feature-spotlight-1', delay: 0 },
      { type: 'email', template: 'success-story', delay: 7 },
      { type: 'email', template: 'feature-spotlight-2', delay: 14 },
      { type: 'email', template: 'tips-tricks', delay: 21 }
    ]
  },
  {
    id: 'growth',
    name: 'Growth',
    triggers: [
      { type: 'days_since_signup', value: { min: 60 } },
      { type: 'health_score', value: { min: 70 } }
    ],
    actions: [
      { type: 'email', template: 'upgrade-benefits', delay: 0 },
      { type: 'email', template: 'case-study', delay: 14 },
      { type: 'in_app', template: 'upgrade-prompt', delay: 7 }
    ]
  },
  {
    id: 'renewal',
    name: 'Renewal',
    triggers: [
      { type: 'subscription', value: 'renewal_in_60_days' }
    ],
    actions: [
      { type: 'email', template: 'renewal-reminder-60', delay: 0 },
      { type: 'task', template: 'csm-outreach', delay: 0 },
      { type: 'email', template: 'renewal-reminder-30', delay: 30 },
      { type: 'email', template: 'renewal-reminder-7', delay: 53 }
    ]
  },
  {
    id: 'at-risk',
    name: 'At Risk',
    triggers: [
      { type: 'health_score', value: { max: 40 } }
    ],
    actions: [
      { type: 'slack_notify', template: 'at-risk-alert', delay: 0 },
      { type: 'email', template: 'we-miss-you', delay: 1 },
      { type: 'task', template: 'csm-intervention', delay: 0 },
      { type: 'email', template: 'how-can-we-help', delay: 3 }
    ]
  }
];

// Process lifecycle for all customers
export async function processLifecycleAutomation(): Promise<void> {
  const customers = await db.customer.findMany({
    include: {
      subscription: true,
      healthScore: true,
      lifecycleState: true
    }
  });

  for (const customer of customers) {
    await processCustomerLifecycle(customer);
  }
}

async function processCustomerLifecycle(customer: any): Promise<void> {
  // Determine current stage
  const currentStage = determineLifecycleStage(customer);

  if (!currentStage) return;

  // Check if stage changed
  const previousStage = customer.lifecycleState?.stageId;

  if (currentStage.id !== previousStage) {
    // Update stage
    await db.customerLifecycleState.upsert({
      where: { customerId: customer.id },
      create: {
        customerId: customer.id,
        stageId: currentStage.id,
        enteredAt: new Date()
      },
      update: {
        stageId: currentStage.id,
        enteredAt: new Date()
      }
    });

    // Execute stage entry actions
    for (const action of currentStage.actions) {
      await scheduleAction(customer.id, action);
    }
  }
}

function determineLifecycleStage(customer: any): LifecycleStage | null {
  const daysSinceSignup = differenceInDays(new Date(), customer.createdAt);
  const healthScore = customer.healthScore?.score || 50;
  const renewalDays = customer.subscription?.currentPeriodEnd
    ? differenceInDays(customer.subscription.currentPeriodEnd, new Date())
    : null;

  // Check stages in priority order
  if (healthScore < 40) {
    return lifecycleStages.find(s => s.id === 'at-risk')!;
  }

  if (renewalDays !== null && renewalDays <= 60) {
    return lifecycleStages.find(s => s.id === 'renewal')!;
  }

  if (daysSinceSignup < 14) {
    return lifecycleStages.find(s => s.id === 'onboarding')!;
  }

  if (daysSinceSignup < 60) {
    return lifecycleStages.find(s => s.id === 'adoption')!;
  }

  return lifecycleStages.find(s => s.id === 'growth')!;
}

async function scheduleAction(
  customerId: string,
  action: Action
): Promise<void> {
  const scheduledFor = action.delay
    ? addDays(new Date(), action.delay)
    : new Date();

  await db.scheduledAction.create({
    data: {
      customerId,
      actionType: action.type,
      template: action.template,
      scheduledFor,
      status: 'pending'
    }
  });
}

Churn Prediction

// lib/customer-success/churn-prediction.ts
import { db } from '@/lib/db';

interface ChurnPrediction {
  customerId: string;
  churnProbability: number;
  riskFactors: RiskFactor[];
  recommendedActions: string[];
}

interface RiskFactor {
  factor: string;
  impact: 'high' | 'medium' | 'low';
  currentValue: number;
  threshold: number;
}

export async function predictChurn(customerId: string): Promise<ChurnPrediction> {
  const riskFactors: RiskFactor[] = [];
  let churnScore = 0;

  // Factor 1: Login frequency decline
  const loginDecline = await calculateLoginDecline(customerId);
  if (loginDecline > 50) {
    riskFactors.push({
      factor: 'Login frequency decreased',
      impact: 'high',
      currentValue: loginDecline,
      threshold: 50
    });
    churnScore += 25;
  }

  // Factor 2: Feature usage decline
  const featureDecline = await calculateFeatureUsageDecline(customerId);
  if (featureDecline > 30) {
    riskFactors.push({
      factor: 'Feature usage decreased',
      impact: 'medium',
      currentValue: featureDecline,
      threshold: 30
    });
    churnScore += 15;
  }

  // Factor 3: Support sentiment
  const supportSentiment = await analyzeSupportSentiment(customerId);
  if (supportSentiment < 0.3) {
    riskFactors.push({
      factor: 'Negative support interactions',
      impact: 'high',
      currentValue: supportSentiment,
      threshold: 0.3
    });
    churnScore += 20;
  }

  // Factor 4: NPS score
  const npsScore = await getLatestNPS(customerId);
  if (npsScore !== null && npsScore < 7) {
    riskFactors.push({
      factor: 'Low NPS score',
      impact: npsScore < 5 ? 'high' : 'medium',
      currentValue: npsScore,
      threshold: 7
    });
    churnScore += npsScore < 5 ? 25 : 15;
  }

  // Factor 5: Key user departure
  const keyUserLeft = await checkKeyUserDeparture(customerId);
  if (keyUserLeft) {
    riskFactors.push({
      factor: 'Key user left organization',
      impact: 'high',
      currentValue: 1,
      threshold: 0
    });
    churnScore += 20;
  }

  // Factor 6: Contract value change
  const contractDecreased = await checkContractDecrease(customerId);
  if (contractDecreased) {
    riskFactors.push({
      factor: 'Contract value decreased',
      impact: 'medium',
      currentValue: 1,
      threshold: 0
    });
    churnScore += 15;
  }

  const churnProbability = Math.min(100, churnScore);

  // Generate recommended actions
  const recommendedActions = generateRecommendations(riskFactors);

  return {
    customerId,
    churnProbability,
    riskFactors,
    recommendedActions
  };
}

function generateRecommendations(riskFactors: RiskFactor[]): string[] {
  const recommendations: string[] = [];

  const factorTypes = riskFactors.map(f => f.factor);

  if (factorTypes.includes('Login frequency decreased')) {
    recommendations.push('Schedule re-engagement call');
    recommendations.push('Send personalized win-back email');
  }

  if (factorTypes.includes('Feature usage decreased')) {
    recommendations.push('Offer feature training session');
    recommendations.push('Share relevant use case examples');
  }

  if (factorTypes.includes('Negative support interactions')) {
    recommendations.push('Escalate to senior support');
    recommendations.push('CSM personal outreach');
  }

  if (factorTypes.includes('Low NPS score')) {
    recommendations.push('Schedule NPS follow-up call');
    recommendations.push('Address specific feedback');
  }

  if (factorTypes.includes('Key user left organization')) {
    recommendations.push('Identify and onboard new champion');
    recommendations.push('Executive sponsor outreach');
  }

  return recommendations;
}

// Batch process all customers
export async function runChurnPredictions(): Promise<void> {
  const customers = await db.customer.findMany({
    where: { subscription: { status: 'active' } }
  });

  for (const customer of customers) {
    const prediction = await predictChurn(customer.id);

    // Store prediction
    await db.churnPrediction.upsert({
      where: { customerId: customer.id },
      create: {
        customerId: customer.id,
        probability: prediction.churnProbability,
        riskFactors: prediction.riskFactors,
        predictedAt: new Date()
      },
      update: {
        probability: prediction.churnProbability,
        riskFactors: prediction.riskFactors,
        predictedAt: new Date()
      }
    });

    // Alert if high risk
    if (prediction.churnProbability > 70) {
      await createChurnAlert(customer.id, prediction);
    }
  }
}

NPS Survey Automation

// components/NPS/NPSSurvey.tsx
'use client';

import { useState } from 'react';

interface NPSSurveyProps {
  onSubmit: (score: number, feedback?: string) => void;
  onDismiss: () => void;
}

export function NPSSurvey({ onSubmit, onDismiss }: NPSSurveyProps) {
  const [score, setScore] = useState<number | null>(null);
  const [feedback, setFeedback] = useState('');
  const [step, setStep] = useState<'score' | 'feedback'>('score');

  const handleScoreSelect = (value: number) => {
    setScore(value);
    setStep('feedback');
  };

  const handleSubmit = () => {
    if (score !== null) {
      onSubmit(score, feedback || undefined);
    }
  };

  return (
    <div className="fixed bottom-4 right-4 w-96 bg-white rounded-lg shadow-xl p-6">
      <button
        onClick={onDismiss}
        className="absolute top-2 right-2 text-gray-400"
      >
        ×
      </button>

      {step === 'score' ? (
        <>
          <h3 className="text-lg font-semibold mb-4">
            How likely are you to recommend us?
          </h3>

          <div className="flex gap-1 mb-4">
            {Array.from({ length: 11 }, (_, i) => (
              <button
                key={i}
                onClick={() => handleScoreSelect(i)}
                className={`w-8 h-8 rounded text-sm font-medium transition ${
                  i <= 6 ? 'hover:bg-red-100' :
                  i <= 8 ? 'hover:bg-yellow-100' : 'hover:bg-green-100'
                } ${score === i ? 'ring-2 ring-blue-500' : 'bg-gray-100'}`}
              >
                {i}
              </button>
            ))}
          </div>

          <div className="flex justify-between text-xs text-gray-500">
            <span>Not at all likely</span>
            <span>Extremely likely</span>
          </div>
        </>
      ) : (
        <>
          <h3 className="text-lg font-semibold mb-2">
            {score! >= 9 ? "That's great! What do you love most?" :
             score! >= 7 ? "Thanks! How can we improve?" :
             "We're sorry to hear that. What went wrong?"}
          </h3>

          <textarea
            value={feedback}
            onChange={(e) => setFeedback(e.target.value)}
            placeholder="Your feedback helps us improve..."
            className="w-full p-3 border rounded-lg mb-4"
            rows={3}
          />

          <div className="flex gap-2">
            <button
              onClick={() => setStep('score')}
              className="px-4 py-2 text-gray-600"
            >
              Back
            </button>
            <button
              onClick={handleSubmit}
              className="flex-1 px-4 py-2 bg-blue-600 text-white rounded-lg"
            >
              Submit
            </button>
          </div>
        </>
      )}
    </div>
  );
}

Best Practices

AreaRecommendation
**Health Score**Update daily, weight factors appropriately
**Lifecycle**Personalize based on segment and behavior
**Churn**Act on predictions within 48 hours
**NPS**Survey at key moments, not randomly
**Automation**Always allow human override

Fazit

Customer Success Automation ermöglicht:

  1. Health Scores: Frühzeitige Risiko-Erkennung
  2. Lifecycle Emails: Skalierbare Kommunikation
  3. Churn Prediction: Proaktive Intervention
  4. NPS: Kontinuierliches Feedback

Automatisierung skaliert Customer Success ohne Qualitätsverlust.


Bildprompts

  1. "Customer health score dashboard, gauge visualization with risk levels"
  2. "Lifecycle email automation flow, triggered sequences"
  3. "Churn prediction model, risk factors highlighted"

Quellen