📧 Email Automation
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 TASKS2───────────────────────3 4📨 INBOX MANAGEMENT5├── Filter & sort incoming emails6├── Auto-label/folder organization7├── Spam/newsletter handling8└── Priority flagging9 10✉️ RESPONSES11├── Auto-acknowledgment12├── Out-of-office replies13├── Template responses14└── Follow-up reminders15 16📊 PROCESSING17├── Extract attachments18├── Save to cloud storage19├── Create tasks from emails20├── Log to spreadsheets21 22📝 DIGESTS23├── Daily email summaries24├── Team update consolidation25├── Newsletter aggregation26└── Report compilationGmail Setup in n8n
Connect Gmail:
Text
11. Add Gmail node22. Create new credential33. Choose OAuth244. Sign in with Google55. Grant permissions:6 - Read emails7 - Send emails8 - Modify labelsRequired Scopes:
Text
1https://www.googleapis.com/auth/gmail.modify2https://www.googleapis.com/auth/gmail.send3https://www.googleapis.com/auth/gmail.labelsWorkflow 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": false13 }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 email2const email = $input.item.json;3const from = email.from.toLowerCase();4const subject = email.subject.toLowerCase();56let labels = [];7let priority = 'normal';89// VIP senders10const vipDomains = ['@importantclient.com', '@boss.com'];11if (vipDomains.some(domain => from.includes(domain))) {12 labels.push('VIP');13 priority = 'high';14}1516// Finance related17if (subject.match(/invoice|receipt|payment|billing/)) {18 labels.push('Finance');19}2021// Meeting requests22if (subject.match(/meeting|call|schedule|zoom|teams/)) {23 labels.push('Meetings');24}2526// Support tickets27if (from.includes('support') || subject.match(/ticket|issue|bug/)) {28 labels.push('Support');29}3031return {32 ...email,33 autoLabels: labels,34 priority: priority35};Workflow 2: Auto-Acknowledgment
Send Auto-Reply for Important Emails:
Text
1WORKFLOW: Auto-Acknowledge VIP2──────────────────────────────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 reply2const email = $input.item.json;3const senderName = email.from.split('<')[0].trim();45const acknowledgment = `6Hi ${senderName},78Thank you for your email. I've received it and will respond in detail within 24 hours.910If this is urgent, please call me at [phone] or mark as urgent in the subject.1112Best regards,13[Your Name]1415---16This is an automated acknowledgment.17`;1819return {20 to: email.from,21 subject: `Re: ${email.subject}`,22 body: acknowledgment,23 originalId: email.id24};Business Hours Check:
JavaScript
1// Only auto-reply during business hours2const now = new Date();3const hour = now.getHours();4const day = now.getDay();56const isBusinessHours = (7 day >= 1 && day <= 5 && // Mon-Fri8 hour >= 9 && hour < 18 // 9 AM - 6 PM9);1011if (!isBusinessHours) {12 return {13 shouldAutoReply: true,14 message: "Auto-reply: Will respond next business day"15 };16}1718return {19 shouldAutoReply: false20};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;23// Extract due date from email4const 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);67// Extract task title8let taskTitle = email.subject;9if (taskTitle.startsWith('Re:') || taskTitle.startsWith('Fwd:')) {10 taskTitle = taskTitle.replace(/^(Re:|Fwd:)\s*/i, '');11}1213return {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 Digest2──────────────────────────────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 emails2const emails = $input.all();34const categories = {5 urgent: [],6 vip: [],7 newsletters: [],8 social: [],9 other: []10};1112const vipSenders = ['boss@company.com', 'ceo@company.com'];1314for (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}3031return { categories };Generate Digest HTML:
JavaScript
1const { categories } = $input.item.json;23const 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};1617const 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 skipped41 </p>42</body>43</html>44`;4546return { 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 types2const email = $input.item.json;3const attachments = email.attachments || [];45const allowedTypes = ['.pdf', '.xlsx', '.docx', '.csv'];67const validAttachments = attachments.filter(att => {8 const ext = att.filename.toLowerCase().slice(-5);9 return allowedTypes.some(type => ext.includes(type));10});1112if (validAttachments.length === 0) {13 return null; // Skip this email14}1516return {17 emailId: email.id,18 subject: email.subject,19 from: email.from,20 attachments: validAttachments21};Workflow 6: Follow-Up Reminder
Track Unanswered Emails:
Text
1WORKFLOW: Follow-Up Tracker2───────────────────────────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 reply2const sentEmail = $input.item.json;3const threadId = sentEmail.threadId;45// If thread has more messages after our sent email, there's a reply6const threadMessages = $('Get Thread').item.json.messages;7const sentTimestamp = new Date(sentEmail.internalDate);89const hasReply = threadMessages.some(msg => {10 const msgDate = new Date(msg.internalDate);11 return msgDate > sentTimestamp && !msg.labelIds.includes('SENT');12});1314const daysSinceSent = Math.floor(15 (Date.now() - sentTimestamp) / (1000 * 60 * 60 * 24)16);1718return {19 ...sentEmail,20 hasReply,21 daysSinceSent,22 needsFollowUp: !hasReply && daysSinceSent >= 323};Best Practices
Email Automation Tips:
Text
1DO's2────3✅ Start small, automate one workflow at a time4✅ Always test with your own emails first5✅ Set up error notifications6✅ Keep auto-replies professional7✅ Include unsubscribe option for bulk emails8 9DON'Ts10──────11❌ Auto-reply to auto-replies (loop!)12❌ Process spam folder13❌ Send automated emails too frequently14❌ Ignore email threading15❌ Auto-delete without backupPrevent Reply Loops:
JavaScript
1// Check for auto-reply indicators2const email = $input.item.json;34const 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);1112if (isAutoReply) {13 // Don't send auto-reply to auto-replies!14 return null;15}1617return email;Bài Tập Thực Hành
Email Automation Challenge
Build your email system:
- Create email filter for VIP senders
- Build auto-acknowledgment for important emails
- Set up email-to-task workflow
- Design morning email digest
- 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.
