Google Forms Slack webhook setup
How to POST every Google Form submission to a Slack incoming webhook using Apps Script. The raw HOWTO for developers, and where a managed tool starts to make sense.
- Free generator at the end
- Manual + managed options
- Honest about trade-offs
Two things and ~5 minutes
- A Slack incoming webhook URL for the channel you want messages to land in. Looks like
https://hooks.slack.com/services/T.../B.../XXX. Create one inside Slack on the channel you pick. - A Google Formwith at least one question. You'll attach an Apps Script to it that POSTs every response to the webhook.
Create the Slack incoming webhook
- 1In Slack, open the channel you want to receive messagesClick the channel name → Integrations → Add an app.
- 2Search for 'Incoming Webhooks' and install (if needed)If your workspace doesn't already have it installed, Slack walks through approving it.
- 3Add a new webhook for that channelSlack generates a unique URL bound to the channel you picked. Copy it.
- 4Treat the URL like a passwordAnyone with the URL can post to that channel as the webhook's app. Don't paste it into shared documents.
Write the Apps Script
Open your Google Form. Click the three-dot menu (top-right) → Apps Script. Delete the placeholder code and paste this, replacing WEBHOOK_URL with the URL you copied:
const WEBHOOK_URL = "PASTE_YOUR_WEBHOOK_URL_HERE";
function installFormBridge() {
var form = FormApp.getActiveForm();
// 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("Installed.");
}
function onFormSubmit(e) {
var form = FormApp.getActiveForm();
var response = e.response;
var items = response.getItemResponses();
var lines = [];
for (var i = 0; i < items.length; i++) {
var title = items[i].getItem().getTitle();
var answer = items[i].getResponse();
if (Array.isArray(answer)) answer = answer.join(", ");
lines.push("*" + title + ":* " + answer);
}
var payload = {
text: "New response from " + form.getTitle(),
blocks: [
{ type: "section", text: { type: "mrkdwn", text: ":inbox_tray: *New form response*" } },
{ type: "section", text: { type: "mrkdwn", text: "*Form:* " + form.getTitle() } },
{ type: "divider" },
{ type: "section", text: { type: "mrkdwn", text: lines.join("\n") } }
]
};
UrlFetchApp.fetch(WEBHOOK_URL, {
method: "post",
contentType: "application/json",
payload: JSON.stringify(payload),
muteHttpExceptions: true
});
}Save the script with ⌘S. In the function dropdown at the top of the editor, choose installFormBridge and click Run. Google will ask you to authorise the script the first time, click Advanced → Go to ... (unsafe) → Allow. The warning is because the script is unverified, not because it's actually unsafe; it's your script and you can read every line.
Don't want to copy this by hand? Our free Apps Script generator emits the same script with your webhook URL pre-filled, plus a TEST_MODE toggle for dry-running.
Send a test response
Open your Google Form and fill it in. Within a few seconds, you should see a Slack message in the channel the webhook is bound to.
If nothing arrives: open the Apps Script editor again, click Executions in the left sidebar, and look for the most recent onFormSubmitrun. Failures show the HTTP status and Slack's response. Common ones:
- HTTP 404 / “no_service”: the webhook URL is wrong or has been revoked. Create a new one.
- HTTP 403 / “invalid_token”: Slack rejected the request. Re-create the webhook.
- HTTP 400 / “invalid_payload”: the JSON shape is off; check the script wasn't edited.
Our free webhook checker POSTs a test payload to any HTTPS URL and translates Slack's opaque error codes into plain English. Useful for narrowing down the failure without leaving the browser.
When the manual path stops paying off
For one form, one channel, one developer, the script above is plenty. Where it starts hurting:
- Multiple channels by answer. Adding IF-THEN routing inside the script is writing a tiny custom routing engine. Maintainable for one person; opaque to the rest of the team.
- Retries that don't double-post. Apps Script retries on transient errors without dedupe. Slack can show duplicate messages. Fixing this means storing the response ID somewhere and checking it before each post.
- Delivery audit log.Apps Script's Executions log is engineer-facing and ephemeral. Showing a teammate or client “here's the actual Slack response code we got” needs purpose-built tooling.
- Multiple forms or multiple clients. The script lives inside one form. To run this for 10 forms, you maintain 10 scripts. To run it for 10 clients with their own webhooks, you maintain 10 sets of credentials in 10 separate forms.
- When the form question gets renamed. The script reads question titles. A renamed question silently breaks the rule that depended on it.
How RouteForms compares to keeping it manual
RouteForms is the managed version of this hop. Same Apps Script idea, except the script POSTs to a RouteForms webhook (we generate it pre-filled), and RouteForms does the routing, dedupe, log, and retry on its end.
- IF-THEN routing rules in a dashboard, with 10 operators and a live simulator
- Idempotency on the Google Forms response ID, retries can't double-post
- Per-form delivery log with Slack's HTTP response code
- One-click retry on failed deliveries
- Failed-delivery email alerts on paid plans
- Client workspaces for agencies on the Agency plan
- Free for 30 responses a month, $7/month for unlimited
The migration is the same Apps Script swap: paste a new pre-filled script (we generate it inside your account), run it, done. Detailed walkthrough on the main guide and the without-Zapier migration page.
Frequently asked questions
What's a Slack incoming webhook?▾
A Slack incoming webhook is a URL Slack gives you that's bound to a specific channel. You POST a JSON payload to that URL and Slack posts the payload as a message in the channel. It's the simplest way to programmatically send messages to Slack, no bot, no OAuth, no app installation in the workspace beyond creating the webhook once.
What's Google Apps Script?▾
Apps Script is Google's serverless JavaScript runtime built into the Workspace ecosystem. Every Google Form has access to its own Apps Script editor (three-dot menu → Apps Script). You can attach a script to the form, set a trigger to run it on every form submission, and inside the script call any HTTPS endpoint, including a Slack webhook URL.
Is the manual setup hard?▾
Not for one form posting to one channel, that's maybe 20 lines of code and 5 minutes once you have the webhook URL. It gets harder when you need conditional routing, idempotent retries, a delivery log, or to manage many forms. At that point a managed tool pays for itself.
Can Apps Script retry on failure?▾
UrlFetchApp will retry on transient errors automatically. The catch is that Apps Script doesn't dedupe the retries, so if the first call succeeded but the response didn't reach Apps Script, the retry posts a second Slack message. RouteForms enforces uniqueness on the Google Forms response ID at the database layer so retries can't double-post.
Can I do routing in Apps Script?▾
Yes, with code. You'd write IF-THEN logic inside onFormSubmit, look up the response field values, pick a webhook URL, and POST to it. The cost is ongoing maintenance: when a form question gets renamed, the script breaks silently. When the team grows, fewer people on it can read the script. Routing rules in a UI fix both problems.
How do I get the Slack webhook URL?▾
In Slack, open the channel you want messages to land in, then click the channel name → Integrations → Add an app → search for 'Incoming Webhooks' → install if needed → choose the channel → copy the hooks.slack.com URL. The URL is the credential; treat it like a password.
Is there a free generator?▾
Yes, our Apps Script generator at /tools/google-forms-to-slack-generator builds the Apps Script for you. Paste a webhook URL, optionally a form name and TEST_MODE toggle, and it emits a script you can copy-paste into your form. Runs entirely in your browser; the URL never touches our servers.
When should I switch from manual Apps Script to RouteForms?▾
When you start needing routing rules, an audit-friendly delivery log, idempotent retries, failed-delivery alerts, or you're running multiple forms or multiple clients. The Free plan covers 30 responses/month, enough to try it alongside your manual setup.
Skip the script, let RouteForms handle the hop
Free for 30 responses a month. Routing rules, delivery logs, retries, and alerts are all included on paid plans starting at $7/mo.
Keep reading
Free tool that builds the Apps Script for you, paste a webhook URL, copy the result.
POST a test payload to any HTTPS webhook, see Slack's response decoded into plain English.
The end-to-end product walkthrough.
What conditional routing buys you when you grow past 'one form, one channel'.