AutomationFlowsAI & RAG › AI Email Agent - Self Learning

AI Email Agent - Self Learning

AI Email Agent - Self Learning. Uses gmail, httpRequest. Scheduled trigger; 14 nodes.

Cron / scheduled trigger★★★★☆ complexity14 nodesGmailHTTP Request
AI & RAG Trigger: Cron / scheduled Nodes: 14 Complexity: ★★★★☆ Added:

This workflow follows the Gmail → HTTP Request recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "AI Email Agent - Self Learning",
  "nodes": [
    {
      "id": "schedule",
      "name": "Every 5 min",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -400,
        300
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 5
            }
          ]
        }
      }
    },
    {
      "id": "manual",
      "name": "Manual Test",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -400,
        450
      ],
      "parameters": {}
    },
    {
      "id": "get-emails",
      "name": "Get Unread Emails",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        -150,
        375
      ],
      "parameters": {
        "operation": "getAll",
        "limit": 3,
        "filters": {
          "q": "is:unread -category:promotions -category:social -category:updates"
        }
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "id": "has-emails",
      "name": "Has Emails?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        80,
        375
      ],
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "check-id",
              "leftValue": "={{ $json.id }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "exists"
              }
            }
          ],
          "combinator": "and"
        }
      }
    },
    {
      "id": "prepare-text",
      "name": "Prepare Email Text",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        300,
        300
      ],
      "parameters": {
        "jsCode": "// Prepare email text for embedding\nconst email = $input.first().json;\n\n// Combine subject and snippet for embedding\nconst emailText = `Subject: ${email.Subject || email.subject || 'No Subject'}\\nFrom: ${email.From || email.from || 'Unknown'}\\nContent: ${email.snippet || ''}`;\n\n// Create unique ID for this email\nconst emailId = email.id || email.messageId;\n\nreturn [{\n  json: {\n    emailId: emailId,\n    threadId: email.threadId,\n    from: email.From || email.from,\n    subject: email.Subject || email.subject,\n    snippet: email.snippet,\n    textForEmbedding: emailText,\n    labels: email.labels || [],\n    receivedAt: new Date().toISOString()\n  }\n}];"
      }
    },
    {
      "id": "get-embedding",
      "name": "Get Embedding (Gemini)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        530,
        300
      ],
      "parameters": {
        "method": "POST",
        "url": "https://generativelanguage.googleapis.com/v1beta/models/text-embedding-004:embedContent",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "key",
              "value": "YOUR_GEMINI_API_KEY"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ content: { parts: [{ text: $json.textForEmbedding }] } }) }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        }
      }
    },
    {
      "id": "search-similar",
      "name": "Search Similar (Qdrant)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        760,
        300
      ],
      "parameters": {
        "method": "POST",
        "url": "https://YOUR_QDRANT_CLUSTER.cloud.qdrant.io:6333/collections/email_memory/points/search",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "api-key",
              "value": "YOUR_QDRANT_API_KEY"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ vector: $json.embedding.values, limit: 3, with_payload: true }) }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        }
      }
    },
    {
      "id": "store-memory",
      "name": "Store in Memory (Qdrant)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        990,
        300
      ],
      "parameters": {
        "method": "PUT",
        "url": "https://YOUR_QDRANT_CLUSTER.cloud.qdrant.io:6333/collections/email_memory/points",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "api-key",
              "value": "YOUR_QDRANT_API_KEY"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ (() => { const prevData = $('Prepare Email Text').first().json; const embedding = $('Get Embedding (Gemini)').first().json.embedding.values; const pointId = Date.now(); return JSON.stringify({ points: [{ id: pointId, vector: embedding, payload: { emailId: prevData.emailId, from: prevData.from, subject: prevData.subject, snippet: prevData.snippet, receivedAt: prevData.receivedAt } }] }); })() }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        }
      }
    },
    {
      "id": "ai-analysis",
      "name": "AI Analysis (Groq)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1220,
        300
      ],
      "parameters": {
        "method": "POST",
        "url": "https://api.groq.com/openai/v1/chat/completions",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_GROQ_API_KEY"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ (() => { const emailData = $('Prepare Email Text').first().json; const similarEmails = $('Search Similar (Qdrant)').first().json.result || []; const similarContext = similarEmails.map(e => `- ${e.payload?.subject || 'No subject'}: ${e.payload?.snippet || ''}`).join('\\n'); const prompt = `You are an intelligent email assistant. Analyze this email and decide how to handle it.\\n\\nCURRENT EMAIL:\\nFrom: ${emailData.from}\\nSubject: ${emailData.subject}\\nContent: ${emailData.snippet}\\n\\nSIMILAR PAST EMAILS FROM MEMORY:\\n${similarContext || 'No similar emails found yet.'}\\n\\nBased on the email and any patterns from similar emails, provide:\\n1. PRIORITY: high, medium, or low\\n2. CATEGORY: work, personal, notification, newsletter, spam, important\\n3. ACTION: star, archive, reply_needed, ignore\\n4. REASON: Brief explanation (1 sentence)\\n\\nRespond ONLY in this JSON format:\\n{\"priority\": \"...\", \"category\": \"...\", \"action\": \"...\", \"reason\": \"...\"}`; return JSON.stringify({ model: 'llama-3.3-70b-versatile', messages: [{ role: 'user', content: prompt }], temperature: 0.3, max_tokens: 200 }); })() }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        }
      }
    },
    {
      "id": "process-ai",
      "name": "Process AI Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1450,
        300
      ],
      "parameters": {
        "jsCode": "// Process AI response and prepare actions\nconst emailData = $('Prepare Email Text').first().json;\nconst aiResponse = $input.first().json;\n\nlet decision = {\n  priority: 'medium',\n  category: 'unknown',\n  action: 'ignore',\n  reason: 'Could not parse AI response'\n};\n\ntry {\n  const content = aiResponse.choices[0].message.content;\n  const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\n  if (jsonMatch) {\n    decision = JSON.parse(jsonMatch[0]);\n  }\n} catch (e) {\n  console.log('Parse error:', e.message);\n}\n\nreturn [{\n  json: {\n    emailId: emailData.emailId,\n    threadId: emailData.threadId,\n    from: emailData.from,\n    subject: emailData.subject,\n    ai_priority: decision.priority,\n    ai_category: decision.category,\n    ai_action: decision.action,\n    ai_reason: decision.reason,\n    shouldStar: decision.action === 'star' || decision.priority === 'high',\n    processed_at: new Date().toISOString()\n  }\n}];"
      }
    },
    {
      "id": "should-star",
      "name": "Should Star?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1680,
        300
      ],
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "check-star",
              "leftValue": "={{ $json.shouldStar }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        }
      }
    },
    {
      "id": "star-email",
      "name": "Star Important",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1910,
        225
      ],
      "parameters": {
        "operation": "addLabels",
        "messageId": "={{ $json.emailId }}",
        "labelIds": [
          "STARRED"
        ]
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "id": "done",
      "name": "Done",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        2140,
        300
      ],
      "parameters": {}
    },
    {
      "id": "no-emails",
      "name": "No Emails",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        300,
        450
      ],
      "parameters": {}
    }
  ],
  "connections": {
    "Every 5 min": {
      "main": [
        [
          {
            "node": "Get Unread Emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manual Test": {
      "main": [
        [
          {
            "node": "Get Unread Emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Unread Emails": {
      "main": [
        [
          {
            "node": "Has Emails?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Emails?": {
      "main": [
        [
          {
            "node": "Prepare Email Text",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No Emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Email Text": {
      "main": [
        [
          {
            "node": "Get Embedding (Gemini)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Embedding (Gemini)": {
      "main": [
        [
          {
            "node": "Search Similar (Qdrant)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Similar (Qdrant)": {
      "main": [
        [
          {
            "node": "Store in Memory (Qdrant)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store in Memory (Qdrant)": {
      "main": [
        [
          {
            "node": "AI Analysis (Groq)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Analysis (Groq)": {
      "main": [
        [
          {
            "node": "Process AI Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process AI Response": {
      "main": [
        [
          {
            "node": "Should Star?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Should Star?": {
      "main": [
        [
          {
            "node": "Star Important",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Done",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Star Important": {
      "main": [
        [
          {
            "node": "Done",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "saveDataErrorExecution": "all",
    "saveDataSuccessExecution": "all",
    "saveManualExecutions": true
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

AI Email Agent - Self Learning. Uses gmail, httpRequest. Scheduled trigger; 14 nodes.

Source: https://gist.github.com/przemo198004-hue/eb73403ddc73e25bbd58abda428f0d9f — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

This automation includes order analysis, product comparison, management approval, and automated product deactivation, with full reporting and transparency. 1st of Every Month at 8 AM, it triggers a sc

HTTP Request, Gmail
AI & RAG

BSW Growth Agent · Lite (Free Tier). Uses googleSheets, googleDrive, httpRequest, gmail. Scheduled trigger; 21 nodes.

Google Sheets, Google Drive, HTTP Request +1
AI & RAG

Founder's Discovery Engine. Uses googleSheets, googleDrive, httpRequest, gmail. Scheduled trigger; 21 nodes.

Google Sheets, Google Drive, HTTP Request +1
AI & RAG

It identifies SKUs with low inventory per source and sends daily alerts via:

Slack, HTTP Request, Gmail
AI & RAG

This n8n template helps Magento 2 merchants automatically send customised, beautifully branded coupon emails to high-value customers.

HTTP Request, Gmail