appscript.dev
Automation Beginner Gmail

Send a weekly digest of unreplied emails

Surface inbox threads you haven't answered in a single Monday-morning summary.

Published Jul 22, 2025

A busy inbox quietly hides the threads that matter. An email gets read on a phone, marked for “later”, and slides down the list — and the sender is left waiting. By the time it resurfaces the reply is overdue and the relationship is a little frostier than it needed to be.

Awadesh at Northwind wanted to start every Monday knowing exactly which threads he still owed a reply on, without scrolling the whole inbox. This script builds that list for him. Once a week it searches for unread threads where the last message came from someone else, formats them into a short digest, and emails it to himself — a single, scannable to-do list waiting at 7am.

What you’ll need

  • A Gmail account — the script runs as you and reads your own inbox.
  • Nothing to set up in advance. The digest is sent to the address running the script, so there is no sheet, label, or template to create.
  • Optional: a sense of which Gmail search filters fit your inbox, so you can tighten the query (see “Tighten the filter”).

The script

// How far back to look for threads that might still need a reply.
const LOOKBACK = 'newer_than:14d';

/**
 * Builds a once-a-week digest of inbox threads where the most recent
 * message came from someone other than you, then emails it to yourself.
 */
function weeklyUnrepliedDigest() {
  // 1. Work out who "me" is, so we can spot our own replies.
  const me = Session.getActiveUser().getEmail();

  // 2. Find recent unread threads in the inbox that we did not start.
  const threads = GmailApp.search(`in:inbox is:unread -from:me ${LOOKBACK}`);

  // 3. Keep only threads whose last message is from the other side.
  //    This drops threads we have already answered.
  const overdue = threads.filter((thread) => {
    const messages = thread.getMessages();
    const last = messages[messages.length - 1];
    return last.getFrom() !== me;
  });

  // 4. Nothing outstanding — skip the email entirely.
  if (overdue.length === 0) {
    Logger.log('Inbox zero on replies — no digest sent.');
    return;
  }

  // 5. Turn each overdue thread into a one-line, linked summary.
  const lines = overdue.map((thread) => {
    const lastMessage = thread.getMessages().slice(-1)[0];
    const from = lastMessage.getFrom().replace(/<.+>/, '').trim();
    const url = `https://mail.google.com/mail/u/0/#inbox/${thread.getId()}`;
    return `• [${thread.getFirstMessageSubject()}](${url}) — ${from}`;
  });

  // 6. Send the digest to yourself.
  GmailApp.sendEmail(
    me,
    `${overdue.length} threads still owe a reply`,
    lines.join('\n'),
  );
  Logger.log(`Digest sent with ${overdue.length} thread(s).`);
}

How it works

  1. Session.getActiveUser().getEmail() returns the address the script runs under, which is later used to recognise your own messages.
  2. The Gmail search in:inbox is:unread -from:me newer_than:14d collects recent unread threads you did not send. The 14-day LOOKBACK keeps very old threads out of the digest.
  3. For each thread the script inspects the last message. If that message is not from you, the thread still needs a reply. This neatly handles the case where you replied but the other side wrote back again.
  4. If nothing is outstanding, the script logs that and stops — no empty digest lands in your inbox.
  5. Each overdue thread becomes a bullet with a clickable link straight to the thread, plus the sender’s display name (the <address> part is stripped).
  6. GmailApp.sendEmail sends the assembled list to yourself with the count in the subject line.

Example run

Suppose three threads are unanswered when the digest runs on Monday morning. The email you receive looks like this:

Subject: 3 threads still owe a reply

Q3 retainer renewal — Priya Shah • Logo files — final round? — Tom Reed • Invoice 1182 query[email protected]

Each subject is a link straight into the thread, so clearing the list is three clicks rather than three searches.

Trigger it

This runs once a week, so set a time-driven trigger:

  1. In the Apps Script editor open Triggers (the clock icon).
  2. Add a trigger for weeklyUnrepliedDigest, choose Time-driven, then Week timer, Every Monday, and the 7am to 8am slot.
  3. Approve the authorisation prompt the first time it runs.

Monday at 7am means the digest is the first thing waiting when the working week starts.

Watch out for

  • is:unread is doing real work here. If you read a thread without replying, it drops out of the digest — so the list reflects unread and unanswered, not every thread you owe. Drop is:unread if you want a stricter list, but expect it to be longer.
  • The last-message check uses an exact getFrom() string match. If you send mail from an alias, that alias will not equal me and your own replies may be counted as the other side’s. Add the alias to the comparison if so.
  • Automated senders (newsletters, notifications) can clutter the digest. Use the filters below to exclude them.
  • The links use mailbox u/0. If you run multiple Google accounts in one browser, the digest may open the wrong mailbox — change u/0 to your account index.

Tighten the filter

  • Add label:support to focus the digest on a single shared inbox or label.
  • Add -from:(noreply OR notifications) to ignore machine-generated senders.
  • Add is:important to surface only the threads Gmail already flags as high-priority.

Related