, or escalation.
1. Data Models
export interface SupportTicket {
id: string;
channel: 'email' | 'chat' | 'twitter' | 'github';
content: string;
userId?: string;
metadata: Record<string, any>;
receivedAt: Date;
}
export interface TriageResult {
action: 'AUTO_REPLY' | 'CREATE_TICKET' | 'ESCALATE';
payload: AutoReplyPayload | TicketPayload | EscalationPayload;
confidence: number;
tags: string[];
}
interface AutoReplyPayload {
templateId: string;
variables: Record<string, string>;
}
interface TicketPayload {
priority: 'low' | 'medium' | 'high' | 'critical';
category: string;
summary: string;
}
interface EscalationPayload {
reason: string;
requiresHumanReview: true;
}
2. The Triage Router
This engine uses pattern matching and keyword analysis to route tickets. In production, this can be extended with lightweight ML models for sentiment and intent classification.
import { SupportTicket, TriageResult, AutoReplyPayload } from './types';
// Configuration for auto-responses
const AUTO_REPLY_RULES = [
{
pattern: /(?:login|sign.?in|access|password)/i,
templateId: 'auth_troubleshooting',
category: 'authentication',
priority: 'medium',
},
{
pattern: /(?:billing|invoice|payment|refund|subscription)/i,
templateId: 'billing_info',
category: 'billing',
priority: 'high',
},
{
pattern: /(?:bug|error|crash|broken|not.?working)/i,
templateId: 'bug_report_ack',
category: 'bug',
priority: 'medium',
},
];
// Knowledge base resolver simulation
const resolveKBMatch = (content: string): string | null => {
const lowerContent = content.toLowerCase();
if (lowerContent.includes('how to export') || lowerContent.includes('download data')) {
return 'kb_export_guide';
}
if (lowerContent.includes('api key') || lowerContent.includes('authentication')) {
return 'kb_api_setup';
}
return null;
};
export async function triageTicket(ticket: SupportTicket): Promise<TriageResult> {
const { content, channel, userId } = ticket;
// 1. Check for immediate KB resolution
const kbMatch = resolveKBMatch(content);
if (kbMatch) {
return {
action: 'AUTO_REPLY',
payload: {
templateId: 'kb_redirect',
variables: { articleId: kbMatch },
} as AutoReplyPayload,
confidence: 0.95,
tags: ['self_service', kbMatch],
};
}
// 2. Apply Rule-Based Triage
for (const rule of AUTO_REPLY_RULES) {
if (rule.pattern.test(content)) {
// High confidence auto-reply for common issues
if (rule.category === 'billing' || rule.category === 'authentication') {
return {
action: 'AUTO_REPLY',
payload: {
templateId: rule.templateId,
variables: { userId: userId || 'unknown' },
} as AutoReplyPayload,
confidence: 0.85,
tags: [rule.category, 'auto_resolved'],
};
}
// Lower confidence issues get a ticket with tags
return {
action: 'CREATE_TICKET',
payload: {
priority: rule.priority,
category: rule.category,
summary: `Auto-categorized: ${rule.category}`,
},
confidence: 0.75,
tags: [rule.category, 'needs_review'],
};
}
}
// 3. Fallback: Escalate for human review
// Only 10-15% of tickets should reach here
return {
action: 'ESCALATE',
payload: {
reason: 'Unclassified intent or complex request',
requiresHumanReview: true,
},
confidence: 0.4,
tags: ['manual_triage'],
};
}
3. Integration Workflow
- Ingestion Layer: Use a service like Forward Email or a webhook receiver to capture all inbound messages. Transform them into the
SupportTicket interface.
- Processing: Invoke
triageTicket.
- If
AUTO_REPLY: Send response immediately via the originating channel. Log the interaction.
- If
CREATE_TICKET: Push to a lightweight issue tracker (e.g., GitHub Issues, Linear, or a private Notion DB). Apply tags.
- If
ESCALATE: Push to a high-priority queue with a Slack notification to the founder.
- Feedback Loop: A nightly cron job aggregates tags from the support pipeline. If
tag_frequency > threshold, automatically create a "Product Improvement" issue in the roadmap.
Rationale
- TypeScript: Provides type safety for the pipeline, ensuring data integrity across integrations.
- Rule-Based First: ML is overkill for early-stage solo ops. Regex and keyword matching cover 80% of cases with zero latency and full transparency.
- Confidence Scoring: Allows for dynamic thresholding. As the system learns, confidence scores can adjust routing logic.
- Tag-Driven Roadmap: Ensures support data directly influences product decisions, turning support from a cost center into a growth lever.
Pitfall Guide
1. The Hero Complex
Mistake: Responding to every ticket personally to "show you care."
Impact: Creates dependency on the founder. Response times become erratic. Burnout accelerates.
Best Practice: Define clear SLAs. Use templates for 80% of responses. Personalization should be reserved for high-value or complex cases. Customers prefer consistent, accurate answers over slow, personal ones.
Mistake: Using separate tools for email, chat, Twitter, and GitHub issues.
Impact: Fragmented context. Missed tickets. Increased cognitive load switching between apps.
Best Practice: Centralize. Use a single inbox or a unified API layer. If using SaaS, ensure all channels feed into one dashboard. For code-centric teams, GitHub Issues or Linear can serve as the single source of truth.
3. KB Rot
Mistake: Creating a knowledge base but never updating it.
Impact: Users find outdated info, lose trust, and submit tickets anyway. Support volume increases.
Best Practice: Implement a "Review Date" on all articles. Automate stale content alerts. Link ticket resolutions back to KB updates. Treat the KB as code: version-controlled and reviewed.
4. Over-Automation of Edge Cases
Mistake: Using bots for ambiguous or emotional queries.
Impact: User frustration. Perceived lack of empathy. Churn risk.
Best Practice: Set confidence thresholds. If the triage engine confidence is below 0.6, escalate to human. Always provide an "Escape Hatch" in auto-replies: "If this doesn't solve your issue, reply to this email for human assistance."
5. Ignoring the "Why"
Mistake: Resolving tickets without analyzing root causes.
Impact: Recurring tickets. Product debt accumulates. Support volume grows linearly with users.
Best Practice: Weekly review of ticket tags. If "Password Reset" tickets spike, investigate auth UX. Fix the product to eliminate the support need. Support is the canary in the coal mine for product quality.
6. Asynchronous Friction
Mistake: Expecting instant replies in async channels or forcing live chat.
Impact: Interruption of deep work. User anxiety from delayed live chat responses.
Best Practice: Embrace async. Set expectations: "We reply within 24 hours." Use status pages and auto-replies to manage expectations. Disable live chat unless you have a dedicated support window.
7. Data Silos
Mistake: Support data stays in the support tool.
Impact: Product decisions made without customer insight. Marketing misses key objections.
Best Practice: Sync support metrics to a central dashboard. Export ticket summaries to the product roadmap. Share common objections with marketing for FAQ updates.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| 0-100 Users | Email + GitHub Issues | Low overhead. Direct communication. Issues track product bugs naturally. | $0 |
| 100-1,000 Users | Centralized Inbox + Triage Bot | Volume increases. Triage bot reduces manual sorting. Centralized inbox prevents missed tickets. | ~$20/mo |
| Complex B2B SaaS | Linear/Intercom + Custom Triage | High value per user. Requires context-rich routing and integration with CRM/Project tools. | ~$50-100/mo |
| Code-First Product | GitHub Discussions + Issues | Users are developers. Native tooling reduces friction. Issues map directly to codebase. | $0 |
| High Volume B2C | HelpScout/Zendesk + AI Assist | Volume requires robust macros, AI suggestions, and team scaling capabilities. | ~$100+/mo |
Configuration Template
Use this JSON configuration to seed your triage engine. Customize patterns and templates based on your product domain.
{
"triageConfig": {
"autoReplyThreshold": 0.8,
"escalationThreshold": 0.5,
"rules": [
{
"id": "rule_auth_01",
"pattern": "/(?:login|signin|password|reset)/i",
"action": "AUTO_REPLY",
"template": "auth_guide",
"priority": "medium",
"tags": ["auth", "account"]
},
{
"id": "rule_billing_01",
"pattern": "/(?:invoice|payment|refund|cancel)/i",
"action": "AUTO_REPLY",
"template": "billing_faq",
"priority": "high",
"tags": ["billing", "finance"]
},
{
"id": "rule_bug_01",
"pattern": "/(?:error|crash|bug|traceback)/i",
"action": "CREATE_TICKET",
"priority": "high",
"category": "bug",
"tags": ["bug", "quality"]
}
],
"templates": {
"auth_guide": {
"subject": "How to reset your password",
"body": "Hi {{name}},\n\nTo reset your password, visit {{reset_link}}. If you continue to have issues, reply to this email.\n\nBest,\n{{founder_name}}"
},
"billing_faq": {
"subject": "Billing and Invoice Information",
"body": "Hi {{name}},\n\nYou can view all invoices and manage your subscription at {{billing_portal}}.\n\nFor refunds, please check our policy: {{refund_policy_link}}.\n\nBest,\n{{founder_name}}"
}
}
}
}
Quick Start Guide
- Ingest: Set up a dedicated support email (e.g.,
support@yourdomain.com) and configure a forwarder to a webhook endpoint running the triage script.
- Seed: Populate the
triageConfig with regex patterns derived from your last 50 support emails. Create matching templates.
- Ack: Enable the auto-reply module. Test with a sample ticket to verify the
AUTO_REPLY vs ESCALATE logic.
- Protect: Block 2-hour focus sessions in your calendar. Configure the triage system to only notify you for
ESCALATE actions or priority: critical tickets during these blocks.
- Review: At the end of week one, export ticket tags. Identify the top 3 categories. Update the KB and add new rules to the triage config. Iterate.