PullNotifier Logo
Published on

Make Slack App: make slack app from scratch — GitHub Notifier

Authors

To build a Slack app from scratch, you'll need to register it, sort out the permissions, and then write some server-side code to make it all work. For a GitHub notifier, this means creating a bot that listens for webhook triggers from your repository—like a new pull request—and then posts a nicely formatted message into the right Slack channel. It's a powerful way to connect two of your most essential tools and automate a big chunk of your workflow.

Why Bother Building a Custom GitHub Notifier for Slack?

Before we jump into the code, let's talk about the "why." Sure, the official GitHub integration for Slack exists, but building your own gives you a level of control and precision that you just can't get from an off-the-shelf solution. Think of it as a serious productivity upgrade, turning what can be a noisy, chaotic notification channel into a focused hub for your entire development lifecycle.

The biggest win here is slashing context switching. Instead of bouncing between GitHub emails, the GitHub UI, and Slack, your team gets instant, actionable PR updates right where they're already collaborating. Imagine a PR opening and the right reviewers are immediately mentioned in their team channel. No more manual @-mentions or worrying that someone missed the notification.

Fine-Tune Your Code Review Process

A custom notifier lets you cut through the noise. You get to decide exactly which events trigger a notification. Do you only care when a PR is opened, approved, or has merge conflicts? You can build that logic. This kind of customization ensures every single alert is relevant and prompts action, which is a huge help in speeding up code reviews and shortening your deployment cycles.

Before you start building, it's worth checking out the landscape of existing GitHub monitoring solutions to get a feel for what’s possible and spot any gaps your custom app could fill.

A custom solution moves your team from passively receiving information to actively managing the development workflow. It creates a centralized, automated source of truth that keeps everyone synchronized without overwhelming them with unnecessary alerts.

Let's do a quick comparison to see where a custom app really shines.

Default GitHub App vs Your Custom Slack App

FeatureDefault GitHub AppYour Custom Slack App
Notification FilteringBasic filtering for common events (commits, PRs). Can be noisy.Fully customizable. Notify on specific labels, file changes, or complex conditions.
Message ContentStandard, generic message format. Limited customization.Completely control the message layout, content, and calls-to-action.
Reviewer MentionsMentions based on GitHub username.Map GitHub users to Slack IDs for reliable, direct pings.
Workflow IntegrationLimited to predefined actions.Trigger other workflows, post to dynamic channels, or integrate with other tools.
Permission ScopesOften requires broad permissions.Minimal, targeted permissions. You only request what you absolutely need.
Cost & MaintenanceFree, but managed by GitHub/Slack. No control over changes.Requires development and hosting, but gives you total control and ownership.

While the default app is great for getting started, a custom solution gives you the power to build something that fits your team's workflow perfectly.

Tap Into a Massive Ecosystem

Building your own integration is more valuable than ever. By early 2025, Slack's daily active user count shot up to 42 million globally, with a developer community of over 500,000. When you create your own app, you're tapping into a huge, highly engaged user base that relies on custom workflows to get things done.

The official integration is a decent starting point, and we cover its features in our guide on how to use the official GitHub Slack app, but a custom build is where you unlock true workflow automation tailored to how your team works.

Laying the Foundation: Your Slack App and Permissions

Every great Slack app starts its life in the Slack API dashboard. This is where you'll register your application, give it an identity, and carefully configure what it's allowed to do. Think of this initial setup less as a chore and more like drawing up the blueprint for your entire project.

First things first, head over to the Slack API site and click "Create New App." You'll need to give it a name—something clear and descriptive like "GitHub PR Notifier" works well—and pick a development workspace. This part is pretty straightforward, but it’s where you’ll generate the essential credentials that bring your app to life.

Once you've created it, you'll land on a configuration screen that acts as the control center for your app's core settings.

Diagram illustrating how custom Slack apps eliminate noise, improve focus, and increase speed in workflows.

From here, you'll manage everything from permissions and event subscriptions to how the app is distributed.

Scopes and the Principle of Least Privilege

With the app created, your next critical task is defining its permissions, which Slack calls OAuth scopes. Scopes dictate exactly what your app is allowed to do inside a workspace. It's vital to follow the principle of least privilege: only request the permissions you absolutely need. Asking for too much can make users think twice about installing your app and introduces unnecessary security risks.

For our GitHub notifier, we only need two scopes to get started:

*   **`chat:write`**: This is the big one. It allows your app to post messages in channels it's a part of, which is the core of our notification functionality.
*   **`channels:read`**: This scope lets your app see a list of public channels. While not essential for the V1, it's incredibly useful for future features, like letting a user pick a channel from a dropdown in your app's settings.

By sticking to just these two, you build trust and keep the app secure and focused. You can always come back and add more scopes later as you build out more features.

Giving Your App a Voice with a Bot User

To actually post messages, your app needs an identity. In Slack, you do this by creating a Bot User. Head to the "Bot Users" section in your app settings (sometimes it's tucked under "App Home") and add one. This gives your app a name and a presence, letting it act like any other member of the team.

The bot token generated here is what your server will use to authenticate with the Slack API.

One of the most critical pieces of information you'll find in the "Basic Information" section is your Signing Secret. Guard this key carefully and never share it. Slack uses it to sign all incoming requests (like webhook events), allowing your server to verify they are legitimate and not from a malicious actor trying to spoof them.

Building the Core Logic with Bolt for JavaScript

Alright, time to roll up our sleeves and write the code that brings this Slack app to life. We'll be using Slack's own Bolt for JavaScript framework. It’s a fantastic tool that really simplifies app development, and we'll pair it with the ever-popular Express.js to build a solid server that can handle events from both Slack and GitHub.

In this section, I’ll walk you through the code, complete with comments explaining each part. We’ll start by spinning up a basic Express server, initializing our Bolt app with the credentials we grabbed earlier, and then building out the logic to process incoming data. The goal is a clean, easy-to-understand codebase you can build on later.

A developer's desk featuring a MacBook Pro and an iMac displaying code, with 'BUILD WITH BOLT' overlay.

Initializing Your Bolt and Express Server

First things first, let's get the foundation of our server set up. You'll need to install a couple of packages from npm: @slack/bolt and express.

// Import the necessary modules
const { App, ExpressReceiver } = require('@slack/bolt');
const express = require('express');

// Initialize a new Express application
const expressApp = express();

// Initialize the Bolt app with your credentials
// It's best practice to store these in environment variables
const boltApp = new App({
  token: process.env.SLACK_BOT_TOKEN,
  signingSecret: process.env.SLACK_SIGNING_SECRET,
  // Use the Express app as a custom receiver
  receiver: new ExpressReceiver({
    signingSecret: process.env.SLACK_SIGNING_SECRET,
    endpoints: '/slack/events',
    app: expressApp
  })
});

This first block of code gets both an Express server and a Bolt app instance running. Notice how we pass our expressApp into Bolt's ExpressReceiver? This is a neat trick that lets Bolt manage all the Slack-specific routes (like /slack/events) while leaving the rest of our server free for custom endpoints, like the one we'll build for our GitHub webhook.

Security First: Storing credentials like your SLACK_BOT_TOKEN and SLACK_SIGNING_SECRET in environment variables is non-negotiable. Hardcoding them directly into your source code is a major security risk that could expose your workspace.

Verifying Requests and Handling GitHub Payloads

With the server initialized, the next job is to create a dedicated endpoint to listen for webhooks from GitHub. This part is critical: we need to verify that incoming requests are genuinely from GitHub and not some malicious actor. We'll do this by validating a signature sent in the request headers against a secret you'll set up in your GitHub repo settings (more on that later).

Let's add the code to handle the incoming pull request data. We'll create a new route, /github/events, which will parse the JSON payload from GitHub.

// Enable Express to parse JSON bodies
expressApp.use(express.json());

// Create a dedicated endpoint for GitHub webhooks
expressApp.post('/github/events', async (req, res) => {
  // TODO: Add GitHub webhook secret verification logic here

  const payload = req.body;
  const action = payload.action;

  // We only care about specific pull request actions
  if (payload.pull_request) {
    console.log(`Received a pull request event: ${action}`);

    // Extract the key information we need for the notification
    const pr = payload.pull_request;
    const prTitle = pr.title;
    const prUrl = pr.html_url;
    const prAuthor = pr.user.login;
    const repoName = payload.repository.full_name;

    // TODO: Add logic to map repo to a channel and post a message
  }

  // Acknowledge receipt of the webhook
  res.status(200).send('Event received');
});

This snippet accomplishes a few important things:

  • Enables JSON Parsing: express.json() is middleware that tells our server to automatically parse incoming JSON payloads into a JavaScript object.
  • Defines the Endpoint: We're setting up a POST route at /github/events to catch the data.
  • Extracts Key Data: Inside the handler, we pull out the essential details from the PR payload, like its title, URL, and author.

This structured approach keeps the GitHub logic separate from the Slack event handling, which makes your code much easier to maintain as you make slack app features more complex. Our server is now ready to receive data. The next step is to connect it to GitHub and then write the logic to post those beautiful notifications into Slack.

Connecting GitHub Webhooks to Your Slack App

Alright, our server is up and listening. Now for the fun part: getting GitHub to actually talk to it. This is where webhooks come in. Think of a webhook as an automated message that GitHub fires off to our server whenever something interesting happens, like activity on a pull request.

Setting this up is surprisingly simple. Just head over to your GitHub repository, click on Settings, and find the Webhooks section. From there, you'll add a new webhook, which basically just tells GitHub where to send its updates.

Configuring the Webhook Payload

There are two critical fields you need to get right here: the Payload URL and the Content type.

The Payload URL is the public address for the /github/events endpoint we just built. For the content type, make sure you select application/json so our Express server knows how to read the incoming data.

Now, it's really tempting to just select "Send me everything" when choosing which events to subscribe to, but trust me, you don't want that. It will absolutely flood your server with noise. Instead, choose "Let me select individual events" and subscribe only to Pull requests. This keeps our app focused and efficient.

A huge part of building secure integrations is verifying that the data you receive is legitimate. When you're connecting services with webhooks, solid API design best practices are non-negotiable for creating an app that's both robust and reliable.

Securing Your Endpoint with a Webhook Secret

The Secret field is your best friend when it comes to security. It's just a random string of text that you create, and GitHub will use it to sign every single payload it sends over. On our end, the Node.js server will use that same secret to verify the payload is genuinely from GitHub and hasn't been messed with.

*   Generate a strong, random string to use as your secret.
*   Paste it into the GitHub webhook configuration.
*   Make sure you store the *exact same* secret in your app's environment variables.

Never, ever hardcode your webhook secret in your source code. Treat it like you would any API key or password. This verification step is a must-have for any production-ready application.

The demand for custom integrations like this is exploding. The Slack developer community is massive, with hundreds of thousands of custom apps already in active use. With Slack's revenue projected to hit $4.22 billion in 2025, building a secure and valuable app means tapping into a seriously thriving ecosystem. You can read more about Slack's impressive growth on their official blog.

Crafting and Sending Intelligent Notifications

A laptop displaying a Slack-like logo and '2-4', with a 'Smart Notifications' bubble, coffee, and books on a wooden desk.

A great notification isn't just about dumping data into a channel. It’s about delivering the right information, to the right people, at precisely the right moment. Now that our server is hooked up to GitHub, we can move beyond basic alerts and start building dynamic, context-rich messages using Slack's Block Kit.

This is where you turn raw JSON into something genuinely useful and well-formatted. Block Kit is Slack’s UI framework for crafting messages with interactive components. Instead of a flat line of text, you can build rich layouts with sections, dividers, and even buttons. This is absolutely essential when you make slack app notifications that your team will actually appreciate.

Our goal is to turn a potentially disruptive ping into a productive tool by presenting the most crucial PR info at a glance.

Building a Dynamic Message Layout

Let's take the pull request data we pulled earlier and shape it into a structured Block Kit message. The plan is to include the PR title as a bolded header, mention the author, and add a direct button to view the request on GitHub. This simple structure makes the notification immediately actionable.

Here’s a quick function that puts together the Block Kit payload.

const createPRNotification = (prData) => {
  return {
    blocks: [
      {
        type: "section",
        text: {
          type: "mrkdwn",
          text: `*New Pull Request: ${prData.title}*`
        }
      },
      {
        type: "context",
        elements: [
          {
            type: "mrkdwn",
            text: `Opened by *${prData.author}* in \`${prData.repoName}\``
          }
        ]
      },
      {
        type: "actions",
        elements: [
          {
            type: "button",
            text: {
              type: "plain_text",
              text: "View on GitHub",
              emoji: true
            },
            url: prData.url
          }
        ]
      }
    ]
  };
};

This organized format is worlds more effective than a plain text message. It presents information logically, making it easy for developers to get the gist of an update and decide what to do next without ever leaving Slack.

Implementing Smart Routing and Mentions

A truly intelligent app knows where to send notifications and who to bug. Hardcoding a single channel ID is fine for a quick test run, but it’s not a scalable solution. A much better approach is to create a simple mapping system—think a JSON object or a small database table—that connects GitHub repositories to specific Slack channel IDs.

This mapping gives you a ton of flexibility:

  • #team-frontend gets pings for the webapp-ui repository.
  • #team-backend sees updates from the api-service repository.
  • #devops-alerts is notified about changes to any infrastructure-as-code repos.

Beyond just routing, you can layer in rules for smart mentions. For instance, when a PR gets approved, you could automatically @-mention the original author to let them know it’s ready to merge. It’s a small touch, but it closes the feedback loop instantly. Automating these little interactions is a fantastic way to boost team efficiency and ensure nothing slips through the cracks. It's also a great way to align with your team's broader goals for improving code quality with GitHub Actions and Slack.

The intelligence of your app is defined by its ability to reduce cognitive load. Smart routing and conditional mentions ensure that every notification is a signal, not noise, empowering your team to act decisively on relevant updates.

Testing Deployment and Going Live

Alright, the code is written and your core logic is in place. But we're not quite at the finish line yet. Before you push this app to a live server where your team will rely on it, you need to put it through its paces with some rigorous local testing. This is where a tool like ngrok becomes your best friend.

Ngrok works by creating a secure, public URL that tunnels directly to your local development server. This little bit of magic lets you temporarily point your GitHub webhook to your own machine, allowing you to receive real-time payloads from actual pull request events. It’s the perfect way to debug and tweak your logic without going through a full deployment for every minor change.

Simulating Real-World Scenarios

With ngrok up and running, you can start mimicking the exact situations your app will handle once it's live. This is your best chance to squash bugs and smooth out the user experience before anyone else even sees it.

*   **Open a new pull request:** Does your notification fire off to the correct channel? Is the Block Kit message showing up exactly as you designed it?
*   **Add a comment or push a new commit:** Just as important, make sure these events *don't* trigger a new notification. Keeping the channel clean is key.
*   **Close or merge the PR:** Check if the app handles these final states gracefully without any errors.

Running through these scenarios confirms your app behaves predictably. If you find notifications aren't showing up when they should, our guide on how to fix GitHub Slack notifications that aren't sending has some great troubleshooting steps.

Pushing to Production and Distribution

Once you're confident that your app is stable and working as expected, it's time to go live. Deploying a Node.js application is pretty straightforward on services like Heroku or other cloud providers. After you've deployed, the only thing left is to update your GitHub webhook URL to point to your new live server endpoint instead of the temporary ngrok one.

Now for the final step: distribution. Head over to your Slack app's settings, where you can generate a public installation link. Just share this link with your team, and they'll be able to authorize the app and add it to your workspace in just a few clicks.

Even with a solid plan, you're bound to run into a few questions when building your own Slack app. Let's walk through some of the common things developers ask when they start building a GitHub notifier, so you can skip the guesswork.

Can This App Work with Private GitHub Repositories?

Yes, absolutely. The great news is that the process is exactly the same for both public and private repositories. GitHub webhooks can be set up on any repository where you have admin rights. As long as your deployed server is reachable from the public internet, GitHub can securely push webhook payloads to it.

Just be careful your app's logic doesn't accidentally leak sensitive information from a private repo into a public Slack channel. That’s a slip-up you definitely want to avoid.

What Are the Hosting Costs to Expect?

Hosting for an app like this can be surprisingly cheap—often even free. For smaller teams or a personal project, the free tiers from services like Heroku, Vercel, or Render are usually more than enough to handle a moderate amount of webhook traffic.

If your team's activity starts to pick up, you might need to jump to a paid plan, which usually starts around $5-10 per month. The main things that will bump you into a paid tier are your uptime needs and the sheer volume of webhook events you're processing.

How Should I Handle Slack API Rate Limits?

This is a smart question, especially for apps that might get slammed with traffic, but it's usually not a major issue for a standard GitHub notifier. Slack's Bolt for JS framework comes with built-in retry logic that handles most rate-limiting hiccups for you automatically.

The main API method we’re using here is chat.postMessage. It's a Tier 3 method, which gives you a comfortable ceiling of around 50+ requests per minute. It's pretty rare for a single repository to generate enough pull request activity to hit that limit.

That said, if you're building this for a massive, high-traffic monorepo, it's a good practice to implement a simple queue and a backoff strategy in your code. This will help space out the messages and keep everything running smoothly if you ever get close to the limit.


Ready to build smarter, not harder? PullNotifier delivers focused GitHub notifications without the custom code. Start your free trial today!