appscript.dev
Automation Beginner Gmail

Bridge a Gmail label to a Slack channel

Notify the Northwind #alerts Slack channel whenever priority-flagged email arrives.

Published Feb 17, 2026

Important email lands in an inbox that nobody is watching closely enough. Northwind’s team lives in Slack all day, but the urgent client message sits unread in Gmail because no one happened to refresh. The fix is to bring the alert to where people already are.

This script bridges a Gmail label to a Slack channel. Apply the priority label to a thread — by hand or with a filter — and the script posts a short summary to #alerts with a link straight back to the thread. It tracks what it has already announced with a sub-label, so each thread is posted once and never repeated.

What you’ll need

  • A priority Gmail label, applied to threads either manually or by a Gmail filter.
  • A Slack incoming webhook URL for the #alerts channel, saved as SLACK_WEBHOOK in Script Properties — see Store API keys and secrets securely.
  • Nothing else — the script creates the priority/alerted tracking label itself on first run.

The script

// Gmail label that marks a thread as priority.
const PRIORITY_LABEL = 'priority';

// Sub-label the script applies once a thread has been posted to Slack,
// so the same thread is never announced twice.
const ALERTED_LABEL = 'priority/alerted';

/**
 * Posts a Slack alert for every priority-labelled thread that has not
 * been alerted yet, then marks it as alerted.
 */
function alertSlackForPriority() {
  // 1. Look up the priority label, and the "alerted" sub-label —
  //    creating the sub-label if it does not exist yet.
  const label = GmailApp.getUserLabelByName(PRIORITY_LABEL);
  if (!label) {
    Logger.log('No "' + PRIORITY_LABEL + '" label found — nothing to do.');
    return;
  }
  const alerted = GmailApp.getUserLabelByName(ALERTED_LABEL)
    || GmailApp.createLabel(ALERTED_LABEL);

  // 2. Read the Slack webhook URL from Script Properties.
  const webhook = PropertiesService.getScriptProperties()
    .getProperty('SLACK_WEBHOOK');

  // 3. Walk every thread that currently carries the priority label.
  for (const thread of label.getThreads()) {
    // 4. Skip threads already posted — they carry the "alerted" sub-label.
    if (thread.getLabels().some((l) => l.getName() === ALERTED_LABEL)) {
      continue;
    }

    // 5. Build a Slack message from the first message in the thread.
    const msg = thread.getMessages()[0];
    const url = `https://mail.google.com/mail/u/0/#inbox/${thread.getId()}`;

    UrlFetchApp.fetch(webhook, {
      method: 'post',
      contentType: 'application/json',
      payload: JSON.stringify({
        text: `:rotating_light: *${msg.getSubject()}* from ${msg.getFrom()}\n` +
          `<${url}|Open thread>`,
      }),
    });

    // 6. Mark the thread as alerted so the next run skips it.
    thread.addLabel(alerted);
  }
}

How it works

  1. alertSlackForPriority looks up the priority label and stops early if it does not exist. It then fetches the priority/alerted sub-label, creating it the first time the script runs.
  2. It reads the Slack webhook URL from Script Properties so the URL stays out of the code.
  3. It loops over every thread that currently carries the priority label.
  4. For each thread, it checks whether the priority/alerted sub-label is already present — if so, the thread has been posted before and is skipped.
  5. For a new thread, it takes the first message, builds a deep link to the thread, and posts a formatted message to Slack via the webhook.
  6. It adds the priority/alerted sub-label so the thread is never announced again on a later run.

Example run

A thread arrives and gets the priority label. On the next run, #alerts shows:

:rotating_light: Contract renewal — needs sign-off today from Dana Reed [email protected] Open thread

The script then adds priority/alerted to that thread. Five minutes later it runs again, sees the sub-label, and posts nothing — no duplicate ping.

Trigger it

This should run on a schedule so alerts land soon after a thread is labelled:

  1. In the Apps Script editor, open Triggers (the clock icon).
  2. Add a trigger for alertSlackForPriority, choose Time-driven, then Minutes timer, and set it to Every 5 minutes.
  3. Approve the authorisation prompt the first time — the script needs Gmail and external-request access.

Five minutes is a sensible balance between a fast alert and staying well within quota. Tighten or loosen it to suit how urgent priority really is.

Watch out for

  • The script reads getThreads() with no paging, which returns up to 500 threads. Keep the priority label tidy — archive or relabel old threads — so the list stays small.
  • It posts the subject and sender into Slack. If #alerts is a broad channel, consider that anyone in it sees those details.
  • An invalid or revoked webhook URL makes UrlFetchApp.fetch throw, which stops the run before the thread is marked alerted — so it retries next time. That is usually fine, but a permanently broken webhook will fail every run.
  • Re-applying the priority label to an already-alerted thread does nothing — the priority/alerted sub-label is still there. Remove that sub-label by hand if you genuinely want a thread re-announced.
  • Slack incoming webhooks are rate-limited to roughly one message per second. A sudden flood of labelled threads in one run could hit that ceiling.

Related