Integrations 8 min read

Building Slack Notifications with Webhooks: A Practical Guide

Turn any webhook event into a Slack notification. Learn how to build a webhook-to-Slack pipeline with formatted messages, error alerts, and smart routing.

H

HookWatch Team

February 22, 2026

Webhooks fire when things happen. Slack is where your team lives. Connecting the two means your team knows about critical events the moment they occur — new orders, failed payments, deployment completions, security alerts.

This guide walks through building a webhook-to-Slack pipeline from scratch.

Setting Up Slack Incoming Webhooks

First, create a Slack app with an incoming webhook:

  1. Go to api.slack.com/apps and create a new app
  2. Under Incoming Webhooks, toggle it on
  3. Click Add New Webhook to Workspace
  4. Select the channel and authorize
  5. Copy the webhook URL

Your Slack webhook URL looks like this:

Code
https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX

Test it with a simple curl:

Bash
curl -X POST -H 'Content-Type: application/json' \
  --data '{"text": "Hello from webhooks!"}' \
  https://hooks.slack.com/services/T00/B00/XXX

The Architecture

Code
Webhook Provider → HookWatch → Your Server → Slack
     (Stripe)       (proxy)     (formatter)   (channel)

HookWatch receives the webhook, ensures delivery, and forwards it to your server. Your server formats the event into a Slack message and sends it to the appropriate channel.

Why not send directly to Slack? Because:

  • You need to format raw webhook payloads into readable messages
  • You want to route different events to different channels
  • You need filtering to avoid notification fatigue
  • You want retry logic if Slack is temporarily down

Building the Webhook Handler

Javascript
const express = require('express');
const app = express();
app.use(express.json());

const SLACK_CHANNELS = {
  payments: 'https://hooks.slack.com/services/T00/B01/payments',
  orders: 'https://hooks.slack.com/services/T00/B02/orders',
  alerts: 'https://hooks.slack.com/services/T00/B03/alerts'
};

app.post('/webhooks/handler', async (req, res) => {
  const event = req.body;

  try {
    const slackMessage = formatEvent(event);
    const channel = routeEvent(event);

    await sendToSlack(channel, slackMessage);
    res.status(200).json({ processed: true });
  } catch (error) {
    console.error('Failed to process webhook:', error);
    res.status(500).json({ error: 'Processing failed' });
  }
});

Formatting Messages with Block Kit

Plain text messages get ignored. Slack's Block Kit lets you create structured, visually distinct notifications:

Javascript
function formatPaymentEvent(event) {
  const { amount, currency, customer_email, status } = event.data;
  const emoji = status === 'succeeded' ? ':white_check_mark:' : ':x:';

  return {
    blocks: [
      {
        type: 'header',
        text: {
          type: 'plain_text',
          text: `${emoji} Payment ${status}`
        }
      },
      {
        type: 'section',
        fields: [
          {
            type: 'mrkdwn',
            text: `*Amount:*\n${(amount / 100).toFixed(2)} ${currency.toUpperCase()}`
          },
          {
            type: 'mrkdwn',
            text: `*Customer:*\n${customer_email}`
          }
        ]
      },
      {
        type: 'context',
        elements: [
          {
            type: 'mrkdwn',
            text: `Event ID: ${event.id} | ${new Date().toISOString()}`
          }
        ]
      }
    ]
  };
}

Smart Routing

Not every event belongs in every channel. Route based on event type and severity:

Javascript
function routeEvent(event) {
  const type = event.type || event.event;

  // Payment events
  if (type.startsWith('payment.') || type.startsWith('invoice.')) {
    return SLACK_CHANNELS.payments;
  }

  // Order events
  if (type.startsWith('order.') || type.startsWith('checkout.')) {
    return SLACK_CHANNELS.orders;
  }

  // Failures go to alerts
  if (type.includes('failed') || type.includes('error')) {
    return SLACK_CHANNELS.alerts;
  }

  // Default channel
  return SLACK_CHANNELS.alerts;
}

Filtering Noise

Your team doesn't need a notification for every single event. Filter by severity and frequency:

Javascript
const NOTIFY_EVENTS = new Set([
  'payment.succeeded',
  'payment.failed',
  'order.completed',
  'subscription.canceled',
  'charge.disputed'
]);

function shouldNotify(event) {
  const type = event.type || event.event;

  // Always notify for high-priority events
  if (NOTIFY_EVENTS.has(type)) return true;

  // Notify for failures regardless of type
  if (event.data?.status === 'failed') return true;

  // Skip everything else
  return false;
}

app.post('/webhooks/handler', async (req, res) => {
  const event = req.body;

  if (!shouldNotify(event)) {
    return res.status(200).json({ skipped: true });
  }

  // ... send to Slack
});

Error Handling

Slack occasionally returns 429 (rate limited) or 5xx errors. Handle these gracefully:

Javascript
async function sendToSlack(webhookUrl, message, retries = 3) {
  for (let attempt = 1; attempt <= retries; attempt++) {
    const response = await fetch(webhookUrl, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(message)
    });

    if (response.ok) return;

    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After') || 5;
      await new Promise(r => setTimeout(r, retryAfter * 1000));
      continue;
    }

    if (response.status >= 500 && attempt < retries) {
      await new Promise(r => setTimeout(r, attempt * 2000));
      continue;
    }

    throw new Error(
      `Slack API error: ${response.status} ${response.statusText}`
    );
  }
}

Using HookWatch Alerts Instead

If you don't need custom formatting, HookWatch has built-in Slack alert integration. Configure it from the dashboard:

  1. Go to Alerts in the HookWatch dashboard
  2. Click Add Alert
  3. Select Slack as the channel
  4. Paste your Slack webhook URL
  5. Set conditions (e.g., "alert when failure rate exceeds 5%")

This gives you failure notifications without writing any code. Use the custom approach above when you need formatted messages for business events (new orders, payments, signups).

Production Checklist

Before going live with webhook-to-Slack notifications:

  • Rate limit your messages — batch events if volume is high
  • Include event IDs — makes it easy to look up details in HookWatch
  • Add action buttons — link to dashboards, retry pages, or customer profiles
  • Use threads — group related events (e.g., all events for one order)
  • Monitor delivery — HookWatch tracks whether your handler responded successfully
  • Test with the Sandbox — send sample events through HookWatch's Sandbox to verify formatting before going live
Tags: slacknotificationsintegrationstutorialwebhooks

Share this article

Ready to try HookWatch?

Start monitoring your webhooks in minutes. No credit card required.

Start Free Today