Better systems, calmer workdays.Explore the newest guides

How to Send Automatic Emails from Google Sheets (Without Zapier)

Turn rows in a spreadsheet into reliable email notifications—with a simple script and a few important safeguards.

WORKFLOW ATLAS / FIELDNOTE 05
MAKE WORK FLOW BETTER ↗

You don't need an expensive automation subscription to send a routine email when a spreadsheet row changes state. Google Apps Script can read rows, check a condition, send an email, and record when the message was sent.

In this tutorial, we'll build a small approval-notification workflow. A team member marks a request Approved in Google Sheets; a scheduled script sends the email and records a timestamp. We'll also cover the failure cases that matter when you rely on automation for real work.

What you'll build: Google Sheets → Apps Script condition → email notification → sent timestamp. This is a starter pattern, not a bulk-marketing email system.

1. Prepare the spreadsheet

Create a Google Sheet and rename its first tab to Requests. Add these column headings in row one:

A — Name B — Email C — Status D — Last Sent
Taylor taylor@example.com Approved
Sam sam@example.com Pending

Use email addresses you control when testing. Last Sent must be empty for a message to be sent. The example deliberately requires the exact status Approved (case-insensitive) to avoid accidentally emailing every row.

If other teammates edit the sheet, protect the Last Sent column so they don't erase audit information by mistake.

2. Add an Apps Script

Open Extensions → Apps Script from the sheet. Replace the starter code with the following:

function sendApprovedEmails() {
  const lock = LockService.getScriptLock();
  if (!lock.tryLock(15000)) return;

  try {
    const sheet = SpreadsheetApp.getActiveSpreadsheet()
      .getSheetByName('Requests');
    if (!sheet) throw new Error('Requests tab not found');

    const lastRow = sheet.getLastRow();
    if (lastRow < 2) return;

    const rows = sheet.getRange(2, 1, lastRow - 1, 4)
      .getValues();

    rows.forEach((row, index) => {
      const [name, email, status, sentAt] = row;
      const approved = String(status).trim().toLowerCase()
        === 'approved';
      const address = String(email).trim();
      const looksLikeEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
        .test(address);

      if (!approved || sentAt || !looksLikeEmail) return;
      if (MailApp.getRemainingDailyQuota() < 1) return;

      const body = `Hello ${name || 'there'},\n\n` +
        'Your request has been approved.\n\n' +
        'Best,\nOperations Team';

      MailApp.sendEmail({
        to: address,
        subject: 'Your request has been approved',
        body: body
      });
      sheet.getRange(index + 2, 4).setValue(new Date());
    });
  } finally {
    lock.releaseLock();
  }
}

The lock reduces the chance that two overlapping executions process the same row. The timestamp prevents ordinary repeat sends: when the next trigger runs, previously processed rows are skipped.

Important limitation: if an email is sent but writing the timestamp fails, a later execution can send it again. This is an at-least-once starter workflow, not a guaranteed exactly-once delivery system. For high-value notifications, use a durable message queue or a separate dispatch ledger with reconciliation.

3. Authorize and test manually

  1. Save the project and name it something descriptive, such as Request Notifications.
  2. Choose sendApprovedEmails in the function selector and click Run.
  3. Review the Google authorization prompt and approve only if you trust the code and understand its access.
  4. Confirm that an email arrives in the test inbox, and the Last Sent cell contains a timestamp.
  5. Run it a second time. The same row should not be sent again while that timestamp remains.

During testing, prefer an account you own. Never point a new automation at a customer distribution list before testing the complete workflow.

4. Make it automatic with a time-driven trigger

In the Apps Script editor, open Triggers → Add Trigger, select the sendApprovedEmails function, choose Time-driven, and choose an interval that suits your business.

Why not a simple onEdit? Google's simple triggers cannot use services that require authorization, such as sending email. An installable trigger can run authorized services, and a time-driven trigger is often easier to audit and reason about.

A clock trigger does not necessarily fire at the exact minute you selected. If your process needs immediate delivery or stronger guarantees, move the sending logic into a backend job queue.

5. Handle failures and limits

If the emails stop, check these items in order:

  • The sheet name: it must match Requests in the example.
  • The status: Approved is required; Pending does nothing.
  • The sent timestamp: clear it only when you intentionally want to resend.
  • The execution logs: the Apps Script Executions view identifies failed runs.
  • Sending quotas: Google applies different daily email-recipient limits for consumer and Workspace accounts, and these can change.
  • Permissions: the account creating the trigger is the account whose permissions are used for the trigger.

The example's quota guard avoids attempting more sends when the available daily recipient count is exhausted. For a large spreadsheet, add batching, structured logging, and an error notification mechanism.

6. Improve the workflow before production

A more robust version should use a separate Send Status column with values such as Pending, Sent, and Error, store an external dispatch ID, and support controlled retries. It should also validate recipient addresses and never place private customer data into public logs.

For commercial messaging, you must also address consent, relevant email marketing rules, unsubscribes, and data retention. This tutorial is intended for legitimate operational email notifications, not unsolicited outreach.

Useful references

The takeaway

Start with one process you can describe clearly: a condition, an action, and a record of what happened. Test the failure path—not just the happy path—before calling your automation done.

✳

Keep the good stuff moving.

One useful workflow at a time. Explore another guide or share this one with someone who needs it.

Explore more guides
Search the fieldnotes

Popular: AI tutorials n8n Google Sheets triggers