DEV Community

Software Solutions
Software Solutions

Posted on

Building an Intelligent AI Email Assistant Using OpenAI and Node.js

Inboxes today are louder than ever. Between support tickets, sales inquiries, and automated notifications, developers and teams waste hours sorting messages, categorizing priority, and drafting repetitive responses.

Building an AI Email Assistant that auto-summarizes incoming threads, extracts intent/sentiment, and drafts context-aware replies is one of the most practical applications of Large Language Models today.

In this tutorial, we will build a production-grade AI email processor using Node.js, TypeScript, and OpenAI’s Structured Outputs.


1. System Architecture

Rather than passing raw email text into a basic prompt and getting back unpredictable string outputs, we structure our pipeline into discrete stages:

+------------------+     +-----------------------+     +------------------------+
|  Incoming Email  | --> |  OpenAI Structured    | --> |  Automated Workflow /  |
|  (Raw Content)   |     |  Parsing (Zod Schema) |     |  Draft Output Router   |
+------------------+     +-----------------------+     +------------------------+

Enter fullscreen mode Exit fullscreen mode

Key Workflow Steps:

  1. Categorization: Classifies emails into categories like Support, Sales, Billing, or Spam.
  2. Sentiment & Priority Analysis: Detects urgency (urgent, high, normal, low) and tone.
  3. Structured Entity Extraction: Pulls actionable items, deadlines, and contact names.
  4. Draft Generation: Generates a professional reply matching company tone.

2. Setting Up the Project

Initialize a new Node.js TypeScript project and install the required packages:

mkdir ai-email-assistant
cd ai-email-assistant
npm init -y
npm install openai zod dotenv
npm install -D typescript @types/node tsx
npx tsc --init

Enter fullscreen mode Exit fullscreen mode

Create a .env file in your root folder:


Code snippet

OPENAI_API_KEY=your_openai_api_key_here

Enter fullscreen mode Exit fullscreen mode

3. Defining the Schema with Zod

By utilizing Structured Outputs, OpenAI guarantees that the model response strictly adheres to our defined JSON schema. No regex parsing or fallback error-handling required.

Create src/schema.ts:


import { z } from 'zod';

export const EmailAnalysisSchema = z.object({
  category: z.enum(['sales', 'support', 'billing', 'feedback', 'spam']),
  priority: z.enum(['urgent', 'high', 'normal', 'low']),
  sentiment: z.enum(['positive', 'neutral', 'negative']),
  summary: z.string().describe('A 1-2 sentence concise summary of the email content.'),
  actionableItems: z.array(z.string()).describe('List of specific tasks requested by the sender.'),
  suggestedReply: z.string().describe('A polite, professional response addressing all concerns in the email.'),
  requiresHumanReview: z.boolean().describe('Set to true if sensitive keywords, legal matters, or billing disputes are present.')
});

export type EmailAnalysis = z.infer<typeof EmailAnalysisSchema>;

Enter fullscreen mode Exit fullscreen mode

4. Building the Core Email Assistant Service

Now, let's create the service that calls OpenAI's Structured Outputs interface to process the incoming payload.

Create src/emailProcessor.ts:


import OpenAI from 'openai';
import { zodResponseFormat } from 'openai/helpers/zod';
import { EmailAnalysisSchema, EmailAnalysis } from './schema';
import dotenv from 'dotenv';

dotenv.config();

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

export async function processIncomingEmail(emailBody: string, senderEmail: string): Promise<EmailAnalysis null |> {
  try {
    const completion = await openai.beta.chat.completions.parse({
      model: 'gpt-4o-2024-08-06',
      messages: [
        {
          role: 'system',
          content: `You are an executive AI Email Assistant for an enterprise SaaS platform. 
          Analyze incoming emails carefully, assess priority and urgency, and generate a professional, context-aware draft response.`
        },
        {
          role: 'user',
          content: `From: ${senderEmail}\n\nEmail Content:\n${emailBody}`
        }
      ],
      response_format: zodResponseFormat(EmailAnalysisSchema, 'email_analysis'),
      temperature: 0.2, // Low temperature for consistent classification
    });

    return completion.choices[0].message.parsed;
  } catch (error) {
    console.error('Error processing email:', error);
    return null;
  }
}

Enter fullscreen mode Exit fullscreen mode

5. Testing the Implementation

Let's test the pipeline against an incoming support ticket email.

Create src/index.ts:


import { processIncomingEmail } from './emailProcessor';

const sampleEmail = `
Hi Support Team,

Our billing department noticed that we were double-billed for invoice #INV-9402 on our account this morning. 
This needs to be resolved immediately as it caused an overdraw on our department corporate card.

Please refund the duplicate charge of $499 as soon as possible and send updated receipts.

Thanks,
Sarah Jenkins
Operations Manager | Acme Corp
`;

async function run() {
  console.log('Processing incoming email...\n');
  const result = await processIncomingEmail(sampleEmail, 's.jenkins@acme.com');

  if (result) {
    console.log('=== AI Email Assistant Output ===\n');
    console.log(`Category:       ${result.category.toUpperCase()}`);
    console.log(`Priority:       ${result.priority.toUpperCase()}`);
    console.log(`Sentiment:      ${result.sentiment}`);
    console.log(`Requires Human: ${result.requiresHumanReview ? 'YES' : 'NO'}`);
    console.log(`\nSummary:\n${result.summary}`);
    console.log(`\nActionable Items:`);
    result.actionableItems.forEach(item => console.log(` - ${item}`));
    console.log(`\nSuggested Draft Reply:\n----------------------------------------\n${result.suggestedReply}\n----------------------------------------`);
  }
}

run();

Enter fullscreen mode Exit fullscreen mode

Run the script using tsx:


Bash

npx tsx src/index.ts

Enter fullscreen mode Exit fullscreen mode

Expected Structured Output:


{
  "category": "billing",
  "priority": "urgent",
  "sentiment": "negative",
  "summary": "The customer reported duplicate billing for invoice #INV-9402 and requested an immediate $499 refund.",
  "actionableItems": [
    "Verify duplicate charge for invoice #INV-9402",
    "Process $499 refund",
    "Send updated receipts to s.jenkins@acme.com"
  ],
  "requiresHumanReview": true,
  "suggestedReply": "Hi Sarah,\n\nThank you for reaching out. I sincerely apologize for the inconvenience caused by the duplicate charge on invoice #INV-9402. I have escalated this directly to our billing department to process the $499 refund immediately. We will send over the updated receipts as soon as the transaction is complete.\n\nBest regards,\nSupport Team"
}

Enter fullscreen mode Exit fullscreen mode

6. Production Best Practices

When deploying an AI email workflow to production environments:

  1. Human-in-the-Loop Safeguards: Use requiresHumanReview flag to automatically queue sensitive, legal, or billing-related emails for agent approval before sending automated replies.

  2. Rate Limiting & Queueing: Pass incoming webhooks into a background worker queue (e.g., BullMQ with Redis) to process API calls asynchronously without dropping requests during spike hours.

  3. Context Injection: Retrieve recent user ticket history or CRM account data from your database and append it to the system message prompt for hyper-personalized responses.

Conclusion & Next Steps

Integrating OpenAI with structured schema enforcement allows you to build reliable, high-volume AI automation pipelines that eliminate repetitive administrative tasks while maintaining full control over output quality.

Looking to integrate custom AI agents, automated workflow systems, or enterprise-grade software into your business stack?

πŸ‘‰ Partner with Software Solutions for custom web application development, AI integrations, backend engineering, and cloud automation.

How are you leveraging OpenAI in your current backend services? Let me know in the comments below!

Top comments (0)