- Published on
AI Email Automation — Drafting, Classifying, and Routing Emails at Scale
- Authors

- Name
- Sanjeev Sharma
- @webcoderspeed1
Introduction
Email remains the dominant communication channel in business, but managing hundreds of inbound messages daily is exhausting and error-prone. AI email automation accelerates response time, ensures consistent quality, and routes messages to the right teams instantly. Unlike generic email filters, LLMs understand context, intent, and nuance. This guide covers building systems that draft, classify, prioritize, and route emails intelligently while maintaining compliance.
- Email Intent Classification
- Priority Scoring
- Auto-Draft Generation with Context
- Tone Adjustment
- Template Population
- Response Suggestion Generation
- Email Thread Summarization
- Routing to Correct Team
- Confidence-Based Human Review Queue
- Unsubscribe and Compliance Handling
- Checklist
- Conclusion
Email Intent Classification
Classify emails into actionable categories:
type EmailIntent = 'support' | 'sales' | 'spam' | 'inquiry' | 'feedback' | 'billing' | 'technical';
interface ClassifiedEmail {
id: string;
from: string;
subject: string;
body: string;
intent: EmailIntent;
confidence: number;
metadata: Record<string, any>;
}
async function classifyEmail(email: RawEmail): Promise<ClassifiedEmail> {
const classificationPrompt = `
Classify this email by intent:
From: ${email.from}
Subject: ${email.subject}
Body: ${email.body}
Intent categories:
- support: User has a problem and needs help
- sales: Potential customer inquiry or partnership
- spam: Unsolicited marketing, phishing
- inquiry: General question about product/service
- feedback: User reporting feature request or bug
- billing: Payment or subscription issue
- technical: Infrastructure or integration issue
Return JSON: { intent, confidence (0-1), reasoning }
`;
const result = JSON.parse(await llm.generate(classificationPrompt));
return {
id: email.id,
from: email.from,
subject: email.subject,
body: email.body,
intent: result.intent,
confidence: result.confidence,
metadata: { reasoning: result.reasoning }
};
}
Priority Scoring
Determine which emails need immediate attention:
interface PriorityScore {
score: number; // 0-100
urgency: 'critical' | 'high' | 'normal' | 'low';
reasons: string[];
}
async function scorePriority(email: ClassifiedEmail): Promise<PriorityScore> {
const priorityPrompt = `
Score the priority of this email (0-100 scale):
From: ${email.from}
Intent: ${email.intent}
Subject: ${email.subject}
Body: ${email.body}
Urgency factors:
- Repeated contact attempts (high priority)
- Payment/billing issues (high priority)
- User mentions time sensitivity
- VIP/enterprise customer
- Service outage reported
- Contains explicit urgency language
Return JSON: { score, urgency, reasons (array) }
`;
const result = JSON.parse(await llm.generate(priorityPrompt));
return {
score: result.score,
urgency: result.urgency,
reasons: result.reasons
};
}
Auto-Draft Generation with Context
Generate contextual email drafts:
interface DraftEmail {
to: string;
subject: string;
body: string;
tone: 'formal' | 'casual' | 'friendly' | 'apologetic';
isAutoGenerated: boolean;
}
async function generateDraft(
email: ClassifiedEmail,
context: { customer?: any; order?: any; account?: any }
): Promise<DraftEmail> {
const draftPrompt = `
Generate a professional email response to this incoming email:
From: ${email.from}
Original subject: ${email.subject}
Original message: ${email.body}
Intent: ${email.intent}
Customer info: ${JSON.stringify(context.customer)}
Order info: ${JSON.stringify(context.order)}
Requirements:
- Keep response under 200 words
- Address customer by name if available
- Acknowledge their specific concern
- Provide next steps or timeline
- Include relevant contact info
Return JSON: { subject, body }
`;
const draft = JSON.parse(await llm.generate(draftPrompt));
return {
to: email.from,
subject: draft.subject,
body: draft.body,
tone: detectTone(draft.body),
isAutoGenerated: true
};
}
Tone Adjustment
Allow operators to adjust response tone:
async function adjustTone(
originalBody: string,
targetTone: 'formal' | 'casual' | 'friendly' | 'apologetic'
): Promise<string> {
const toneGuides = {
formal: 'Professional language, minimal contractions, structured format',
casual: 'Conversational, relaxed, use contractions naturally',
friendly: 'Warm, empathetic, personal touches, exclamation points ok',
apologetic: 'Acknowledge the problem, take responsibility, offer solution'
};
const tonePrompt = `
Adjust the tone of this email to be more ${targetTone}:
Original:
${originalBody}
Tone guide: ${toneGuides[targetTone]}
Maintain the same information and meaning.
Return only the adjusted email body.
`;
return llm.generate(tonePrompt);
}
Template Population
Use templates for repetitive responses:
interface EmailTemplate {
id: string;
name: string;
subject: string;
body: string; // With {{placeholder}} syntax
applicableIntents: EmailIntent[];
}
async function populateTemplate(
template: EmailTemplate,
email: ClassifiedEmail,
context: Record<string, any>
): Promise<DraftEmail> {
let subject = template.subject;
let body = template.body;
// Simple variable substitution
const variables = {
customer_name: context.customer?.name || 'Customer',
ticket_id: context.ticketId,
order_id: context.order?.id,
product_name: context.product?.name,
...context
};
for (const [key, value] of Object.entries(variables)) {
subject = subject.replace(`{{${key}}}`, String(value));
body = body.replace(`{{${key}}}`, String(value));
}
return {
to: email.from,
subject,
body,
tone: 'formal',
isAutoGenerated: true
};
}
Response Suggestion Generation
Offer multiple response options:
interface ResponseOption {
draft: DraftEmail;
rationale: string;
estimatedSendTime: number; // ms to compose
}
async function generateResponseOptions(
email: ClassifiedEmail,
context: any,
count: number = 3
): Promise<ResponseOption[]> {
const optionsPrompt = `
Generate ${count} different email responses to this customer:
Original email: ${email.body}
Intent: ${email.intent}
Customer: ${context.customer?.name}
Vary the responses in:
1. Tone (formal vs friendly)
2. Detail level (comprehensive vs concise)
3. Offers or solutions presented
Return JSON array with { subject, body, rationale }
`;
const options = JSON.parse(await llm.generate(optionsPrompt));
return options.map((opt: any) => ({
draft: {
to: email.from,
subject: opt.subject,
body: opt.body,
tone: 'formal',
isAutoGenerated: true
},
rationale: opt.rationale,
estimatedSendTime: 0
}));
}
Email Thread Summarization
Summarize long conversations:
interface ThreadSummary {
keyIssues: string[];
customerSentiment: 'positive' | 'neutral' | 'negative';
previousAttempts: string[];
recommendedNextStep: string;
}
async function summarizeThread(
emails: ClassifiedEmail[]
): Promise<ThreadSummary> {
const threadContext = emails
.map(e => `[${e.from}] ${e.subject}\n${e.body}`)
.join('\n\n---\n\n');
const summaryPrompt = `
Summarize this email thread in terms of key issues and sentiment:
${threadContext}
Identify:
1. Main issues discussed
2. Customer sentiment progression
3. What''s been tried before
4. What should happen next
Return JSON with these fields.
`;
return JSON.parse(await llm.generate(summaryPrompt));
}
Routing to Correct Team
Assign emails to appropriate departments:
interface RoutingDecision {
team: string;
queue: string;
priority: number;
assignee?: string;
escalationReason?: string;
}
async function routeEmail(
email: ClassifiedEmail,
priority: PriorityScore,
context: any
): Promise<RoutingDecision> {
const routingPrompt = `
Route this email to the correct team:
Intent: ${email.intent}
Priority score: ${priority.score}
Customer type: ${context.customer?.tier}
Previous tickets: ${context.previousTicketCount}
Teams available:
- support-tier1 (fast-track simple issues)
- support-tier2 (complex technical issues)
- sales (new opportunities)
- billing (payments, subscriptions)
- escalations (VIP, legal concerns)
Return JSON: { team, queue, priority (1-5), escalationReason if needed }
`;
return JSON.parse(await llm.generate(routingPrompt));
}
Confidence-Based Human Review Queue
Route uncertain cases to humans:
async function triageEmail(
email: ClassifiedEmail,
classification: ClassifiedEmail,
draft: DraftEmail,
routing: RoutingDecision
): Promise<'auto_send' | 'human_review'> {
const reviewThreshold = 0.88; // Require 88%+ confidence for auto-send
const confidence = {
classificationConfidence: classification.confidence,
draftQuality: await assessDraftQuality(draft),
routingConfidence: 0.95 // Static routing after extensive rules
};
const averageConfidence = Object.values(confidence)
.reduce((a, b) => a + b) / Object.keys(confidence).length;
if (averageConfidence < reviewThreshold) {
return 'human_review';
}
return 'auto_send';
}
async function assessDraftQuality(draft: DraftEmail): Promise<number> {
const qualityChecks = {
hasGreeting: draft.body.match(/^(Hi|Hello|Dear)/i) ? 1 : 0,
hasClosing: draft.body.match(/(Best regards|Thanks|Sincerely)/i) ? 1 : 0,
hasActionItem: draft.body.match(/(will|next|action|steps)/i) ? 1 : 0,
noPlaceholders: draft.body.includes('{{') ? 0 : 1,
lengthReasonable: draft.body.length > 50 && draft.body.length < 1000 ? 1 : 0.5
};
return Object.values(qualityChecks).reduce((a, b) => a + b) / Object.keys(qualityChecks).length;
}
Unsubscribe and Compliance Handling
Respect user preferences and regulations:
interface EmailCompliance {
canSend: boolean;
unsubscribeReason?: string;
requiresConsent?: boolean;
}
async function checkCompliance(
email: ClassifiedEmail
): Promise<EmailCompliance> {
// Check unsubscribe lists, GDPR/CAN-SPAM compliance
const isUnsubscribed = await unsubscribeDb.exists(email.from);
const consentGiven = await consentDb.hasConsent(email.from, 'marketing');
if (isUnsubscribed) {
return {
canSend: false,
unsubscribeReason: 'User is on global unsubscribe list'
};
}
if (email.intent === 'sales' && !consentGiven) {
return {
canSend: true,
requiresConsent: true
};
}
return { canSend: true };
}
Checklist
- Classify emails into 6-8 intent categories with confidence scoring
- Score priority 0-100 based on urgency signals and customer value
- Generate contextual drafts using customer and order data
- Allow tone adjustment (formal, casual, friendly, apologetic)
- Build library of templates with placeholder variables
- Generate 2-3 response options to let operators choose
- Summarize email threads for context before responding
- Route to appropriate teams based on intent and priority
- Require human review if confidence < 88% on any component
- Check unsubscribe lists and GDPR/CAN-SPAM compliance
- Log all auto-generated emails and track accuracy
- A/B test template variations and measure response rates
Conclusion
AI email automation transforms reactive customer service into proactive, responsive support. By combining intent classification, priority scoring, smart drafting, and intelligent routing, you can handle 3-5x more inbound emails without proportional headcount growth. Start with high-confidence auto-responses and simple routing, gradually expanding as your system proves reliable. Always maintain human-in-the-loop for sensitive issues, ambiguous cases, and regulatory compliance.