Yes, it has been a really long time since I wrote my last blog entry. I would say I’ve been busy.. but luckily, I’ve made space again to publish some technical writings / blog posts and get back into the spirit of the web as an open space to create content.

Let me get back to tonight’s topic: E-mail automation using Google Apps Scripting

So I have for the last year and a half maintained by own personal domain for e-mail which keeps my personal development projects and consulting seperate from any work email and my Having an entire Google Workspace to myself just gives a lot of benefit: it allows me to tie up a lot of loose ends and authentication between the realms you are expected to juggle.

But the coolest thing with having your domain, is that you have unlimited mailboxes - and I wanted to be able to partition my emails with some labelling magic.

I called the script,

The Google Workspace MX delegation is the same as this blog, . When I configured the mail I only set up one mailbox called and then setup a catch-all to basically catch all emails sent to <anything> and deliver it to

Google has the following steps to configure the catch-all. and I’ve provided images of my configuration and the advice at the bottom of this document (if you are interested).

This is how it works

On Initialization, a global dictionary (LABEL_DICT) of all labels are retrieved for this mailbox.

First, I created a Gmail Rule as follows that whenever an email not destinated for either or my older email address, it adds the unicode label “🆕” (indicatorLabelString).

Note the negative operator in front of the email address

Second, the AppScript basically filters by that Label and then either attaches a label (if it exists in the global LABEL_DICT) or creates it and then attaches the label to the mail.

The indicatorLabelString 🆕 is then removed and the enumeration continues until there are no more emails tagged with 🆕.

The App Script Code

// Configure this trigger to run often
// (*how* often depends on the desired response time *and* how willing you are to risk hitting Google Apps Script’s rate limits;
// 10 minutes is good enough for me generally)

var LABEL_DICT = {}
var EMAIL_WORKSPACE_DOMAIN = "" // do not include the @ sign, so for, put

function getUserLabels() {
  GmailApp.getUserLabels().map((value) => {
    LABEL_DICT[value.getName().toString()] = value;
    return { "label": value, "name": value.getName() };


function getLabelByName(name) {
  return LABEL_DICT.hasOwnProperty(name) ? LABEL_DICT[name] : undefined;

function createLabelIfNotExists(name) {
  var newLabel = null;
  var label = getLabelByName(name);
  if (!label) {
    newLabel = GmailApp.createLabel(name.toString());
    LABEL_DICT[name.toString()] = newLabel;
    label = newLabel;
  return label;

function getLabelByEmailType(emailAddress) {
  if (!emailAddress) {
  let emailLower = emailAddress.toString().toLowerCase();
  if (emailLower.toString().endsWith(EMAIL_WORKSPACE_DOMAIN)) {
    let futureLabel = emailLower.toString().split("@");
    return createLabelIfNotExists("📬 " + futureLabel[0]);

function triggerScriptForEmail() {
  let indicatorLabelString = "🆕";
  var LABEL_TO_REMOVE = LABEL_DICT[indicatorLabelString];
  var threads = new Array();
  while (Array.isArray(threads)) {
    Logger.log(`Calling getUserLabelByName = ${indicatorLabelString}`);
    threads = GmailApp.getUserLabelByName(indicatorLabelString).getThreads(0, GET_THREADS_AT_TIME);
    Logger.log(`Thread length == ${threads.length}`);
    if (threads.length == 0) {
      Logger.log('All gone');
    threads.forEach((thread) => {
      var messages = GmailApp.getMessagesForThread(thread);
      var emailAddresses = => (msg.getTo ? [msg.getTo()] : [])).map((value) => value.toString().match(/([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)/gi));
      [ Set(emailAddresses)].forEach((recipient) => {
        Logger.log(`Checking email address ${recipient}`);
        var labelToAttach = getLabelByEmailType(recipient);
        if (labelToAttach) {
          Logger.log(`Attaching label ${labelToAttach.getName()}`);


      Logger.log(`Removing indicator label`);
      return null;

function doGet(e) {
  return `OK`;

How to configure the Google Workspace Catch-all

At the time of writing this blog post, the current steps were defined.

Interesting, I ended up with something different to that under

This is how my configuration looks:

Regardless, I believe the outcome is the same is similar. If not, let me know…