Build a Google Form to Sheets Approval Workflow with Apps Script
Approval workflows — expense requests, leave applications, purchase orders — are a perfect fit for Google Forms + Sheets + Apps Script. Here's how to build one end to end.
The setup
A Google Form collects submissions (e.g., an expense request).
Responses flow into a linked Google Sheet.
An Apps Script runs on form submit to notify an approver by email.
The approver clicks Approve or Reject directly from the email.
The sheet Status column updates automatically.
Step 1 — Create your Google Form
Create a form with fields like:
Name (Short answer)
Amount (Short answer)
Reason (Paragraph)
Link it to a Google Sheet via Responses > Link to Sheets.
Add a Status column manually in the sheet (column E, for example). This is where we'll write "Approved" or "Rejected".
Step 2 — Send an approval email on form submit
Open the linked sheet, go to Extensions > Apps Script, and add this script:
functiononFormSubmit(e){const sheet =SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();const row = e.range.getRow();const values = e.range.getValues()[0];const name = values[1];// Column Bconst amount = values[2];// Column Cconst reason = values[3];// Column Dconst approverEmail ='[email protected]';const scriptUrl =ScriptApp.getService().getUrl();const approveUrl =`${scriptUrl}?action=approve&row=${row}`;const rejectUrl =`${scriptUrl}?action=reject&row=${row}`;const subject =`Approval Required: Expense Request from ${name}`;const body =` <p>${name} has submitted an expense request.</p>
<p><strong>Amount:</strong> $${amount}</p>
<p><strong>Reason:</strong> ${reason}</p>
<br>
<a href="${approveUrl}" style="padding:10px 20px;background:#34a853;color:#fff;text-decoration:none;border-radius:4px;">Approve</a>
<a href="${rejectUrl}" style="padding:10px 20px;background:#ea4335;color:#fff;text-decoration:none;border-radius:4px;">Reject</a>
`;GmailApp.sendEmail(approverEmail, subject,'',{htmlBody: body });Logger.log(`Approval email sent for row ${row}`);}
Step 3 — Handle the approver's response
Deploy the script as a Web App (Execute as: Me, Who has access: Anyone) and add a doGet function:
functioncreateFormTrigger(){const ss =SpreadsheetApp.getActiveSpreadsheet();ScriptApp.newTrigger('onFormSubmit').forSpreadsheet(ss).onFormSubmit().create();}
Run createFormTrigger() once to wire up the trigger.
How it all works together
Someone submits the form.
onFormSubmit fires, sends an HTML email with Approve/Reject links.
The approver clicks a link — it hits the deployed Web App URL.
doGet updates the Status column in the sheet.
This is a solid foundation you can extend with notifications to the requester, multi-level approvals, or deadline reminders.