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 →
{
"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.
sqlite
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 →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
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
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
Remixed Backup your workflows to GitHub from Solomon's work. Check out his templates.
Remixed Backup your workflows to GitHub from Solomon's work. Check out his templates.
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