Free tool · no sign-up

Routing rules → Google Apps Script translator

Define your IF-THEN rules in the UI below. Get back a standalone Google Apps Script that runs the rules on every form submission and posts to the matching Slack webhook. Runs entirely in your browser, your webhook URLs never touch our servers.

Fallback for any response that doesn't match a rule. Leave blank if you want unmatched responses skipped.

Leave blank to use the Google Form's own title.

Routing rules

Rules run top-to-bottom. First match wins. Each rule needs its own Slack webhook URL.

#1
#2
Generated Apps Script

0 rules with a field name and webhook URL, others are commented out in the output.

// Google Forms → Slack, with routing rules baked in.
//
// Generated by https://routeforms.com/tools/routing-rules-to-apps-script
//
// Install (90 seconds):
//   1. In your Google Form, click the three-dot menu → Apps Script.
//   2. Replace the placeholder code with this file. Save (Cmd+S).
//   3. Run installFormBridge once from the function dropdown.
//   4. Authorise when Google asks.
//   5. Submit a test response to verify.
//
// The script walks RULES top-to-bottom on every submission. The first
// matching rule wins and its webhookUrl receives the Slack message. If
// no rule matches, DEFAULT_WEBHOOK_URL receives the message. If
// DEFAULT_WEBHOOK_URL is empty, the script logs a warning and does not
// post.

const FORM_NAME_OVERRIDE = "";
const DEFAULT_WEBHOOK_URL = "PASTE_YOUR_DEFAULT_WEBHOOK_HERE";

// Rules are evaluated top to bottom. First match wins.
const RULES = [
  // No rules defined, every response goes to DEFAULT_WEBHOOK_URL.
];

// --- Install ------------------------------------------------------------

function installFormBridge() {
  var form = FormApp.getActiveForm();
  if (!form) {
    throw new Error(
      "No active form. Open this script editor from inside your Google Form: " +
        "Form → three-dot menu → Apps Script."
    );
  }
  // Wipe any existing onFormSubmit trigger so re-running install is safe.
  var existing = ScriptApp.getProjectTriggers();
  for (var i = 0; i < existing.length; i++) {
    if (existing[i].getHandlerFunction() === "onFormSubmit") {
      ScriptApp.deleteTrigger(existing[i]);
    }
  }
  ScriptApp.newTrigger("onFormSubmit").forForm(form).onFormSubmit().create();
  Logger.log("\u2713 Installed. Submit a test response to verify.");
}

// --- Rule evaluation ----------------------------------------------------

function pickField(data, fieldName) {
  if (!data) return null;
  var target = String(fieldName || "").trim().toLowerCase();
  for (var k in data) {
    if (String(k).trim().toLowerCase() === target) {
      var v = data[k];
      if (v === null || v === undefined) return null;
      return String(v);
    }
  }
  return null;
}

function evaluateRule(rule, data) {
  var fieldValue = pickField(data, rule.fieldName);
  var ruleValue = String(rule.value || "").trim();
  var op = rule.operator;

  if (op === "IS_EMPTY") return fieldValue === null || String(fieldValue).trim() === "";
  if (op === "IS_NOT_EMPTY") return fieldValue !== null && String(fieldValue).trim() !== "";
  if (fieldValue === null) return false;

  var fv = String(fieldValue).trim().toLowerCase();
  var rv = ruleValue.toLowerCase();

  if (op === "EQUALS") return fv === rv;
  if (op === "NOT_EQUALS") return fv !== rv;
  if (op === "CONTAINS") return fv.indexOf(rv) !== -1;
  if (op === "DOES_NOT_CONTAIN") return fv.indexOf(rv) === -1;

  if (
    op === "GREATER_THAN" ||
    op === "GREATER_THAN_OR_EQUAL" ||
    op === "LESS_THAN" ||
    op === "LESS_THAN_OR_EQUAL"
  ) {
    // Currency / commas / unit tolerance — '₹50,000' compares as 50000.
    var a = parseFloat(String(fieldValue).replace(/[^\d.-]/g, ""));
    var b = parseFloat(ruleValue);
    if (isNaN(a) || isNaN(b)) return false;
    if (op === "GREATER_THAN") return a > b;
    if (op === "GREATER_THAN_OR_EQUAL") return a >= b;
    if (op === "LESS_THAN") return a < b;
    return a <= b;
  }
  return false;
}

// --- Submission handler -------------------------------------------------

function onFormSubmit(e) {
  try {
    var form = FormApp.getActiveForm();
    var response = e.response;
    var items = response.getItemResponses();
    var data = {};
    for (var i = 0; i < items.length; i++) {
      var ir = items[i];
      var title = ir.getItem().getTitle();
      var answer = ir.getResponse();
      if (Array.isArray(answer)) answer = answer.join(", ");
      data[title] = answer;
    }
    var formTitle = FORM_NAME_OVERRIDE || form.getTitle();

    // Find the first matching rule.
    var matched = null;
    for (var j = 0; j < RULES.length; j++) {
      if (evaluateRule(RULES[j], data)) { matched = RULES[j]; break; }
    }
    var webhookUrl = matched ? matched.webhookUrl : DEFAULT_WEBHOOK_URL;
    if (!webhookUrl) {
      Logger.log("No matching rule and no default webhook, skipping.");
      return;
    }

    var payload = buildSlackPayload(formTitle, data, matched);
    UrlFetchApp.fetch(webhookUrl, {
      method: "post",
      contentType: "application/json",
      payload: JSON.stringify(payload),
      muteHttpExceptions: true
    });
  } catch (err) {
    Logger.log("Form bridge error: " + err);
  }
}

// --- Slack Block Kit message --------------------------------------------

function buildSlackPayload(formTitle, data, matched) {
  var lines = [];
  for (var k in data) {
    if (String(k).indexOf("_") === 0) continue; // skip meta fields
    var v = data[k];
    var rendered = (v === null || v === undefined)
      ? "\u2014"
      : (typeof v === "object" ? JSON.stringify(v) : String(v));
    if (rendered.length > 500) rendered = rendered.slice(0, 500) + "\u2026";
    lines.push("*" + k + ":* " + rendered);
  }
  var matchedLine = matched
    ? ("\n*Matched rule:* " + matched.name)
    : "\n*Matched rule:* (no rule matched, default channel)";
  return {
    text: "New response from " + formTitle,
    blocks: [
      { type: "section", text: { type: "mrkdwn", text: ":inbox_tray: *New form response*" } },
      { type: "section", text: { type: "mrkdwn", text: "*Form:* " + formTitle + matchedLine + "\n*Submitted:* " + new Date().toLocaleString() } },
      { type: "divider" },
      { type: "section", text: { type: "mrkdwn", text: lines.join("\n") || "_(no fields)_" } }
    ]
  };
}
Install

Get the script running in 90 seconds

  1. 1
    Open the Apps Script editor inside your Google Form
    Three-dot menu (top-right) → Apps Script. A new tab opens with a blank script.
  2. 2
    Replace the placeholder with your generated script
    Delete function myFunction() {}, paste the generated script above, save with ⌘S.
  3. 3
    Run installFormBridge once
    In the function dropdown at the top of the editor, choose installFormBridge and click Run. This creates the form-submit trigger.
  4. 4
    Authorise when Google asks
    Click AdvancedGo to ... (unsafe)Allow. The warning shows because the script is unverified, it's yours, you can read every line.
  5. 5
    Submit a test response
    Fill in your Google Form once. Slack should show the message in the channel of the rule that matched (or the default channel if none matched).
Under the hood

What the generated script does

  • installFormBridge(), wipes any existing onFormSubmit trigger and creates a fresh one. Re-running install is safe.
  • onFormSubmit(e), fires on every submission. Reads each itemResponses entry, collapses checkbox arrays into comma-joined strings, and looks up the first matching rule.
  • evaluateRule(rule, data), the operator semantics, identical to RouteForms's engine. Case-insensitive equals/contains, currency-tolerant numeric comparison.
  • buildSlackPayload(formTitle, data, matched), builds the Block Kit message: header, form name + matched rule name + submission time, divider, the field list.
  • The matched rule's webhookUrl receives the POST. If no rule matched, DEFAULT_WEBHOOK_URL receives it. If the default is empty too, the script logs a warning instead of posting.
What this tool isn't

When to use RouteForms instead

This script handles the routing. It does not handle the operational stuff that shows up once you're live. If any of the following matter to you, RouteForms is the managed version:

  • Per-form delivery log.Apps Script's Executions log is engineer-only. RouteForms records every Slack post with the matched rule, destination, and Slack's HTTP response, readable, filterable, exportable.
  • One-click retries on failure. If Slack rejects a post, the script logs it and moves on. RouteForms gives you a Retry button per submission.
  • Idempotency.Apps Script's UrlFetchApp can retry on transient errors without dedupe. Slack can show the same response twice. RouteForms enforces a unique constraint on the Google Forms response ID at the database layer.
  • Failed-delivery alerts.The script won't email you when delivery starts failing. RouteForms (paid plans) sends one when a failure streak starts.
  • Rule simulator. Editing rules in the script means cracking open the Apps Script editor. RouteForms has a live simulator that pre-fills with your latest real submission.
  • Multi-client workspaces. Running this for 10 clients means 10 sets of webhooks in 10 forms. The Agency plan gives you client workspaces from one account.

The free plan covers 30 responses a month, enough to validate the difference. The main guide walks through the managed setup end-to-end.

FAQ

Frequently asked questions

What does this tool produce?

A standalone Google Apps Script that implements IF-THEN routing for a Google Form. Paste a few rules in the UI (field name, operator, value, destination Slack webhook URL), and the tool emits a script that walks the rules top-to-bottom on every submission, posts to the first matching webhook, and falls back to a default webhook if no rule matches.

How is this different from the Apps Script generator?

Our other generator at /tools/google-forms-to-slack-generator emits a script that posts every response to one webhook. This translator adds the routing logic, different responses go to different webhooks based on the rules. Same install flow, but the script can fan responses across multiple Slack channels (or even workspaces).

Do I need RouteForms to use the generated script?

No. The script is self-contained, it runs entirely on Google's Apps Script infrastructure and posts directly to your Slack webhooks. You don't need a RouteForms account.

What operators are supported?

The full RouteForms operator set: equals, does not equal, contains, does not contain, greater than, greater than or equal, less than, less than or equal, is empty, is not empty. Numeric operators tolerate currency symbols and commas — '₹50,000' compares as 50000 against the rule value.

How does the script pick the destination?

It walks the rules from top to bottom and posts to the first rule that matches. If no rule matches, it posts to the default webhook URL you provided. If the default is also empty, it logs a warning to Apps Script's Executions log and doesn't post.

What does the Slack message look like?

A Block Kit message with an inbox-tray header, the form name and matched rule name in the meta section, a divider, then each visible field as a labelled line. The rule name shows in the message so a teammate reading the channel can tell why the response landed there.

When should I use RouteForms instead of this script?

When you start needing a per-form delivery log, one-click retries on failures, idempotency so Apps Script retries don't double-post, failed-delivery email alerts, a rule simulator, or agency-style multi-client workspaces. The script gives you the routing; RouteForms adds operational visibility and safety nets on top.

Can I test the script before deploying?

Test the rules first with our free /tools/routing-rule-simulator, paste a sample submission and the rules, see which fires. Then generate the script here and install. For sanity-checking the final flow, use /tools/slack-webhook-tester to POST a test payload to each webhook URL and confirm Slack accepts it.

Want operational safety on top of the routing?

RouteForms adds the delivery log, retries, idempotency, and alerts on top of the same rule model. Free for 30 responses a month.