Why deploy notifications matter
A team without deploy notifications finds out about a deploy when something breaks. With them, everyone sees what shipped, who shipped it, and whether it succeeded — in the channel they already watch. It's a five-minute setup that pays off every single day.
This guide wires deploy notifications into Slack using an incoming webhook, with a rich message that's actually useful (not just "deploy happened").
Step 1: create a Slack incoming webhook
- 1Go to https://api.slack.com/apps and create an app (or use an existing one).
- 2Enable Incoming Webhooks.
- 3Add New Webhook to Workspace, pick the channel (e.g.,
#deploys). - 4Copy the webhook URL — it looks like
https://hooks.slack.com/services/T000/B000/XXXX.
Treat this URL as a secret — anyone with it can post to your channel. Store it as an env var, never in the repo.
Step 2: a minimal notification
The simplest possible notification is a POST with a text field:
curl -X POST "$SLACK_WEBHOOK_URL" \
-H "content-type: application/json" \
-d '{"text":":rocket: Deployed myapp to production"}'That works, but a good notification carries context.
Step 3: a rich Block Kit message
Slack's Block Kit lets you format a structured message. Here's a deploy notification with the fields that matter:
// notify-slack.js
const webhook = process.env.SLACK_WEBHOOK_URL;
async function notify({ service, env, status, commit, author, url }) {
const ok = status === "success";
const payload = {
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: `${ok ? ":white_check_mark:" : ":x:"} *${service}* deploy to *${env}* ${ok ? "succeeded" : "FAILED"}`,
},
},
{
type: "section",
fields: [
{ type: "mrkdwn", text: `*Commit:*\n\`${commit.slice(0, 7)}\`` },
{ type: "mrkdwn", text: `*Author:*\n${author}` },
],
},
{
type: "actions",
elements: [
{ type: "button", text: { type: "plain_text", text: "View deploy" }, url },
],
},
],
};
const res = await fetch(webhook, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(payload),
});
if (!res.ok) throw new Error(`slack notify failed: ${res.status}`);
}The useful fields: service, environment, success/failure, short commit SHA, author, and a link back to the deploy. That's enough to triage at a glance.
Step 4: hook it into your deploy
There are two clean places to trigger the notification:
Option A — in your CI/build pipeline, after the deploy step:
node notify-slack.js --status=success --service=myapp --env=production \
--commit="$GIT_SHA" --author="$GIT_AUTHOR"Option B — as a post-deploy hook / webhook from the platform. If your platform emits a deploy webhook (start/success/failure), point it at a tiny edge function that reformats the payload into Slack's format. This decouples notifications from your build script.
// edge function: platform deploy webhook -> Slack
export default async function handler(req) {
const event = await req.json();
await notify({
service: event.service,
env: event.environment,
status: event.status, // 'success' | 'failed'
commit: event.commit,
author: event.author,
url: event.dashboardUrl,
});
return new Response("ok");
}On PandaStack you can deploy this reformatter as an edge function (included on every tier) and store SLACK_WEBHOOK_URL as a secret env var.
Step 5: notify on failures loudly
Successes are nice; failures are critical. Make failure messages visually distinct and consider an @here or channel mention for production failures:
if (!ok && env === "production") {
payload.blocks.unshift({
type: "section",
text: { type: "mrkdwn", text: ":rotating_light: <!here> *Production deploy failed*" },
});
}Don't over-mention — reserve @here/@channel for genuine production failures, or people mute the channel.
Best practices
- Store the webhook URL as a secret, scoped to the deploy environment.
- Notify on start, success, and failure — start tells people "hold your merges," success/failure closes the loop.
- Include a link back to logs/dashboard so someone can investigate in one click.
- Separate channels for staging vs. production noise.
- Rate-limit if you deploy very frequently, so the channel stays readable.
- Don't leak secrets into the message body (e.g., never echo env vars).
References
- Slack incoming webhooks: https://api.slack.com/messaging/webhooks
- Slack Block Kit reference: https://api.slack.com/block-kit
- Block Kit Builder (visual designer): https://app.slack.com/block-kit-builder
- Slack message formatting (mrkdwn): https://api.slack.com/reference/surfaces/formatting
- Slack rate limits: https://api.slack.com/apis/rate-limits
---
Want deploy alerts in Slack without standing up infrastructure? Deploy the webhook reformatter as a PandaStack edge function and store the secret safely. Start free at https://dashboard.pandastack.io