diff --git a/workflows/Filter and label Gmail emails using keyword-based rules and Google Sheets logging-13903/readme-13903.md b/workflows/Filter and label Gmail emails using keyword-based rules and Google Sheets logging-13903/readme-13903.md
new file mode 100644
index 000000000..4ea028772
--- /dev/null
+++ b/workflows/Filter and label Gmail emails using keyword-based rules and Google Sheets logging-13903/readme-13903.md
@@ -0,0 +1,939 @@
+Filter and label Gmail emails using keyword-based rules and Google Sheets logging
+
+https://n8nworkflows.xyz/workflows/filter-and-label-gmail-emails-using-keyword-based-rules-and-google-sheets-logging-13903
+
+
+# Filter and label Gmail emails using keyword-based rules and Google Sheets logging
+
+## 1. Workflow Overview
+
+This workflow monitors a Gmail inbox, fetches each newly received email in full, classifies it using keyword/domain-based rules, then applies Gmail labels and optionally logs the result to Google Sheets.
+
+Its main use case is automated inbox triage:
+- detect likely cold outreach and suppress it,
+- detect marketing/promotional emails,
+- flag ambiguous messages for manual review,
+- preserve legitimate operational/business emails.
+
+The workflow contains one entry point and then splits into classification-driven branches.
+
+### 1.1 Input Reception and Message Retrieval
+The workflow starts when Gmail receives a new message. It then retrieves the full message content so the later logic can access sender, subject, and body text.
+
+### 1.2 Email Data Preparation
+The workflow normalizes key fields into a simpler structure: combined text content, sender details, message ID, and subject.
+
+### 1.3 Rule-Based Classification
+A Code node performs all classification logic. It uses:
+- trusted sender domains,
+- legitimate business indicators,
+- cold outreach keywords,
+- sales-tool sender domains,
+- suspicious “needs review” phrasing,
+- unsubscribe markers.
+
+This node returns a normalized classification payload.
+
+### 1.4 Cold Outreach Handling
+If the email is classified as `cold_outreach`, the workflow:
+- adds a Gmail label,
+- removes the message from Inbox,
+- marks it as read,
+- prepares a structured logging row,
+- appends it to Google Sheets.
+
+### 1.5 Needs Review Handling
+If the email is classified as `needs_review`, the workflow:
+- adds a Gmail label,
+- prepares structured logging fields,
+- appends the case to Google Sheets.
+
+### 1.6 Marketing Handling
+If the email is classified as `marketing`, the workflow:
+- adds a Gmail label,
+- prepares structured logging fields,
+- appends the result to Google Sheets.
+
+### 1.7 Legitimate Handling
+If the email is neither cold outreach, nor needs review, nor marketing, it is treated as `legitimate`. The workflow:
+- adds a Gmail label,
+- prepares structured logging fields,
+- appends the result to Google Sheets.
+
+---
+
+## 2. Block-by-Block Analysis
+
+## 2.1 Input Reception and Message Retrieval
+
+**Overview:**
+This block listens for incoming Gmail messages and retrieves the full email object. It converts the lightweight trigger event into a richer payload for downstream analysis.
+
+**Nodes Involved:**
+- Gmail Trigger
+- Get a message
+
+### Node: Gmail Trigger
+- **Type and technical role:** `n8n-nodes-base.gmailTrigger` — polling trigger for new Gmail messages.
+- **Configuration choices:**
+ - Polling mode is set to run every minute.
+ - No additional Gmail filters are configured.
+- **Key expressions or variables used:**
+ - None.
+- **Input and output connections:**
+ - Entry point of the workflow.
+ - Outputs to `Get a message`.
+- **Version-specific requirements:**
+ - Type version `1.2`.
+ - Requires Gmail trigger support in the installed n8n version.
+- **Edge cases or potential failure types:**
+ - Gmail authentication failure.
+ - Polling delay or API quota issues.
+ - Trigger may pick up emails you did not intend if filters remain empty.
+- **Sub-workflow reference:**
+ - None.
+
+### Node: Get a message
+- **Type and technical role:** `n8n-nodes-base.gmail` — fetches the full Gmail message by ID.
+- **Configuration choices:**
+ - Operation: `get`
+ - `messageId` is pulled from the trigger event: `{{ $json.id }}`
+ - `simple` is disabled, so the node returns a fuller Gmail message structure.
+- **Key expressions or variables used:**
+ - `={{ $json.id }}`
+- **Input and output connections:**
+ - Input from `Gmail Trigger`
+ - Output to `Extract Email Data`
+- **Version-specific requirements:**
+ - Type version `2.1`
+- **Edge cases or potential failure types:**
+ - If the trigger payload lacks `id`, the lookup fails.
+ - Gmail API permissions may allow trigger events but not full message retrieval if scopes are incomplete.
+ - Message may become unavailable between trigger and retrieval in rare race conditions.
+- **Sub-workflow reference:**
+ - None.
+
+---
+
+## 2.2 Email Data Preparation
+
+**Overview:**
+This block extracts and reshapes the Gmail message into fields that are easier to reference. It also creates a combined content field used by the classifier.
+
+**Nodes Involved:**
+- Extract Email Data
+
+### Node: Extract Email Data
+- **Type and technical role:** `n8n-nodes-base.set` — creates a simplified analysis payload.
+- **Configuration choices:**
+ - Assigns:
+ - `email_content` = subject + text body
+ - `sender_email` = first sender address
+ - `sender_name` = first sender name or `Unknown`
+ - `message_id` = Gmail message ID
+ - `Subject` = subject
+ - `executeOnce` is enabled.
+ - `alwaysOutputData` is enabled.
+- **Key expressions or variables used:**
+ - `={{ $json.subject + ' ' + $json.text }}`
+ - `={{ $json.from.value[0].address }}`
+ - `={{ $json.from.value[0].name || 'Unknown' }}`
+ - `={{ $json.id }}`
+ - `={{ $json.subject }}`
+- **Input and output connections:**
+ - Input from `Get a message`
+ - Output to `Analyze Email Content`
+- **Version-specific requirements:**
+ - Type version `3.3`
+- **Edge cases or potential failure types:**
+ - If `subject` or `text` is missing, expression output may become incomplete or fail depending on payload shape.
+ - `from.value[0]` assumes sender parsing succeeded; malformed or unusual email formats can break this expression.
+ - `executeOnce` can be problematic if later adapted for batched processing.
+- **Sub-workflow reference:**
+ - None.
+
+---
+
+## 2.3 Rule-Based Classification
+
+**Overview:**
+This is the core decision engine. It scores the email and assigns one of four categories: `cold_outreach`, `marketing`, `needs_review`, or `legitimate`.
+
+**Nodes Involved:**
+- Analyze Email Content
+- Check If Cold Outreach
+- Check if Needs_review
+- Check if Marketing
+
+### Node: Analyze Email Content
+- **Type and technical role:** `n8n-nodes-base.code` — custom JavaScript classifier.
+- **Configuration choices:**
+ - Defines internal arrays for:
+ - trusted domains,
+ - legitimate patterns,
+ - cold outreach keywords,
+ - sales tool domains,
+ - review indicators.
+ - Converts content and sender email to lowercase for case-insensitive checks.
+ - Extracts sender domain from email.
+ - Prioritizes trusted domain classification first.
+ - Calculates:
+ - `legitimate_score`
+ - `review_score`
+ - detected keywords
+ - sales-tool detection
+ - unsubscribe flag
+ - Returns a structured classification object.
+- **Key expressions or variables used:**
+ - Reads from input:
+ - `$input.first().json.email_content`
+ - `$input.first().json.sender_email`
+ - `$input.first().json.sender_name`
+ - `$input.first().json.message_id`
+ - Returns fields such as:
+ - `email_type`
+ - `confidence`
+ - `classification_reason`
+ - `found_keywords`
+ - `found_legitimate_patterns`
+ - `found_review_indicators`
+ - `analysis_timestamp`
+- **Input and output connections:**
+ - Input from `Extract Email Data`
+ - Output to `Check If Cold Outreach`
+- **Version-specific requirements:**
+ - Type version `2`
+ - Requires Code node JavaScript execution support.
+- **Edge cases or potential failure types:**
+ - If `sender_email` is missing, domain extraction becomes unreliable.
+ - Name repetition check uses the sender name instead of the recipient name; this may not capture the intended personalization signal.
+ - The code assumes textual body content exists and is readable.
+ - Classification defaults to `legitimate` for unknown content, which may be too permissive for some use cases.
+ - Trusted-domain matching uses `includes()`, which can create false positives for deceptive domains containing a trusted string.
+- **Sub-workflow reference:**
+ - None.
+
+### Node: Check If Cold Outreach
+- **Type and technical role:** `n8n-nodes-base.if` — branch on `email_type`.
+- **Configuration choices:**
+ - Condition: `email_type` equals `cold_outreach`
+ - True output goes to outreach handling.
+ - False output continues to next check.
+- **Key expressions or variables used:**
+ - `={{ $json.email_type }}`
+- **Input and output connections:**
+ - Input from `Analyze Email Content`
+ - True output to `Label as Outreach`
+ - False output to `Check if Needs_review`
+- **Version-specific requirements:**
+ - Type version `2`
+- **Edge cases or potential failure types:**
+ - If `email_type` is absent or misspelled, the message falls through the false branch.
+- **Sub-workflow reference:**
+ - None.
+
+### Node: Check if Needs_review
+- **Type and technical role:** `n8n-nodes-base.if` — branch on `needs_review`.
+- **Configuration choices:**
+ - Condition: `email_type` equals `needs_review`
+- **Key expressions or variables used:**
+ - `={{ $json.email_type }}`
+- **Input and output connections:**
+ - Input from `Check If Cold Outreach` false branch
+ - True output to `Label as Needs Review`
+ - False output to `Check if Marketing`
+- **Version-specific requirements:**
+ - Type version `2`
+- **Edge cases or potential failure types:**
+ - Same as above: absent or malformed `email_type` causes fallback to later logic.
+- **Sub-workflow reference:**
+ - None.
+
+### Node: Check if Marketing
+- **Type and technical role:** `n8n-nodes-base.if` — final classification split before defaulting to legitimate.
+- **Configuration choices:**
+ - Condition: `email_type` equals `marketing`
+ - True output goes to marketing handling.
+ - False output goes to legitimate handling.
+- **Key expressions or variables used:**
+ - `={{ $json.email_type }}`
+- **Input and output connections:**
+ - Input from `Check if Needs_review` false branch
+ - True output to `Label as Marketing/Ads`
+ - False output to `Label as Legitimate`
+- **Version-specific requirements:**
+ - Type version `2`
+- **Edge cases or potential failure types:**
+ - Any unknown classification value is implicitly treated as legitimate because the false branch leads there.
+- **Sub-workflow reference:**
+ - None.
+
+---
+
+## 2.4 Cold Outreach Handling
+
+**Overview:**
+This block performs the strongest cleanup actions. Cold outreach messages are labeled, removed from Inbox, marked read, and logged.
+
+**Nodes Involved:**
+- Label as Outreach
+- Remove from Inbox
+- Mark as Read
+- Edit Fields (outreach)
+- Log to Spreadsheet (outreach)
+
+### Node: Label as Outreach
+- **Type and technical role:** `n8n-nodes-base.gmail` — adds a Gmail label to the message.
+- **Configuration choices:**
+ - Operation: `addLabels`
+ - Label ID: `Label_1684442270786871755`
+ - Message ID from classifier output.
+- **Key expressions or variables used:**
+ - `={{ $json.message_id }}`
+- **Input and output connections:**
+ - Input from `Check If Cold Outreach` true branch
+ - Output to `Remove from Inbox`
+- **Version-specific requirements:**
+ - Type version `2`
+- **Edge cases or potential failure types:**
+ - Label ID must exist in the connected Gmail account.
+ - Imported workflows often fail here until the user replaces label IDs with real IDs from their mailbox.
+- **Sub-workflow reference:**
+ - None.
+
+### Node: Remove from Inbox
+- **Type and technical role:** `n8n-nodes-base.gmail` — removes the `INBOX` label.
+- **Configuration choices:**
+ - Operation: `removeLabels`
+ - Label removed: `INBOX`
+ - Message ID: `={{ $json.id }}`
+- **Key expressions or variables used:**
+ - `={{ $json.id }}`
+- **Input and output connections:**
+ - Input from `Label as Outreach`
+ - Output to `Mark as Read`
+- **Version-specific requirements:**
+ - Type version `2`
+- **Edge cases or potential failure types:**
+ - This node uses `id`, not `message_id`. It depends on the Gmail node output preserving a field named `id`.
+ - If Gmail output shape differs, the node can fail or target no message.
+- **Sub-workflow reference:**
+ - None.
+
+### Node: Mark as Read
+- **Type and technical role:** `n8n-nodes-base.gmail` — marks the message as read.
+- **Configuration choices:**
+ - Operation: `markAsRead`
+ - Message ID: `={{ $json.id }}`
+- **Key expressions or variables used:**
+ - `={{ $json.id }}`
+- **Input and output connections:**
+ - Input from `Remove from Inbox`
+ - Output to `Edit Fields (outreach)`
+- **Version-specific requirements:**
+ - Type version `2`
+- **Edge cases or potential failure types:**
+ - Same output-shape dependency as `Remove from Inbox`.
+- **Sub-workflow reference:**
+ - None.
+
+### Node: Edit Fields (outreach)
+- **Type and technical role:** `n8n-nodes-base.set` — creates a clean row for spreadsheet logging.
+- **Configuration choices:**
+ - Pulls values from previous nodes via direct node references, not only current input.
+ - Produces:
+ - `analysis_timestamp`
+ - `message_id`
+ - `sender_email`
+ - `sender_name`
+ - `subject`
+ - `email_type`
+ - `confidence`
+ - `keywords_found`
+- **Key expressions or variables used:**
+ - `{{ $('Analyze Email Content').first().json.analysis_timestamp }}`
+ - `{{ $('Analyze Email Content').first().json.message_id }}`
+ - `{{ $('Analyze Email Content').first().json.sender_email }}`
+ - `{{ $('Extract Email Data').first().json.sender_name }}`
+ - `{{ $('Extract Email Data').first().json.Subject }}`
+ - `{{ $('Analyze Email Content').first().json.found_keywords.join(', ') }}`
+- **Input and output connections:**
+ - Input from `Mark as Read`
+ - Output to `Log to Spreadsheet (outreach)`
+- **Version-specific requirements:**
+ - Type version `3.4`
+- **Edge cases or potential failure types:**
+ - If the referenced nodes do not execute in the current run, expressions can fail.
+ - If `found_keywords` is not an array, `.join(', ')` fails.
+- **Sub-workflow reference:**
+ - None.
+
+### Node: Log to Spreadsheet (outreach)
+- **Type and technical role:** `n8n-nodes-base.googleSheets` — appends a log row.
+- **Configuration choices:**
+ - Operation: `append`
+ - Mapped columns:
+ - timestamp
+ - message_id
+ - sender_email
+ - sender_name
+ - subject
+ - email_type
+ - confidence
+ - keywords_found
+ - Sheet name points to `Sheet1` (`gid=0`)
+ - `documentId` is currently empty and must be supplied.
+- **Key expressions or variables used:**
+ - Standard field mappings from current JSON.
+- **Input and output connections:**
+ - Input from `Edit Fields (outreach)`
+ - No downstream connection.
+- **Version-specific requirements:**
+ - Type version `4`
+- **Edge cases or potential failure types:**
+ - Missing Google Sheets credentials.
+ - Empty `documentId` means this node will not work until configured.
+ - Sheet schema must contain matching headers if using mapped columns.
+- **Sub-workflow reference:**
+ - None.
+
+---
+
+## 2.5 Needs Review Handling
+
+**Overview:**
+This block labels uncertain emails for manual inspection and logs them. It does not remove them from the Inbox or mark them read.
+
+**Nodes Involved:**
+- Label as Needs Review
+- Edit Fields (needs review)
+- Log to Spreadsheet (needs review)
+
+### Node: Label as Needs Review
+- **Type and technical role:** `n8n-nodes-base.gmail` — adds the review label.
+- **Configuration choices:**
+ - Operation: `addLabels`
+ - Label ID: `Label_221566828550691632`
+ - Message ID from classifier output.
+- **Key expressions or variables used:**
+ - `={{ $json.message_id }}`
+- **Input and output connections:**
+ - Input from `Check if Needs_review` true branch
+ - Output to `Edit Fields (needs review)`
+- **Version-specific requirements:**
+ - Type version `2`
+- **Edge cases or potential failure types:**
+ - Invalid label ID after import is the most common issue.
+- **Sub-workflow reference:**
+ - None.
+
+### Node: Edit Fields (needs review)
+- **Type and technical role:** `n8n-nodes-base.set` — prepares spreadsheet row data.
+- **Configuration choices:**
+ - Same field structure as outreach logging.
+- **Key expressions or variables used:**
+ - Same pattern as outreach edit node.
+- **Input and output connections:**
+ - Input from `Label as Needs Review`
+ - Output to `Log to Spreadsheet (needs review)`
+- **Version-specific requirements:**
+ - Type version `3.4`
+- **Edge cases or potential failure types:**
+ - Same node-reference dependency risks as other Set logging nodes.
+- **Sub-workflow reference:**
+ - None.
+
+### Node: Log to Spreadsheet (needs review)
+- **Type and technical role:** `n8n-nodes-base.googleSheets` — appends a review log row.
+- **Configuration choices:**
+ - Operation: `append`
+ - Sheet points to tab `Needs_review`
+ - `documentId` is empty and must be configured.
+- **Key expressions or variables used:**
+ - Standard mapped current fields.
+- **Input and output connections:**
+ - Input from `Edit Fields (needs review)`
+ - No downstream connection.
+- **Version-specific requirements:**
+ - Type version `4`
+- **Edge cases or potential failure types:**
+ - Empty `documentId`
+ - Missing sheet or column mismatch
+ - Credential errors
+- **Sub-workflow reference:**
+ - None.
+
+---
+
+## 2.6 Marketing Handling
+
+**Overview:**
+This block applies a marketing/promotions label and writes the result to Google Sheets. It is reached only if the message was not cold outreach and not needs review.
+
+**Nodes Involved:**
+- Label as Marketing/Ads
+- Edit Fields (marketing)
+- Log to Spreadsheet (marketing/Ads)
+
+### Node: Label as Marketing/Ads
+- **Type and technical role:** `n8n-nodes-base.gmail` — adds the marketing label.
+- **Configuration choices:**
+ - Operation: `addLabels`
+ - Label ID: `Label_999978575131975706`
+- **Key expressions or variables used:**
+ - `={{ $json.message_id }}`
+- **Input and output connections:**
+ - Input from `Check if Marketing` true branch
+ - Output to `Edit Fields (marketing)`
+- **Version-specific requirements:**
+ - Type version `2`
+- **Edge cases or potential failure types:**
+ - Label ID must be replaced if moving to another Gmail account.
+- **Sub-workflow reference:**
+ - None.
+
+### Node: Edit Fields (marketing)
+- **Type and technical role:** `n8n-nodes-base.set` — prepares structured row data.
+- **Configuration choices:**
+ - Same logging fields as the other category branches.
+- **Key expressions or variables used:**
+ - Same node reference expressions as the other edit nodes.
+- **Input and output connections:**
+ - Input from `Label as Marketing/Ads`
+ - Output to `Log to Spreadsheet (marketing/Ads)`
+- **Version-specific requirements:**
+ - Type version `3.4`
+- **Edge cases or potential failure types:**
+ - Same expression dependency issues as the parallel edit nodes.
+- **Sub-workflow reference:**
+ - None.
+
+### Node: Log to Spreadsheet (marketing/Ads)
+- **Type and technical role:** `n8n-nodes-base.googleSheets` — appends marketing log rows.
+- **Configuration choices:**
+ - Operation: `append`
+ - Sheet tab points to `Marketing`
+ - `documentId` is empty and must be filled in.
+- **Key expressions or variables used:**
+ - Standard mapped fields.
+- **Input and output connections:**
+ - Input from `Edit Fields (marketing)`
+ - No downstream connection.
+- **Version-specific requirements:**
+ - Type version `4`
+- **Edge cases or potential failure types:**
+ - Missing Google Sheets spreadsheet ID
+ - Tab/header mismatch
+- **Sub-workflow reference:**
+ - None.
+
+---
+
+## 2.7 Legitimate Handling
+
+**Overview:**
+This is the default branch for messages considered safe or operational. The workflow labels them and logs the result.
+
+**Nodes Involved:**
+- Label as Legitimate
+- Edit Fields (Legitimate)
+- Log to Spreadsheet (legitimate)
+
+### Node: Label as Legitimate
+- **Type and technical role:** `n8n-nodes-base.gmail` — adds the legitimate label.
+- **Configuration choices:**
+ - Operation: `addLabels`
+ - Label ID: `Label_9187111232+1234567890`
+- **Key expressions or variables used:**
+ - `={{ $json.message_id }}`
+- **Input and output connections:**
+ - Input from `Check if Marketing` false branch
+ - Output to `Edit Fields (Legitimate)`
+- **Version-specific requirements:**
+ - Type version `2`
+- **Edge cases or potential failure types:**
+ - The label ID format looks unusual and should be verified in Gmail before use.
+- **Sub-workflow reference:**
+ - None.
+
+### Node: Edit Fields (Legitimate)
+- **Type and technical role:** `n8n-nodes-base.set` — prepares spreadsheet row data for legitimate messages.
+- **Configuration choices:**
+ - Same field structure as all other logging branches.
+- **Key expressions or variables used:**
+ - Same node references as the other edit nodes.
+- **Input and output connections:**
+ - Input from `Label as Legitimate`
+ - Output to `Log to Spreadsheet (legitimate)`
+- **Version-specific requirements:**
+ - Type version `3.4`
+- **Edge cases or potential failure types:**
+ - Same expression dependency issues as other edit nodes.
+- **Sub-workflow reference:**
+ - None.
+
+### Node: Log to Spreadsheet (legitimate)
+- **Type and technical role:** `n8n-nodes-base.googleSheets` — appends log rows for legitimate emails.
+- **Configuration choices:**
+ - Operation: `append`
+ - Sheet tab points to `Legitimate`
+ - `documentId` is empty and must be set.
+- **Key expressions or variables used:**
+ - Standard mapped fields.
+- **Input and output connections:**
+ - Input from `Edit Fields (Legitimate)`
+ - No downstream connection.
+- **Version-specific requirements:**
+ - Type version `4`
+- **Edge cases or potential failure types:**
+ - Same Sheets setup issues as other log nodes.
+- **Sub-workflow reference:**
+ - None.
+
+---
+
+## 2.8 Sticky Notes / Embedded Documentation
+
+**Overview:**
+These nodes are documentation-only elements inside the canvas. They provide setup guidance, logic explanation, and logging notes.
+
+**Nodes Involved:**
+- Sticky Note
+- Sticky Note1
+- Sticky Note2
+- Sticky Note3
+
+### Node: Sticky Note
+- **Type and technical role:** `n8n-nodes-base.stickyNote` — visual documentation.
+- **Configuration choices:**
+ - Describes overall workflow and Gmail label setup.
+- **Key expressions or variables used:**
+ - None.
+- **Input and output connections:**
+ - None.
+- **Version-specific requirements:**
+ - Type version `1`
+- **Edge cases or potential failure types:**
+ - None; informational only.
+- **Sub-workflow reference:**
+ - None.
+
+### Node: Sticky Note1
+- **Type and technical role:** `n8n-nodes-base.stickyNote` — visual documentation.
+- **Configuration choices:**
+ - Explains extraction and analysis area.
+- **Input and output connections:**
+ - None.
+- **Version-specific requirements:**
+ - Type version `1`
+- **Edge cases or potential failure types:**
+ - None.
+- **Sub-workflow reference:**
+ - None.
+
+### Node: Sticky Note2
+- **Type and technical role:** `n8n-nodes-base.stickyNote` — visual documentation.
+- **Configuration choices:**
+ - Explains keyword filtering and customization.
+- **Input and output connections:**
+ - None.
+- **Version-specific requirements:**
+ - Type version `1`
+- **Edge cases or potential failure types:**
+ - None.
+- **Sub-workflow reference:**
+ - None.
+
+### Node: Sticky Note3
+- **Type and technical role:** `n8n-nodes-base.stickyNote` — visual documentation.
+- **Configuration choices:**
+ - Explains post-classification actions and optional Google Sheets logging.
+- **Input and output connections:**
+ - None.
+- **Version-specific requirements:**
+ - Type version `1`
+- **Edge cases or potential failure types:**
+ - None.
+- **Sub-workflow reference:**
+ - None.
+
+---
+
+## 3. Summary Table
+
+| Node Name | Node Type | Functional Role | Input Node(s) | Output Node(s) | Sticky Note |
+|---|---|---|---|---|---|
+| Gmail Trigger | Gmail Trigger | Entry point; detects new Gmail messages | | Get a message | ## Workflow Overview +Gmail Setup
This workflow automatically filters incoming Gmail emails using keyword detection.
The workflow analyzes the email subject and snippet to determine whether the email is likely:
• Cold outreach
• Marketing / promotional
• Needs manual review
• Legitimate communication
Before running the workflow, create the following Gmail labels:
Cold Outreach
Marketing
Needs Review
Legitimate
You can create these labels in Gmail by selecting "Create new label" in the left sidebar.
The Gmail trigger node listens for new incoming emails and starts the workflow whenever a message arrives. |
+| Get a message | Gmail | Retrieves full Gmail message details | Gmail Trigger | Extract Email Data | ## Email Data Extraction and Analyze
These nodes retrieve the full Gmail message and analyze them.
The workflow extracts useful fields such as:
• Sender email
• Subject line
• Email snippet
• Timestamp
These values are then used in the filtering logic to determine the email category. |
+| Extract Email Data | Set | Normalizes sender, subject, body, and message ID | Get a message | Analyze Email Content | ## Email Data Extraction and Analyze
These nodes retrieve the full Gmail message and analyze them.
The workflow extracts useful fields such as:
• Sender email
• Subject line
• Email snippet
• Timestamp
These values are then used in the filtering logic to determine the email category. |
+| Analyze Email Content | Code | Applies rule-based classification logic | Extract Email Data | Check If Cold Outreach | ## Email Data Extraction and Analyze
These nodes retrieve the full Gmail message and analyze them.
The workflow extracts useful fields such as:
• Sender email
• Subject line
• Email snippet
• Timestamp
These values are then used in the filtering logic to determine the email category. |
+| Check If Cold Outreach | If | Routes cold outreach vs other email types | Analyze Email Content | Label as Outreach; Check if Needs_review | ## Keyword Filtering Logic
This section checks the email subject and snippet for keywords commonly associated with:
Cold outreach:
sales outreach, partnership opportunity, quick call, growth strategy, etc.
Marketing emails:
newsletter, promotion, discount, marketing campaign, etc.
If the workflow detects these patterns, the email is classified accordingly.
Emails that do not match clear patterns are sent to the "Needs Review" category.
You can customize these keyword lists to better match your inbox. |
+| Check if Needs_review | If | Routes needs-review emails vs next classification step | Check If Cold Outreach | Label as Needs Review; Check if Marketing | ## Keyword Filtering Logic
This section checks the email subject and snippet for keywords commonly associated with:
Cold outreach:
sales outreach, partnership opportunity, quick call, growth strategy, etc.
Marketing emails:
newsletter, promotion, discount, marketing campaign, etc.
If the workflow detects these patterns, the email is classified accordingly.
Emails that do not match clear patterns are sent to the "Needs Review" category.
You can customize these keyword lists to better match your inbox. |
+| Check if Marketing | If | Routes marketing emails vs legitimate default | Check if Needs_review | Label as Marketing/Ads; Label as Legitimate | ## Keyword Filtering Logic
This section checks the email subject and snippet for keywords commonly associated with:
Cold outreach:
sales outreach, partnership opportunity, quick call, growth strategy, etc.
Marketing emails:
newsletter, promotion, discount, marketing campaign, etc.
If the workflow detects these patterns, the email is classified accordingly.
Emails that do not match clear patterns are sent to the "Needs Review" category.
You can customize these keyword lists to better match your inbox. |
+| Label as Outreach | Gmail | Adds Gmail label for cold outreach | Check If Cold Outreach | Remove from Inbox | ##Email Actions and Optional Logging
After classification, the workflow applies the appropriate Gmail label.
Actions performed:
• Apply the label (Cold Outreach / Marketing / Needs Review / Legitimate)
• Mark the email as read
• Optionally remove it from the inbox
Optional: Google Sheets logging
The workflow can log processed emails to Google Sheets to keep a record of:
• timestamp
• sender
• subject
• classification
Note: make sure to make a google sheet in advance with those columns.
You can disable the logging node if you do not need this feature. |
+| Remove from Inbox | Gmail | Removes Inbox label from outreach emails | Label as Outreach | Mark as Read | ##Email Actions and Optional Logging
After classification, the workflow applies the appropriate Gmail label.
Actions performed:
• Apply the label (Cold Outreach / Marketing / Needs Review / Legitimate)
• Mark the email as read
• Optionally remove it from the inbox
Optional: Google Sheets logging
The workflow can log processed emails to Google Sheets to keep a record of:
• timestamp
• sender
• subject
• classification
Note: make sure to make a google sheet in advance with those columns.
You can disable the logging node if you do not need this feature. |
+| Mark as Read | Gmail | Marks outreach emails as read | Remove from Inbox | Edit Fields (outreach) | ##Email Actions and Optional Logging
After classification, the workflow applies the appropriate Gmail label.
Actions performed:
• Apply the label (Cold Outreach / Marketing / Needs Review / Legitimate)
• Mark the email as read
• Optionally remove it from the inbox
Optional: Google Sheets logging
The workflow can log processed emails to Google Sheets to keep a record of:
• timestamp
• sender
• subject
• classification
Note: make sure to make a google sheet in advance with those columns.
You can disable the logging node if you do not need this feature. |
+| Edit Fields (outreach) | Set | Prepares outreach log row | Mark as Read | Log to Spreadsheet (outreach) | ##Email Actions and Optional Logging
After classification, the workflow applies the appropriate Gmail label.
Actions performed:
• Apply the label (Cold Outreach / Marketing / Needs Review / Legitimate)
• Mark the email as read
• Optionally remove it from the inbox
Optional: Google Sheets logging
The workflow can log processed emails to Google Sheets to keep a record of:
• timestamp
• sender
• subject
• classification
Note: make sure to make a google sheet in advance with those columns.
You can disable the logging node if you do not need this feature. |
+| Log to Spreadsheet (outreach) | Google Sheets | Logs outreach classification | Edit Fields (outreach) | | ##Email Actions and Optional Logging
After classification, the workflow applies the appropriate Gmail label.
Actions performed:
• Apply the label (Cold Outreach / Marketing / Needs Review / Legitimate)
• Mark the email as read
• Optionally remove it from the inbox
Optional: Google Sheets logging
The workflow can log processed emails to Google Sheets to keep a record of:
• timestamp
• sender
• subject
• classification
Note: make sure to make a google sheet in advance with those columns.
You can disable the logging node if you do not need this feature. |
+| Label as Needs Review | Gmail | Adds Gmail label for review cases | Check if Needs_review | Edit Fields (needs review) | ##Email Actions and Optional Logging
After classification, the workflow applies the appropriate Gmail label.
Actions performed:
• Apply the label (Cold Outreach / Marketing / Needs Review / Legitimate)
• Mark the email as read
• Optionally remove it from the inbox
Optional: Google Sheets logging
The workflow can log processed emails to Google Sheets to keep a record of:
• timestamp
• sender
• subject
• classification
Note: make sure to make a google sheet in advance with those columns.
You can disable the logging node if you do not need this feature. |
+| Edit Fields (needs review) | Set | Prepares needs-review log row | Label as Needs Review | Log to Spreadsheet (needs review) | ##Email Actions and Optional Logging
After classification, the workflow applies the appropriate Gmail label.
Actions performed:
• Apply the label (Cold Outreach / Marketing / Needs Review / Legitimate)
• Mark the email as read
• Optionally remove it from the inbox
Optional: Google Sheets logging
The workflow can log processed emails to Google Sheets to keep a record of:
• timestamp
• sender
• subject
• classification
Note: make sure to make a google sheet in advance with those columns.
You can disable the logging node if you do not need this feature. |
+| Log to Spreadsheet (needs review) | Google Sheets | Logs needs-review classification | Edit Fields (needs review) | | ##Email Actions and Optional Logging
After classification, the workflow applies the appropriate Gmail label.
Actions performed:
• Apply the label (Cold Outreach / Marketing / Needs Review / Legitimate)
• Mark the email as read
• Optionally remove it from the inbox
Optional: Google Sheets logging
The workflow can log processed emails to Google Sheets to keep a record of:
• timestamp
• sender
• subject
• classification
Note: make sure to make a google sheet in advance with those columns.
You can disable the logging node if you do not need this feature. |
+| Label as Marketing/Ads | Gmail | Adds Gmail label for marketing emails | Check if Marketing | Edit Fields (marketing) | ##Email Actions and Optional Logging
After classification, the workflow applies the appropriate Gmail label.
Actions performed:
• Apply the label (Cold Outreach / Marketing / Needs Review / Legitimate)
• Mark the email as read
• Optionally remove it from the inbox
Optional: Google Sheets logging
The workflow can log processed emails to Google Sheets to keep a record of:
• timestamp
• sender
• subject
• classification
Note: make sure to make a google sheet in advance with those columns.
You can disable the logging node if you do not need this feature. |
+| Edit Fields (marketing) | Set | Prepares marketing log row | Label as Marketing/Ads | Log to Spreadsheet (marketing/Ads) | ##Email Actions and Optional Logging
After classification, the workflow applies the appropriate Gmail label.
Actions performed:
• Apply the label (Cold Outreach / Marketing / Needs Review / Legitimate)
• Mark the email as read
• Optionally remove it from the inbox
Optional: Google Sheets logging
The workflow can log processed emails to Google Sheets to keep a record of:
• timestamp
• sender
• subject
• classification
Note: make sure to make a google sheet in advance with those columns.
You can disable the logging node if you do not need this feature. |
+| Log to Spreadsheet (marketing/Ads) | Google Sheets | Logs marketing classification | Edit Fields (marketing) | | ##Email Actions and Optional Logging
After classification, the workflow applies the appropriate Gmail label.
Actions performed:
• Apply the label (Cold Outreach / Marketing / Needs Review / Legitimate)
• Mark the email as read
• Optionally remove it from the inbox
Optional: Google Sheets logging
The workflow can log processed emails to Google Sheets to keep a record of:
• timestamp
• sender
• subject
• classification
Note: make sure to make a google sheet in advance with those columns.
You can disable the logging node if you do not need this feature. |
+| Label as Legitimate | Gmail | Adds Gmail label for legitimate emails | Check if Marketing | Edit Fields (Legitimate) | ##Email Actions and Optional Logging
After classification, the workflow applies the appropriate Gmail label.
Actions performed:
• Apply the label (Cold Outreach / Marketing / Needs Review / Legitimate)
• Mark the email as read
• Optionally remove it from the inbox
Optional: Google Sheets logging
The workflow can log processed emails to Google Sheets to keep a record of:
• timestamp
• sender
• subject
• classification
Note: make sure to make a google sheet in advance with those columns.
You can disable the logging node if you do not need this feature. |
+| Edit Fields (Legitimate) | Set | Prepares legitimate log row | Label as Legitimate | Log to Spreadsheet (legitimate) | ##Email Actions and Optional Logging
After classification, the workflow applies the appropriate Gmail label.
Actions performed:
• Apply the label (Cold Outreach / Marketing / Needs Review / Legitimate)
• Mark the email as read
• Optionally remove it from the inbox
Optional: Google Sheets logging
The workflow can log processed emails to Google Sheets to keep a record of:
• timestamp
• sender
• subject
• classification
Note: make sure to make a google sheet in advance with those columns.
You can disable the logging node if you do not need this feature. |
+| Log to Spreadsheet (legitimate) | Google Sheets | Logs legitimate classification | Edit Fields (Legitimate) | | ##Email Actions and Optional Logging
After classification, the workflow applies the appropriate Gmail label.
Actions performed:
• Apply the label (Cold Outreach / Marketing / Needs Review / Legitimate)
• Mark the email as read
• Optionally remove it from the inbox
Optional: Google Sheets logging
The workflow can log processed emails to Google Sheets to keep a record of:
• timestamp
• sender
• subject
• classification
Note: make sure to make a google sheet in advance with those columns.
You can disable the logging node if you do not need this feature. |
+| Sticky Note | Sticky Note | Canvas documentation for setup | | | |
+| Sticky Note1 | Sticky Note | Canvas documentation for extraction/analysis | | | |
+| Sticky Note2 | Sticky Note | Canvas documentation for keyword logic | | | |
+| Sticky Note3 | Sticky Note | Canvas documentation for actions/logging | | | |
+
+---
+
+## 4. Reproducing the Workflow from Scratch
+
+1. **Create required Gmail labels first**
+ - In Gmail, manually create these labels:
+ - Cold Outreach
+ - Marketing
+ - Needs Review
+ - Legitimate
+ - After creating them, note their Gmail label IDs if needed in n8n. In imported workflows, label IDs are account-specific and must usually be reselected.
+
+2. **Optionally prepare Google Sheets for logging**
+ - Create a spreadsheet with separate tabs, for example:
+ - `Sheet1` for outreach
+ - `Needs_review`
+ - `Marketing`
+ - `Legitimate`
+ - Add these columns to each tab:
+ - `timestamp`
+ - `message_id`
+ - `sender_email`
+ - `sender_name`
+ - `subject`
+ - `email_type`
+ - `confidence`
+ - `keywords_found`
+
+3. **Create a new workflow**
+ - Name it something like: `Smart Email Filtering System (using keywords)`.
+
+4. **Add a Gmail Trigger node**
+ - Type: `Gmail Trigger`
+ - Configure Gmail credentials.
+ - Set polling to every minute.
+ - Leave filters empty unless you want only certain emails to trigger the workflow.
+ - This is the entry point.
+
+5. **Add a Gmail node named `Get a message`**
+ - Type: `Gmail`
+ - Operation: `Get`
+ - Set `Message ID` to `{{ $json.id }}`
+ - Disable simplified output (`simple: false`) so full message metadata is available.
+ - Connect `Gmail Trigger -> Get a message`.
+
+6. **Add a Set node named `Extract Email Data`**
+ - Create these fields:
+ - `email_content` = `{{ $json.subject + ' ' + $json.text }}`
+ - `sender_email` = `{{ $json.from.value[0].address }}`
+ - `sender_name` = `{{ $json.from.value[0].name || 'Unknown' }}`
+ - `message_id` = `{{ $json.id }}`
+ - `Subject` = `{{ $json.subject }}`
+ - Enable `Always Output Data`.
+ - Keep `Execute Once` enabled only if processing one message per execution is acceptable.
+ - Connect `Get a message -> Extract Email Data`.
+
+7. **Add a Code node named `Analyze Email Content`**
+ - Paste the JavaScript classification logic from the workflow.
+ - Ensure it returns a single JSON object with fields including:
+ - `message_id`
+ - `email_type`
+ - `confidence`
+ - `classification_reason`
+ - `found_keywords`
+ - `found_legitimate_patterns`
+ - `found_review_indicators`
+ - `sales_tool_detected`
+ - `has_unsubscribe`
+ - `sender_email`
+ - `sender_domain`
+ - `legitimate_score`
+ - `review_score`
+ - `analysis_timestamp`
+ - Connect `Extract Email Data -> Analyze Email Content`.
+
+8. **Add an If node named `Check If Cold Outreach`**
+ - Condition:
+ - left value: `{{ $json.email_type }}`
+ - operator: `equals`
+ - right value: `cold_outreach`
+ - Connect `Analyze Email Content -> Check If Cold Outreach`.
+
+9. **Create cold outreach Gmail action node**
+ - Add a Gmail node named `Label as Outreach`
+ - Operation: `Add Labels`
+ - Select the Gmail label corresponding to `Cold Outreach`
+ - `Message ID` = `{{ $json.message_id }}`
+ - Connect the **true** output of `Check If Cold Outreach` to this node.
+
+10. **Add node `Remove from Inbox`**
+ - Type: `Gmail`
+ - Operation: `Remove Labels`
+ - Label to remove: `INBOX`
+ - `Message ID` = `{{ $json.id }}`
+ - Connect `Label as Outreach -> Remove from Inbox`.
+ - Recommended improvement: if your Gmail node output does not preserve `id`, use `message_id` consistently instead.
+
+11. **Add node `Mark as Read`**
+ - Type: `Gmail`
+ - Operation: `Mark as Read`
+ - `Message ID` = `{{ $json.id }}`
+ - Connect `Remove from Inbox -> Mark as Read`.
+
+12. **Add logging preparation node for outreach**
+ - Type: `Set`
+ - Name: `Edit Fields (outreach)`
+ - Add:
+ - `analysis_timestamp` = `{{ $('Analyze Email Content').first().json.analysis_timestamp }}`
+ - `message_id` = `{{ $('Analyze Email Content').first().json.message_id }}`
+ - `sender_email` = `{{ $('Analyze Email Content').first().json.sender_email }}`
+ - `sender_name` = `{{ $('Extract Email Data').first().json.sender_name }}`
+ - `subject` = `{{ $('Extract Email Data').first().json.Subject }}`
+ - `email_type` = `{{ $('Analyze Email Content').first().json.email_type }}`
+ - `confidence` = `{{ $('Analyze Email Content').first().json.confidence }}`
+ - `keywords_found` = `{{ $('Analyze Email Content').first().json.found_keywords.join(', ') }}`
+ - Connect `Mark as Read -> Edit Fields (outreach)`.
+
+13. **Add outreach Google Sheets logging node**
+ - Type: `Google Sheets`
+ - Name: `Log to Spreadsheet (outreach)`
+ - Operation: `Append`
+ - Configure Google Sheets credentials.
+ - Select spreadsheet document ID.
+ - Select tab `Sheet1` or your outreach tab.
+ - Map the 8 columns from the Set node.
+ - Connect `Edit Fields (outreach) -> Log to Spreadsheet (outreach)`.
+
+14. **Add an If node named `Check if Needs_review`**
+ - Connect the **false** output of `Check If Cold Outreach` to this node.
+ - Condition:
+ - `{{ $json.email_type }}`
+ - equals
+ - `needs_review`
+
+15. **Create needs-review labeling node**
+ - Add Gmail node `Label as Needs Review`
+ - Operation: `Add Labels`
+ - Select the Gmail label corresponding to `Needs Review`
+ - `Message ID` = `{{ $json.message_id }}`
+ - Connect the **true** output of `Check if Needs_review` to it.
+
+16. **Add needs-review logging preparation node**
+ - Add Set node `Edit Fields (needs review)`
+ - Use the same fields and expressions as outreach.
+ - Connect `Label as Needs Review -> Edit Fields (needs review)`.
+
+17. **Add needs-review Sheets node**
+ - Add Google Sheets node `Log to Spreadsheet (needs review)`
+ - Operation: `Append`
+ - Select spreadsheet and tab `Needs_review`
+ - Map the same 8 columns.
+ - Connect `Edit Fields (needs review) -> Log to Spreadsheet (needs review)`.
+
+18. **Add an If node named `Check if Marketing`**
+ - Connect the **false** output of `Check if Needs_review` to this node.
+ - Condition:
+ - `{{ $json.email_type }}`
+ - equals
+ - `marketing`
+
+19. **Create marketing labeling node**
+ - Add Gmail node `Label as Marketing/Ads`
+ - Operation: `Add Labels`
+ - Select the Gmail label corresponding to `Marketing`
+ - `Message ID` = `{{ $json.message_id }}`
+ - Connect the **true** output of `Check if Marketing` to it.
+
+20. **Add marketing logging preparation node**
+ - Add Set node `Edit Fields (marketing)`
+ - Use the same field mappings as the previous Set logging nodes.
+ - Connect `Label as Marketing/Ads -> Edit Fields (marketing)`.
+
+21. **Add marketing Sheets node**
+ - Add Google Sheets node `Log to Spreadsheet (marketing/Ads)`
+ - Operation: `Append`
+ - Select spreadsheet and tab `Marketing`
+ - Map the same 8 fields.
+ - Connect `Edit Fields (marketing) -> Log to Spreadsheet (marketing/Ads)`.
+
+22. **Create legitimate labeling node**
+ - Add Gmail node `Label as Legitimate`
+ - Operation: `Add Labels`
+ - Select the Gmail label corresponding to `Legitimate`
+ - `Message ID` = `{{ $json.message_id }}`
+ - Connect the **false** output of `Check if Marketing` to it.
+
+23. **Add legitimate logging preparation node**
+ - Add Set node `Edit Fields (Legitimate)`
+ - Use the same field mappings as the other Set nodes.
+ - Connect `Label as Legitimate -> Edit Fields (Legitimate)`.
+
+24. **Add legitimate Sheets node**
+ - Add Google Sheets node `Log to Spreadsheet (legitimate)`
+ - Operation: `Append`
+ - Select spreadsheet and tab `Legitimate`
+ - Map the same 8 fields.
+ - Connect `Edit Fields (Legitimate) -> Log to Spreadsheet (legitimate)`.
+
+25. **Configure credentials**
+ - **Gmail credentials:** required for:
+ - Gmail Trigger
+ - Get a message
+ - all Gmail action nodes
+ - **Google Sheets credentials:** required for:
+ - all four Google Sheets nodes
+ - Ensure the Gmail connection has enough permission to:
+ - read messages,
+ - modify labels,
+ - mark as read,
+ - remove Inbox label.
+
+26. **Validate label IDs and spreadsheet IDs**
+ - Re-select each Gmail label in the Gmail nodes after import/manual build.
+ - Fill every Google Sheets `documentId`; the source workflow leaves them blank.
+ - Confirm each sheet tab exists.
+
+27. **Test each branch**
+ - Send or use sample emails that should match:
+ - cold outreach,
+ - marketing,
+ - needs review,
+ - legitimate.
+ - Verify:
+ - label applied correctly,
+ - Inbox removal only happens for outreach,
+ - mark-as-read only happens for outreach,
+ - sheet log row is appended correctly.
+
+28. **Activate the workflow**
+ - Once all credentials, label mappings, and spreadsheet references are valid, activate the workflow.
+
+### Sub-workflow setup
+- This workflow does **not** invoke any sub-workflows.
+- There are no Execute Workflow nodes or workflow-call dependencies.
+
+---
+
+## 5. General Notes & Resources
+
+| Note Content | Context or Link |
+|---|---|
+| The workflow is inactive in the provided export. | Activate only after credentials, labels, and Sheets targets are configured. |
+| Gmail label IDs are mailbox-specific and usually cannot be reused as-is across accounts. | Re-select labels manually in each Gmail node. |
+| All Google Sheets logging nodes have an empty `documentId` in the provided workflow. | Must be filled before logging works. |
+| The classifier defaults unknown messages to `legitimate`. | Consider changing this to `needs_review` if you prefer a safer posture. |
+| Trusted-domain matching uses substring inclusion. | Tighten this to exact-domain or subdomain-safe matching if spoof resistance matters. |
+| The sticky notes instruct users to create labels in Gmail before building. | Gmail setup guidance embedded in canvas notes. |
+| Logging is optional according to the workflow notes. | You can disable or delete all Google Sheets nodes if not needed. |
\ No newline at end of file