appscript.dev
Automation Beginner Gmail Sheets

Tag VIP senders and prioritize their threads

Maintain a VIP list in a Sheet and auto-star and label their incoming mail.

Published Dec 9, 2025

Every inbox has a handful of people who must never be missed — the client whose brief unblocks the week, the partner waiting on a decision, the supplier holding a deadline. The trouble is they look exactly like everyone else in the inbox until you open the message and realise it has been sitting there for two hours.

Northwind keeps a short list of these must-respond-today contacts — eight names in a Google Sheet. This script reads that list every few minutes and, the moment mail arrives from one of them, stars the messages and applies a vip label. The list lives in a Sheet rather than the code, so anyone on the team can add or remove a contact without touching Apps Script.

What you’ll need

  • A Google Sheet with a tab listing your VIP contacts. Put one email address per row in column A, with a header such as email in row 1.
  • The spreadsheet’s ID, taken from its URL, dropped into the config below.
  • Nothing else — the script creates the vip label itself if it does not exist.

The script

// The spreadsheet that holds your VIP contact list (one email per row).
const VIPS_SHEET_ID = '1abcVipsSheetId';

// The Gmail label applied to VIP threads.
const VIP_LABEL = 'vip';

// Only look at mail from the last day, so each run stays fast.
const SEARCH_WINDOW = 'newer_than:1d';

/**
 * Reads the VIP list from the Sheet and, for each contact, stars and
 * labels any of their recent threads that haven't been tagged yet.
 * Designed to run on a short time-driven trigger.
 */
function tagVipThreads() {
  // 1. Read column A of the VIPs sheet and drop blank rows.
  const emails = SpreadsheetApp.openById(VIPS_SHEET_ID)
    .getSheets()[0]
    .getRange('A2:A')
    .getValues()
    .flat()
    .filter(Boolean);

  // 2. Bail out early if the list is empty — nothing to do.
  if (emails.length === 0) {
    Logger.log('VIP list is empty — nothing to tag.');
    return;
  }

  // 3. Find the vip label, creating it on first run.
  const label = GmailApp.getUserLabelByName(VIP_LABEL)
    || GmailApp.createLabel(VIP_LABEL);

  // 4. For each VIP, search recent mail that isn't already labelled.
  let tagged = 0;
  for (const email of emails) {
    const query = `from:${email} ${SEARCH_WINDOW} -label:${VIP_LABEL}`;
    const threads = GmailApp.search(query);

    for (const thread of threads) {
      // Label the whole thread, then star every message in it.
      thread.addLabel(label);
      thread.getMessages().forEach((message) => message.star());
      tagged++;
    }
  }

  Logger.log(`Tagged ${tagged} VIP thread(s).`);
}

How it works

  1. tagVipThreads opens the VIPs spreadsheet and reads column A into a plain list of addresses, flattening the range and dropping any blank cells.
  2. If the list is empty it logs a message and stops, so an empty Sheet never causes a wasted run.
  3. It looks up the vip label and creates it the first time the script runs, so there is no manual label setup.
  4. For each VIP address it runs a Gmail search scoped to the last day and excluding anything already carrying the vip label — the -label:vip part is what stops the same thread being processed on every run.
  5. For each matching thread it applies the label and stars every message inside, so the conversation stands out both in the label view and in the inbox.
  6. It logs how many threads it tagged, which is handy when checking the trigger is doing its job.

Example run

Say the VIPs sheet looks like this:

email
[email protected]
[email protected]
[email protected]

If Priya sends a message in the last 24 hours, the next run finds the thread, applies the vip label, and stars the message:

  • Before: thread sits unmarked in the inbox among everything else.
  • After: thread carries the vip label and a yellow star, so it is visible at a glance and reachable from the vip label in one click.

Threads from non-VIP senders are ignored entirely, and a thread already labelled vip is skipped on the next run.

Trigger it

This automation should feel near-real-time, so run it on a short interval:

  1. In the Apps Script editor, open Triggers (the clock icon).
  2. Click Add Trigger.
  3. Choose tagVipThreads, event source Time-driven, type Minutes timer, and pick Every 5 minutes.
  4. Save, and approve the authorisation prompt the first time it runs.

Watch out for

  • The search uses newer_than:1d, so a thread is only catchable within a day of arriving. If the trigger is paused for longer than that, older VIP mail will be missed. Widen the window if you expect gaps.
  • from:${email} matches the address as Gmail sees it. If a VIP emails you from a different address — a personal account, a new domain — it will not be caught until you add that address to the Sheet.
  • The -label:vip filter is what prevents re-processing. If you remove the label from a thread by hand, the next run will re-tag and re-star it.
  • Every run reads the Sheet and runs one search per VIP. With a short list this is cheap; with hundreds of VIPs the searches add up, so keep the list to the contacts that genuinely need it.
  • Starring touches every message in the thread. On a long existing thread that means a lot of stars at once — expected behaviour, but worth knowing.

Related