Author: By Raj

Part of our Google Sheets Automation Expert guides. Need this built for your team? Hire a Google Apps Script developer.

Estimated reading time: 10 minutes

Build an Approval Workflow in Google Sheets with Apps Script

Approval workflows in Google Sheets combine status columns, data validation, email notifications, and optional web apps for managers who prefer not to edit the grid directly.

Typical flow: employee sets Status to Submitted → script emails approver with row link → approver sets Approved/Rejected → audit log appends timestamp and actor via Session.getActiveUser().getEmail().

Lock rows after approval with sheet.protect().setWarningOnly(false) or move approved rows to an Archive tab. Cross-link /blog/google-sheets-data-validation-apps-script for status lists.

State machine design

Define allowed transitions in an object: Draft→Submitted only, Submitted→Approved|Rejected. Reject scripts that jump Draft→Approved.

Store transition rules in Config sheet so ops can adjust without developer for text labels.

Notifier functions

MailApp.sendEmail with subject including request ID from column A. Include SpreadsheetApp.getActive().getUrl() + '#gid=' + sheet.getSheetId() + '&range=' + row.

Throttle emails if bulk import sets many rows to Submitted, send digest.

Audit log tab

Append [timestamp, user, row, oldStatus, newStatus] on each valid transition via appendRow.

Protect Audit tab from editors except admins.

Optional approver web app

HtmlService buttons call google.script.run.approveRow(id) for executives on mobile. Deploy execute-as user accessing sheet, domain-restricted.

See /blog/apps-script-crud-app-sheets for UI patterns.

Example code

function handleStatusChange(e) {
  const sh = e.range.getSheet();
  const row = e.range.getRow();
  const status = e.range.getValue();
  const id = sh.getRange(row, 1).getValue();
  if (status === 'Submitted') {
    MailApp.sendEmail('approver@company.com', 'Approval needed #' + id, 'Review row ' + row);
    sh.getSheetByName('Audit').appendRow([new Date(), Session.getActiveUser().getEmail(), id, status]);
  }
}
ApproachBest forTradeoff
Apps Script nativeGoogle Workspace-centric workflows6-min limit, quotas
Zapier / MakeNo-code, many connectorsPer-task cost, vendor lock-in
Python + CloudHeavy data / MLHosting cost, separate auth
Google Sheets automation expertProduction custom logicBuild cost, you own code

FAQ

How do I capture who approved?

Use installable onEdit; Session.getActiveUser().getEmail() returns the editor on that event. For web app approvals, log the authenticated user from doGet session.

Can one approver pool work?

Yes. Send mail to a group alias or round-robin via a Queue sheet listing on-call approver emails updated weekly.

What about multi-step approvals?

Use columns Approver1Status and Approver2Status or a small state machine table, escalate after 48h with time-driven checks.

Does Google have native Approvals?

Workspace Approvals exists for some flows; Apps Script customizes logic tied to Sheets formulas and external systems.

How to prevent edit after approved?

Protect the row range except finance role, or move to Archive sheet read-only for requesters.

Need this done for you? I handle this as part of my consulting work, fixed-price quote within 24 hours.

Book a call with Raj →

Get the full Build an Approval Workflow in Google Sheets with Apps Script script template

I'll email you a production-ready, commented version you can deploy in 10 minutes.

Need help with this? I handle this as part of my Google Sheets Automation Expert service.

CRM, dashboards, invoices, inventory, and report automation in Sheets.

See how it works →