It’s such a beautifully, useful and simple thing that I had to write about it…
Sooner or later your app needs to tell you something: a CRON job failed, some queue is backing up, a quota is almost gone, a big customer just signed up. You don’t need a paid service, an SDK, or the joy of debugging email deliverability for this. A Slack Incoming Webhook is a secret URL you POST JSON to, and that’s the whole trick.
Step 1 – Create the webhook
In Slack: create an app (api.slack.com/apps > Create New App > From scratch), turn on Incoming Webhooks, then Add New Webhook to Workspace and pick a channel. You get back a URL like:
https://hooks.slack.com/services/T00000000/B00000000/xxxxxxxxxxxxxxxxxxxxxxxx
That URL is the credential. You don’t need anything else, no secrets, no IDs, nothing – those are for other Slack stuff. For “post a message to this channel”, the URL is enough.
Step 2 – Store it as a secret
Drop it in your environment, never in the repo:
# .env (git-ignored)
SLACK_WEBHOOK_URL="https://hooks.slack.com/services/T00000000/B00000000/xxxx"
Step 3 – A tiny reusable helper
A few lines of code, no deps, and – importantly – best-effort: meaning a notification that fails will not take your app down with it.
// notify.ts
type NotifyOpts = {
title?: string;
level?: "info" | "warn" | "error"
};
const EMOJI = {
info: "ℹ️",
warn: "⚠️",
error: "🚨"
} as const;
export async function notify(
text: string,
opts: NotifyOpts = {}
): Promise<void> {
const url = process.env.SLACK_WEBHOOK_URL;
if (!url) return; // no-op, don't blow up
const emoji = EMOJI[opts.level ?? "info"];
const heading = opts.title
? `${emoji} *${opts.title}*\n`
: "";
try {
await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text: heading + text }),
});
} catch (err) {
console.error("notify failed (non-fatal):", err);
}
}
Use it anywhere
await notify("Mr. Dude, stuff happened! ✅");
await notify(
`DB usage is at ${percent}% of the plan cap.`, {
title: "Quota warning",
level: "warn",
});
That’s a working alert system. Really.
Want it to look nicer?
Slack is really good at this stuff; the messages can carry Block Kit blocks for headings and compact key/value grids. Keep the top-level text too, it’s the fallback used in push notifications:
body: JSON.stringify({
text: "Quota warning", // fallback
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: "🚨 *Quota warning*"
}
},
{ type: "section", fields: [
{ type: "mrkdwn", text: "*Used:*\n84%" },
{ type: "mrkdwn", text: "*Plan:*\nScaler" },
]},
],
})
Two rules, and you’re done
- Best-effort, always. Wrap the send in
try/catchand do a no-op when the URL is missing. Your alerting is the least important thing in any request; it must never be the thing that breaks it. - The URL is a secret. Anyone who has it can post to your channel. Keep it out of git, and if it leaks, regenerate it in Slack (Incoming Webhooks > remove/add); no code changes, just swap the environment variable.
BTW, same pattern works for Discord and Telegram if Slack isn’t your thing; a different URL and a slightly different JSON body, basically same code; or use Python if you’re a guru :-).
I’m honestly wondering if there’s an easier way. I’d probably use it.
That’s it.
Enjoy!
Slack logo rendered with Krbn.

Don't keep it to yourself!…