This workflow corresponds to n8n.io template #17205 — we link there as the canonical source.
This workflow follows the Gmail → Google Drive 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": "Save starred Gmail emails as Markdown notes in Obsidian (via Google Drive)",
"nodes": [
{
"id": "f5000s01-0000-4000-8000-000000000001",
"name": "Sticky Note \u2014 read me first",
"type": "n8n-nodes-base.stickyNote",
"position": [
-800,
-80
],
"parameters": {
"width": 700,
"height": 940,
"content": "## Save the emails that matter as notes in your Obsidian vault \u2014 automatically, once a day\n\n### How it works\nStar (or label) an email in Gmail and this workflow turns it into a clean Markdown note in your Obsidian vault. Once a day it finds every starred email you haven't saved yet and writes each one as a note \u2014 a YAML frontmatter block (title, from, date, tags) followed by the full body \u2014 into a Google Drive folder. Point that folder at your vault's Drive-synced location and the notes just appear in Obsidian. The moment a note is written, the email is labelled `obsidian-saved`, so it is never captured twice, even on manual re-runs.\n\nNo Obsidian plugin and no AI key needed \u2014 it writes plain `.md` files, so it runs 100% free on n8n Cloud.\n\n### Setup\n1. Connect **Gmail** and **Google Drive** (one-click Google sign-in on n8n Cloud).\n2. In Gmail, create a label called `obsidian-saved` \u2014 it is the \"already captured\" marker.\n3. In **\ud83d\udcc1 Save note to vault**, pick the Drive folder your Obsidian vault syncs from (e.g. an `Inbox` folder inside the vault).\n4. In **\ud83c\udff7\ufe0f Mark email saved**, select that same `obsidian-saved` label.\n5. (Optional) In **\u2699\ufe0f Your settings**, change which emails to capture (default: starred) or the tag added to each note.\n6. Activate. It runs once a day at 7am.\n\n### Honest notes\nThis reads the emails you star and writes files to your Drive \u2014 run it once with a single starred test email before trusting it with your whole inbox. Built with retries and an idempotent `obsidian-saved` label, so a crash mid-run can't create duplicate notes tomorrow. It captures the plain-text body of the email; attachments and rich HTML formatting are not carried into the note."
},
"typeVersion": 1
},
{
"id": "f5000s02-0000-4000-8000-000000000002",
"name": "Sticky Note \u2014 section 1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-60,
-80
],
"parameters": {
"color": 7,
"width": 560,
"height": 940,
"content": "## 1. Find emails to save\nRuns daily at 7am. Pulls every email that matches your capture rule (default: starred) and isn't already labelled `obsidian-saved` \u2014 so each run only ever sees what's new."
},
"typeVersion": 1
},
{
"id": "f5000s03-0000-4000-8000-000000000003",
"name": "Sticky Note \u2014 section 2",
"type": "n8n-nodes-base.stickyNote",
"position": [
520,
-80
],
"parameters": {
"color": 7,
"width": 460,
"height": 940,
"content": "## 2. Build the note\nTurns each email into a Markdown file: a YAML frontmatter block (title, from, date, tags) followed by the body. Filenames are date-prefixed and cleaned of the characters Obsidian and your OS dislike, so every note drops cleanly into the vault."
},
"typeVersion": 1
},
{
"id": "f5000s04-0000-4000-8000-000000000004",
"name": "Sticky Note \u2014 section 3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1000,
-80
],
"parameters": {
"color": 7,
"width": 500,
"height": 940,
"content": "## 3. Save + mark done\nWrites the note into your vault's Google Drive folder, then immediately labels the email `obsidian-saved`. That label is the guard that makes re-runs safe: a captured email is skipped forever after, so you never get a duplicate note."
},
"typeVersion": 1
},
{
"id": "f5000001-0000-4000-8000-00000000000a",
"name": "Every day at 7am",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
0,
320
],
"parameters": {
"rule": {
"interval": [
{
"field": "days",
"triggerAtHour": 7
}
]
}
},
"typeVersion": 1.2
},
{
"id": "f5000002-0000-4000-8000-00000000000b",
"name": "\u2699\ufe0f Your settings",
"type": "n8n-nodes-base.set",
"position": [
180,
320
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "s1",
"name": "searchQuery",
"type": "string",
"value": "is:starred -label:obsidian-saved"
},
{
"id": "s2",
"name": "noteTag",
"type": "string",
"value": "email"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "f5000003-0000-4000-8000-00000000000c",
"name": "Find starred emails",
"type": "n8n-nodes-base.gmail",
"maxTries": 3,
"position": [
360,
320
],
"parameters": {
"simple": false,
"filters": {
"q": "={{ $('\u2699\ufe0f Your settings').first().json.searchQuery }}"
},
"options": {},
"operation": "getAll",
"returnAll": true
},
"retryOnFail": true,
"typeVersion": 2.1,
"waitBetweenTries": 5000
},
{
"id": "f5000004-0000-4000-8000-00000000000d",
"name": "Build the note",
"type": "n8n-nodes-base.code",
"position": [
540,
320
],
"parameters": {
"jsCode": "// Turn each starred email into a Markdown note for your Obsidian vault.\n// Field names vary a little between Gmail node versions, so we read defensively.\nconst cfg = $('\u2699\ufe0f Your settings').first().json;\nconst pad = (n) => String(n).padStart(2, '0');\n\n// Strip the characters Obsidian and common filesystems reject in a filename.\nconst slug = (s) => String(s || 'note')\n .replace(/[\\\\/:*?\"<>|#^\\[\\]]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, 80) || 'note';\n\nconst esc = (s) => String(s == null ? '' : s).replaceAll('\"', \"'\");\nconst notes = [];\n\nfor (const item of $input.all()) {\n const m = item.json;\n const subject = m.subject || m.Subject || '(no subject)';\n const from = m.from || m.From || m.sender || '';\n const rawDate = m.date || m.Date || m.internalDate || '';\n const body = m.text || m.textPlain || m.textAsHtml || m.body || m.snippet || '';\n\n // Build a safe date prefix; fall back to today if the header is unparseable.\n let d = new Date(rawDate);\n if (isNaN(d.getTime())) d = new Date();\n const stamp = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;\n\n const filename = `${stamp} ${slug(subject)}.md`;\n\n const frontmatter = [\n '---',\n `title: \"${esc(subject)}\"`,\n `from: \"${esc(from)}\"`,\n `date: ${stamp}`,\n 'source: gmail',\n `tags: [${cfg.noteTag || 'email'}]`,\n '---',\n '',\n ].join('\\n');\n\n const markdown = `${frontmatter}# ${subject}\\n\\n**From:** ${from} \\n**Date:** ${stamp}\\n\\n${body}\\n`;\n\n notes.push({\n json: {\n message_id: m.id,\n filename,\n markdown,\n },\n });\n}\n\nreturn notes;"
},
"typeVersion": 2
},
{
"id": "f5000005-0000-4000-8000-00000000000e",
"name": "One email at a time",
"type": "n8n-nodes-base.splitInBatches",
"position": [
720,
320
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "f5000006-0000-4000-8000-00000000000f",
"name": "Save note to vault",
"type": "n8n-nodes-base.googleDrive",
"maxTries": 3,
"position": [
1060,
336
],
"parameters": {
"name": "={{ $json.filename }}",
"content": "={{ $json.markdown }}",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"options": {},
"folderId": {
"__rl": true,
"mode": "list",
"value": ""
},
"operation": "createFromText"
},
"retryOnFail": true,
"typeVersion": 3,
"waitBetweenTries": 5000
},
{
"id": "f5000007-0000-4000-8000-000000000010",
"name": "Mark email saved",
"type": "n8n-nodes-base.gmail",
"maxTries": 3,
"position": [
1260,
336
],
"parameters": {
"labelIds": [],
"messageId": "={{ $('One email at a time').item.json.message_id }}",
"operation": "addLabels"
},
"retryOnFail": true,
"typeVersion": 2.1,
"waitBetweenTries": 5000
}
],
"settings": {
"executionOrder": "v1"
},
"connections": {
"Build the note": {
"main": [
[
{
"node": "One email at a time",
"type": "main",
"index": 0
}
]
]
},
"Every day at 7am": {
"main": [
[
{
"node": "\u2699\ufe0f Your settings",
"type": "main",
"index": 0
}
]
]
},
"Mark email saved": {
"main": [
[
{
"node": "One email at a time",
"type": "main",
"index": 0
}
]
]
},
"Save note to vault": {
"main": [
[
{
"node": "Mark email saved",
"type": "main",
"index": 0
}
]
]
},
"Find starred emails": {
"main": [
[
{
"node": "Build the note",
"type": "main",
"index": 0
}
]
]
},
"One email at a time": {
"main": [
[],
[
{
"node": "Save note to vault",
"type": "main",
"index": 0
}
]
]
},
"\u2699\ufe0f Your settings": {
"main": [
[
{
"node": "Find starred emails",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow runs daily, finds starred Gmail emails that are not yet labeled as saved, converts each email into a Markdown note with YAML frontmatter, uploads the note to a Google Drive folder synced with your Obsidian vault, and then labels the email to prevent duplicates.…
Source: https://n8n.io/workflows/17205/ — 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 n8n workflow sends personalized outreach emails automatically while enforcing strict safety rules such as email validation, spam checks, daily limits, and human-like delays.
This workflow automates the backup of all your n8n workflows to a specified Google Drive folder. It operates in two main phases: Orchestration (Scheduled Task): The workflow is initiated by a Schedule
This workflow is an automated invoice payment tracking and reminder system for the Polish accounting service iFirma.pl. It monitors unpaid and overdue invoices, then automatically sends escalating rem
Automatically extract structured information from emails using AI-powered document analysis. This workflow processes emails from specified domains, classifies them by type, and extracts structured dat
This weekly workflow helps you stay on top of SEO visibility losses by automatically detecting when your previously strong keywords fall out of Google’s top 10 results.