# 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 ```typescript // 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 { 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 { 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 { 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 ```typescript // 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 { 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 { // 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 { 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 ```typescript // 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 { 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 { 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 ```tsx // 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(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 (
{step === 'score' ? ( <>

How likely are you to recommend us?

{Array.from({ length: 11 }, (_, i) => ( ))}
Not at all likely Extremely likely
) : ( <>

{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?"}