← All posts

How I'd automate customer onboarding for a SaaS company

8 min read

A practical walkthrough of automating SaaS customer onboarding: welcome sequences, data collection, account setup, and smart check-ins. No enterprise tools.

Automated multi-stage onboarding system uses webhooks and smart sentiment check-ins to guide users, flagging struggles.

Customer onboarding is one of those processes that starts manual and stays manual for way too long. A new user signs up. Someone on the team sends a welcome email. A few days later, someone remembers to check if they've set up their account. Maybe there's a spreadsheet tracking who's been contacted. Maybe there isn't.

It works when you have 10 signups a month. At 50+, things start falling through the cracks. New users don't get their welcome email for 2 days. Nobody follows up with the ones who signed up but never logged in again. The team is spending 8-12 hours a week on what should be a predictable, repeatable process.

I've seen this pattern across a dozen SaaS companies. Here's how I'd build the automation pipeline from scratch, no enterprise onboarding platform required.

The 5-stage pipeline

The system breaks down into 5 stages, each triggered automatically by the previous one:

  1. Signup trigger catches the new user event
  2. Welcome sequence sends personalised emails over the first 48 hours
  3. Data collection gathers the information you need to configure their account
  4. Account setup provisions their workspace based on what they told you
  5. Smart check-ins at day 3 and day 7 with sentiment-aware routing

Each stage is simple on its own. The value comes from wiring them together so nothing gets missed.

Stage 1: Signup trigger

Every SaaS product fires an event when someone creates an account. Whether it's a webhook from Stripe, a database trigger, or an API event from your auth provider, the implementation is the same: catch the event and push it into a job queue.

# Webhook handler (simplified)
@app.route("/webhooks/signup", methods=["POST"])
def handle_signup():
    user = request.json
    queue.enqueue("onboarding.start", {
        "user_id": user["id"],
        "email": user["email"],
        "name": user["name"],
        "plan": user["plan"],
        "signed_up_at": datetime.utcnow().isoformat(),
    })
    return {"status": "queued"}, 202

The job queue is important. You don't want the signup flow to block while emails are being sent or APIs are being called. Use Redis + RQ, Celery, or even a simple Postgres-backed queue. The queue guarantees every signup gets processed, even if something downstream is temporarily unavailable.

Stage 2: Welcome sequence

Not a single welcome email. A sequence of 3 messages timed to match the new user's first 48 hours:

Email 1 (immediate): Welcome + one specific action. Not "check out all our features." Something concrete: "Click here to create your first project." Include the user's name, their plan name, and a single call to action.

Email 2 (24 hours): Only if they haven't completed the key action. "I noticed you haven't created your first project yet. Here's a 2-minute video showing how." This is your re-engagement touchpoint.

Email 3 (48 hours): Shift to value. "Three things other [plan name] customers set up in their first week." Social proof with practical guidance.

The logic for suppression is straightforward: check whether the user completed the key action before sending the next email. If they did, skip it. Nobody likes getting "you haven't done X" when they already have.

def should_send_email_2(user_id):
    """Check if user has completed the key onboarding action."""
    completed = db.query(
        "SELECT completed_at FROM onboarding_actions "
        "WHERE user_id = %s AND action = 'first_project'",
        [user_id]
    )
    return completed is None  # Only send if action not completed

Stage 3: Data collection

Most SaaS products need information from users to configure their experience properly. Team size, industry, primary use case, integration preferences. Collecting this during signup creates friction. Collecting it afterwards, as a separate low-pressure step, gets much higher completion rates.

I use a simple approach: a short form (4-5 fields max) sent as the second interaction after the welcome email. Frame it as helping them get more value, not as data collection.

The form responses feed directly into the next stage. If someone says they're in "e-commerce" and their primary use case is "customer support", that information shapes their account setup.

Tip

Keep it under 5 fields. Every additional question drops completion by roughly 10-15%. Ask what you need to configure their experience, not what your marketing team wants to know.

Stage 4: Account setup

This is where the automation pays for itself. Instead of a generic empty dashboard, the user gets a pre-configured workspace based on their responses from stage 3.

If they said "customer support" as primary use case: pre-create a sample support workflow, add example templates, enable the relevant integrations by default.

If they said "team of 10-20": set up sensible defaults for team collaboration features and send them an invite-your-team prompt earlier in the sequence.

The implementation is a mapping function:

SETUP_CONFIGS = {
    "customer_support": {
        "templates": ["ticket_response", "escalation", "feedback_request"],
        "integrations": ["zendesk", "intercom"],
        "default_views": ["queue", "metrics"],
    },
    "sales": {
        "templates": ["outreach", "follow_up", "proposal"],
        "integrations": ["hubspot", "salesforce"],
        "default_views": ["pipeline", "activity"],
    },
    # ... more use cases
}

def provision_workspace(user_id, use_case, team_size):
    config = SETUP_CONFIGS.get(use_case, SETUP_CONFIGS["default"])
    create_templates(user_id, config["templates"])
    enable_integrations(user_id, config["integrations"])
    set_default_views(user_id, config["default_views"])

    if team_size and int(team_size) > 5:
        trigger_team_invite_flow(user_id)

This takes 10 minutes of compute time and saves the user 30 minutes of manual setup. More importantly, they see value immediately instead of staring at an empty dashboard.

Stage 5: Smart check-ins

Here's where the automation gets interesting. At day 3 and day 7, the system sends check-in messages. But instead of a generic "how's it going?", the content is based on what the user has actually done.

Active user (logged in 3+ times, used core features): "You've already [specific thing they did]. Here's what most people do next." Reinforce momentum.

Partially active (logged in but hasn't used core features): "I noticed you've logged in a few times but haven't tried [feature]. Here's why it's worth it." Specific guidance for the gap.

Inactive (hasn't logged in since signup): "Is something not working? Reply to this email and I'll help you get set up." Direct human escalation.

The inactive path is the important one. These are people who signed up with intent and then hit a wall. An automated email that invites a reply, and actually routes that reply to a real person, recovers a surprising number of them.

def classify_engagement(user_id, days_since_signup):
    sessions = get_login_count(user_id, days=days_since_signup)
    features_used = get_feature_usage(user_id)

    if sessions >= 3 and len(features_used) >= 2:
        return "active"
    elif sessions >= 1:
        return "partial"
    else:
        return "inactive"

def send_checkin(user_id, day):
    engagement = classify_engagement(user_id, days_since_signup=day)

    if engagement == "inactive":
        send_email(user_id, template=f"checkin_day{day}_inactive")
        create_support_ticket(user_id, priority="high",
            note=f"Day {day} inactive user. Needs human follow-up.")
    elif engagement == "partial":
        missing = get_unused_core_features(user_id)
        send_email(user_id, template=f"checkin_day{day}_partial",
            context={"missing_feature": missing[0]})
    else:
        send_email(user_id, template=f"checkin_day{day}_active",
            context={"achievement": get_recent_activity(user_id)})

What this costs to build

For a SaaS company doing 50+ signups/month, here's the realistic breakdown:

| Component | Cost | Time | |---|---|---| | Webhook + queue setup | £500-£800 | 2-3 days | | Email sequence (3 templates + logic) | £800-£1,200 | 3-4 days | | Data collection form + integration | £400-£600 | 1-2 days | | Account provisioning logic | £1,000-£1,500 | 3-5 days | | Smart check-in system | £1,200-£1,800 | 4-5 days | | Total | £4,000-£6,000 | 2-3 weeks |

Ongoing costs are minimal: email sending (pennies per email via SendGrid or Postmark), queue infrastructure (typically covered by existing hosting), and monitoring time (~1 hour/week to check metrics and tweak templates).

What this saves

The time savings are straightforward. A team manually onboarding 50 users/month spends roughly 8-12 hours/week on welcome emails, follow-ups, account configuration, and check-ins. The automation handles 90%+ of that.

But the bigger win is consistency. Every user gets the right sequence at the right time. Nobody falls through the cracks. The day-3 check-in always happens, even when the team is busy with a product launch or dealing with a support spike.

And the data from the check-in system feeds back into product decisions. If 40% of users are "partially active" at day 3 and the common missing feature is the same one, that tells you something about your product's UX, not just your onboarding.

Key Takeaways

  • Break onboarding into 5 stages: trigger, welcome, data collection, setup, check-ins. Automate each independently.
  • Use a job queue so signup never blocks on downstream processing.
  • Pre-configure workspaces based on user responses. Show value on first login, not after 30 minutes of setup.
  • Smart check-ins at day 3 and 7 should route inactive users to a real person, not just send another generic email.
  • Total cost: £4,000-£6,000. Saves 8-12 hours/week at 50+ signups/month.

If you're running a SaaS product and onboarding is still mostly manual, this is one of the highest-ROI automation projects you can do. The pipeline is simple, the savings are immediate, and the data you collect makes every future product decision better. Get in touch if you want to talk through the specifics for your product.

Related reading: