[NewMeet PingPage — status pages for your users](https://pingpage.live/)

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:*
${(amount / 100).toFixed(2)} ${currency.toUpperCase()}`\
          },\
          {\
            type: 'mrkdwn',\
            text: `*Customer:*
${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
