1 min read
AutomatisierungCustomer 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
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
| Area | Recommendation |
|---|---|
| **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:
- Health Scores: Frühzeitige Risiko-Erkennung
- Lifecycle Emails: Skalierbare Kommunikation
- Churn Prediction: Proaktive Intervention
- NPS: Kontinuierliches Feedback
Automatisierung skaliert Customer Success ohne Qualitätsverlust.
Bildprompts
- "Customer health score dashboard, gauge visualization with risk levels"
- "Lifecycle email automation flow, triggered sequences"
- "Churn prediction model, risk factors highlighted"