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.
02_LLM_Pipeline v1.0. Uses executeWorkflowTrigger, httpRequest, seaTable. Event-driven trigger; 65 nodes.
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.