DEV Community

BMarsaw
BMarsaw

Posted on

Ask HN: Support SaaS - Building vs Buying Customer Support Tools in 2024

Ask HN: Support SaaS - Building vs Buying Customer Support Tools in 2024

If you've shipped a SaaS product, you've faced this question: how do you handle customer support without it consuming your entire team?

The Hacker News thread "Ask HN: Support SaaS" reveals a consistent pattern - founders start with email, quickly get overwhelmed, then either overpay for enterprise tools or waste months building custom solutions. Neither path is optimal.

This article cuts through the noise. I'll share what actually works based on building multiple developer-focused SaaS products, including specific recommendations, integration patterns, and the decision framework you need.

The Real Problem with Support SaaS Tools

Most support platforms weren't built for technical products. They're designed for e-commerce or B2C companies handling "Where's my order?" tickets, not developers debugging API integration issues.

Here's what breaks:

Context switching kills productivity. Your support tool lives in isolation while customer data sits in your database, error logs live in Sentry, and usage metrics hide in your analytics platform. Every ticket becomes an archaeological expedition.

Pricing models punish growth. Per-agent pricing makes hiring support expensive. Per-ticket pricing encourages bad behavior (ignoring customers). Per-contact pricing explodes as you scale.

Integration overhead is real. Most tools require you to pipe customer data into their system, maintain synchronization, and build custom integrations just to see basic context.

When to Build Your Own Support System

Build when:

  1. Your product IS support infrastructure. If you're building DevOps tools, monitoring platforms, or developer APIs, your support system should demonstrate your product's capabilities.

  2. You have extreme customization needs. One founder I know built a custom support system that automatically creates staging environments with replicated customer data for every ticket. Try doing that with Zendesk.

  3. You have engineering bandwidth to spare. Realistically, a basic support system takes 2-3 weeks to build and requires ongoing maintenance.

Here's a minimal support system using Python/FastAPI and React:

python

backend/tickets.py

from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
from typing import List
import httpx

app = FastAPI()

class TicketService:
def init(self, db: Session):
self.db = db

async def create_ticket(self, user_id: int, subject: str, body: str):
    # Enrich with context automatically
    user_data = await self._get_user_context(user_id)
    error_logs = await self._get_recent_errors(user_id)
    usage_stats = await self._get_usage_stats(user_id)

    ticket = Ticket(
        user_id=user_id,
        subject=subject,
        body=body,
        context={
            "user": user_data,
            "recent_errors": error_logs,
            "usage": usage_stats,
            "plan": user_data.get("subscription_plan")
        }
    )
    self.db.add(ticket)
    self.db.commit()

    # Auto-categorize and route
    await self._auto_categorize(ticket)
    return ticket

async def _get_user_context(self, user_id: int):
    """Pull relevant context from your existing database"""
    user = self.db.query(User).filter(User.id == user_id).first()
    return {
        "email": user.email,
        "created_at": user.created_at,
        "plan": user.subscription_plan,
        "last_login": user.last_login,
        "total_api_calls": user.api_call_count
    }

async def _get_recent_errors(self, user_id: int):
    """Fetch from error tracking"""
    # Integration with your error tracking system
    return await self.db.query(ErrorLog)\
        .filter(ErrorLog.user_id == user_id)\
        .order_by(ErrorLog.created_at.desc())\
        .limit(10).all()
Enter fullscreen mode Exit fullscreen mode

The React component for this is straightforward:

typescript
// TicketView.tsx
import React from 'react';
import { useQuery } from '@tanstack/react-query';

interface TicketContext {
user: UserData;
recent_errors: Error[];
usage: UsageStats;
plan: string;
}

const TicketView: React.FC<{ ticketId: string }> = ({ ticketId }) => {
const { data: ticket } = useQuery(['ticket', ticketId],
() => fetch(/api/tickets/${ticketId}).then(r => r.json())
);

return (



{ticket.subject}


{ticket.body}


  {/* This is the killer feature - automatic context */}
  <aside className="ticket-context">
    <h3>User Context</h3>
    <dl>
      <dt>Plan</dt>
      <dd>{ticket.context.plan}</dd>

      <dt>Last API Call</dt>
      <dd>{ticket.context.usage.last_call}</dd>

      <dt>Recent Errors</dt>
      <dd>
        {ticket.context.recent_errors.map(err => (
          <div key={err.id} className="error-preview">
            <code>{err.message}</code>
            <span>{err.timestamp}</span>
          </div>
        ))}
      </dd>
    </dl>
  </aside>
</div>

);
};

When to Buy (And Which Ones Don't Suck)

Buy when you need to ship fast and support isn't your differentiator.

Plain (plain.com) - Built for technical teams. Thread-based, lives in email, actually good API. No per-agent pricing nonsense. This is what I use.

Linear (linear.app) - Not a support tool, but many dev tool companies use it for customer issues. Customers file issues directly. Everything stays in one system. Works if your customers are technical.

Intercom - Expensive but powerful if you need proactive messaging and automation. The API is solid for custom integrations. Overkill for early-stage.

Avoid: Zendesk (bloated, expensive), Freshdesk (death by a thousand integrations), HelpScout (fine but unremarkable).

The Hybrid Approach That Actually Works

The smart move? Use a lightweight tool but build critical integrations.

Here's a webhook handler that enriches incoming support tickets:

typescript
// webhooks/support-enrichment.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { prisma } from '@/lib/prisma';
import { plain } from '@/lib/plain-client';

export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'POST') {
return res.status(405).end();
}

const { threadId, customerEmail } = req.body;

// Fetch rich context from your database
const user = await prisma.user.findUnique({
where: { email: customerEmail },
include: {
subscription: true,
apiKeys: true,
recentEvents: {
take: 50,
orderBy: { createdAt: 'desc' }
}
}
});

if (!user) {
return res.status(200).json({ message: 'No user found' });
}

// Add context to the ticket automatically
await plain.addThreadLabels(threadId, [
plan:${user.subscription.plan},
mrr:${user.subscription.mrr},
health:${calculateHealthScore(user)}
]);

await plain.addTimelineEntry(threadId, {
type: 'custom',
title: 'User Context',
body: `
Account Details

  • Plan: ${user.subscription.plan}
  • MRR: $${user.subscription.mrr}
  • Created: ${user.createdAt.toLocaleDateString()}
  • Last active: ${user.lastActiveAt.toLocaleDateString()}

Recent Activity
${user.recentEvents.slice(0, 5).map(e => - ${e.type}: ${e.description}).join('\n')}
`
});

return res.status(200).json({ success: true });
}

This approach gives you 80% of a custom system's benefits while maintaining 20% of the complexity.

The Decision Framework

Use this flowchart:

  1. < 100 customers? Just use email. Seriously. Gmail + canned responses is enough.

  2. 100-1000 customers, technical product? Plain or Linear. Build webhook enrichments.

  3. 1000+ customers, non-technical users? Intercom or build custom. No middle ground here.

  4. Support IS your product? Build it. Make it a feature.

Conclusion

The "Ask HN: Support SaaS" question doesn't have one answer. It has three:

  • Early stage: Email + discipline + canned responses
  • Growth stage: Lightweight tool + smart integrations
  • Scale stage: Build it or pay enterprise prices

The mistake is jumping to enterprise tools too early or building custom too late. Most developer-focused SaaS companies thrive in the hybrid zone - a simple tool augmented with custom code that surfaces the context support needs.

Stop overthinking it. Pick a tool that has a good API, spend two days building enrichment webhooks, and get back to building your actual product. Your customers care more about fast, contextual responses than which ticketing system you use.

The best support system is the one that lets your team solve problems quickly. Everything else is optimization theater.


🛠 Recommended Tools

  • Supabase — Open-source Firebase alternative with PostgreSQL and built-in auth
  • Stripe — Payment processing with a developer-first API
  • Sentry — Error tracking and performance monitoring — free for small projects

Disclosure: some links above may earn a referral commission if you sign up.


📚 Recommended Reading

Want to go deeper on SaaS? These are worth it:

These are affiliate links — if you buy through them I earn a small commission at no extra cost to you.

Top comments (0)