<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Karuna Labs</title>
    <description>The latest articles on DEV Community by Karuna Labs (@karunalabs).</description>
    <link>https://dev.to/karunalabs</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4174349%2Fb54fcd1e-4549-4d2e-aefe-99ab428d970b.png</url>
      <title>DEV Community: Karuna Labs</title>
      <link>https://dev.to/karunalabs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/karunalabs"/>
    <language>en</language>
    <item>
      <title>Auto-Fill "Last Contacted" From Gmail in Google Sheets (30 Lines of Apps Script)</title>
      <dc:creator>Karuna Labs</dc:creator>
      <pubDate>Sat, 10 Oct 2026 00:16:25 +0000</pubDate>
      <link>https://dev.to/karunalabs/auto-fill-last-contacted-from-gmail-in-google-sheets-30-lines-of-apps-script-kbf</link>
      <guid>https://dev.to/karunalabs/auto-fill-last-contacted-from-gmail-in-google-sheets-30-lines-of-apps-script-kbf</guid>
      <description>&lt;p&gt;If you track clients, leads or candidates in a Google Sheet, you probably have a &lt;strong&gt;Last contacted&lt;/strong&gt; column, and it's probably wrong. Nobody updates it by hand reliably. Gmail already knows the answer, so let's have the sheet ask Gmail.&lt;/p&gt;

&lt;p&gt;By the end of this post you'll have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a script that fills &lt;strong&gt;Last contacted&lt;/strong&gt;, &lt;strong&gt;Last from (Me/Them)&lt;/strong&gt;, &lt;strong&gt;Last subject&lt;/strong&gt; and a &lt;strong&gt;thread link&lt;/strong&gt; for every row,&lt;/li&gt;
&lt;li&gt;a status formula that turns those columns into &lt;em&gt;Needs reply / Follow up / Gone cold&lt;/em&gt;,&lt;/li&gt;
&lt;li&gt;an optional daily trigger, so it updates itself every morning.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No add-ons, no third-party server. Everything runs in your own Google account.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Set up the sheet
&lt;/h2&gt;

&lt;p&gt;Make a tab called &lt;code&gt;Contacts&lt;/code&gt; with this header row:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;A&lt;/th&gt;
&lt;th&gt;B&lt;/th&gt;
&lt;th&gt;C&lt;/th&gt;
&lt;th&gt;D&lt;/th&gt;
&lt;th&gt;E&lt;/th&gt;
&lt;th&gt;F&lt;/th&gt;
&lt;th&gt;G&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;Email&lt;/td&gt;
&lt;td&gt;Last contacted&lt;/td&gt;
&lt;td&gt;Last from&lt;/td&gt;
&lt;td&gt;Last subject&lt;/td&gt;
&lt;td&gt;Thread&lt;/td&gt;
&lt;td&gt;Status&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Fill in A and B. The script writes C to F, and G is a formula.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Add the script
&lt;/h2&gt;

&lt;p&gt;In the sheet, open &lt;strong&gt;Extensions › Apps Script&lt;/strong&gt;, delete the placeholder code, and paste:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SHEET_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Contacts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FIRST_ROW&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// row 1 is the header&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;EMAIL_COL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// column B&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OUT_COL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// write to C:F&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateLastContacted&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActive&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getSheetByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SHEET_NAME&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLastRow&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lastRow&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;FIRST_ROW&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FIRST_ROW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EMAIL_COL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastRow&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;FIRST_ROW&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValues&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Your own addresses, so we can tell "Me" from "Them".&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;me&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveUser&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getEmail&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;GmailApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAliases&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;emails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="c1"&gt;// Newest thread where this address appears in From, To or Cc.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`{from:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; to:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; cc:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;} -in:drafts -in:chats`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;GmailApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;msgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;getMessages&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isDraft&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;last&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;msgs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;msgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;last&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;last&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFrom&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fromMe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;me&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;last&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDate&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;fromMe&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Me&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Them&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;last&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSubject&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;getPermalink&lt;/span&gt;&lt;span class="p"&gt;()];&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FIRST_ROW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OUT_COL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GmailApp.search()&lt;/code&gt; takes the same query syntax as the Gmail search box. Curly braces mean OR, so &lt;code&gt;{from:x to:x cc:x}&lt;/code&gt; matches any thread that involves the address. Results come back newest first, so asking for one result gives you the most recent conversation.&lt;/li&gt;
&lt;li&gt;Drafts are filtered out. Otherwise a half-written reply would count as "contacted".&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GmailApp.getAliases()&lt;/code&gt; covers send-as addresses, so mail you send from an alias still counts as &lt;strong&gt;Me&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The script reads one block of emails and writes one block of results. Writing cell by cell is the usual reason Apps Script scripts are slow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Run&lt;/strong&gt; and pick &lt;code&gt;updateLastContacted&lt;/code&gt;. The first time, Google asks you to authorize the script. Because it's your own unverified script, you'll see "Google hasn't verified this app". Click &lt;strong&gt;Advanced › Go to (project name)&lt;/strong&gt;. That's expected for personal scripts: it means Google hasn't reviewed it, not that anything is wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Turn columns into a follow-up list
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;G2&lt;/code&gt;, paste this and fill it down:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=IF(C2="", "No history",
  IF(D2="Them", "Needs reply",
  IF(TODAY()-INT(C2)&amp;gt;21, "Gone cold",
  IF(TODAY()-INT(C2)&amp;gt;3, "Follow up", "Waiting"))))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What it says:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Needs reply:&lt;/strong&gt; they wrote last. That's on you, today.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Follow up:&lt;/strong&gt; you wrote last more than 3 days ago.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gone cold:&lt;/strong&gt; nobody has written in 3 weeks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Waiting:&lt;/strong&gt; you wrote recently, so give them time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Change 3 and 21 to suit your work. Recruiters often use 2 days, and B2B sales a week. Then add conditional formatting (&lt;strong&gt;Format › Conditional formatting › Text is exactly "Needs reply"&lt;/strong&gt;, red fill) and a filter view on G. That filter view is now your morning to-do list.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Run it every morning (optional)
&lt;/h2&gt;

&lt;p&gt;Add this function and run it &lt;strong&gt;once&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;installDailyTrigger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ScriptApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newTrigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;updateLastContacted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeBased&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;everyDays&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;atHour&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// in the script's time zone (Project Settings)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check &lt;strong&gt;Triggers&lt;/strong&gt; (the clock icon) to make sure there's only one, because running the installer twice gives you two triggers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits you will hit (and how to deal with them)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;6-minute runtime.&lt;/strong&gt; Each Apps Script execution is capped at 6 minutes, and each row is one Gmail search plus a thread read. A few hundred rows is usually fine. For thousands, process a slice per run: save the next row index with &lt;code&gt;PropertiesService.getScriptProperties()&lt;/code&gt; and pick up from there on the next trigger.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Daily Gmail quotas.&lt;/strong&gt; Consumer Gmail accounts get lower daily read quotas than Workspace accounts. A daily run over a normal contact list stays well under them, but don't put this on a 1-minute trigger.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Group threads.&lt;/strong&gt; If a thread includes other people, the last message might be from a colleague, which counts as &lt;strong&gt;Them&lt;/strong&gt;. If that matters, check &lt;code&gt;last.getTo()&lt;/code&gt; and &lt;code&gt;last.getCc()&lt;/code&gt; as well.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared mailboxes.&lt;/strong&gt; The script only sees the Gmail account it runs as.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ways to extend it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Add rows from a label:&lt;/strong&gt; search &lt;code&gt;label:new-lead&lt;/code&gt;, add every sender who isn't already in column B, then remove the label.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email yourself the list:&lt;/strong&gt; after updating, collect the rows marked &lt;em&gt;Needs reply&lt;/em&gt; or &lt;em&gt;Follow up&lt;/em&gt; and send them to yourself with &lt;code&gt;MailApp.sendEmail()&lt;/code&gt; (it's your own address, so you're not emailing anyone else).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto next-follow-up date:&lt;/strong&gt; when &lt;em&gt;Last from&lt;/em&gt; is &lt;strong&gt;Me&lt;/strong&gt; and the next-follow-up cell is empty, set it to the date plus 3 days.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The companion script that labels threads &lt;strong&gt;waiting on them&lt;/strong&gt; and &lt;strong&gt;waiting on me&lt;/strong&gt;: &lt;a href="https://karunalabs.hashnode.dev/how-to-find-unanswered-emails-in-gmail-both-directions-with-a-free-apps-script" rel="noopener noreferrer"&gt;https://karunalabs.hashnode.dev/how-to-find-unanswered-emails-in-gmail-both-directions-with-a-free-apps-script&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Not ready for code? A comparison of free ways to track Gmail follow-ups, from Gmail nudges to spreadsheet templates: &lt;a href="https://medium.com/@labskaruna/free-streak-alternatives-in-2026-what-you-actually-get-without-paying-49-a-seat-1c694891d3af" rel="noopener noreferrer"&gt;https://medium.com/@labskaruna/free-streak-alternatives-in-2026-what-you-actually-get-without-paying-49-a-seat-1c694891d3af&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The point stands either way. If a fact already exists in Gmail, don't make a human copy it into a spreadsheet.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was written with AI assistance. The script was checked against the Apps Script reference before publishing.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Questions about the script? Drop them in the comments. Happy to help debug queries or quota issues.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>googleappsscript</category>
      <category>javascript</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
