Intermediate~15 min setupCommunication & EmailVerified April 2026
Slack logo
Gmail logo

How to Route Gmail Support Emails to Slack with Pipedream

Watches a Gmail support inbox for new emails, parses sender, subject, and body, then posts a formatted summary to a Slack channel so your team can coordinate a response.

Steps and UI details are based on platform versions at time of writing — check each platform for the latest interface.

Best for

Small to mid-size support teams who triage tickets in Slack and want email context without switching to Gmail

Not ideal for

Teams already using a dedicated helpdesk like Zendesk or Intercom — those tools have native Slack integrations with ticket state management built in

Sync type

real-time

Use case type

routing

Real-World Example

💡

A 12-person e-commerce company routes all emails sent to [email protected] into their #support-queue Slack channel. Before this workflow, two support reps would open Gmail in separate tabs and sometimes reply to the same email within minutes of each other. Now every incoming email posts a formatted Slack card with sender, subject, and a 300-character body preview, and the team threads discussion before anyone drafts a reply.

What Will This Cost?

Drag the slider to your expected monthly volume.

/mo
505005K50K

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

Skip the setup

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.

A Gmail account with full access to the support inbox — OAuth requires sign-in credentials, not just forwarding access
Gmail API scopes: gmail.readonly and gmail.labels — granted during the OAuth flow in step 3
A Slack workspace where you have permission to install apps and post to the target channel
Slack OAuth scopes: chat:write and channels:read — granted when connecting the Slack account in step 7
A Pipedream account — the free tier supports up to 10,000 credits/month, enough for roughly 3,300 three-step workflow runs

Field Mapping

Map these fields between your apps.

FieldAPI Name
Required
Sender Name
Sender Email
Subject Line
Body Preview
Gmail Thread ID
Gmail Message ID
2 optional fields▸ show
Received Timestamp
To Address

Step-by-Step Setup

1

pipedream.com > Workflows > New Workflow

Create a new Pipedream Workflow

Go to pipedream.com and sign in. From the left sidebar click 'Workflows', then click the blue 'New Workflow' button in the top right. Give the workflow a name like 'Gmail Support → Slack' so it's easy to find later. You'll land on the workflow canvas with a prompt to choose a trigger.

  1. 1Click 'Workflows' in the left sidebar
  2. 2Click the blue 'New Workflow' button
  3. 3Type a name: 'Gmail Support → Slack'
  4. 4Click 'Save' or press Enter to confirm the name
What you should see: You should see an empty workflow canvas with a single 'Add Trigger' block at the top.
2

Workflow Canvas > Add Trigger > Gmail > New Email

Add the Gmail New Email trigger

Click the 'Add Trigger' block. In the search bar type 'Gmail' and select the Gmail app. Choose the trigger called 'New Email'. This trigger uses Pipedream's Gmail push integration, which creates a Gmail watch via Google's API — it fires within seconds of a message arriving, not on a polling interval.

  1. 1Click the 'Add Trigger' block
  2. 2Type 'Gmail' in the search bar
  3. 3Click the Gmail app icon
  4. 4Select 'New Email' from the trigger list
What you should see: A Gmail trigger configuration panel opens on the right side of the screen.
Common mistake — Do NOT select 'New Email Matching Search' unless you want to filter by a query string. The plain 'New Email' trigger fires for every inbound message, which is what you want here — you'll filter by label in step 3.
Pipedream
+
click +
search apps
Slack
SL
Slack
Add the Gmail New Email trig…
Slack
SL
module added
3

Trigger Panel > Gmail > Connect Account > Google OAuth

Connect your Gmail support account

In the trigger panel, click 'Connect Account' under the Gmail section. A Google OAuth popup appears — sign in with the Google account that owns your support inbox (e.g. [email protected]), not your personal account. After signing in, grant Pipedream the requested Gmail scopes. Back in the trigger panel, set the 'Label' field to 'INBOX' or a specific Gmail label you've created for support emails.

  1. 1Click 'Connect Account' in the Gmail trigger panel
  2. 2Select or add the support Google account in the OAuth popup
  3. 3Grant all requested permissions and click 'Continue'
  4. 4Set the Label dropdown to 'INBOX' or your support-specific label
What you should see: The trigger panel shows a green checkmark next to your connected Gmail account and the label is set.
Common mistake — If the support inbox is a Google Workspace shared inbox or group alias, it must be connected as an actual Google account with its own credentials. Gmail groups (like [email protected] forwarded to a group) cannot authenticate via OAuth — you'll need a real mailbox.
Pipedream settings
Connection
Choose a connection…Add
click Add
Slack
Log in to authorize
Authorize Pipedream
popup window
Connected
green checkmark
4

Trigger Panel > Generate Test Event > Select Event

Test the trigger with a real email

Click 'Generate Test Event' at the bottom of the trigger panel. Pipedream will either fetch the most recent email in the inbox or prompt you to send a test email to the connected address. Send a real test email with a realistic subject line like 'Order #8821 not delivered'. Once Pipedream detects it, click 'Select' next to the event to use it as sample data for the remaining steps.

  1. 1Click 'Generate Test Event'
  2. 2Send a test email to the connected support address if prompted
  3. 3Wait for the event to appear in the list (usually under 30 seconds)
  4. 4Click 'Select' next to the test event
What you should see: You should see a JSON payload in the trigger panel showing fields like `from`, `subject`, `snippet`, `body`, and `threadId`.
Pipedream
▶ Deploy & test
executed
Slack
Gmail
Gmail
🔔 notification
received
5

Workflow Canvas > + > Run Node.js Code

Add a Node.js code step to parse and format the email

Click the '+' button below the trigger to add a new step. Choose 'Run Node.js code'. This step extracts the sender name, email address, subject, and a truncated body preview from the raw Gmail payload. You'll write the parsing logic here so the Slack message is clean and readable rather than dumping raw JSON into the channel.

  1. 1Click the '+' button below the Gmail trigger
  2. 2Click 'Run Node.js code' from the step options
  3. 3Clear the default code in the editor
  4. 4Paste your formatting code (see Pro Tip section below)
What you should see: A code editor opens with a default async function. After pasting and running your code, the 'Return Value' panel on the right shows structured fields like `senderName`, `senderEmail`, `subject`, and `bodyPreview`.
Common mistake — Gmail's `payload.body.data` is Base64url-encoded. If you try to use it directly in Slack, you'll get garbled text. The code step must decode it using `Buffer.from(data, 'base64url').toString('utf-8')` before extracting a preview.

Paste this into the Node.js code step (step 5). It handles both plain-text and multipart HTML emails, decodes the Base64url body, truncates the preview to 300 characters, extracts sender name and email separately, formats the received timestamp, and returns a clean object that the Slack Block Kit step consumes directly. Replace the `export default` function contents with this code.

JavaScript — Code Stepexport default defineComponent({
▸ Show code
export default defineComponent({
  async run({ steps, $ }) {
    const message = steps.trigger.event;

... expand to see full code

export default defineComponent({
  async run({ steps, $ }) {
    const message = steps.trigger.event;
    const headers = message.payload.headers;

    // Extract headers
    const getHeader = (name) =>
      headers.find(h => h.name.toLowerCase() === name.toLowerCase())?.value || '';

    const fromRaw = getHeader('From');
    const subject = getHeader('Subject') || '(no subject)';
    const toAddress = getHeader('To');
    const dateRaw = getHeader('Date');

    // Parse sender name and email from 'From' header
    const fromMatch = fromRaw.match(/^(.+?)\s*<(.+?)>$/);
    const senderName = fromMatch ? fromMatch[1].trim().replace(/^"|"$/g, '') : fromRaw;
    const senderEmail = fromMatch ? fromMatch[2] : fromRaw;

    // Recursively find plain text body in potentially nested MIME parts
    function findPlainText(part) {
      if (part.mimeType === 'text/plain' && part.body?.data) {
        return Buffer.from(part.body.data, 'base64url').toString('utf-8');
      }
      if (part.parts) {
        for (const child of part.parts) {
          const result = findPlainText(child);
          if (result) return result;
        }
      }
      return null;
    }

    const rawBody = findPlainText(message.payload) || '';
    const bodyPreview = rawBody.replace(/\s+/g, ' ').trim().slice(0, 300) +
      (rawBody.length > 300 ? '...' : '');

    // Format timestamp
    const receivedAt = message.internalDate
      ? new Date(parseInt(message.internalDate)).toLocaleString('en-US', {
          month: 'short', day: 'numeric', year: 'numeric',
          hour: 'numeric', minute: '2-digit', timeZoneName: 'short'
        })
      : dateRaw;

    // Build Gmail deep link
    const threadId = message.threadId;
    const gmailLink = `https://mail.google.com/mail/u/0/#all/${threadId}`;

    // Deduplication guard using Pipedream data store
    const db = $.service.db;
    const seen = await db.get('seen_message_ids') || [];
    if (seen.includes(message.id)) {
      $.flow.exit('Duplicate message — already processed');
    }
    const updated = [message.id, ...seen].slice(0, 100);
    await db.set('seen_message_ids', updated);

    return {
      senderName,
      senderEmail,
      subject,
      bodyPreview: bodyPreview || '(no body content)',
      receivedAt,
      gmailLink,
      threadId,
      toAddress,
      messageId: message.id
    };
  }
});
6

Code Step Panel > Test > Return Value

Test the code step output

Click 'Test' in the code step panel. Pipedream runs the function against the sample email you selected in step 4. Check the 'Return Value' tab on the right side of the editor. You should see clean, decoded values for each field. If `bodyPreview` is empty, the email might use `payload.parts` instead of `payload.body` — multipart emails (HTML + plain text) structure their content differently.

  1. 1Click the 'Test' button in the code step
  2. 2Click the 'Return Value' tab in the right panel
  3. 3Confirm `senderName`, `senderEmail`, `subject`, and `bodyPreview` all have readable values
  4. 4If any field is empty, check the 'Logs' tab for errors
What you should see: Return Value tab shows something like: `{ senderName: 'Maria Chen', senderEmail: '[email protected]', subject: 'Order #8821 not delivered', bodyPreview: 'Hi, I placed an order 5 days ago and...' }`
Common mistake — Multipart emails (most HTML emails) store plain text in `payload.parts[0].body.data` and HTML in `payload.parts[1].body.data`. If your bodyPreview is null, add a fallback that walks `payload.parts` recursively.

Paste this into the Node.js code step (step 5). It handles both plain-text and multipart HTML emails, decodes the Base64url body, truncates the preview to 300 characters, extracts sender name and email separately, formats the received timestamp, and returns a clean object that the Slack Block Kit step consumes directly. Replace the `export default` function contents with this code.

JavaScript — Code Stepexport default defineComponent({
▸ Show code
export default defineComponent({
  async run({ steps, $ }) {
    const message = steps.trigger.event;

... expand to see full code

export default defineComponent({
  async run({ steps, $ }) {
    const message = steps.trigger.event;
    const headers = message.payload.headers;

    // Extract headers
    const getHeader = (name) =>
      headers.find(h => h.name.toLowerCase() === name.toLowerCase())?.value || '';

    const fromRaw = getHeader('From');
    const subject = getHeader('Subject') || '(no subject)';
    const toAddress = getHeader('To');
    const dateRaw = getHeader('Date');

    // Parse sender name and email from 'From' header
    const fromMatch = fromRaw.match(/^(.+?)\s*<(.+?)>$/);
    const senderName = fromMatch ? fromMatch[1].trim().replace(/^"|"$/g, '') : fromRaw;
    const senderEmail = fromMatch ? fromMatch[2] : fromRaw;

    // Recursively find plain text body in potentially nested MIME parts
    function findPlainText(part) {
      if (part.mimeType === 'text/plain' && part.body?.data) {
        return Buffer.from(part.body.data, 'base64url').toString('utf-8');
      }
      if (part.parts) {
        for (const child of part.parts) {
          const result = findPlainText(child);
          if (result) return result;
        }
      }
      return null;
    }

    const rawBody = findPlainText(message.payload) || '';
    const bodyPreview = rawBody.replace(/\s+/g, ' ').trim().slice(0, 300) +
      (rawBody.length > 300 ? '...' : '');

    // Format timestamp
    const receivedAt = message.internalDate
      ? new Date(parseInt(message.internalDate)).toLocaleString('en-US', {
          month: 'short', day: 'numeric', year: 'numeric',
          hour: 'numeric', minute: '2-digit', timeZoneName: 'short'
        })
      : dateRaw;

    // Build Gmail deep link
    const threadId = message.threadId;
    const gmailLink = `https://mail.google.com/mail/u/0/#all/${threadId}`;

    // Deduplication guard using Pipedream data store
    const db = $.service.db;
    const seen = await db.get('seen_message_ids') || [];
    if (seen.includes(message.id)) {
      $.flow.exit('Duplicate message — already processed');
    }
    const updated = [message.id, ...seen].slice(0, 100);
    await db.set('seen_message_ids', updated);

    return {
      senderName,
      senderEmail,
      subject,
      bodyPreview: bodyPreview || '(no body content)',
      receivedAt,
      gmailLink,
      threadId,
      toAddress,
      messageId: message.id
    };
  }
});
7

Workflow Canvas > + > Slack > Send Message to Channel

Add the Slack step to post the message

Click '+' below the code step and search for 'Slack'. Select the Slack app and choose the action 'Send Message to Channel'. Connect your Slack workspace via the Connected Accounts flow. In the 'Channel' field, type or select your support channel name (e.g. #support-queue). Set the 'Message Text' field using the outputs from your code step.

  1. 1Click '+' below the code step
  2. 2Search for 'Slack' and select the Slack app
  3. 3Choose 'Send Message to Channel'
  4. 4Click 'Connect Account' and authorize Pipedream in your Slack workspace
  5. 5Select your support channel from the Channel dropdown
What you should see: Slack step is configured with your workspace connected and the target channel selected. The step panel shows empty Message Text and Blocks fields ready for input.
Common mistake — Map fields using the variable picker — don't type field names manually. Hand-typed variable names often have invisible spacing errors that produce blank output.
message template
🔔 New Record: {{text}} {{user}}
channel: {{channel}}
ts: {{ts}}
#sales
🔔 New Record: Jane Smith
Company: Acme Corp
8

Slack Step Panel > Blocks field > {} variable picker

Build the Slack message with Block Kit formatting

In the Slack step, use the 'Blocks' field rather than the plain 'Message Text' field. Block Kit lets you format the support ticket summary with bold headers, field rows, and a direct link to Gmail. Reference the outputs from your code step using Pipedream's variable picker — click the `{}` icon next to any field to browse available values from previous steps.

  1. 1Click inside the 'Blocks' field in the Slack step
  2. 2Click the '{}' variable picker icon
  3. 3Select values from your code step: `steps.nodejs.$return_value.senderName`, `subject`, `bodyPreview`
  4. 4Paste the Block Kit JSON structure from the Pro Tip section
  5. 5Replace hardcoded strings with the variable references
What you should see: The Blocks field contains a JSON array with section blocks. The variable references appear highlighted in orange, confirming Pipedream recognizes them as dynamic values.
Common mistake — Slack's Block Kit 'section' blocks have a 3000-character text limit. If you pass a full email body, the step will error. Keep bodyPreview under 300 characters — the code step in step 5 should truncate it before it reaches here.
9

Workflow Canvas > Test Workflow (top toolbar)

Test the full workflow end-to-end

Click 'Test Workflow' at the top of the canvas. Pipedream runs all steps sequentially using the sample event from step 4. Watch the step indicators — each one turns green when it succeeds. Then open your #support-queue Slack channel and confirm the message appeared with correct sender, subject, and body preview. Check that the Gmail link in the message opens the correct thread.

  1. 1Click 'Test Workflow' in the top toolbar
  2. 2Watch each step turn green in sequence
  3. 3Open your Slack workspace and navigate to #support-queue
  4. 4Confirm the message shows the correct sender name, email, subject, and preview
What you should see: A formatted Slack message appears in your support channel within 5 seconds of clicking Test, showing all parsed email fields and a working Gmail deep link.
Common mistake — Test runs use your stored sample event — they do NOT send a new email through Gmail. If you change the Gmail label filter or the code step logic after this test, run another end-to-end test with a fresh email before deploying.
10

Workflow Canvas > Deploy (top right button)

Deploy and enable the workflow

Click the 'Deploy' button in the top right corner of the workflow canvas. Pipedream activates the Gmail push subscription and the workflow goes live. The status indicator next to the workflow name changes from 'Paused' to 'Active'. From this point, every new email arriving in the connected Gmail inbox triggers the workflow in real time — no polling interval, no delay beyond Gmail's own push latency (typically under 10 seconds).

  1. 1Click the blue 'Deploy' button in the top right
  2. 2Confirm the deployment in the dialog if prompted
  3. 3Verify the workflow status shows 'Active' in the left sidebar
What you should see: The workflow status badge reads 'Active' in green. Send a real email to your support address and it should appear in Slack within 10-15 seconds.
Common mistake — Gmail push subscriptions expire after 7 days unless renewed. Pipedream handles renewal automatically, but only if the workflow stays active. If you pause the workflow for more than 7 days, the push subscription will lapse and you'll need to reconnect the Gmail account in the trigger step to restart it.

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

VerdictWhy n8n for this workflow

Use Pipedream for this if your team has at least one person comfortable reading Node.js. The Gmail push integration means emails land in Slack in under 10 seconds — faster than Zapier's minimum 1-minute polling or Make's 15-minute default on the free plan. The code step also lets you handle messy real-world emails: multipart MIME structures, malformed From headers, Base64url-encoded bodies. If your team is non-technical and wants a click-through setup with zero code, use Zapier — the Gmail + Slack Zap takes 8 minutes to configure and works fine for low volumes.

Cost

Pipedream's free tier gives you 10,000 credits per month. This workflow uses 3 steps, so each email costs 3 credits. That covers 3,333 emails per month for free. If your support volume is 500 emails/month, you're well within the free tier with 8,500 credits to spare. Above 3,333 emails/month you'll hit the Basic plan at $19/month, which gives you 100,000 credits — enough for 33,000 emails. Zapier's equivalent (Starter at $19.99/month) caps at 750 tasks, which runs out at 750 emails. For high-volume support teams, Pipedream is significantly cheaper.

Tradeoffs

Zapier handles the Gmail trigger better for non-technical users — the UI surfaces label filters as a simple dropdown rather than requiring you to understand Gmail's API label structure. Make's scenario builder lets you visually map the From header parsing without writing code, which is faster to set up but harder to extend. n8n has a native Gmail node with built-in MIME parsing that removes the need for a custom code step — if you're self-hosting, n8n handles this workflow with less setup complexity. Power Automate has a solid Gmail connector via the Gmail connector in the M365 ecosystem, but its Slack connector is weaker and often requires a third-party HTTP request step. Pipedream is still the right call here because the combination of instant Gmail push, a real code step, and the built-in data store for deduplication gives you a production-grade workflow you'd otherwise need to build three separate tools to replicate.

Three things you'll hit after going live. First, Gmail's push subscription silently expires every 7 days — Pipedream auto-renews it, but if your workflow was paused or your account hit a credit limit during renewal, the subscription lapses and emails stop triggering the workflow with no error message. Check your workflow's event count every few days for the first two weeks. Second, reply emails and out-of-office auto-responses will hit the trigger too. Add a filter in the code step that checks for `In-Reply-To` or `Auto-Submitted` headers and exits early for those. Third, HTML-only emails (no plain text part) return an empty body preview unless you strip HTML tags — add a basic regex to remove `<[^>]+>` tags from the HTML part as a fallback when no text/plain part exists.

Ideas for what to build next

  • Add Slack Thread Replies Back to GmailUse a Slack trigger on new messages in the support thread to auto-draft a reply in Gmail, giving agents a pre-written response to edit and send — cuts reply time without removing human review.
  • Route by Email Content to Different ChannelsAdd keyword detection in the code step to route billing emails to #support-billing and shipping issues to #support-shipping — one Gmail source, multiple Slack destinations, no manual sorting.
  • Log Every Ticket to a Google SheetAdd a fourth step that appends a row to a Google Sheet with sender, subject, timestamp, and a Gmail link — gives you a running ticket log you can sort and report on without a dedicated helpdesk tool.

Related guides

Was this guide helpful?
Slack + Gmail overviewPipedream profile →