Lý thuyết
45 phút
Bài 1/13

Email Automation

Tự động hóa email workflow với n8n - filters, auto-replies, organization và email digests

📧 Email Automation

Email Management

Email là công cụ không thể thiếu nhưng cũng là nguồn stress lớn. Bài này hướng dẫn automate email workflows để tiết kiệm hàng giờ mỗi tuần.

Email Automation Overview

Common Email Tasks to Automate:

Text
1AUTOMATABLE EMAIL TASKS
2───────────────────────
3
4📨 INBOX MANAGEMENT
5├── Filter & sort incoming emails
6├── Auto-label/folder organization
7├── Spam/newsletter handling
8└── Priority flagging
9
10✉️ RESPONSES
11├── Auto-acknowledgment
12├── Out-of-office replies
13├── Template responses
14└── Follow-up reminders
15
16📊 PROCESSING
17├── Extract attachments
18├── Save to cloud storage
19├── Create tasks from emails
20├── Log to spreadsheets
21
22📝 DIGESTS
23├── Daily email summaries
24├── Team update consolidation
25├── Newsletter aggregation
26└── Report compilation

Gmail Setup in n8n

Connect Gmail:

Text
11. Add Gmail node
22. Create new credential
33. Choose OAuth2
44. Sign in with Google
55. Grant permissions:
6 - Read emails
7 - Send emails
8 - Modify labels

Required Scopes:

Text
1https://www.googleapis.com/auth/gmail.modify
2https://www.googleapis.com/auth/gmail.send
3https://www.googleapis.com/auth/gmail.labels

Workflow 1: Email Filter & Organizer

Auto-Label Important Emails:

JSON
1{
2 "name": "Email Auto-Labeler",
3 "nodes": [
4 {
5 "name": "Gmail Trigger",
6 "type": "n8n-nodes-base.gmailTrigger",
7 "parameters": {
8 "pollTimes": {
9 "item": [{ "mode": "everyMinute" }]
10 },
11 "filters": {
12 "includeSpamTrash": false
13 }
14 }
15 },
16 {
17 "name": "Check Sender",
18 "type": "n8n-nodes-base.switch",
19 "parameters": {
20 "rules": {
21 "rules": [
22 {
23 "value": "@important-client.com",
24 "output": "VIP"
25 },
26 {
27 "value": "@newsletter",
28 "output": "Newsletter"
29 },
30 {
31 "value": "invoice|receipt",
32 "output": "Finance"
33 }
34 ]
35 }
36 }
37 },
38 {
39 "name": "Add VIP Label",
40 "type": "n8n-nodes-base.gmail",
41 "parameters": {
42 "operation": "addLabels",
43 "messageId": "={{ $('Gmail Trigger').item.json.id }}",
44 "labelIds": ["Label_VIP"]
45 }
46 }
47 ]
48}

Label Detection Logic:

JavaScript
1// Code node: Classify email
2const email = $input.item.json;
3const from = email.from.toLowerCase();
4const subject = email.subject.toLowerCase();
5
6let labels = [];
7let priority = 'normal';
8
9// VIP senders
10const vipDomains = ['@importantclient.com', '@boss.com'];
11if (vipDomains.some(domain => from.includes(domain))) {
12 labels.push('VIP');
13 priority = 'high';
14}
15
16// Finance related
17if (subject.match(/invoice|receipt|payment|billing/)) {
18 labels.push('Finance');
19}
20
21// Meeting requests
22if (subject.match(/meeting|call|schedule|zoom|teams/)) {
23 labels.push('Meetings');
24}
25
26// Support tickets
27if (from.includes('support') || subject.match(/ticket|issue|bug/)) {
28 labels.push('Support');
29}
30
31return {
32 ...email,
33 autoLabels: labels,
34 priority: priority
35};

Workflow 2: Auto-Acknowledgment

Send Auto-Reply for Important Emails:

Text
1WORKFLOW: Auto-Acknowledge VIP
2──────────────────────────────
3
4[Gmail Trigger]
5
6
7[Filter: From VIP?]
8
9 ├── Yes ──► [Send Acknowledgment]
10 │ │
11 │ ▼
12 │ [Create Reminder]
13
14 └── No ──► [Normal Processing]

Auto-Reply Template:

JavaScript
1// Code node: Generate reply
2const email = $input.item.json;
3const senderName = email.from.split('<')[0].trim();
4
5const acknowledgment = `
6Hi ${senderName},
7
8Thank you for your email. I've received it and will respond in detail within 24 hours.
9
10If this is urgent, please call me at [phone] or mark as urgent in the subject.
11
12Best regards,
13[Your Name]
14
15---
16This is an automated acknowledgment.
17`;
18
19return {
20 to: email.from,
21 subject: `Re: ${email.subject}`,
22 body: acknowledgment,
23 originalId: email.id
24};

Business Hours Check:

JavaScript
1// Only auto-reply during business hours
2const now = new Date();
3const hour = now.getHours();
4const day = now.getDay();
5
6const isBusinessHours = (
7 day >= 1 && day <= 5 && // Mon-Fri
8 hour >= 9 && hour < 18 // 9 AM - 6 PM
9);
10
11if (!isBusinessHours) {
12 return {
13 shouldAutoReply: true,
14 message: "Auto-reply: Will respond next business day"
15 };
16}
17
18return {
19 shouldAutoReply: false
20};

Workflow 3: Email to Task

Convert Emails to Tasks:

Text
1[Gmail Trigger]
2
3
4[Filter: Has "TODO" or from Boss]
5
6
7[Extract Task Info]
8
9
10[Create Task in Todoist/Notion/Trello]
11
12
13[Archive Email]
14
15
16[Slack Notification]

Task Extraction Code:

JavaScript
1const email = $input.item.json;
2
3// Extract due date from email
4const dueDateMatch = email.body.match(/due:?\s*(\d{1,2}\/\d{1,2}|\d{4}-\d{2}-\d{2})/i);
5const priorityMatch = email.body.match(/priority:?\s*(high|medium|low)/i);
6
7// Extract task title
8let taskTitle = email.subject;
9if (taskTitle.startsWith('Re:') || taskTitle.startsWith('Fwd:')) {
10 taskTitle = taskTitle.replace(/^(Re:|Fwd:)\s*/i, '');
11}
12
13return {
14 title: taskTitle.substring(0, 100),
15 description: `From: ${email.from}\n\n${email.snippet}`,
16 dueDate: dueDateMatch ? dueDateMatch[1] : null,
17 priority: priorityMatch ? priorityMatch[1] : 'medium',
18 sourceEmailId: email.id,
19 link: `https://mail.google.com/mail/u/0/#inbox/${email.id}`
20};

Create Notion Task:

JSON
1{
2 "name": "Create Notion Task",
3 "type": "n8n-nodes-base.notion",
4 "parameters": {
5 "operation": "create",
6 "databaseId": "your-database-id",
7 "properties": {
8 "Name": {
9 "title": [{ "text": { "content": "={{ $json.title }}" }}]
10 },
11 "Status": {
12 "select": { "name": "To Do" }
13 },
14 "Source": {
15 "select": { "name": "Email" }
16 },
17 "Due Date": {
18 "date": { "start": "={{ $json.dueDate }}" }
19 }
20 }
21 }
22}

Workflow 4: Email Digest

Daily Email Summary:

Text
1WORKFLOW: Morning Email Digest
2──────────────────────────────
3
4[Schedule: 7 AM]
5
6
7[Get Unread Emails]
8
9
10[Categorize & Summarize]
11
12
13[Generate HTML Report]
14
15
16[Send Digest to Self]

Fetch & Categorize:

JavaScript
1// Code node: Categorize emails
2const emails = $input.all();
3
4const categories = {
5 urgent: [],
6 vip: [],
7 newsletters: [],
8 social: [],
9 other: []
10};
11
12const vipSenders = ['boss@company.com', 'ceo@company.com'];
13
14for (const email of emails) {
15 const from = email.json.from.toLowerCase();
16 const subject = email.json.subject.toLowerCase();
17
18 if (subject.includes('urgent') || subject.includes('asap')) {
19 categories.urgent.push(email.json);
20 } else if (vipSenders.some(vip => from.includes(vip))) {
21 categories.vip.push(email.json);
22 } else if (from.includes('newsletter') || from.includes('substack')) {
23 categories.newsletters.push(email.json);
24 } else if (from.includes('linkedin') || from.includes('facebook')) {
25 categories.social.push(email.json);
26 } else {
27 categories.other.push(email.json);
28 }
29}
30
31return { categories };

Generate Digest HTML:

JavaScript
1const { categories } = $input.item.json;
2
3const formatEmails = (emails, emoji) => {
4 if (emails.length === 0) return '';
5
6 let html = emails.map(e => `
7 <li>
8 <strong>${e.from.split('<')[0]}</strong><br>
9 <a href="https://mail.google.com/mail/u/0/#inbox/${e.id}">${e.subject}</a>
10 <p style="color: #666; margin: 5px 0;">${e.snippet.substring(0, 100)}...</p>
11 </li>
12 `).join('');
13
14 return `<h3>${emoji} ${emails.length} emails</h3><ul>${html}</ul>`;
15};
16
17const digest = `
18<!DOCTYPE html>
19<html>
20<head>
21 <style>
22 body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; }
23 h2 { color: #333; border-bottom: 2px solid #4CAF50; }
24 h3 { color: #555; }
25 ul { list-style: none; padding: 0; }
26 li { background: #f9f9f9; padding: 10px; margin: 5px 0; border-radius: 5px; }
27 a { color: #1a73e8; text-decoration: none; }
28 </style>
29</head>
30<body>
31 <h2> Morning Email Digest</h2>
32 <p>You have <strong>${Object.values(categories).flat().length}</strong> unread emails</p>
33
34 ${formatEmails(categories.urgent, '🔴 Urgent')}
35 ${formatEmails(categories.vip, '⭐ VIP')}
36 ${formatEmails(categories.other, '📬 Other')}
37
38 <p style="color: #666; font-size: 12px;">
39 ${categories.newsletters.length} newsletters,
40 ${categories.social.length} social notifications skipped
41 </p>
42</body>
43</html>
44`;
45
46return { digest };

Workflow 5: Attachment Handler

Save Attachments to Cloud:

Text
1[Gmail Trigger: Has Attachment]
2
3
4[Get Attachment Data]
5
6
7[Google Drive: Upload]
8
9
10[Update Spreadsheet Log]
11
12
13[Archive Email]

Attachment Processing:

JavaScript
1// Filter for specific attachment types
2const email = $input.item.json;
3const attachments = email.attachments || [];
4
5const allowedTypes = ['.pdf', '.xlsx', '.docx', '.csv'];
6
7const validAttachments = attachments.filter(att => {
8 const ext = att.filename.toLowerCase().slice(-5);
9 return allowedTypes.some(type => ext.includes(type));
10});
11
12if (validAttachments.length === 0) {
13 return null; // Skip this email
14}
15
16return {
17 emailId: email.id,
18 subject: email.subject,
19 from: email.from,
20 attachments: validAttachments
21};

Workflow 6: Follow-Up Reminder

Track Unanswered Emails:

Text
1WORKFLOW: Follow-Up Tracker
2───────────────────────────
3
4[Schedule: Daily at 9 AM]
5
6
7[Get Sent Emails (last 3 days)]
8
9
10[For Each: Check for Reply]
11
12
13[Filter: No Reply Yet]
14
15
16[Create Reminder]
17
18
19[Send Slack Summary]

Check Reply Status:

JavaScript
1// For each sent email, check if there's a reply
2const sentEmail = $input.item.json;
3const threadId = sentEmail.threadId;
4
5// If thread has more messages after our sent email, there's a reply
6const threadMessages = $('Get Thread').item.json.messages;
7const sentTimestamp = new Date(sentEmail.internalDate);
8
9const hasReply = threadMessages.some(msg => {
10 const msgDate = new Date(msg.internalDate);
11 return msgDate > sentTimestamp && !msg.labelIds.includes('SENT');
12});
13
14const daysSinceSent = Math.floor(
15 (Date.now() - sentTimestamp) / (1000 * 60 * 60 * 24)
16);
17
18return {
19 ...sentEmail,
20 hasReply,
21 daysSinceSent,
22 needsFollowUp: !hasReply && daysSinceSent >= 3
23};

Best Practices

Email Automation Tips:

Text
1DO's
2────
3✅ Start small, automate one workflow at a time
4✅ Always test with your own emails first
5✅ Set up error notifications
6✅ Keep auto-replies professional
7✅ Include unsubscribe option for bulk emails
8
9DON'Ts
10──────
11❌ Auto-reply to auto-replies (loop!)
12❌ Process spam folder
13❌ Send automated emails too frequently
14❌ Ignore email threading
15❌ Auto-delete without backup

Prevent Reply Loops:

JavaScript
1// Check for auto-reply indicators
2const email = $input.item.json;
3
4const isAutoReply = (
5 email.headers['auto-submitted'] === 'auto-replied' ||
6 email.headers['x-auto-response-suppress'] ||
7 email.subject.toLowerCase().includes('auto-reply') ||
8 email.subject.toLowerCase().includes('automatic reply') ||
9 email.from.toLowerCase().includes('noreply')
10);
11
12if (isAutoReply) {
13 // Don't send auto-reply to auto-replies!
14 return null;
15}
16
17return email;

Bài Tập Thực Hành

Email Automation Challenge

Build your email system:

  1. Create email filter for VIP senders
  2. Build auto-acknowledgment for important emails
  3. Set up email-to-task workflow
  4. Design morning email digest
  5. Implement follow-up reminder system

Reclaim your inbox! 📧

Email Workflow Checklist

Before Going Live

Test these scenarios:

  • Auto-reply doesn't create loops
  • Filters correctly categorize emails
  • Tasks created with correct info
  • Digest sends at right time
  • Attachments saved properly
  • Error handling works

Key Takeaways

Remember
  • 📧 Filter first - Don't process everything
  • Business hours - Respect timing
  • 🔄 Prevent loops - Check for auto-replies
  • 📝 Log everything - Track what's automated
  • 🧪 Test thoroughly - Before going live

Tiếp Theo

Bài tiếp theo: Calendar Workflows - Reminders, meeting prep, và follow-up automation.