Zero
Zero
Back

Send Slack Alerts from your App using Slack Webhook URLs

Sending alerts from your production services to your Slack workspace can speed up your team's responses to incidents and sales opportunities. This article will walk you through integrating with the Slack API from a Remix application.

Sam Magura

Sam Magura

Staircases

Slack is one of the leading platforms for real-time communication. But the platform is useful for more than just messaging your coworkers — Slack is also a convenient place to aggregate and monitor alerts from across your business. For example, your development team may want to receive an alert when a production server's CPU usage exceeds a certain threshold, and your sales team may want to receive an alert when a new lead is acquired.

Having these alerts in Slack allows your teams to react more quickly than if the alert was sent as an email. Decreasing the time between an alert being generated and a team member taking action can have a huge impact, for example if the alert indicates that a production system is about to go down.

The other good thing is that integrating your services with the Slack API is incredibly easy. Simply create a Slack app, and then you can send Slack messages from your code by making HTTP POST calls to the app's webhook URL.

The rest of this article will provide a step-by-step guide to sending Slack messages from your application, with the Slack webhook URL retrieved from Zero at runtime. We'll be using the Remix  full stack JavaScript framework, but the steps can easily be adapted to any programming environment that allows you to make HTTP calls.

🔗 The full code for this example is available in the zerosecrets/examples  GitHub repository.

Creating a Slack App

Before we can start sending messages from our code, we need to create a Slack app and connect it to our Slack workspace. You can do this by following these steps:

  1. Go to https://api.slack.com/apps .
  2. Click "Create New App".
  3. Select "From scratch".
  4. Enter a name for the app and select your workspace.
    Creating a Slack app
  5. Under "Add features and functionality", select "Incoming Webhooks". Turn the toggle to "on".
  6. Click "Add New Webhook to Workspace".
  7. Select the the channel you want your app to post to. We'll be using a dedicated #alerts channel.
    Selecting the channel the Slack app will post to
  8. Copy the webhook URL, which will look something like https://hooks.slack.com/services/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. Your URL is a secret and should not be shared publicly or committed to your git repository.

If you want, you can copy the sample Curl request and run it in a terminal to verify that the app is working.

Secure your secrets conveniently

Zero is a modern secrets manager built with usability at its core. Reliable and secure, it saves time and effort.

Zero dashboard

Adding the Webhook URL as a Secret in Zero

We can simplify our application's configuration by storing the webhook URL in Zero with the rest of our API keys. To do so, log in to Zero and create a new project. Then click the "Add secret" button and fill out the form as shown here:

Adding the Slack webhook URL as a secret in Zero
Adding the Slack webhook URL as a secret in Zero

Creating the Remix Application

Remix is a relatively new full stack JavaScript application framework that is based on React. Overall, Remix is fairly similar to Next.js , but is faster  and offers a unified model for data fetching. Though, the Next.js team seems likely to catch up to Remix with the new app directory  that was launched with Next.js 13 as a beta feature. While we'll be using Remix in this guide, you can follow roughly the same steps if working with Next.js or any other web application framework.

To create a new Remix application, run npx create-remix@latest slack-alerts and answer the questions when prompted:

1
2
3
4
5
6
shell
npx create-remix@latest slack-alerts

   ? What type of app do you want to create? Just the basics
   ? Where do you want to deploy? Choose Remix App Server if you're unsure; it's easy to change deployment targets. Vercel
   ? TypeScript or JavaScript? TypeScript
   ? Do you want me to run `npm install`? Yes

Feel free to choose whichever deployment target your prefer — it won't affect the code we write to integrate with Slack.

Our application only needs to contain a single page, so we can put our UI code directly in app/routes/index.tsx. Let's render a simple form that asks for the user's name and the alert text they would like to send to Slack:

Simple Remix UI for sending an alert to Slack
Simple Remix UI for sending an alert to Slack

The code for the form will look something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
typescript
export default function Index() {
  const actionData = useActionData<ActionData>()
  const errors = actionData?.errors

  return (
    <div>
      <h1>Create an Alert in Slack</h1>

      <Form method="post">
        <div className="form-group">
          <label htmlFor="nameInput">Your name</label>
          <input id="nameInput" name="name" />
          {errors?.name && <p className="validation-error">{errors.name}</p>}
        </div>

        <div className="form-group">
          <label htmlFor="textInput">Alert text</label>
          <textarea id="textInput" name="text" rows={6} />
          {errors?.text && <p className="validation-error">{errors.text}</p>}
        </div>

        <button type="submit">Create alert</button>
      </Form>

      {actionData?.success && <p className="success">Created alert!</p>}
    </div>
  )
}

When the form is submitted, it will execute the action function which is defined in the same file. Our action will validate the form data and then call createAlert() if everything looks good.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
typescript
type ActionData =
  | {
      success: boolean

      errors?: {
        name: null | string
        text: null | string
      }
    }
  | undefined

export const action: ActionFunction = async ({request}) => {
  const formData = await request.formData()

  const name = formData.get('name') as string | null
  const text = formData.get('text') as string | null

  const errors = {
    name: name ? null : 'Name is required.',
    text: text ? null : 'Alert text is required.',
  }
  const hasErrors = Object.values(errors).some((errorMessage) => errorMessage)
  if (hasErrors) {
    return json<ActionData>({success: false, errors})
  }

  await createAlert({name: name!, text: text!})

  return json<ActionData>({success: true})
}

Sending the Alert

The next step is to implement createAlert. This function will run server side and send an HTTP POST to the Slack webhook URL, using the data that was submitted through the form. The createAlert function should be placed in app/models/alert.server.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
typescript
interface CreateAlertOptions {
  name: string
  text: string
}

export async function createAlert({name, text}: CreateAlertOptions): Promise<void> {
  const slackWebhookUrl = await getSlackWebhookUrl()

  const body = JSON.stringify({text: `${name} created a new alert: ${text}`})

  // Node 18+ is required to use the built-in `fetch` function
  const response = await fetch(slackWebhookUrl, {
    method: 'POST',
    body,
    headers: {
      'Content-Type': 'application/json',
    },
  })

  if (!response.ok) {
    let responseText = ''

    try {
      responseText = await response.text()
    } catch {}

    throw new Error(
      `Posting to Slack returned a ${response.status} status code. ` + `The response was:\n\n${responseText}`,
    )
  }
}

Here, getSlackWebhookUrl is a function that will retrieve the secret webhook URL from Zero using the Zero TypeScript SDK. You can install the SDK with

1
shell
npm install @zerosecrets/zero

and use it like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
typescript
import {zero} from '@zerosecrets/zero'

let slackWebhookUrl: string | undefined

export async function getSlackWebhookUrl(): Promise<string> {
  // Don't call Zero on every request
  if (slackWebhookUrl) {
    return slackWebhookUrl
  }

  if (!process.env.ZERO_TOKEN) {
    throw new Error('Did you forget to set the ZERO_TOKEN environment variable?')
  }

  const secrets = await zero({
    token: process.env.ZERO_TOKEN,
    pick: ['slack'],
  }).fetch()

  slackWebhookUrl = secrets.slack?.['ALERTS_WEBHOOK_URL']

  if (!slackWebhookUrl) {
    throw new Error('Did not receive the Slack webhook URL.')
  }

  return slackWebhookUrl
}

Now when you run your app, you'll need to provide the Zero token as an environment variable:

1
shell
ZERO_TOKEN='YOUR-ZERO-TOKEN' npm run dev

If you were deploying this to a platform like Vercel, the Zero token should also be added as an environment variable through the Vercel UI.

Finally try running the app and entering a name and the content of your alert. If it works, you'll see the alert in your Slack workspace!

The alert appears as a message in the Slack channel
The alert appears as a message in the Slack channel

Conclusion

This article showed how to send a message to a Slack channel from your code using the Slack API. We used Remix for our demonstration app, but you can integrate with Slack just as easily from any other web framework.

While our simple app had the user manually creating an alert, similar to PagerDuty, it is even more powerful to automatically create alerts when certain events occur (like when the load on your database exceeds some threshold). In these scenarios, you can use the exact same code that was shown in this article to create the alert. The more involved part is figuring out how to integrate with the monitoring system you use to determine when an alert should be sent.


Other articles

A cube made of smaller cubes

Build an Email Summary Bot using Claude AI, Gmail, and Slack (Part 2)

Use the Claude AI assistant to summarize emails in just a few lines of code.

A tall office building

Build an Email Summary Bot using Claude AI, Gmail, and Slack (Part 1)

Integrate with the Gmail Push Notifications API to enable your app to intelligently respond to new emails.

Secure your secrets

Zero is a modern secrets manager built with usability at its core. Reliable and secure, it saves time and effort.