

How to Forward Gmail Meeting Notes to Slack with Pipedream
Watches Gmail for emails containing meeting notes or action items and forwards a formatted summary to the appropriate Slack channel in real time.
Steps and UI details are based on platform versions at time of writing — check each platform for the latest interface.
Best for
Teams where external meeting confirmations and action items land in one inbox but need to reach multiple Slack channels fast.
Not ideal for
If you need meeting notes pulled from a calendar tool like Google Calendar or Zoom — start with those triggers instead of Gmail.
Sync type
real-timeUse case type
notificationReal-World Example
A 20-person consulting firm routes all client meeting follow-ups from a shared Gmail inbox to project-specific Slack channels like #client-acme and #client-globex. Before this workflow, the ops lead manually forwarded emails 2-3 times per day and team members frequently missed action items until the next standup. Now the relevant channel gets a formatted Slack message within 30 seconds of the email arriving.
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 Pipedream
Copy the pre-built Pipedream blueprint and paste it straight into Pipedream. 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.
Field Mapping
Map these fields between your apps.
| Field | API Name | |
|---|---|---|
| Required | ||
| Email Subject | ||
| Sender Name and Address | ||
| Email Body (Plain Text) | ||
| Email Received Date | ||
| Target Slack Channel | ||
| Gmail Message ID | ||
2 optional fields▸ show
| Email Thread ID | |
| Recipient List (To / CC) |
Step-by-Step Setup
pipedream.com > Workflows > New Workflow
Create a new Pipedream workflow
Go to pipedream.com and sign in. Click 'New Workflow' in the top-left of the Workflows dashboard. You'll land on the workflow builder canvas with a single empty trigger block at the top. Give the workflow a name — something like 'Gmail Meeting Notes → Slack' — by clicking the untitled field at the top of the canvas. This name shows up in your workflow list and in Pipedream's error notification emails, so make it descriptive.
- 1Sign in at pipedream.com
- 2Click 'New Workflow' in the top-left sidebar
- 3Click the untitled workflow name field at the top and type 'Gmail Meeting Notes → Slack'
- 4Click the trigger block labeled 'Add a trigger'
Trigger Block > Search Apps > Gmail > New Email
Add the Gmail trigger
In the trigger block, search for 'Gmail' and select it from the app list. Choose the trigger event 'New Email' — this fires every time a new email lands in the inbox. Pipedream uses Gmail's push notification API here, not polling, so events fire within seconds of email delivery. You'll see a configuration panel on the right side of the screen where you'll connect your account and set filters.
- 1Click the trigger block on the canvas
- 2Type 'Gmail' in the search field
- 3Select 'Gmail' from the app list
- 4Choose 'New Email' as the trigger event
Trigger Config Panel > Connect Account > Google OAuth
Connect your Gmail account
In the trigger configuration panel, click 'Connect Account' under the Gmail section. Pipedream opens a Google OAuth popup. Sign in with the Gmail account that receives meeting follow-ups — this must be the actual inbox where those emails land. Grant the requested scopes, which include reading mail and managing push notifications. After approving, the account appears in the dropdown labeled with your email address.
- 1Click 'Connect Account' in the right-side config panel
- 2Sign in with the Gmail account that receives meeting emails
- 3Click 'Allow' on the Google permissions screen
- 4Confirm your email address appears in the account dropdown
Trigger Config Panel > Label > Search > Test Trigger
Configure the Gmail label or search filter
In the trigger config panel, set the 'Label' field to the Gmail label where meeting follow-ups land — for example, 'meeting-notes' or 'INBOX'. If you don't use labels, leave it as 'INBOX' and you'll filter by subject line content in the next code step. Optionally, set the 'Search' field to a Gmail search string like 'subject:(meeting OR follow-up OR action items)' to pre-filter at the trigger level. Click 'Test Trigger' at the bottom of the panel to pull a real email as sample data.
- 1Set the 'Label' dropdown to 'INBOX' or your specific meeting label
- 2In the 'Search' field, type: subject:(meeting OR "follow-up" OR "action items")
- 3Click 'Test Trigger' at the bottom of the config panel
- 4Select a real meeting email from the results to use as sample data
Canvas > + > Run Node.js Code
Add a Node.js code step to parse and classify the email
Click the '+' button below the trigger block to add a new step. Choose 'Run Node.js code' (not an app). This code step reads the raw email data and does two things: checks whether the email body actually contains meeting-related content, and determines which Slack channel to route it to based on keywords or sender domain. Paste the code provided in the pro tip section below into the code editor. The output of this step — channelId and formattedMessage — gets used by the Slack step next.
- 1Click the '+' button below the Gmail trigger block
- 2Click 'Run Node.js code' in the step type list
- 3Clear the default code in the editor
- 4Paste the classification and formatting code from the pro tip below
- 5Click 'Test' to run the step against your sample email
Paste this into the Node.js code step added in Step 5. It decodes the Gmail base64 body, classifies the email as meeting-related using keyword matching, routes it to the correct Slack channel based on sender domain, and builds a formatted mrkdwn message with extracted action items. The function returns 'isMeetingEmail: false' to halt non-meeting emails at the filter step.
JavaScript — Code Stepimport { Buffer } from 'buffer';▸ Show code
import { Buffer } from 'buffer';
export default defineComponent({
async run({ steps, $ }) {... expand to see full code
import { Buffer } from 'buffer';
export default defineComponent({
async run({ steps, $ }) {
const email = steps.trigger.event;
// Decode base64url email body (handles multipart MIME)
let bodyText = '';
try {
const parts = email.payload?.parts;
if (parts && parts.length > 0) {
const textPart = parts.find(p => p.mimeType === 'text/plain');
bodyText = textPart
? Buffer.from(textPart.body.data, 'base64url').toString('utf-8')
: Buffer.from(email.payload.body.data || '', 'base64url').toString('utf-8');
} else {
bodyText = Buffer.from(email.payload?.body?.data || '', 'base64url').toString('utf-8');
}
} catch (err) {
$.flow.exit('Could not decode email body: ' + err.message);
}
// Meeting classification keywords
const subject = email.payload.headers.find(h => h.name === 'Subject')?.value || '';
const meetingKeywords = ['meeting', 'follow-up', 'followup', 'action items', 'recap',
'minutes', 'call summary', 'discussion summary', 'confirmed', 'kickoff'];
const isMeetingEmail = meetingKeywords.some(k =>
subject.toLowerCase().includes(k) || bodyText.toLowerCase().includes(k)
);
if (!isMeetingEmail) {
return { isMeetingEmail: false };
}
// Extract sender details
const fromHeader = email.payload.headers.find(h => h.name === 'From')?.value || '';
const dateHeader = email.payload.headers.find(h => h.name === 'Date')?.value || '';
const receivedDate = new Date(dateHeader).toLocaleString('en-US', {
month: 'short', day: 'numeric', year: 'numeric',
hour: 'numeric', minute: '2-digit', timeZoneName: 'short'
});
// Extract sender domain for channel routing
const emailMatch = fromHeader.match(/<([^>]+)>/) || [null, fromHeader];
const senderEmail = emailMatch[1];
const senderDomain = senderEmail.split('@')[1]?.replace('.com', '').replace('.', '-') || 'general';
// Channel routing map — update these to match your real Slack channel IDs
const channelMap = {
'acmecorp': 'C0123456789', // #client-acme
'globexsolutions': 'C9876543210', // #client-globex
'default': 'C1111111111' // #meetings-general
};
const channelId = channelMap[senderDomain] || channelMap['default'];
// Extract action items (lines starting with numbered lists or bullet indicators)
const actionItemRegex = /(?:\d+[.)\s]|[-•*]\s)(.+)/g;
const actionItems = [...bodyText.matchAll(actionItemRegex)].map(m => m[1].trim());
const actionBlock = actionItems.length > 0
? '*Action Items:*\n' + actionItems.map(a => `• ${a}`).join('\n')
: bodyText.slice(0, 300).trim() + (bodyText.length > 300 ? '...' : '');
// Build Slack mrkdwn message
const isConfirmation = subject.toLowerCase().includes('confirm');
const emoji = isConfirmation ? '📅' : '📋';
const label = isConfirmation ? 'Meeting Confirmed' : 'Meeting Follow-up';
const formattedMessage =
`*${emoji} ${label}:* ${subject}\n` +
`*From:* ${fromHeader}\n` +
`*Received:* ${receivedDate}\n\n` +
actionBlock;
return {
isMeetingEmail: true,
channelId,
formattedMessage,
subject,
senderEmail,
receivedDate
};
}
});Canvas > + > Filter
Add a filter step to stop non-meeting emails
Click '+' below the code step and choose 'Filter' from the built-in Pipedream helpers. Set the condition to: steps.classify_email.isMeetingEmail === true. This prevents every email in the inbox from reaching Slack — only emails the code step flagged as meeting-related will continue. Without this filter, your Slack channel fills up with unrelated emails the moment someone's inbox gets a newsletter or receipt.
- 1Click '+' below the Node.js code step
- 2Select 'Filter' from the helpers section
- 3Set the left-hand field to: {{steps.classify_email.$return_value.isMeetingEmail}}
- 4Set the operator to 'is true'
- 5Click 'Continue' to save the filter condition
Canvas > + > Slack > Send a Message
Add the Slack step to post the message
Click '+' below the filter step and search for 'Slack'. Select the 'Send a Message' action. In the config panel, connect your Slack workspace via OAuth — click 'Connect Account' and authorize Pipedream's Slack app in your workspace. For the 'Channel' field, reference the output from the code step: {{steps.classify_email.$return_value.channelId}}. For the 'Message Text' field, reference: {{steps.classify_email.$return_value.formattedMessage}}.
- 1Click '+' below the filter step
- 2Search for 'Slack' and select it
- 3Choose 'Send a Message' as the action
- 4Click 'Connect Account' and authorize Pipedream in your Slack workspace
- 5Set 'Channel' to {{steps.classify_email.$return_value.channelId}}
- 6Set 'Message Text' to {{steps.classify_email.$return_value.formattedMessage}}
channel: {{channel}}
ts: {{ts}}
Slack Step > Advanced Options > Blocks / Username / Unfurl Links
Configure Slack message formatting
In the Slack step config panel, expand 'Advanced Options'. Enable 'Use Blocks' if you want a structured Slack Block Kit message — this looks significantly better than plain text for meeting summaries. Alternatively, the code step can output pre-formatted mrkdwn text with bold sender names and bullet-point action items. Set 'Username' to something like 'Meeting Bot' and upload a bot icon URL if desired. Check 'Unfurl Links' to off — meeting notes often contain calendar links that generate noisy previews.
- 1Click 'Advanced Options' inside the Slack step config panel
- 2Set 'Username' to 'Meeting Bot'
- 3Set 'Unfurl Links' to false
- 4Set 'Unfurl Media' to false
- 5If using Block Kit, set 'Blocks' to {{steps.classify_email.$return_value.blocks}}
Workflow Canvas > ••• Menu > Add Error Handler > Slack > Send a Message
Add error handling with a catch step
Click the three-dot menu on the workflow canvas and select 'Add Error Handler'. This creates a parallel branch that runs if any step throws an error. In the error handler, add a Slack step that posts to a private #alerts or #automation-errors channel with the error message and which workflow failed. This is essential for production — without it, a Gmail API hiccup or malformed email silently drops the message with no notification.
- 1Click the three-dot '•••' menu in the top-right of the workflow canvas
- 2Select 'Add Error Handler'
- 3In the error handler branch, click '+' and add a Slack step
- 4Set the channel to your internal #alerts or #automation-errors channel
- 5Set the message to: 'Gmail→Slack meeting workflow failed: {{error.message}}'
Canvas > Deploy Button > Events Log
Deploy and verify the live workflow
Click 'Deploy' in the top-right corner of the workflow canvas. The workflow status changes from 'Draft' to 'Active'. Send a real test email to the Gmail account with a subject containing 'meeting notes' or 'action items'. Watch the Pipedream event log (left sidebar > Events) — you should see a new event appear within 30 seconds of sending the email. Click the event to inspect each step's input and output in the execution detail view.
- 1Click the blue 'Deploy' button in the top-right corner
- 2Confirm the workflow status shows 'Active'
- 3Send a real test email to the connected Gmail account
- 4Click 'Events' in the left sidebar to watch incoming events
- 5Click the new event entry to open the execution detail view
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 Pipedream for this if you need the message in Slack within 30 seconds of email delivery and you're comfortable with a small amount of JavaScript. Pipedream's Gmail trigger uses Google's push notification API, not polling — that's a real technical difference that cuts delivery latency from 1-15 minutes (Zapier's average) down to under a minute. You also get a full Node.js environment to decode base64 email bodies, extract action items with regex, and route to different channels based on sender domain — all in one step. If nobody on your team touches code and routing logic isn't needed, use Zapier instead. Its Gmail → Slack path is 4 clicks and zero code.
Pipedream's free tier gives you 10,000 credits/month. This workflow consumes roughly 3-4 credits per email (trigger + code step + filter + Slack). At 20 meeting emails per day, that's ~2,400 credits/month — well inside the free tier. At 100 emails/day you'd hit ~9,000 credits/month, still free but close to the ceiling. The next tier is $19/month for 100,000 credits. Zapier's equivalent on a paid plan costs $29.99/month for 2,000 tasks. For the same 100 emails/day (3,000 tasks/month), you're at Zapier's $73.50/month tier. Pipedream is cheaper by roughly $54/month at moderate volume.
Zapier's 'Email Parser by Zapier' is genuinely better at extracting structured fields from templated meeting confirmation emails — no code required. Make's scenario builder has better visual conditional branching if your routing logic involves more than 3-4 channel destinations. n8n self-hosted is the right call if your emails contain sensitive client data and you can't send it through a third-party SaaS. Power Automate wins only if your team already lives in Microsoft 365 and the meeting emails are coming from Outlook, not Gmail. Pipedream is still the right pick here because the Gmail push API integration is solid, the Node.js step handles the base64 MIME decoding that trips up no-code tools, and the pricing doesn't punish you for having an active inbox.
Three things you'll hit after setup. First: Gmail's push notification subscription silently lapses after 7 days if Pipedream's renewal attempt fails — the workflow stops processing with zero error notification unless you've set up health alerts. Check the Events log on day 8. Second: some email clients send HTML-only emails with no plain text MIME part. Your keyword matching returns false positives or misses entirely because you're trying to search raw HTML tags for the word 'meeting'. Add html-to-text as a Pipedream npm import on day one, not after users report missing Slack messages. Third: Slack's API rate limit for chat.postMessage is 1 message per second per channel. If a batch of meeting emails arrives simultaneously (common Monday morning scenario), some Slack posts will return a rate_limited error. The Slack step in Pipedream doesn't retry automatically — you need to catch that error and add an exponential backoff loop in code, or space out posts using setTimeout.
Ideas for what to build next
- →Add a Google Calendar event lookup — When the email contains a meeting confirmation, cross-reference the date and time against Google Calendar to check for conflicts and include that context in the Slack message. Add a Pipedream 'Google Calendar: Get Events' step between the classification code and the Slack step.
- →Log all meeting summaries to a Google Sheet — Add a second Slack-parallel step that writes the sender, subject, date, and extracted action items to a Google Sheet tab. This creates a running log of all external commitments without any manual copy-paste.
- →Route to multiple channels simultaneously — Modify the code step to return an array of channel IDs instead of one, then replace the Slack step with a loop using Pipedream's $.send.http or a secondary code step that iterates over the array and posts to each channel — useful when a meeting involves multiple internal teams.
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