Home Services Portfolio Blog Trainings Contact Hire Me

If your team is still doing things manually in Google Sheets — sending reports, validating data entry, cleaning up formatting — you're leaving hours on the table every single week.

Google Apps Script is a free, built-in scripting language that lets you automate virtually anything inside Google Workspace. It runs on Google's servers, requires no software installation, and can be triggered on a schedule or by events like form submissions and cell edits.

After building automations for dozens of businesses on Upwork, here are the five I recommend to every single client.

1 Automated Weekly Email Reports

This is the most impactful automation you can set up. Instead of manually pulling numbers, formatting them, and emailing stakeholders every Monday morning — let Apps Script do it for you.

How It Works

The script reads key metrics from your Google Sheet, formats them into a clean HTML email, and sends it to a list of recipients on a schedule you define. You can include tables, conditional formatting (e.g., red for metrics that dropped), and even inline charts.

function sendWeeklyReport() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Dashboard');
  const revenue = sheet.getRange('B2').getValue();
  const orders = sheet.getRange('B3').getValue();
  const avgOrder = sheet.getRange('B4').getValue();

  const html = `
    <h2>Weekly Performance Report</h2>
    <table style="border-collapse: collapse; width: 100%;">
      <tr>
        <td style="padding: 12px; border: 1px solid #ddd;"><strong>Revenue</strong></td>
        <td style="padding: 12px; border: 1px solid #ddd;">$${revenue.toLocaleString()}</td>
      </tr>
      <tr>
        <td style="padding: 12px; border: 1px solid #ddd;"><strong>Orders</strong></td>
        <td style="padding: 12px; border: 1px solid #ddd;">${orders}</td>
      </tr>
      <tr>
        <td style="padding: 12px; border: 1px solid #ddd;"><strong>Avg Order Value</strong></td>
        <td style="padding: 12px; border: 1px solid #ddd;">$${avgOrder.toFixed(2)}</td>
      </tr>
    </table>
  `;

  MailApp.sendEmail({
    to: '[email protected]',
    subject: 'Weekly Performance Report - ' + new Date().toLocaleDateString(),
    htmlBody: html
  });
}

Set a time-driven trigger for every Monday at 8 AM, and your team gets a consistent, error-free report without anyone lifting a finger.

Pro tip: Add a "Last Sent" timestamp to your sheet so you always know when the last report went out.

2 Form Submission Auto-Processor

Google Forms dump raw responses into a sheet, but the data usually needs processing — assigning categories, sending confirmation emails, notifying the right team member, or formatting values.

How It Works

Using an onFormSubmit trigger, the script runs automatically every time a new response comes in. It can:

  • Send a personalized confirmation email to the respondent
  • Route the submission to the right person based on the answer (e.g., sales vs. support)
  • Auto-populate a project tracker or CRM sheet
  • Add timestamps and status columns
function onFormSubmit(e) {
  const responses = e.namedValues;
  const name = responses['Name'][0];
  const email = responses['Email'][0];
  const department = responses['Department'][0];

  // Send confirmation
  MailApp.sendEmail({
    to: email,
    subject: 'Thanks for your submission, ' + name,
    body: 'We received your request and will get back to you within 24 hours.'
  });

  // Route to the right team
  const routingMap = {
    'Sales': '[email protected]',
    'Support': '[email protected]',
    'Billing': '[email protected]'
  };

  if (routingMap[department]) {
    MailApp.sendEmail({
      to: routingMap[department],
      subject: 'New inquiry from ' + name,
      body: 'Department: ' + department + '\nEmail: ' + email
    });
  }
}

This one automation eliminates the delay between form submission and team action — which is often where leads go cold.

3 Data Validation and Cleanup Trigger

If multiple people enter data into your sheets, you know the pain: inconsistent formatting, typos, blank required fields, wrong date formats. Instead of cleaning up after everyone, prevent the mess in the first place.

How It Works

An onEdit trigger watches for changes in specific columns and automatically standardizes the input:

  • Capitalize names and titles consistently
  • Trim extra whitespace from all text entries
  • Validate email format and highlight invalid ones in red
  • Auto-fill dates, timestamps, and calculated fields
  • Prevent duplicate entries by checking existing data
function onEdit(e) {
  const sheet = e.source.getActiveSheet();
  const range = e.range;
  const col = range.getColumn();
  const row = range.getRow();

  // Only process the "Contacts" sheet, skip header row
  if (sheet.getName() !== 'Contacts' || row === 1) return;

  const value = range.getValue();

  // Column B: Auto-capitalize names
  if (col === 2 && typeof value === 'string') {
    const capitalized = value.trim().replace(/\b\w/g, c => c.toUpperCase());
    if (capitalized !== value) range.setValue(capitalized);
  }

  // Column C: Validate email
  if (col === 3 && typeof value === 'string') {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(value.trim())) {
      range.setBackground('#FFCDD2'); // Light red
      SpreadsheetApp.getActiveSpreadsheet().toast(
        'Invalid email format in row ' + row, 'Validation Warning'
      );
    } else {
      range.setBackground(null); // Clear
    }
  }

  // Column A: Auto-timestamp
  if (col >= 2 && col <= 5) {
    sheet.getRange(row, 1).setValue(new Date());
  }
}

This kind of real-time data hygiene is what separates a messy spreadsheet from a reliable data source.

4 Automated Inventory / Stock Alerts

If you track inventory, stock levels, or any kind of threshold-based metric in Google Sheets, you need automated alerts. Waiting until someone manually checks the sheet is how you end up with stockouts and missed deadlines.

How It Works

A scheduled script scans your inventory sheet, identifies items below their reorder threshold, and sends a consolidated alert email with everything that needs attention.

function checkInventoryLevels() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Inventory');
  const data = sheet.getDataRange().getValues();
  const headers = data[0];
  const lowStockItems = [];

  for (let i = 1; i < data.length; i++) {
    const product = data[i][0];     // Column A: Product Name
    const current = data[i][2];     // Column C: Current Stock
    const reorder = data[i][3];     // Column D: Reorder Point

    if (current <= reorder) {
      lowStockItems.push({
        product: product,
        current: current,
        reorder: reorder
      });
    }
  }

  if (lowStockItems.length > 0) {
    let html = '<h2>Low Stock Alert</h2><table style="border-collapse: collapse;">';
    html += '<tr style="background: #F7941D; color: white;">';
    html += '<th style="padding: 10px;">Product</th>';
    html += '<th style="padding: 10px;">Current</th>';
    html += '<th style="padding: 10px;">Reorder Point</th></tr>';

    lowStockItems.forEach(item => {
      html += '<tr>';
      html += '<td style="padding: 10px; border: 1px solid #ddd;">' + item.product + '</td>';
      html += '<td style="padding: 10px; border: 1px solid #ddd; color: red;">' + item.current + '</td>';
      html += '<td style="padding: 10px; border: 1px solid #ddd;">' + item.reorder + '</td>';
      html += '</tr>';
    });

    html += '</table>';

    MailApp.sendEmail({
      to: '[email protected]',
      subject: 'Low Stock Alert - ' + lowStockItems.length + ' items need reordering',
      htmlBody: html
    });
  }
}

Run this daily or even twice a day. You'll never be caught off guard by a stockout again.

5 Cross-Sheet Data Sync

Many businesses have data scattered across multiple Google Sheets — sales in one, expenses in another, a master report in a third. Manually copying data between them is tedious and error-prone.

How It Works

Apps Script can read from any Google Sheet you have access to using its spreadsheet ID. A scheduled sync script pulls the latest data from source sheets and updates your master report automatically.

function syncDataToMaster() {
  // Source sheets
  const salesSheet = SpreadsheetApp.openById('SALES_SHEET_ID').getSheetByName('Data');
  const expenseSheet = SpreadsheetApp.openById('EXPENSE_SHEET_ID').getSheetByName('Data');

  // Master sheet
  const master = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Master');

  // Pull sales data
  const salesData = salesSheet.getDataRange().getValues();
  const salesSummary = salesData.slice(1).reduce((sum, row) => sum + row[3], 0);

  // Pull expense data
  const expenseData = expenseSheet.getDataRange().getValues();
  const expenseSummary = expenseData.slice(1).reduce((sum, row) => sum + row[2], 0);

  // Update master
  const today = new Date();
  master.appendRow([
    today,
    salesSummary,
    expenseSummary,
    salesSummary - expenseSummary  // Profit
  ]);

  // Log sync
  SpreadsheetApp.getActiveSpreadsheet().toast(
    'Data synced successfully at ' + today.toLocaleTimeString(),
    'Sync Complete'
  );
}

This is especially powerful when combined with the weekly email report (automation #1). Your master sheet stays up-to-date, and your reports pull from a single source of truth.

Getting Started

To create any of these automations:

  1. Open your Google Sheet
  2. Go to Extensions > Apps Script
  3. Paste the script and modify it for your specific sheet structure
  4. Set up a trigger: click the clock icon in the left sidebar and create a time-driven or event-driven trigger
  5. Authorize the script when prompted (it needs permission to read your sheets and send emails)

Each of these scripts can be built and deployed in under an hour. The ROI is immediate — you'll save that time back in the first week.

Need Help Setting This Up?

If you'd rather have a professional build and customize these automations for your specific workflows, I'm here to help. I've built Google Apps Script solutions for businesses of all sizes on Upwork — from simple email triggers to complex multi-sheet data pipelines.

Get in touch for a free consultation, or hire me on Upwork to get started right away.

All Articles Hire Me

Want Data Tips Delivered to Your Inbox?

Subscribe to the ChromiumData newsletter for weekly insights, tutorials, and data tips straight to your inbox.

Subscribe Now