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.
Rules run top-to-bottom. First match wins. Each rule needs its own Slack webhook URL.
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)_" } }
]
};
}
Get the script running in 90 seconds
- 1Open the Apps Script editor inside your Google FormThree-dot menu (top-right) → Apps Script. A new tab opens with a blank script.
- 2Replace the placeholder with your generated scriptDelete
function myFunction() {}, paste the generated script above, save with⌘S. - 3Run installFormBridge onceIn the function dropdown at the top of the editor, choose
installFormBridgeand click Run. This creates the form-submit trigger. - 4Authorise when Google asksClick Advanced → Go to ... (unsafe) → Allow. The warning shows because the script is unverified, it's yours, you can read every line.
- 5Submit a test responseFill 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).
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 eachitemResponsesentry, 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
webhookUrlreceives the POST. If no rule matched,DEFAULT_WEBHOOK_URLreceives it. If the default is empty too, the script logs a warning instead of posting.
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.
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.
Keep reading
Test your rules against a sample submission before generating the script.
Simpler version that POSTs every response to a single webhook, no routing.
POST a test payload to each Slack webhook URL to verify it's working before deploying.
The rule model, operators, ordering, defaults, explained in detail.
All the real options for this hop. RouteForms, SlackQ, Form Director, Zapier, Make, Apps Script, raw webhooks, ranked by fit.