

How to Forward Gmail Emails to Slack Channels with n8n
Polls Gmail for emails matching specific senders, subjects, or labels, then posts formatted summaries to designated Slack channels automatically.
Steps and UI details are based on platform versions at time of writing — check each platform for the latest interface.
Best for
Teams that need emails from specific senders or with specific labels routed to the right Slack channel without anyone manually copy-pasting
Not ideal for
Organizations that need true real-time forwarding under 30 seconds — use a Gmail push notification via Google Pub/Sub instead
Sync type
scheduledUse case type
notificationReal-World Example
A 12-person e-commerce team receives vendor invoices, customer support emails, and shipping alerts all in one Gmail inbox. Before this workflow, the ops manager forwarded emails manually to #vendors, #support, and #logistics in Slack — averaging 25 manual forwards per day. After setting up n8n with label-based routing, every email lands in the right channel within 5 minutes of arrival, and the ops manager stopped touching it entirely.
What Will This Cost?
Drag the slider to your expected monthly volume.
Each platform counts differently — Zapier: 1 task per trigger. Make: 1 operation per module per record. n8n: 1 execution per run.





Prices shown for annual billing. Based on published pricing as of April 2026.
Estimated ROI
1000
min saved/mo
$583
labor value/mo
Free
no platform cost
Based on ~2 min manual effort per operation at $35/hr fully loaded labor cost.
Implementation
Import this workflow directly into n8n
Copy the pre-built n8n blueprint and paste it straight into n8n. All modules, filters, and field mappings are already configured — you just need to connect your accounts.
Before You Start
Make sure you have everything ready.
Optional
Field Mapping
Map these fields between your apps.
| Field | API Name | |
|---|---|---|
| Required | ||
| Message ID | ||
| Sender Address | ||
| Subject Line | ||
| Plain Text Body | ||
| Slack Channel ID | ||
| Slack Message Text | ||
3 optional fields▸ show
| Sender Name | |
| Date Received | |
| Gmail Labels |
Step-by-Step Setup
n8n Dashboard > + New Workflow
Create a new n8n workflow
Log into your n8n instance at your self-hosted URL or app.n8n.cloud. Click the orange '+' button in the top-left sidebar to create a blank workflow. Give it a name like 'Gmail → Slack Forwarder' by clicking 'Untitled Workflow' at the top. You'll land on a blank canvas with a 'Click to add first step' prompt in the center.
- 1Log into your n8n instance
- 2Click the orange '+' button in the left sidebar
- 3Click 'Untitled Workflow' at the top center and type 'Gmail → Slack Forwarder'
- 4Press Enter to save the name
Canvas > + Node > Gmail > Email Received (Trigger)
Add the Gmail trigger node
Click the '+' node on the canvas to open the node picker. Search for 'Gmail' in the search box and select it. Under the Trigger section, choose 'Email Received' — this is the polling trigger that checks for new emails on a schedule. You'll see a configuration panel slide in from the right with fields for credentials, filters, and poll interval.
- 1Click the '+' node in the center of the canvas
- 2Type 'Gmail' in the search field
- 3Click 'Gmail' under the Apps section
- 4Select 'Email Received' under Trigger
- 5Click 'Create New Credential' if you haven't connected Gmail yet
Gmail Node Panel > Credential to connect with > Create New Credential
Connect your Gmail account
In the Gmail node panel, click 'Create New Credential' next to the credentials dropdown. A pop-up will ask for your OAuth Client ID and Client Secret — get these from Google Cloud Console under APIs & Services > Credentials. Paste them in, click 'Sign in with Google', and authorize n8n to read your Gmail. Once connected, the credential name appears in the dropdown with a green checkmark.
- 1Click the credentials dropdown and select 'Create New Credential'
- 2Open Google Cloud Console at console.cloud.google.com in a new tab
- 3Navigate to APIs & Services > Credentials and copy your OAuth 2.0 Client ID and Secret
- 4Paste both values into the n8n credential form
- 5Click 'Sign in with Google' and complete the OAuth flow
Gmail Node Panel > Poll Times > Filters
Configure Gmail filters and poll interval
Set 'Poll Times' to every 5 minutes using the cron expression '*/5 * * * *' — this is the shortest practical interval for email forwarding without hammering the API. Under 'Filters', choose your filter type: use 'Sender' to match a specific email address, 'Subject' for keyword matching, or 'Label Name' to catch pre-labeled emails. You can also combine filters using the 'Simple' filter syntax or switch to 'Custom' to write a Gmail search query directly like 'from:[email protected] label:urgent'.
- 1Click the 'Poll Times' field and select 'Every X Minutes'
- 2Set the interval to 5
- 3Scroll down to 'Filters' and click 'Add Filter'
- 4Select filter type: Sender, Subject, or Label Name
- 5Enter your filter value, e.g. '[email protected]' or 'invoices'
Gmail Node Panel > Test Step button
Test the Gmail trigger
Click 'Test Step' at the bottom of the Gmail node panel. n8n polls your Gmail inbox right now and returns up to 10 matching emails as JSON objects. You'll see the raw output in the panel on the right — each item contains fields like subject, from, text, html, date, and messageId. Confirm the emails shown match your filter criteria before moving on. If you see zero results, send yourself a test email matching your filter, wait 60 seconds, and try again.
- 1Click 'Test Step' at the bottom of the Gmail node panel
- 2Wait 5-10 seconds for n8n to poll Gmail
- 3Review the output items in the right-side data panel
- 4Confirm subject, from, and body fields are populated correctly
Canvas > + Connector from Gmail Node > Search 'IF' > IF (Core)
Add an IF node to route by channel
Click the '+' connector coming out of the Gmail node to add the next node. Search for 'IF' and add the core IF node. This is where you split emails into different Slack channels based on sender or label. Set Condition 1 to check '{{ $json.from.value[0].address }}' contains 'vendor.com' for the vendor channel route. Add more conditions using the '+Add Condition' button for each channel you want to route to. The IF node has a 'true' and 'false' output branch — true goes to your first Slack channel, false continues to additional IF nodes or a default channel.
- 1Click the '+' connector on the right side of the Gmail node
- 2Search for 'IF' and select 'IF' under Core nodes
- 3Set Value 1 to '{{ $json.from.value[0].address }}'
- 4Set Operation to 'Contains'
- 5Set Value 2 to your target domain or email, e.g. 'vendor.com'
channel: {{channel}}
ts: {{ts}}
Canvas > IF Node True Branch > + > Slack > Message > Post
Add the Slack node to the true branch
Click the '+' connector on the 'true' output of the IF node. Search for 'Slack' and add it. Set Resource to 'Message' and Operation to 'Post'. Connect your Slack credentials by clicking 'Create New Credential' — you'll need a Slack Bot Token (starts with xoxb-) from your Slack app configuration at api.slack.com/apps. Set Channel to the target channel name like '#vendor-comms'. In the Message Text field, build your formatted message using Gmail fields.
- 1Click the '+' connector on the 'true' output of the IF node
- 2Search for 'Slack' and select it
- 3Set Resource to 'Message' and Operation to 'Post'
- 4Click 'Create New Credential', paste your Slack Bot Token (xoxb-...)
- 5Set Channel to your target channel, e.g. '#vendor-comms'
Slack Node Panel > Text Field
Build the Slack message format
In the Slack node's Text field, write a formatted message that includes the key email fields. Use n8n's expression syntax with double curly braces to pull values from the Gmail node. A clean format includes the sender, subject, snippet of the body, and a note about the original email. Keep the body preview short — 300 characters is enough to give context without flooding the channel. You can also use Slack's Block Kit markup if you toggle 'Send as Blocks' on, but plain text with line breaks works fine for most teams.
- 1Click into the 'Text' field in the Slack node panel
- 2Click the expression toggle (the '{x}' icon) to enable dynamic values
- 3Type your message template using n8n expression syntax
- 4Preview the rendered message by clicking 'Test Step'
Canvas > IF Node False Branch > + > No Operation (or additional Slack node)
Handle the default (false) branch
Click the '+' connector on the 'false' output of the IF node. If you have a catch-all channel like '#general-emails', add another Slack node here pointed at that channel. If you only want specific emails forwarded and nothing else, add a 'No-Op' node (search for 'No Operation') to explicitly terminate the false branch — this prevents n8n from throwing an error about an unconnected branch. Label this branch clearly so future you knows it's intentional.
- 1Click the '+' connector on the 'false' output of the IF node
- 2Search for 'No Operation' and add it if you want to drop non-matching emails
- 3Alternatively, add another Slack node for a catch-all channel
- 4Double-click the No Operation node and add a note: 'Non-matching emails — intentionally dropped'
Canvas > Test Workflow button (bottom toolbar)
Test the full workflow end-to-end
Click 'Test Workflow' at the bottom of the canvas. n8n runs the entire workflow using the last data fetched from Gmail. Watch each node light up green (success) or red (error) as execution flows through. Click any node to see its input and output data side by side. Verify that the Slack message actually appeared in your target channel — check Slack now. If a node is red, click it to read the error message before proceeding.
- 1Click 'Test Workflow' in the bottom toolbar
- 2Watch the execution flow across nodes on the canvas
- 3Click the Slack node to confirm the output shows a successful message_id
- 4Open Slack and verify the message appeared in the correct channel
Canvas > Active/Inactive Toggle (top right) > Executions Tab
Activate the workflow
Click the 'Inactive' toggle in the top-right of the canvas to flip it to 'Active'. n8n will now poll Gmail every 5 minutes automatically. The workflow card in your dashboard shows a green 'Active' badge. Monitor the first few executions by clicking into the workflow and checking the Executions tab — you'll see a log of every poll with input/output data and execution time. Set up an error notification (via the workflow Settings > Error Workflow option) so you're alerted if the workflow fails silently.
- 1Click the 'Inactive' toggle in the top-right corner of the canvas
- 2Confirm the toggle switches to 'Active' with a green indicator
- 3Click the 'Executions' tab to monitor the first few runs
- 4Optionally go to Settings > Error Workflow and connect an error notification workflow
This Code node runs between the Gmail trigger and the IF node. It strips HTML from the email body, truncates the plain text to 300 characters, formats the date into a readable string, and checks a static data store of already-processed message IDs to block duplicates. Paste this as a Code node (using JavaScript) immediately after the Gmail trigger on your canvas.
JavaScript — Code Node// n8n Code Node: Email formatter + deduplication▸ Show code
// n8n Code Node: Email formatter + deduplication // Place this between Gmail Trigger and IF node const items = $input.all();
... expand to see full code
// n8n Code Node: Email formatter + deduplication
// Place this between Gmail Trigger and IF node
const items = $input.all();
const processedIds = $getWorkflowStaticData('global').processedMessageIds || [];
const results = [];
for (const item of items) {
const email = item.json;
const messageId = email.id;
// Skip if we've already forwarded this email
if (processedIds.includes(messageId)) {
continue;
}
// Strip HTML tags from body if plain text is missing
let bodyText = email.text || '';
if (!bodyText && email.html) {
bodyText = email.html
.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
.replace(/<[^>]+>/g, ' ')
.replace(/\s+/g, ' ')
.trim();
}
// Truncate body to 300 characters
const bodyPreview = bodyText.length > 300
? bodyText.substring(0, 297) + '...'
: bodyText;
// Format the received date
const receivedDate = new Date(email.date);
const formattedDate = receivedDate.toLocaleString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: '2-digit',
hour12: true
});
// Extract sender info safely
const senderAddress = email.from?.value?.[0]?.address || '[email protected]';
const senderName = email.from?.value?.[0]?.name || senderAddress;
// Build the Slack message text
const slackMessage = [
`📧 *New email from ${senderName}* (${senderAddress})`,
`*Subject:* ${email.subject || '(no subject)'}`,
`*Received:* ${formattedDate}`,
'',
bodyPreview
].join('\n');
// Mark this message as processed
processedIds.push(messageId);
results.push({
json: {
...email,
senderAddress,
senderName,
bodyPreview,
formattedDate,
slackMessage
}
});
}
// Persist the updated list of processed IDs (keeps last 500 to prevent unbounded growth)
const staticData = $getWorkflowStaticData('global');
staticData.processedMessageIds = processedIds.slice(-500);
return results;Going live
Production Checklist
Before you turn this on for real, confirm each item.
Troubleshooting
Common errors and how to fix them.
Frequently Asked Questions
Common questions about this workflow.
Analysis
Use n8n for this if you're self-hosting, need custom routing logic beyond what a simple Zap allows, or want to keep email data off third-party servers. The code node gives you full control over how messages are formatted, deduplicated, and filtered — you're not limited to the field mapping UI that other platforms enforce. A second good reason: if you're already running n8n for other workflows, adding this costs zero extra dollars. The one case where you'd skip n8n: if your team has no one comfortable reading a JSON path like from.value[0].address, go with Zapier instead. The setup is 10 minutes and requires zero code.
n8n Cloud's Starter plan costs $20/month and includes 2,500 executions. At a 5-minute poll interval, you burn through roughly 8,640 executions per month just from the Gmail trigger firing — even when no emails match. That puts you on the Growth plan at $50/month. Self-hosted n8n costs nothing in licensing. If you run it on a $6/month DigitalOcean droplet, your total cost is $6/month regardless of execution count. Zapier by comparison charges per task: at 50 forwarded emails per day (1,500/month), you're on the $19.99/month Starter plan. Make's free tier handles 1,000 operations/month, which covers about 33 forwarded emails per day before you hit the $9/month Core plan.
Zapier handles this use case in fewer clicks — their Gmail 'New Email Matching Search' trigger has a clean UI for building Gmail search queries without touching JSON paths, and setup genuinely takes 8 minutes. Make's Gmail module is similarly straightforward and its scenario debugger is better than n8n's for stepping through failed executions one item at a time. Pipedream's Gmail source supports real push notifications via Google Pub/Sub out of the box, giving you sub-5-second latency vs n8n's 5-minute polling floor — that's a real gap if timing matters. Power Automate's Gmail connector is thin and unreliable; skip it for this workflow. n8n wins on the code node: no other platform gives you a full JavaScript runtime mid-workflow with access to static data for deduplication, and you control the exact Slack message format without fighting a GUI.
Three things you'll hit after setup. First: Gmail's API daily quota is 1 billion units per day for most projects, but the 'list messages' call costs 5 units per request and 'get message' costs 5 more — at a 5-minute poll interval that's around 3,000 units per day, well under the limit, but if you duplicate workflows per account carelessly you can exhaust quota on a shared Cloud project. Second: n8n's static data store (used for deduplication) is stored in the workflow's metadata and has no automatic expiry — after 6 months of high email volume, the processedIds array can grow large enough to slow execution. The code node above caps it at 500 entries, which is the right call. Third: Slack's Block Kit formatting is tempting but the Slack node's 'Blocks' field requires a valid JSON array — a malformed block silently drops the message without an error in n8n's execution log. Stick with plain text formatting until you've verified the workflow is stable.
Ideas for what to build next
- →Add a digest mode for low-priority channels — Instead of posting every email instantly, batch emails into a single daily or hourly digest message in Slack using n8n's Schedule trigger and an Aggregate node. Reduces Slack noise for channels like #newsletters or #vendor-updates.
- →Create a Slack reply that logs back to Gmail — Extend this workflow to listen for Slack thread replies on forwarded emails and send those replies back to the original Gmail thread using the Gmail 'Reply' operation. Keeps the email thread and Slack conversation in sync.
- →Attach email files to the Slack message — Pull email attachments using the Gmail node's 'Download Attachment' operation and upload them to Slack alongside the message using the Slack 'Upload File' operation. Useful for invoices and reports that need to be reviewed in Slack directly.
Related guides
How to Share Notion Meeting Notes to Slack with Pipedream
~15 min setup
How to Share Notion Meeting Notes to Slack with Power Automate
~15 min setup
How to Share Notion Meeting Notes to Slack with n8n
~20 min setup
How to Send Notion Meeting Notes to Slack with Zapier
~8 min setup
How to Share Notion Meeting Notes to Slack with Make
~12 min setup
How to Create Notion Tasks from Slack with Pipedream
~15 min setup