AINode.jsAutomation
AI Email Reply Generator with Context
TT
TopicTrick Team
AI Email Reply Generator with Context
Email takes up a huge chunk of every professional's day. This tool reads an email thread and generates a contextually appropriate, professional reply — including subject line suggestions, multiple tone variants, and specific talking points you can accept, edit, or regenerate in seconds.
This is Tool 12 of the Build 50 AI Automation Tools course.
What You'll Build
POST /reply— provide an email thread, receive 2-3 reply variants- Tone selector: professional, friendly, concise, assertive, empathetic
POST /reply/refine— refine a draft reply with specific feedback- Optional Gmail API integration to fetch real threads
Setup
bash
mkdir email-reply && cd email-reply
npm init -y
npm install express openai dotenvbash
# .env
OPENAI_API_KEY=sk-your-key-here
PORT=3000Email Reply Service
js
// src/services/emailService.js
import OpenAI from 'openai';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
function formatThread(thread) {
return thread.map((msg, i) =>
`--- Message ${i + 1} ---\nFrom: ${msg.from}\nDate: ${msg.date || 'unknown'}\n\n${msg.body}`
).join('\n\n');
}
export async function generateReply({
thread,
myRole = '',
tone = 'professional',
pointsToAddress = [],
variants = 2,
}) {
const threadText = Array.isArray(thread) ? formatThread(thread) : thread;
const points = pointsToAddress.length > 0
? `\nSpecific points to address:\n${pointsToAddress.map(p => `- ${p}`).join('\n')}`
: '';
const toneGuide = {
professional: 'formal, respectful, business-appropriate',
friendly: 'warm, personable, approachable — like writing to a colleague you know well',
concise: 'short and to the point — 3-5 sentences maximum, no pleasantries',
assertive: 'confident and direct — clear asks and expectations without being aggressive',
empathetic: 'understanding and supportive — acknowledge feelings before addressing the issue',
formal: 'highly formal — full sentences, no contractions, appropriate for legal or executive communication',
};
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{
role: 'system',
content: `You are an expert email writer. Generate ${variants} reply variant(s) for the email thread below.
My role: ${myRole || 'not specified'}
Tone: ${tone} — ${toneGuide[tone] || tone}${points}
Return ONLY a JSON object:
{
"subject": "suggested reply subject line",
"variants": [
{
"label": "string (e.g. 'Professional', 'Concise')",
"body": "complete email body ready to send",
"wordCount": number,
"tone": "description of tone used"
}
],
"keyPointsAddressed": ["list of points from the thread this reply addresses"],
"suggestedActions": ["any follow-up actions implied by this reply"]
}`,
},
{
role: 'user',
content: `EMAIL THREAD:\n\n${threadText}`,
},
],
temperature: 0.7,
response_format: { type: 'json_object' },
});
return JSON.parse(response.choices[0].message.content);
}
export async function refineReply({ originalReply, feedback, tone = 'professional' }) {
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{
role: 'system',
content: `You are an expert email writer. Refine the email draft based on the user's feedback.
Return ONLY a JSON object: {"subject": "string", "body": "refined email body", "changes": ["list of changes made"]}`,
},
{
role: 'user',
content: `ORIGINAL DRAFT:\n${originalReply}\n\nFEEDBACK:\n${feedback}\n\nTone: ${tone}`,
},
],
temperature: 0.5,
response_format: { type: 'json_object' },
});
return JSON.parse(response.choices[0].message.content);
}API Routes + Server
js
// src/server.js
import 'dotenv/config';
import express from 'express';
import { generateReply, refineReply } from './services/emailService.js';
const app = express();
app.use(express.json({ limit: '100kb' }));
app.post('/reply', async (req, res, next) => {
try {
const { thread, myRole, tone, pointsToAddress, variants } = req.body;
if (!thread) return res.status(400).json({ error: 'thread is required' });
const result = await generateReply({ thread, myRole, tone, pointsToAddress, variants });
res.json({ success: true, ...result });
} catch (err) { next(err); }
});
app.post('/reply/refine', async (req, res, next) => {
try {
const { originalReply, feedback, tone } = req.body;
if (!originalReply || !feedback) return res.status(400).json({ error: 'originalReply and feedback required' });
const result = await refineReply({ originalReply, feedback, tone });
res.json({ success: true, ...result });
} catch (err) { next(err); }
});
app.get('/health', (_req, res) => res.json({ status: 'ok' }));
app.use((err, _req, res, _next) => res.status(500).json({ error: err.message }));
app.listen(process.env.PORT ?? 3000, () => console.log('Email Reply Generator running'));Testing
bash
curl -X POST http://localhost:3000/reply \
-H "Content-Type: application/json" \
-d '{
"thread": [
{
"from": "client@company.com",
"date": "2025-11-15",
"body": "Hi, we have been using your platform for 3 months now and are not seeing the ROI we expected. Our team adoption is low and we are considering cancelling. Can we schedule a call?"
}
],
"myRole": "Customer Success Manager at a SaaS company",
"tone": "empathetic",
"pointsToAddress": ["Acknowledge their frustration", "Offer a success review call", "Mention specific features they might not be using"],
"variants": 2
}'Sample response:
json
{
"subject": "Re: Platform ROI — Let's Get This Right",
"variants": [
{
"label": "Empathetic & Action-Oriented",
"body": "Hi [Name],\n\nThank you for being honest with us — I really appreciate you reaching out rather than simply cancelling, and I want to make this right.\n\nLow adoption after 3 months is something we can absolutely turn around. In my experience, the teams that see the biggest ROI are using [Feature A] and [Feature B] — I'd love to walk your team through these on a 30-minute call and set up a 60-day success plan.\n\nAre you free for a call this week? I have Thursday at 2pm or Friday at 10am.\n\nBest,\n[Your Name]",
"wordCount": 98,
"tone": "empathetic, solution-focused"
}
],
"keyPointsAddressed": ["Low adoption", "ROI concerns", "Cancellation risk"],
"suggestedActions": ["Schedule success review call", "Prepare feature adoption report before the call"]
}Gmail API Integration
bash
npm install googleapisjs
import { google } from 'googleapis';
export async function getGmailThread(threadId, auth) {
const gmail = google.gmail({ version: 'v1', auth });
const { data } = await gmail.users.threads.get({ userId: 'me', id: threadId });
return data.messages.map(msg => {
const headers = Object.fromEntries(msg.payload.headers.map(h => [h.name, h.value]));
const body = msg.payload.parts?.[0]?.body?.data
? Buffer.from(msg.payload.parts[0].body.data, 'base64').toString()
: '';
return { from: headers.From, date: headers.Date, body };
});
}Build 50 AI Automation Tools — Tool 12 of 50
Email reply generation is live. Continue to Tool 13 to build a cold outreach personalizer at scale.
Summary
- The full thread context in the system prompt enables GPT-4o to write replies that feel natural and continuous
- Tone variants give the user options without needing to make multiple API calls separately
- Refine endpoint supports a human-in-the-loop workflow — generate, review, refine
- Gmail API integration closes the loop from reading to drafting to sending without leaving your tool
Continue to Tool 13: Cold Outreach Personalizer →
Post Navigation (Previous/Next)
