n8n Customer Onboarding Automation: Building End-to-End Onboarding Workflows
Effective customer onboarding drives retention and satisfaction. This guide shows how to build comprehensive onboarding automation with n8n for seamless customer experiences.
Onboarding Workflow Architecture
New Customer Trigger Handler
// n8n Function Node - New Customer Processor
const webhookData = $input.first().json;
// Validate incoming customer data
const requiredFields = ['email', 'name', 'plan'];
const missingFields = requiredFields.filter(f => !webhookData[f]);
if (missingFields.length > 0) {
throw new Error(`Missing required fields: ${missingFields.join(', ')}`);
}
// Normalize and enrich customer data
const customer = {
id: webhookData.customer_id || `cust_${Date.now()}`,
email: webhookData.email.toLowerCase().trim(),
firstName: extractFirstName(webhookData.name),
lastName: extractLastName(webhookData.name),
fullName: webhookData.name,
company: webhookData.company || null,
plan: webhookData.plan,
planTier: determinePlanTier(webhookData.plan),
source: webhookData.source || 'website',
signupDate: new Date().toISOString(),
timezone: webhookData.timezone || 'UTC',
locale: webhookData.locale || 'en',
metadata: {
utm_source: webhookData.utm_source,
utm_medium: webhookData.utm_medium,
utm_campaign: webhookData.utm_campaign,
referrer: webhookData.referrer
}
};
// Determine onboarding path
const onboardingPath = determineOnboardingPath(customer);
function extractFirstName(fullName) {
return fullName.split(' ')[0];
}
function extractLastName(fullName) {
const parts = fullName.split(' ');
return parts.length > 1 ? parts.slice(1).join(' ') : '';
}
function determinePlanTier(plan) {
const tiers = {
'starter': 'basic',
'professional': 'standard',
'business': 'premium',
'enterprise': 'enterprise'
};
return tiers[plan.toLowerCase()] || 'basic';
}
function determineOnboardingPath(customer) {
// Enterprise customers get white-glove onboarding
if (customer.planTier === 'enterprise') {
return {
type: 'enterprise',
steps: ['sales_handoff', 'kickoff_call', 'custom_setup', 'training', 'go_live'],
assignedCSM: true,
duration: '30_days'
};
}
// Premium gets guided onboarding
if (customer.planTier === 'premium') {
return {
type: 'guided',
steps: ['welcome', 'setup_wizard', 'demo_call', 'training_videos', 'check_in'],
assignedCSM: false,
duration: '14_days'
};
}
// Standard self-service onboarding
return {
type: 'self_service',
steps: ['welcome', 'setup_wizard', 'email_sequence', 'in_app_guides'],
assignedCSM: false,
duration: '7_days'
};
}
return {
json: {
customer,
onboarding: {
path: onboardingPath,
startedAt: new Date().toISOString(),
currentStep: 0,
completedSteps: [],
status: 'started'
}
}
};User Provisioning Workflow
// n8n Function Node - Account Provisioning
const { customer, onboarding } = $input.first().json;
// Generate secure credentials
const provisioning = {
customerId: customer.id,
accountId: `acc_${generateSecureId()}`,
apiKey: `sk_live_${generateSecureId(32)}`,
webhookSecret: `whsec_${generateSecureId(24)}`,
tempPassword: generateTempPassword(),
passwordResetToken: generateSecureId(48),
tokenExpiry: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString() // 24 hours
};
// Determine initial setup based on plan
const initialSetup = {
features: getFeaturesByPlan(customer.plan),
limits: getLimitsByPlan(customer.plan),
defaults: getDefaultSettings(customer.planTier),
integrations: []
};
// Create provisioning tasks
const tasks = [
{
type: 'create_account',
data: {
accountId: provisioning.accountId,
email: customer.email,
name: customer.fullName,
plan: customer.plan
}
},
{
type: 'create_api_credentials',
data: {
accountId: provisioning.accountId,
apiKey: provisioning.apiKey,
webhookSecret: provisioning.webhookSecret
}
},
{
type: 'setup_initial_config',
data: initialSetup
},
{
type: 'create_sample_data',
data: {
accountId: provisioning.accountId,
templates: getSampleTemplates(customer.planTier)
}
}
];
function generateSecureId(length = 16) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
function generateTempPassword() {
const upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const lower = 'abcdefghijklmnopqrstuvwxyz';
const numbers = '0123456789';
const special = '!@#$%^&*';
let password = '';
password += upper.charAt(Math.floor(Math.random() * upper.length));
password += lower.charAt(Math.floor(Math.random() * lower.length));
password += numbers.charAt(Math.floor(Math.random() * numbers.length));
password += special.charAt(Math.floor(Math.random() * special.length));
const allChars = upper + lower + numbers + special;
for (let i = 0; i < 8; i++) {
password += allChars.charAt(Math.floor(Math.random() * allChars.length));
}
return password.split('').sort(() => Math.random() - 0.5).join('');
}
function getFeaturesByPlan(plan) {
const features = {
starter: ['basic_dashboard', 'email_support', 'api_access'],
professional: ['advanced_analytics', 'priority_support', 'api_access', 'integrations'],
business: ['custom_reports', 'dedicated_support', 'advanced_api', 'sso', 'audit_logs'],
enterprise: ['all_features', 'custom_development', 'sla', 'dedicated_infrastructure']
};
return features[plan.toLowerCase()] || features.starter;
}
function getLimitsByPlan(plan) {
const limits = {
starter: { users: 5, storage_gb: 10, api_calls_monthly: 10000 },
professional: { users: 25, storage_gb: 100, api_calls_monthly: 100000 },
business: { users: 100, storage_gb: 500, api_calls_monthly: 500000 },
enterprise: { users: -1, storage_gb: -1, api_calls_monthly: -1 } // Unlimited
};
return limits[plan.toLowerCase()] || limits.starter;
}
function getDefaultSettings(tier) {
return {
notifications: { email: true, in_app: true, slack: tier !== 'basic' },
security: { mfa_required: tier === 'enterprise', session_timeout: 3600 },
privacy: { analytics_enabled: true, crash_reports: true }
};
}
function getSampleTemplates(tier) {
const templates = ['getting_started', 'basic_workflow'];
if (tier !== 'basic') {
templates.push('advanced_automation', 'reporting_template');
}
if (tier === 'enterprise') {
templates.push('enterprise_integration', 'compliance_workflow');
}
return templates;
}
return {
json: {
customer,
onboarding,
provisioning,
tasks
}
};Welcome Email Sequence
// n8n Function Node - Email Sequence Builder
const { customer, onboarding, provisioning } = $input.first().json;
// Build personalized email sequence
const emailSequence = [];
// Day 0 - Welcome Email
emailSequence.push({
day: 0,
hours: 0,
template: 'welcome',
subject: `Welcome to ${process.env.COMPANY_NAME}, ${customer.firstName}!`,
personalization: {
firstName: customer.firstName,
plan: customer.plan,
loginUrl: `${process.env.APP_URL}/login`,
passwordResetUrl: `${process.env.APP_URL}/reset-password?token=${provisioning.passwordResetToken}`,
supportEmail: process.env.SUPPORT_EMAIL
},
tags: ['onboarding', 'day_0', 'welcome']
});
// Day 1 - Getting Started Guide
emailSequence.push({
day: 1,
hours: 10, // Send at 10 AM in customer's timezone
template: 'getting_started',
subject: `Get started with ${process.env.COMPANY_NAME} in 5 minutes`,
personalization: {
firstName: customer.firstName,
quickStartUrl: `${process.env.APP_URL}/quickstart`,
videoTutorialUrl: `${process.env.DOCS_URL}/tutorials/getting-started`,
features: getTopFeatures(customer.plan)
},
tags: ['onboarding', 'day_1', 'education'],
condition: {
type: 'not_completed',
milestone: 'first_login'
}
});
// Day 3 - Feature Spotlight
emailSequence.push({
day: 3,
hours: 14,
template: 'feature_spotlight',
subject: `Did you know? ${getFeatureSpotlight(customer.plan).title}`,
personalization: {
firstName: customer.firstName,
feature: getFeatureSpotlight(customer.plan),
cta_url: `${process.env.APP_URL}/features/${getFeatureSpotlight(customer.plan).slug}`
},
tags: ['onboarding', 'day_3', 'feature'],
condition: {
type: 'not_used',
feature: getFeatureSpotlight(customer.plan).slug
}
});
// Day 5 - Integration Suggestions
if (customer.planTier !== 'basic') {
emailSequence.push({
day: 5,
hours: 10,
template: 'integrations',
subject: `Connect ${process.env.COMPANY_NAME} to your favorite tools`,
personalization: {
firstName: customer.firstName,
suggestedIntegrations: getSuggestedIntegrations(customer),
integrationsUrl: `${process.env.APP_URL}/integrations`
},
tags: ['onboarding', 'day_5', 'integrations']
});
}
// Day 7 - Check-in
emailSequence.push({
day: 7,
hours: 10,
template: 'check_in',
subject: `How's it going, ${customer.firstName}?`,
personalization: {
firstName: customer.firstName,
usageStats: '{{usage_stats}}', // Placeholder for dynamic data
feedbackUrl: `${process.env.APP_URL}/feedback`,
bookCallUrl: onboarding.path.type !== 'self_service' ?
`${process.env.CALENDLY_URL}/onboarding-call` : null
},
tags: ['onboarding', 'day_7', 'engagement']
});
// Day 14 - Success Tips (for guided onboarding)
if (onboarding.path.type !== 'self_service') {
emailSequence.push({
day: 14,
hours: 10,
template: 'success_tips',
subject: `Pro tips for ${customer.plan} users`,
personalization: {
firstName: customer.firstName,
tips: getProTips(customer.planTier),
communityUrl: `${process.env.COMMUNITY_URL}`,
webinarUrl: `${process.env.APP_URL}/webinars`
},
tags: ['onboarding', 'day_14', 'education']
});
}
function getTopFeatures(plan) {
const features = {
starter: [
{ name: 'Dashboard', description: 'View all your data at a glance' },
{ name: 'Reports', description: 'Generate basic reports' }
],
professional: [
{ name: 'Advanced Analytics', description: 'Deep dive into your metrics' },
{ name: 'Integrations', description: 'Connect your favorite tools' }
],
business: [
{ name: 'Custom Reports', description: 'Build reports your way' },
{ name: 'Team Collaboration', description: 'Work together seamlessly' }
],
enterprise: [
{ name: 'SSO', description: 'Secure single sign-on' },
{ name: 'Audit Logs', description: 'Complete activity tracking' }
]
};
return features[plan.toLowerCase()] || features.starter;
}
function getFeatureSpotlight(plan) {
const spotlights = {
starter: { title: 'Automated Reminders', slug: 'reminders', description: 'Never miss important tasks' },
professional: { title: 'Workflow Automation', slug: 'workflows', description: 'Automate repetitive tasks' },
business: { title: 'Custom Dashboards', slug: 'dashboards', description: 'Build your perfect view' },
enterprise: { title: 'API Access', slug: 'api', description: 'Build custom integrations' }
};
return spotlights[plan.toLowerCase()] || spotlights.starter;
}
function getSuggestedIntegrations(customer) {
// Would be based on customer industry/use case
return [
{ name: 'Slack', icon: 'slack', description: 'Get notifications in Slack' },
{ name: 'Google Calendar', icon: 'google-calendar', description: 'Sync your schedule' },
{ name: 'Zapier', icon: 'zapier', description: 'Connect 3000+ apps' }
];
}
function getProTips(tier) {
return [
'Use keyboard shortcuts to navigate faster',
'Set up weekly email digests for team updates',
'Create templates for common tasks'
];
}
// Calculate send times
const scheduledEmails = emailSequence.map(email => {
const sendDate = new Date(customer.signupDate);
sendDate.setDate(sendDate.getDate() + email.day);
sendDate.setHours(email.hours, 0, 0, 0);
return {
...email,
scheduledAt: sendDate.toISOString(),
recipientEmail: customer.email,
recipientName: customer.fullName,
customerId: customer.id
};
});
return {
json: {
customer,
onboarding,
emailSequence: scheduledEmails
}
};Progress Tracking
// n8n Function Node - Onboarding Progress Tracker
const customer = $input.first().json.customer;
const currentProgress = $('Get Current Progress').first()?.json || {};
// Define onboarding milestones
const milestones = {
self_service: [
{ id: 'account_created', name: 'Account Created', weight: 10 },
{ id: 'first_login', name: 'First Login', weight: 15 },
{ id: 'profile_completed', name: 'Profile Completed', weight: 10 },
{ id: 'first_project', name: 'Created First Project', weight: 20 },
{ id: 'invited_team', name: 'Invited Team Member', weight: 15 },
{ id: 'used_core_feature', name: 'Used Core Feature', weight: 20 },
{ id: 'completed_tutorial', name: 'Completed Tutorial', weight: 10 }
],
guided: [
{ id: 'account_created', name: 'Account Created', weight: 5 },
{ id: 'first_login', name: 'First Login', weight: 10 },
{ id: 'profile_completed', name: 'Profile Completed', weight: 5 },
{ id: 'demo_completed', name: 'Demo Call Completed', weight: 15 },
{ id: 'first_project', name: 'Created First Project', weight: 15 },
{ id: 'integration_setup', name: 'Set Up Integration', weight: 15 },
{ id: 'invited_team', name: 'Invited Team', weight: 15 },
{ id: 'training_completed', name: 'Completed Training', weight: 20 }
],
enterprise: [
{ id: 'kickoff_completed', name: 'Kickoff Call Completed', weight: 10 },
{ id: 'requirements_gathered', name: 'Requirements Gathered', weight: 10 },
{ id: 'environment_setup', name: 'Environment Set Up', weight: 15 },
{ id: 'sso_configured', name: 'SSO Configured', weight: 10 },
{ id: 'data_migration', name: 'Data Migration Complete', weight: 15 },
{ id: 'team_training', name: 'Team Training Complete', weight: 15 },
{ id: 'pilot_launched', name: 'Pilot Launched', weight: 10 },
{ id: 'go_live', name: 'Go Live Complete', weight: 15 }
]
};
// Get relevant milestones for customer
const customerMilestones = milestones[currentProgress.onboardingType] || milestones.self_service;
// Calculate progress
const completedMilestones = currentProgress.completedMilestones || [];
const completedWeight = customerMilestones
.filter(m => completedMilestones.includes(m.id))
.reduce((sum, m) => sum + m.weight, 0);
const progressPercentage = Math.round(completedWeight);
// Determine current step
const pendingMilestones = customerMilestones.filter(m => !completedMilestones.includes(m.id));
const nextMilestone = pendingMilestones[0];
// Calculate health score
const healthScore = calculateHealthScore(customer, currentProgress);
// Determine if intervention needed
const intervention = checkForIntervention(customer, currentProgress, healthScore);
function calculateHealthScore(customer, progress) {
let score = 100;
// Days since signup
const daysSinceSignup = Math.floor(
(Date.now() - new Date(customer.signupDate).getTime()) / (1000 * 60 * 60 * 24)
);
// Penalize slow progress
const expectedProgress = Math.min(daysSinceSignup * 10, 70); // Expect ~70% by day 7
const actualProgress = progressPercentage;
if (actualProgress < expectedProgress) {
score -= (expectedProgress - actualProgress);
}
// Penalize no recent activity
const lastActivity = progress.lastActivityAt ?
Math.floor((Date.now() - new Date(progress.lastActivityAt).getTime()) / (1000 * 60 * 60 * 24)) : daysSinceSignup;
if (lastActivity > 3) {
score -= (lastActivity - 3) * 5;
}
// Boost for key actions
if (completedMilestones.includes('invited_team')) score += 10;
if (completedMilestones.includes('integration_setup')) score += 10;
return Math.max(0, Math.min(100, score));
}
function checkForIntervention(customer, progress, healthScore) {
const interventions = [];
// Low health score
if (healthScore < 50) {
interventions.push({
type: 'low_health',
action: 'personal_outreach',
priority: 'high',
message: 'Customer may need assistance'
});
}
// No login after 48 hours
if (!progress.completedMilestones?.includes('first_login')) {
const hoursSinceSignup = (Date.now() - new Date(customer.signupDate).getTime()) / (1000 * 60 * 60);
if (hoursSinceSignup > 48) {
interventions.push({
type: 'no_login',
action: 'reminder_email',
priority: 'medium',
message: 'Customer has not logged in'
});
}
}
// Stalled at specific milestone
if (progress.stalledAt) {
interventions.push({
type: 'stalled_progress',
action: 'help_content',
priority: 'medium',
milestone: progress.stalledAt,
message: `Customer stuck at ${progress.stalledAt}`
});
}
return interventions;
}
// Build progress report
const progressReport = {
customerId: customer.id,
email: customer.email,
plan: customer.plan,
onboardingType: currentProgress.onboardingType || 'self_service',
startedAt: customer.signupDate,
lastActivityAt: currentProgress.lastActivityAt,
progress: {
percentage: progressPercentage,
completedMilestones: completedMilestones,
pendingMilestones: pendingMilestones.map(m => m.id),
nextMilestone: nextMilestone,
totalMilestones: customerMilestones.length
},
healthScore: healthScore,
interventionsNeeded: intervention,
status: progressPercentage >= 100 ? 'completed' :
progressPercentage >= 50 ? 'on_track' :
healthScore < 50 ? 'at_risk' : 'in_progress',
generatedAt: new Date().toISOString()
};
return { json: progressReport };Personalized In-App Guides
// n8n Function Node - In-App Guide Builder
const customer = $input.first().json;
const userBehavior = $('Get User Behavior').first()?.json || {};
// Define guide templates
const guideTemplates = {
welcome_tour: {
id: 'welcome_tour',
name: 'Welcome Tour',
type: 'product_tour',
steps: [
{
target: '#dashboard',
title: 'Your Dashboard',
content: 'This is where you\'ll see all your important metrics at a glance.',
position: 'bottom'
},
{
target: '#create-button',
title: 'Create Your First Project',
content: 'Click here to start your first project. We\'ll guide you through the process.',
position: 'right'
},
{
target: '#settings-menu',
title: 'Customize Your Experience',
content: 'Access settings to personalize your workspace.',
position: 'left'
}
],
trigger: 'first_login',
dismissible: true
},
feature_discovery: {
id: 'feature_discovery',
name: 'Feature Discovery',
type: 'tooltip_sequence',
steps: [
{
target: '#analytics-tab',
title: 'Analytics',
content: 'Deep dive into your data with advanced analytics.',
position: 'bottom',
condition: { plan_tier: ['standard', 'premium', 'enterprise'] }
},
{
target: '#automations',
title: 'Automations',
content: 'Set up automated workflows to save time.',
position: 'right'
}
],
trigger: 'milestone_completed',
milestone: 'first_project'
},
empty_state_prompt: {
id: 'empty_state_prompt',
name: 'Getting Started Prompt',
type: 'modal',
content: {
title: 'Let\'s Get Started!',
body: 'Create your first project to unlock the full potential of our platform.',
cta_text: 'Create Project',
cta_action: 'open_create_modal',
secondary_text: 'Watch Tutorial',
secondary_action: 'open_tutorial'
},
trigger: 'empty_state',
entity: 'projects'
},
integration_prompt: {
id: 'integration_prompt',
name: 'Integration Suggestion',
type: 'banner',
content: {
title: 'Connect Your Tools',
body: 'Integrate with your favorite apps to streamline your workflow.',
cta_text: 'View Integrations',
cta_action: 'navigate_integrations'
},
trigger: 'time_in_app',
threshold_minutes: 30,
condition: { integrations_count: 0 }
},
upgrade_prompt: {
id: 'upgrade_prompt',
name: 'Upgrade Prompt',
type: 'modal',
content: {
title: 'Unlock More Features',
body: 'You\'re using {{usage_percentage}}% of your plan limits. Upgrade to unlock more.',
features: ['Unlimited projects', 'Advanced analytics', 'Priority support'],
cta_text: 'View Plans',
cta_action: 'navigate_pricing'
},
trigger: 'usage_threshold',
threshold: 80
}
};
// Determine which guides to show based on user state
function selectGuidesForUser(customer, behavior) {
const selectedGuides = [];
const now = new Date();
const signupDate = new Date(customer.signupDate);
const daysSinceSignup = Math.floor((now - signupDate) / (1000 * 60 * 60 * 24));
// First login - show welcome tour
if (behavior.loginCount <= 1 && !behavior.completedGuides?.includes('welcome_tour')) {
selectedGuides.push({
...guideTemplates.welcome_tour,
priority: 1,
reason: 'First login'
});
}
// After first project - show feature discovery
if (behavior.projectsCreated >= 1 && !behavior.completedGuides?.includes('feature_discovery')) {
const guide = guideTemplates.feature_discovery;
// Filter steps based on plan
guide.steps = guide.steps.filter(step => {
if (!step.condition) return true;
if (step.condition.plan_tier) {
return step.condition.plan_tier.includes(customer.planTier);
}
return true;
});
selectedGuides.push({
...guide,
priority: 2,
reason: 'First project completed'
});
}
// Empty state - prompt to create
if (behavior.projectsCreated === 0 && daysSinceSignup >= 1) {
selectedGuides.push({
...guideTemplates.empty_state_prompt,
priority: 1,
reason: 'No projects after 24 hours'
});
}
// Integration prompt after spending time in app
if (behavior.totalMinutesInApp >= 30 &&
behavior.integrationsCount === 0 &&
!behavior.dismissedGuides?.includes('integration_prompt')) {
selectedGuides.push({
...guideTemplates.integration_prompt,
priority: 3,
reason: 'No integrations after 30 minutes'
});
}
// Upgrade prompt at usage threshold
if (behavior.usagePercentage >= 80 && customer.planTier !== 'enterprise') {
const guide = { ...guideTemplates.upgrade_prompt };
guide.content.body = guide.content.body.replace('{{usage_percentage}}', behavior.usagePercentage);
selectedGuides.push({
...guide,
priority: 2,
reason: 'High usage'
});
}
return selectedGuides.sort((a, b) => a.priority - b.priority);
}
// Personalize guide content
function personalizeGuide(guide, customer) {
const personalized = JSON.parse(JSON.stringify(guide));
// Replace placeholders
const replacements = {
'{{firstName}}': customer.firstName,
'{{company}}': customer.company || 'your team',
'{{plan}}': customer.plan
};
const contentStr = JSON.stringify(personalized);
let personalizedContent = contentStr;
for (const [placeholder, value] of Object.entries(replacements)) {
personalizedContent = personalizedContent.replace(new RegExp(placeholder, 'g'), value);
}
return JSON.parse(personalizedContent);
}
const selectedGuides = selectGuidesForUser(customer, userBehavior);
const personalizedGuides = selectedGuides.map(guide => personalizeGuide(guide, customer));
return {
json: {
customerId: customer.id,
guides: personalizedGuides,
behavior: userBehavior,
generatedAt: new Date().toISOString()
}
};Completion and Handoff
// n8n Function Node - Onboarding Completion Handler
const progressReport = $input.first().json;
// Check if onboarding is complete
const isComplete = progressReport.progress.percentage >= 100;
const isTimedOut = checkOnboardingTimeout(progressReport);
// Determine next steps
function determineNextSteps(report) {
if (isComplete) {
return {
action: 'complete_onboarding',
tasks: [
{ type: 'send_completion_email', template: 'onboarding_complete' },
{ type: 'update_customer_stage', stage: 'active' },
{ type: 'trigger_nps_survey', delay_days: 7 },
{ type: 'schedule_success_call', condition: 'premium_or_higher' }
]
};
}
if (isTimedOut) {
return {
action: 'escalate_stalled',
tasks: [
{ type: 'notify_csm', priority: 'high' },
{ type: 'send_reengagement_email', template: 'stuck_onboarding' },
{ type: 'create_support_ticket', reason: 'Stalled onboarding' }
]
};
}
if (report.interventionsNeeded.length > 0) {
return {
action: 'intervention_required',
tasks: report.interventionsNeeded.map(i => ({
type: `handle_${i.type}`,
...i
}))
};
}
return {
action: 'continue_monitoring',
tasks: []
};
}
function checkOnboardingTimeout(report) {
const maxDays = {
self_service: 14,
guided: 30,
enterprise: 60
};
const daysSinceStart = Math.floor(
(Date.now() - new Date(report.startedAt).getTime()) / (1000 * 60 * 60 * 24)
);
const timeout = maxDays[report.onboardingType] || 14;
return daysSinceStart > timeout && report.progress.percentage < 100;
}
// Build completion report
const completionReport = {
customerId: progressReport.customerId,
email: progressReport.email,
plan: progressReport.plan,
onboardingType: progressReport.onboardingType,
finalStatus: isComplete ? 'completed' : isTimedOut ? 'timed_out' : 'in_progress',
completionPercentage: progressReport.progress.percentage,
healthScore: progressReport.healthScore,
completedMilestones: progressReport.progress.completedMilestones,
pendingMilestones: progressReport.progress.pendingMilestones,
timeToComplete: isComplete ? calculateTimeToComplete(progressReport) : null,
nextSteps: determineNextSteps(progressReport),
recommendations: generateRecommendations(progressReport),
reportedAt: new Date().toISOString()
};
function calculateTimeToComplete(report) {
const start = new Date(report.startedAt);
const end = new Date();
return Math.floor((end - start) / (1000 * 60 * 60 * 24)); // days
}
function generateRecommendations(report) {
const recommendations = [];
if (report.progress.percentage < 50) {
recommendations.push({
type: 'engagement',
message: 'Consider personalized outreach to improve engagement'
});
}
if (!report.progress.completedMilestones.includes('invited_team')) {
recommendations.push({
type: 'expansion',
message: 'Encourage team invitations for better adoption'
});
}
if (report.healthScore < 70) {
recommendations.push({
type: 'support',
message: 'Proactive support call recommended'
});
}
return recommendations;
}
return { json: completionReport };Best Practices
Onboarding Design
- Personalize by segment: Different customers need different onboarding paths
- Track milestones: Define clear success metrics for each onboarding stage
- Intervene early: Monitor health scores and act before customers churn
- Iterate continuously: Improve based on completion rates and feedback
Automation Tips
- Use conditional email sequences based on user actions
- Implement smart delays between touchpoints
- Combine in-app guides with email for multi-channel engagement
- Track every interaction for optimization
Automated onboarding with n8n ensures consistent, personalized experiences that drive customer success and retention.