Build an Email AI Agent in 30 Minutes
You can build an AI agent that reads incoming email, understands what the sender wants, and writes a reply. Not in a weekend. Not after a week of infrastructure work. In about 30 minutes.
This tutorial will walk you through it. By the end, you'll have a working agent that monitors an inbox, classifies incoming messages, generates context-aware replies, and sends them back on the correct thread.
What You'll Build
A simple support agent. It receives emails at a dedicated inbox, uses OpenAI to understand the intent, drafts a reply, and sends it. The sender sees a natural reply in the same thread. No new conversations. No broken context.
Here's what you need before starting:
- A mailbot account and API key (get one here)
- An OpenAI API key
- Node.js 18+ installed
That's it. No SMTP configuration. No MX record management. No MIME parsing libraries.
Step 1: Set Up the Project
Create a new directory and install two dependencies.
mkdir email-agent && cd email-agent
npm init -y
npm install @yopiesuryadi/mailbot-sdk openai
Create a file called agent.js. This is where everything lives.
import { MailBot } from '@yopiesuryadi/mailbot-sdk';
import OpenAI from 'openai';
const mailbot = new MailBot({ apiKey: process.env.MAILBOT_API_KEY });
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
Two clients. That's your entire infrastructure layer.
Step 2: Create a Dedicated Inbox
Your agent needs its own inbox. In beta, the fastest path is a shared sandbox inbox on mailbot.id. No DNS. No custom domain. Just create the inbox and use the returned inbox ID and inbox address.
const inbox = await mailbot.inboxes.create({
username: 'support-agent',
display_name: 'Support Agent',
});
console.log(`Inbox ready: ${inbox.id} ${inbox.address}`);
This inbox has its own address, its own message history, and its own thread state. In the shared sandbox, the returned address will look like support-agent--abc123@mailbot.id. When someone emails this address, mailbot receives the message, parses it, threads it, and makes it available through the API.
Step 3: Listen for Incoming Email
When a new message arrives, your agent needs to pick it up reliably. The simplest beta path is waitFor(): block until a matching inbound message arrives, then handle it.
while (true) {
const result = await mailbot.messages.waitFor({
inboxId: inbox.id,
direction: 'inbound',
timeoutMs: 30000,
includeThread: true,
});
const message = result.data;
console.log(`New email from ${message.from_address}`);
console.log(`Subject: ${message.subject}`);
console.log(`Thread: ${message.thread_id}`);
await handleMessage(inbox.id, message);
}
This keeps the example honest to the SDK surface that exists today. You can later switch to webhooks if you want push delivery into your own app.
Step 4: Understand the Email with AI
Now for the interesting part. Take the incoming message and ask OpenAI to understand it.
async function classifyIntent(message) {
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{
role: 'system',
content: `You are an email classifier. Categorize the email into one of these intents: billing_question, technical_support, feature_request, general_inquiry. Respond with only the intent label.`,
},
{
role: 'user',
content: `From: ${message.from_address}\nSubject: ${message.subject}\n\n${message.body_text ?? ''}`,
},
],
});
return response.choices[0].message.content.trim();
}
Simple classification first. The intent determines how the agent should respond. You could route billing questions to a different handler than technical support requests. You could escalate feature requests to a human. The structure here is intentionally modular.
Step 5: Generate a Reply
With the intent classified, generate a reply that actually makes sense.
async function generateReply(message, intent) {
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{
role: 'system',
content: `You are a helpful support agent. The customer's intent is: ${intent}. Write a professional, concise reply. Do not make promises you cannot keep. If you don't know the answer, say so honestly and offer to escalate to a human.`,
},
{
role: 'user',
content: `From: ${message.from_address}\nSubject: ${message.subject}\n\n${message.body_text ?? ''}`,
},
],
});
return response.choices[0].message.content;
}
Notice the system prompt. It tells the model to be honest when it doesn't know something. This is important. An AI agent that fabricates answers is worse than no agent at all.
Step 6: Send the Reply on the Correct Thread
This is where most email integrations break down. Sending a reply sounds simple. But if the reply doesn't land in the same thread as the original message, the customer sees two separate conversations in their inbox. Confusing at best. Unprofessional at worst.
mailbot handles threading automatically. When you reply to a message through the reply endpoint, the SDK uses the correct thread context for you. The customer sees a clean, continuous conversation.
async function handleMessage(inboxId, message) {
const intent = await classifyIntent(message);
console.log(`Intent: ${intent}`);
const replyText = await generateReply(message, intent);
await mailbot.messages.reply({
inboxId,
messageId: message.id,
bodyText: replyText,
});
console.log(`Reply sent to ${message.from_address} on thread ${message.thread_id}`);
}
The important part is that you reply to the message itself, not that you manually build email headers. No Message-ID tracking on your end.
Step 7: Run It
Add your environment variables and start the agent.
export MAILBOT_API_KEY=mb_your_api_key
export OPENAI_API_KEY=sk_your_api_key
node agent.js
Send an email to your agent's inbox. Within seconds, you'll have a classified intent and an AI generated reply sitting in the sender's inbox, threaded correctly under the original message.
What You've Accomplished
In roughly 80 lines of code, you've built a system that:
- Receives email at a real address with full authentication (SPF, DKIM, DMARC)
- Parses inbound email automatically
- Classifies sender intent using AI
- Generates a context-aware reply
- Sends that reply on the correct thread
No SMTP server setup. No MIME parsing. No polling loops. No thread header management. mailbot handles the infrastructure. Your code handles the logic.
Going Further
This tutorial covers the foundation. Here's where you take it next.
Add conversation history. Right now, the agent treats each message independently. Use mailbot.threads.get(threadId) to fetch the full conversation and pass previous messages to OpenAI as context. The agent will generate replies that reference earlier parts of the discussion.
Route by intent. Instead of replying to everything, build different handlers for each intent. Billing questions go to one workflow. Technical support goes to another. Feature requests get logged and acknowledged.
Add human escalation. When the AI isn't confident, send a new outbound message or attach a human review workflow around the same thread. The human picks up where the agent left off.
Monitor delivery. Use mailbot's event notifications to track whether your replies were delivered, opened, or bounced. If a reply bounces, you probably have an invalid address. If it's never opened, maybe the subject line needs work.
The agent you built in 30 minutes is a starting point. The inbox, the threading, the delivery infrastructure: those are the foundation. Everything else is logic you layer on top.