How to Build an Email AI Agent Using N8N (FREE Template)

Managing emails can be a time-consuming task, especially if you receive dozens (or hundreds) every day. In this tutorial, we’ll build an Email AI Agent using N8N that drafts replies automatically, waits for your approval, and sends or updates messages based on your commands.

This workflow is perfect for busy professionals, business owners, and automation enthusiasts who want to save time without losing control over their communication.


What You’ll Need


How the Workflow Works

  1. Receive an Email – n8n detects incoming messages via your connected email account.
  2. Generate a Draft – The AI node creates a reply based on the email content.
  3. Send for Review – The draft is sent to you on telegram for review.
  4. Wait for Your Command:
    • “yes” → Sends the drafted reply immediately.
    • “tweak” → Sends the draft back for edits.
    • “cancel” → Stops the process.
  5. Based on your command it executes the step

JavaScript & System Message

  • Gmail Output Cleaner Javascript
const items = [];

for (const item of $input.all()) {
  const json = item.json;

  
  const fromAddress = json.from?.value?.[0]?.address || '';
  const subject = json.subject || '';
  const message = json.text || '';
  const threadId = json.threadId || '';
  const labelId = (json.labelIds || []).find(id => id.startsWith('Label_')) || 'nil';

  items.push({
    json: {
      email_summary: {
        fromAddress,
        subject,
        message,
        threadId,
        labelId,
      }
    }
  });
}

return items;
  • Email Analysing AI Agent System Message
You are an intelligent email classification and assistant agent.

Analyze the following email and return a pure JSON object with:

1. "label" — One of:
   - Customer Support(Label_112345)
   - Invoice/Payment(Label_34335)
   - Job Inquiry(Label_343435)
   - Collaboration(Label_33232)
   - Uncategorized(Label_43223)
   - Urgent(Label_32323)

2. "reply_draft" — A polite, short reply (if needed). Use "null" if no reply is needed.

3. If the email is from a no-reply address (e.g., "no-reply@...") or social/media platforms (e.g., Facebook, Instagram, Twitter, LinkedIn, etc.), do not generate a reply. Set "reply_draft" as "null" regardless of content.

4. After identifying the label, use the label ID given in the brackets and call Gmail Label Tool to apply that label to the email — but do not add it if the email already has one label ID.

5. If a reply is created (i.e., "reply_draft" is not null), always call the Draft Email Tool and include a field called "draftID" in the output JSON with the value from the draft tool (e.g., "r-123456..."). Never omit "draftID" when a draft is generated.

6. Label the email as "Urgent" if it appears time-sensitive, includes deadlines, critical issues, or requests a quick response — even if urgency is implied.

7. All reply drafts should be signed off with:
Best,
 Rajeev

Important:
- Return only a valid JSON object.
- Do NOT include markdown, comments, wrappers, or explanations.
- Ensure that "draftID" is always included if "reply_draft" is not null.

Example Output:
{
  "label": "Collaboration",
  "reply_draft": "Thanks for your email. Happy to explore a collaboration. Let me know your availability.\n\nBest,\n Rajeev",
  "draftID": "r-123456"
}
  • Replace Label ID & Email signature with your own.

  • AI Agent output cleaner Javascript
const rawOutput = items[0].json.output;

// Step 1: Remove markdown/code block wrappers
const cleaned = rawOutput.replace(/```json|```/g, '').trim();

// Step 2: Parse the cleaned string into a JSON object
let parsed;
try {
  parsed = JSON.parse(cleaned);
} catch (err) {
  throw new Error('Failed to parse Gemini response: ' + err.message);
}

// Step 3: Get subject, from, and message
const emailSummary = $node["Code"].json.email_summary || {};
const from = emailSummary.fromAddress || '';
const subject = emailSummary.subject || '';
const fullMessage = emailSummary.message || '';

// Step 4: Remove previous reply (if any)
const splitRegex = /On\s.+wrote:/i;
const message = splitRegex.test(fullMessage)
  ? fullMessage.split(splitRegex)[0].trim()
  : fullMessage.trim();

// Final output (without previousMessage)
return [{
  json: {
    ...parsed,
    from,
    subject,
    message
  }
}];
  • Redis node value script
{{ JSON.stringify({
messageId: $node["Gmail Trigger"].json["id"],
  subject: $node["Gmail Trigger"].json["Subject"],
  draft_reply: $json.reply_draft,
  draftID: $json.draftID
}) }}
  • Javascript for cleaning output from Redis ( “yes” input )
const raw = $json.Draft_Details;

let parsed;
try {
  parsed = JSON.parse(raw);
} catch (err) {
  throw new Error('Invalid JSON in Draft_Details: ' + err.message);
}

// Convert line breaks to <br> for HTML version
const html = parsed.draft_reply.replace(/\n/g, '<br>');

return [
  {
    json: {
      messageId: parsed.messageId,
      draft_reply: html,       // HTML version with <br>
      draftID: parsed.draftID
    }
  }
];
  • Javascript for cleaning output from Redis ( “cancel” input )
const raw = $json.Draft_Details;

let parsed;
try {
  parsed = JSON.parse(raw);
} catch (err) {
  throw new Error('Invalid JSON in Draft_Details: ' + err.message);
}


return [
  {
    json: {
      draftID: parsed.draftID,
          
                  
    }
  }
];
  • Extracting Message ID from Tweak message
{{ $json.message.reply_to_message.text.match(/id:\s*(\w+)/)[1]  }}
  • Draft modifying AI agent output cleaning javascript
const rawOutput = $json.output;

let parsed;
try {
  parsed = JSON.parse(rawOutput);
} catch (err) {
  throw new Error('Failed to parse JSON: ' + err.message);
}

return [
  {
    json: {
      messageId: parsed.messageId,
      draft_reply: parsed.draft_reply,
      draftID: parsed.draftID
      
    }
  }
];
  • Save updated data on Redis
{{ JSON.stringify({
  messageId: $json.messageId,
  draft_reply: $json.draft_reply
}) }}

Key Advantages

Saves Time – No more manually writing every reply.
Keeps Control in Your Hands – You approve before sending.
Scalable – Works for 10 or 100 emails a day.
Persistent State Management – Redis ensures the workflow remembers your last action, even if n8n restarts.


Workflow Outline

  1. Trigger Node – Detect new incoming email.
  2. AI Node – Draft the response.
  3. Telegram Node – Send draft to you for approval.
  4. Wait for Response – Capture your reply (“yes”, “tweak”, “cancel”).
  5. Redis Node – Save/retrieve conversation state.
  6. Conditional Logic – Process based on your input.
  7. Email Node – Send final approved email.

🔗 Download Resources

• Download Workflow JSON File: Download


Example Use Cases

  • Client support responses
  • Sales inquiry follow-ups
  • Internal team communications
  • Personal inbox management

Final Thoughts

With this Email AI Agent, you get the perfect balance between automation and control. The AI saves you time drafting responses, while you still approve the final send — ensuring accuracy and tone.

If you want to see the step-by-step setup in action, check out our video tutorial here:

Keep Reading

Category AI Posted on

How to Self-Host N8N in Just a Few Minutes (Beginner Guide)

If you're tired of paying for n8n Cloud or dealing with workflow limits, the best solution is to self-host n8n on your own VPS.The good news? You don’t need to be a developer or use complex Docker commands — with Hostinger’s new 1-click n8n setup, you can install n8n in just a few minutes. Self-hosting gives you: Full data control No workflow limitations Faster performance A cost of as low as $6/mont…
Continue reading
Category AI Posted on

Analyse Any Business Instantly Using N8N and BrowserAct

If you do cold outreach, you already know how much time it takes to understand a prospect’s business before sending a message. Most people skip this step and that’s why their outreach sounds generic, boring, and gets ignored. In this tutorial, I’ll share an automated workflow built using n8n, BrowserAct, and AI, which: Visits your prospect’s website Scrapes the full content Cleans the messy text Gene…
Continue reading