AutomationFlowsWeb Scraping › Email Reader (execute Workflow Trigger)

Email Reader (execute Workflow Trigger)

05 - Email Reader. Uses executeWorkflowTrigger, sqlite, httpRequest. Event-driven trigger; 7 nodes.

Event trigger★★★★☆ complexity7 nodesExecute Workflow TriggerSqliteHTTP Request
Web Scraping Trigger: Event Nodes: 7 Complexity: ★★★★☆ Added:

This workflow follows the Execute Workflow Trigger → 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": "05 - Email Reader",
  "nodes": [
    {
      "parameters": {},
      "name": "Execute Workflow Trigger",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "typeVersion": 1,
      "position": [
        250,
        300
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT access_token FROM gmail_tokens WHERE user_id = $1 AND expires_at > datetime('now')",
        "additionalFields": {
          "parameters": [
            {
              "name": "user_id",
              "value": "={{ $json.userId }}"
            }
          ]
        }
      },
      "name": "Get Access Token",
      "type": "n8n-nodes-base.sqlite",
      "typeVersion": 2.2,
      "position": [
        450,
        300
      ],
      "credentials": {
        "sqlite": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "GET",
        "url": "https://gmail.googleapis.com/gmail/v1/users/me/messages",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "maxResults",
              "value": "={{ $json.limit || 10 }}"
            },
            {
              "name": "labelIds",
              "value": "={{ $json.label || 'INBOX' }}"
            },
            {
              "name": "q",
              "value": "={{ $json.searchQuery ? $json.searchQuery : 'is:unread' }}"
            }
          ]
        },
        "options": {}
      },
      "name": "Fetch Email List",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        650,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Get list of message IDs\nconst messages = $input.first().json.messages || [];\n\nif (messages.length === 0) {\n  return [{ json: { noEmails: true } }];\n}\n\n// Return each message ID for processing\nreturn messages.map((msg, index) => ({\n  json: {\n    id: msg.id,\n    threadId: msg.threadId,\n    index: index + 1\n  }\n}));"
      },
      "name": "Process Message List",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        850,
        300
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "={{ 'https://gmail.googleapis.com/gmail/v1/users/me/messages/' + $json.id }}",
        "options": {}
      },
      "name": "Fetch Full Email",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        1050,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Parse Gmail message format\nconst email = $input.first().json;\n\n// Extract headers\nconst headers = {};\nemail.payload.headers.forEach(header => {\n  headers[header.name.toLowerCase()] = header.value;\n});\n\n// Extract body\nlet bodyText = '';\nlet bodyHtml = '';\n\nfunction extractBody(parts) {\n  if (!parts) return;\n  \n  parts.forEach(part => {\n    if (part.mimeType === 'text/plain' && part.body.data) {\n      bodyText = Buffer.from(part.body.data, 'base64').toString('utf-8');\n    } else if (part.mimeType === 'text/html' && part.body.data) {\n      bodyHtml = Buffer.from(part.body.data, 'base64').toString('utf-8');\n    } else if (part.parts) {\n      extractBody(part.parts);\n    }\n  });\n}\n\nif (email.payload.parts) {\n  extractBody(email.payload.parts);\n} else if (email.payload.body && email.payload.body.data) {\n  bodyText = Buffer.from(email.payload.body.data, 'base64').toString('utf-8');\n}\n\n// Parse sender\nconst fromHeader = headers.from || 'Unknown';\nconst senderMatch = fromHeader.match(/(.*)<(.*)>/);\nconst senderName = senderMatch ? senderMatch[1].trim() : fromHeader;\nconst senderEmail = senderMatch ? senderMatch[2] : fromHeader;\n\n// Check for attachments\nconst hasAttachments = email.payload.parts && email.payload.parts.some(p => p.filename && p.filename.length > 0);\nconst attachmentNames = hasAttachments \n  ? email.payload.parts.filter(p => p.filename).map(p => p.filename).join(', ')\n  : '';\n\nreturn [{\n  json: {\n    gmail_id: email.id,\n    thread_id: email.threadId,\n    sender_name: senderName,\n    sender_email: senderEmail,\n    subject: headers.subject || '(No Subject)',\n    date: headers.date,\n    snippet: email.snippet,\n    body_text: bodyText.substring(0, 10000), // Limit size\n    body_html: bodyHtml.substring(0, 10000),\n    is_read: !email.labelIds.includes('UNREAD'),\n    labels: JSON.stringify(email.labelIds),\n    has_attachments: hasAttachments,\n    attachment_names: attachmentNames,\n    history_id: email.historyId\n  }\n}];"
      },
      "name": "Parse Email Content",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1250,
        300
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT OR REPLACE INTO emails (gmail_id, thread_id, user_id, sender_name, sender_email, subject, snippet, body_text, body_html, received_at, is_read, labels, has_attachments, attachment_names, cached_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, datetime('now'))",
        "additionalFields": {
          "parameters": [
            {
              "name": "gmail_id",
              "value": "={{ $json.gmail_id }}"
            },
            {
              "name": "thread_id",
              "value": "={{ $json.thread_id }}"
            },
            {
              "name": "user_id",
              "value": "={{ $('Execute Workflow Trigger').first().json.userId }}"
            },
            {
              "name": "sender_name",
              "value": "={{ $json.sender_name }}"
            },
            {
              "name": "sender_email",
              "value": "={{ $json.sender_email }}"
            },
            {
              "name": "subject",
              "value": "={{ $json.subject }}"
            },
            {
              "name": "snippet",
              "value": "={{ $json.snippet }}"
            },
            {
              "name": "body_text",
              "value": "={{ $json.body_text }}"
            },
            {
              "name": "body_html",
              "value": "={{ $json.body_html }}"
            },
            {
              "name": "received_at",
              "value": "={{ $json.date }}"
            },
            {
              "name": "is_read",
              "value": "={{ $json.is_read }}"
            },
            {
              "name": "labels",
              "value": "={{ $json.labels }}"
            },
            {
              "name": "has_attachments",
              "value": "={{ $json.has_attachments }}"
            },
            {
              "name": "attachment_names",
              "value": "={{ $json.attachment_names }}"
            }
          ]
        }
      },
      "name": "Cache Email",
      "type": "n8n-nodes-base.sqlite",
      "typeVersion": 2.2,
      "position": [
        1450,
        300
      ],
      "credentials": {
        "sqlite": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Execute Workflow Trigger": {
      "main": [
        [
          {
            "node": "Get Access Token",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Access Token": {
      "main": [
        [
          {
            "node": "Fetch Email List",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Email List": {
      "main": [
        [
          {
            "node": "Process Message List",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Message List": {
      "main": [
        [
          {
            "node": "Fetch Full Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Full Email": {
      "main": [
        [
          {
            "node": "Parse Email Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Email Content": {
      "main": [
        [
          {
            "node": "Cache Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "tags": [
    "ronkbot",
    "gmail",
    "email",
    "read"
  ]
}

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

05 - Email Reader. Uses executeWorkflowTrigger, sqlite, httpRequest. Event-driven trigger; 7 nodes.

Source: https://github.com/rohankag/ronkbot/blob/db03432335532bdeb4ebcee0c72d9109e04b466f/n8n-workflows/05-email-reader.json — original creator credit. Request a take-down →

More Web Scraping workflows → · Browse all categories →

Related workflows

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

Web Scraping

This template is a powerful, reusable utility for managing stateful, long-running processes. It allows a main workflow to be paused indefinitely at "checkpoints" and then be resumed by external, async

HTTP Request, Execute Workflow Trigger
Web Scraping

Upload files from any source to your account Kommo or AmoCRM with a simple and reusable workflow. It can split a large file into small ones and upload chunks. Works for Kommo and amoCRM There are 3 re

HTTP Request, Execute Workflow Trigger, Stop And Error
Web Scraping

Remixed Backup your workflows to GitHub from Solomon's work. Check out his templates.

HTTP Request, GitHub, Execute Workflow Trigger +1
Web Scraping

Remixed Backup your workflows to GitHub from Solomon's work. Check out his templates.

Execute Workflow Trigger, HTTP Request, GitHub
Web Scraping

This workflow audits your SharePoint Online environment for external sharing risks by identifying files and folders that are shared with anonymous links or external/guest users. It is designed to trav

HTTP Request, Execute Workflow Trigger