You want the nightly job to say “done, 42 rows” (or “failed”) in the team’s Google Chat space instead of somebody’s inbox. Incoming webhooks make that a single POST. postToChatWebhook(webhookUrl, text) sends { text } as application/json with muteHttpExceptions: true. On a non-2xx response it throws a readable Error that includes the HTTP status and the start of the response body, so the failure shows up in your logs and failure emails with a reason attached. Otherwise it returns the parsed response.
When to use
- Job-finished / job-failed pings into a Google Chat space (Slack incoming webhooks take the same
{ text }shape) - “New row needs review” nudges from an onEdit or form-submit handler
- Anywhere you would otherwise email yourself a one-liner
How to use this snippet
/**
* POST a plain-text message to a Chat (or Slack) incoming webhook.
* Throws a readable Error on non-2xx so failures show up in Executions.
*
* @param {string} webhookUrl incoming webhook URL (keep it in Script Properties)
* @param {string} text message text
* @return {Object|string} parsed JSON response (or raw text if not JSON)
*/
function postToChatWebhook(webhookUrl, text) {
if (!webhookUrl) throw new Error('postToChatWebhook: missing webhook URL');
var res = UrlFetchApp.fetch(webhookUrl, {
method: 'post',
contentType: 'application/json',
payload: JSON.stringify({ text: String(text) }),
muteHttpExceptions: true
});
var code = res.getResponseCode();
var body = res.getContentText() || '';
if (code < 200 || code >= 300) {
throw new Error('Chat webhook failed (HTTP ' + code + '): ' + body.slice(0, 200));
}
try {
return body ? JSON.parse(body) : {};
} catch (e) {
return body; // Slack answers with plain "ok"
}
}
Example
function nightlySync() {
var url = PropertiesService.getScriptProperties().getProperty('CHAT_WEBHOOK_URL');
try {
var n = syncRows_(); // your job
postToChatWebhook(url, '✅ Nightly sync done: ' + n + ' rows');
} catch (err) {
// Don't let a broken webhook hide the real error
try { postToChatWebhook(url, '❌ Nightly sync failed: ' + err.message); } catch (e) {}
throw err;
}
}
Tips:
- Treat the webhook URL like a password. Anyone holding it can post to the space. Keep it in Script Properties, not in code or a visible sheet cell.
- Never log the URL itself. The thrown error includes the status and body, not the URL.
- Keep messages short. Chat and Slack both rate-limit webhooks, so batch “42 rows done” rather than one message per row.
- Wrap the failure-path post in its own
tryso a dead webhook doesn’t replace the real error. - Need retries on 429/5xx? Wrap the fetch with retry-urlfetch-with-backoff rather than looping here.
Tip: NitroGAS Co-Pilot can add a done/failed postToChatWebhook ping around an existing job and move the URL into Script Properties. Paste the job function.
Happy Coding!
