Back to Blog
Tutorial8 min read2026-07-05

How to Send Deploy Notifications to Slack

Get a Slack message every time you deploy — who, what, and whether it succeeded. Here's how to wire deploy notifications with incoming webhooks and rich Block Kit messages.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

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

  1. 1Go to https://api.slack.com/apps and create an app (or use an existing one).
  2. 2Enable Incoming Webhooks.
  3. 3Add New Webhook to Workspace, pick the channel (e.g., #deploys).
  4. 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

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also