Skip to content
// Forms · Sheets · Slack

Log form submissions to a spreadsheet in Google Sheets.

Capture every Google Form response into a Sheet with timestamp + parsed fields, then forward a Slack ping for the ones that matter. Single onFormSubmit trigger, no Zapier.

“Every time someone submits my form, I want a row in a sheet and a Slack ping if it's urgent.”

The script

copy · paste · trigger
logFormSubmission.gs
Apps Script
// Bound to the form-response Sheet. Writes to a separate output Sheet,
// then pings a Slack webhook for high-priority entries.
const SHEET_ID = 'PASTE-YOUR-SHEET-ID';
const SLACK_WEBHOOK = 'https://hooks.slack.com/services/...';

function onFormSubmit(e) {
  const sheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName('Responses');
  const responses = e.namedValues;

  const row = [
    new Date(),
    responses['Email'] ? responses['Email'][0] : '',
    responses['Subject'] ? responses['Subject'][0] : '',
    responses['Priority'] ? responses['Priority'][0] : 'normal',
    responses['Body'] ? responses['Body'][0] : '',
  ];
  sheet.appendRow(row);

  if (row[3] === 'urgent') {
    UrlFetchApp.fetch(SLACK_WEBHOOK, {
      method: 'post',
      contentType: 'application/json',
      payload: JSON.stringify({
        text: `Urgent: ${row[2]} from ${row[1]}`,
      }),
    });
  }
}

Need a variant? Gnaw writes a custom version from one sentence — fields, triggers, edge cases handled.

Walkthrough

How it works

This handler uses a Sheets form-submit event, whose namedValues map contains answers indexed by question title. A trigger installed on the Form itself supplies a different event object and cannot use this handler unchanged.

The handler appends parsed fields to a separate output sheet. The appendRow reference does not document a concurrency guarantee; this minimal example does not coordinate overlapping writes. Use a shared lock for cooperating script writers before relying on it for concurrent submissions.

For high-priority entries, the script POSTs to a Slack webhook with UrlFetchApp.fetch. That call counts toward your URL-fetch quota (20K/day free, 100K/day Workspace).

How to set the trigger

Open Apps Script from the form-response spreadsheet. Add an installable trigger for onFormSubmit, with event source From spreadsheet and event type On form submit. Review the requested spreadsheet and external-request permissions.

Set SHEET_ID to a separate output spreadsheet with a Responses tab. Google Forms already writes to the input response sheet; appending there again would duplicate the response. This handler is not compatible with the Form-bound event shape.

Why use Apps Script instead of Zapier

Zapier charges per "task" (one form submission = one task). At 1,000 submissions/month you're on a paid plan. Apps Script is free up to enormous quotas.

This example sends the selected urgent-entry fields to Slack through your configured webhook. Review that external data transfer and keep the webhook private.

Want a custom version?

Describe your sheet and the rule you want. Gnaw writes the Apps Script — fields, triggers, edge cases — in one shot.

FAQ

4 questions
Where do I find the Sheet ID?
Open the Sheet in your browser. The ID is the long string between /d/ and /edit in the URL. Copy it into SHEET_ID.
How do I add more priority levels?
Replace the row[3] === 'urgent' check with a switch or a lookup table. The bulldo.gs generator can scaffold the new shape from a one-line description.
Will this work without a paid Slack workspace?
Yes. Slack incoming webhooks are free on every tier. The webhook URL is the only credential the script needs.
What happens if the trigger fires twice for one submission?
Google deduplicates onFormSubmit triggers — you'll only see one fire per submission. If you do see duplicates, check that you haven't bound the same handler to both the Form and the Sheet.
// one good script a week

Get a working Apps Script snippet in your inbox, weekly.