How to build a Slack bot with the Slack API

Building a Slack bot using the Events API and Web API means wiring up automated responses to slash commands through simple PHP scripts and cURL requests. This tutorial walks through connecting external APIs to Slack for task management or data retrieval.

A Slack bot listens for slash commands from users, hits an external API, and posts the results back into a channel. That’s the whole thing — Events API receives the trigger, your PHP script processes it, Web API sends the response. You can wire up any data source you want in about an hour.

Solution Workflow & Process
Workflow Management Software

Workflow Made Easy

Save Time
Track & Delegate Workflows
Consistent Workflows
Explore this solution

Summary

  • Events API and Web API work like ping-pong — Events API pings your app when something happens in a Slack channel, Web API pongs commands back when your app wants to post a message. Two directions, same game
  • Slash commands trigger your integrations — Create commands like “/todo” or “/temperature” that listen for user input, hit external APIs, and return formatted responses directly in Slack channels
  • PHP and cURL handle the whole request cycle — Use curl_init() to start, curl_setopt() to configure headers and endpoints, curl_exec() to run, curl_close() to finish. Then json_decode() turns the response into something useful
  • Webhook URLs let you post back to Slack — After processing external API data, reinitialize cURL to POST formatted JSON with attachments and markdown to your Slack webhook. That’s what displays the message in the channel. Explore workflow automation

One thing that keeps coming up with workflow automation, integration topics come up constantly — and Slack integration is one of the most requested capabilities. Slack has become the default communication platform for a huge number of teams, and its API opens the door to building some genuinely useful automations.

Why does this matter? Because most teams are drowning in context-switching between apps. A well-built Slack bot can pull information from wherever it lives and drop it right into the conversation — no tab-switching, no copy-pasting, no mess.

One of the most useful applications of the Slack API is building bots. Slack calls them “virtual team members” that help you manage tasks. In this tutorial, we’ll:

  • Break down the Slack API
  • Understand how Slack bots work
  • Build a bot that pulls real task data (and a fun one that generates random useless facts)

Here’s the end result:

Animated GIF - manual alt text recommended for accessibility

Let’s get into it.

How the Slack API works

I’m assuming you already know Slack as a platform. If not — it’s a team communication tool, similar to Discord but built for business. With so many businesses running on Slack, a complete API was inevitable.

Slack’s platform has several APIs for building “apps” — the Real Time Messaging API (now legacy), the Events API, the Web API, and the Conversations API. This tutorial uses the Events API and the Web API. The Events API is the one that notifies your app when something happens in Slack — a message posted, a reaction added, a slash command triggered. The Web API is what your app uses to talk back — posting messages, updating channels, uploading files.

You’ll bounce between these two constantly when building bots. Most developers start with the Events API because it’s the trigger for everything, then realize they need the Web API to actually do anything useful with that trigger. Here’s how they relate.

The Events API and Web API relationship

Building a Slack app generally requires both the Events API and the Web API. They do similar things but in opposite directions.

Think of it as ping-pong. When an “event” happens in a Slack channel, the Events API pings information about that event to your app. When your app processes that information and wants to make a change back in Slack, it uses the Web API to pong commands back.

Both APIs are notifiers and observers — just pointing in different directions.

Table tennis match with players competing and API labels: Events API and Web API Your app is basically a fancy game of ping-pong.

What Slack bots can do

Bots are one type of Slack app that can make your channels genuinely useful. They listen for commands from users and return relevant information. You could build a bot that responds with the current weather when someone types “/temperature” or pulls your company’s quarterly sales figures when someone types “/revenue.” Pretty much any data you can get from an API, you can pipe into a Slack bot command.

Honestly, this is where it gets interesting. Process quality is performance. But a well-structured bot that surfaces the right data at the right time? That’s the kind of small automation that compounds.

In this example, we’re building a Slack bot that listens for the “/todo” command and returns the user’s pending tasks from the Tallyfy API. Tallyfy manages workflows and tasks, so it’s a natural fit for demonstrating how to connect an external data source to Slack.

Building the bot step by step

Required knowledge:

  • PHP
  • cURL

Step 1 — Create the app in Slack

First, create an app within Slack. Click this link, name your app, and pick a workspace.

Slack app creation dialog showing Todo Bot name and HOOT Events workspace selection

Then give it a description, image, and background color.

Slack app directory display information showing Todo Bot app icon, name, and background color settings

Step 2 — Create your request URL

Now you need somewhere to host the app on your end. This means creating a PHP file that listens for events from the Events API, processes them, and sends commands back to Slack.

I’m naming the file “todoBot.php” and hosting it on my own site.

Step 3 — Set up the slash command

Go to the “Slash Commands” tab and click “Create New Command.” You’ll get a form to configure your command.

Make sure the “Request URL” field points to where you’re hosting the PHP file. Fill it out like this and hit save.

Done. Everything is set up on Slack’s side.

Slack command creation form showing fields for command setup including request URL and usage hint

Step 4 — Activate incoming webhooks

This step points your app at the right channel. Go to the “Incoming Webhooks” tab and flip the switch from “Off” to “On.”

Choose the channel you want the app to post to.

HOOT Events integration dialog for Todo Bot showing identity confirmation and channel posting options

Scroll down and you’ll see a “Webhook URL.” Save this — you’ll need it later.

Slack webhook configuration showing workspace URLs with channel and user information

Step 5 — Make the API request

Now we need to pull data from an external API. I’m using the Tallyfy API here, but you could swap in any API that returns data you want to surface in Slack.

We’ll use cURL in PHP. This is probably the trickiest part, but cURL shows up everywhere in API work, so it’s worth understanding.

Inside our conditional statement, we initialize cURL, tell it where to pull data from, set the authentication headers, execute, and close. The request returns a JSON of our organization’s outstanding tasks.


//Initiate cURL
$curl = curl_init();

//Will return the response, if false it prints the response
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

//Set the url, in this case Tallyfy's organization tasks endpoint
curl_setopt($curl, CURLOPT_URL, 'https://api.tallyfy.com/organizations/{ My Tallyfy organization id }/tasks');

//Set the header of our Tallyfy API request to our authenticating information
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
    'accept: application/json',
    'content-type: application/json',
    'authorization: Bearer { My Tallyfy authorization token }',
    'X-Tallyfy-Client: SlackBot Demo'
));

//Execute cURL
$result = curl_exec($curl);

//Close the cURL
curl_close($curl);

Step 6 — Process the API response

Now we need to take the tasks returned by our API request and format them so Slack can display them. We’ll convert tasks into an array of Slack attachments and package them into a cURL body. The JSON structure looks like this:

{
  "mkdwn" : true,
  "text" : "*Tasks to do:*",
  "attachments":
  [
    {
      "color" : "#3DB75C",
      "text" : "This is a task!"
    },{
      "color" : "#3DB75C",
      "text" : "This is also a task!"
    }
  ]
}

First, turn the API result into an array object.

...
// Execute cURL
$result = curl_exec($curl);

//Close the cURL
curl_close($curl);

//Decode our cURL result into an array
$json = json_decode($result, true);

Grab the value for the “data” key.

...
// Execute cURL
$result = curl_exec($curl);

//Close the cURL
curl_close($curl);

//Decode our cURL result into an array
$json = json_decode($result, true);

//Get the Tallyfy data from the JSON array
$data = $json['data'];

Create an empty “attachments” array and loop through each task, adding it as an attachment.

...
// Execute cURL
$result = curl_exec($curl);

//Close the cURL
curl_close($curl);

//Decode our cURL result into an array
$json = json_decode($result, true);

//Get the Tallyfy data from the JSON array
$data = $json['data'];

$attachments = array();
foreach ($data as $task) {
    $task_name = $task['title'];
    array_push($attachments, array(
            'color' => '#3DB75C',
            'text' => $task_name
        )
    );
}

Now use the attachments array to build the cURL body for Slack.

...
// Execute cURL
$result = curl_exec($curl);

//Close the cURL
curl_close($curl);

//Decode our cURL result into an array
$json = json_decode($result, true);

//Get the Tallyfy data from the JSON array
$data = $json['data'];

$attachments = array();
foreach ($data as $task) {
    $task_name = $task['title'];
    array_push($attachments, array(
            'color' => '#3DB75C',
            'text' => $task_name
        )
    );
}

//Create an HTTP body to pass in our Slack POST
$slack_body = array(

//Setting mkdwn to true allows us to bold substrings by encasing them in asterisks
    'mkdwn' => true,
    'text' => "*Tasks to do:*",
    'attachments' => $attachments
);

Ready to send it back.

Step 7 — Post the results to Slack

Now we send the formatted attachments back to Slack. Same pattern — cURL, but this time as a POST to your webhook URL.

...
//Now we have everything we need to post our HTTP body to slack and print a new message
//Reinitialize our cURL
$curl = curl_init();

//Set the cURL to the appropriate webhook (the one we saved earlier in Step 4: "Activate Incoming Webhooks") url for your app and set the cURL to a POST request
curl_setopt($curl, CURLOPT_URL, 'https://hooks.slack.com/services/{ Your slack webhook }');
curl_setopt($curl, CURLOPT_POST, 1);

//Set the cURL's body to a JSON encoding of our slack_body
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($slack_body));

//Receive server response ...
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

//Execute and close our cURL
curl_exec($curl);

curl_close($curl);
}
?>

When you’re done, the complete PHP file should look something like this:

<?php
//Initiate cURL
$curl = curl_init();

//Will return the response, if false it prints the response
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

//Set the url, in this case Tallyfy's organization tasks endpoint
curl_setopt($curl, CURLOPT_URL, 'https://api.tallyfy.com/organizations/{ My Tallyfy organization id }/tasks');

//Set the header of our Tallyfy API request to our authenticating information
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
    'accept: application/json',
    'content-type: application/json',
    'authorization: Bearer { My Tallyfy authorization token }',
    'X-Tallyfy-Client: SlackBot Demo'
));

// Execute cURL
$result = curl_exec($curl);

//Close the cURL
curl_close($curl);

//Decode our cURL result into an array
$json = json_decode($result, true);

//Get the fact, as a string, from the JSON array
$data = $json['data'];

$attachments = array();
foreach ($data as $task) {
    $task_name = $task['title'];
    array_push($attachments, array(
            'color' => '#3DB75C',
            'text' => $task_name
        )
    );
}

//Create an HTTP body to be passed in our Slack POST
$slack_body = array(

//Setting mkdwn to true allows us to bold substrings by encasing them in asterisks
    'mkdwn' => true,
    'text' => "*Tasks to do:*",
    'attachments' => $attachments
);

//Now we have everything we need to post our HTTP body to slack and print a new message
//Reinitialize our cURL
$curl = curl_init();

//Set the cURL to the appropriate webhook (the one we saved earlier in Step 4: "Activate Incoming Webhooks") url for your app and set the cURL to a POST request
curl_setopt($curl, CURLOPT_URL, 'https://hooks.slack.com/services/{ Your slack webhook }');
curl_setopt($curl, CURLOPT_POST, 1);

//Set the cURL's body to a JSON encoding of our slack_body
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($slack_body));

// Receive server response ...
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

//Execute and close our cURL
curl_exec($curl);

curl_close($curl);
?>

We’re almost there. One more step.

Step 8 — Reinstall the bot

No matter what order you do the above steps in, you’ll probably need to uninstall and reinstall the Slack bot from your channel. If you skip this, the channel won’t recognize the slash command.

Go to the channel where you originally installed the bot. You’ll see a message like “added an integration to this channel: Todo Bot.” Click the bot name, choose “Settings,” then “Remove App.” Reinstall the app to the channel.

Note: Every time you install an app to a new channel, the webhook URL changes. That includes uninstalling and reinstalling. Update your PHP file with the new webhook from step 7.

Once you reinstall, type “/todo” and you should see your Tallyfy tasks.

Adapting this for any API

I used the Tallyfy API because it’s what I know best — it handles workflow automation and task management, so pulling task data made sense for a demo. But this same code pattern works with any API.

We built Tallyfy because we kept seeing with teams running complex onboarding workflows, we’ve heard that API availability, webhooks, and real-time processing are the factors that make or break their tool choices. One team running 50-step rollout processes told us that integrating their existing systems via webhooks transformed how they track work in real time.

Here’s the thing that frustrates me about most integration setups — nobody’s building the workflows that AI agents need to follow. A Slack bot is a small example of the bigger pattern: you need a structured process feeding structured data, or the automation is just noise.

To prove the code pattern is flexible, here’s a slightly altered version that relays random useless facts instead of tasks.

<?php
//Initiate cURL
$curl = curl_init();

// Will return the response, if false it prints the response
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

// Set the url, in this case the random fact generator response as JSON
curl_setopt($curl, CURLOPT_URL, 'https://uselessfacts.jsph.pl/random.json?language=en');

// Execute cURL
$result = curl_exec($curl);

//Close the cURL
curl_close($curl);

//Decode our cURL result into an array
$json = json_decode($result, true);

//Get the fact, as a string, from the JSON array
$fact_text = $json['text'];

//Create an HTTP body to be passed in our Slack POST
$slack_body = array(

//Setting mkdwn to true allows us to bold substrings by encasing them in asterisks
    'mkdwn' => true,
    'text' => '*Random fact:*',
    'attachments' => array(
        array(

//You can change the attachment color to whatever you would like
            'color' => '#3DB75C',
            'text' => $fact_text
        )
    )
);

//Now we have everything we need to post our HTTP body to slack and print a new message
//Reinitialize our cURL
$curl = curl_init();

//Set the cURL to the appropriate webhook (the one we saved earlier in Step 4: "Activate Incoming Webhooks") url for your app and set the cURL to a POST request
curl_setopt($curl, CURLOPT_URL, 'https://hooks.slack.com/services/{ Your slack webhook }');
curl_setopt($curl, CURLOPT_POST, 1);

//Set the cURL's body to a JSON encoding of our slack_body
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($slack_body));

// Receive server response ...
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

//Execute and close our cURL
curl_exec($curl);

curl_close($curl);
?>

The result:

Animated GIF - manual alt text recommended for accessibility

Slack pricing

Free
$0
  • 90-day message history
  • 10 app integrations
Pro
$7.25-8.75/user/month
  • Core AI included
  • Minimum 3 users
Business+
$15-18/user/month
  • Advanced AI features
  • 99.9% uptime SLA
Enterprise Grid
Contact sales
* Annual billing required for lower prices* Pricing updated June 2025
Pricing last verified: January 2026. Prices may have changed.

Tallyfy Slack app

We built the bot in this tutorial using Tallyfy’s API, but there’s a dedicated Slack app for Tallyfy that does much more out of the box. If you’re looking to wire up task management to Slack without writing code, that’s the easier path.

We’ve observed that operations teams don’t want to build custom bots — they want things to just work. The Tallyfy Slack app surfaces task notifications, lets people complete workflow steps without leaving Slack, and keeps everything tracked. No PHP required.

If you want to try it, create a free Tallyfy account and connect the Slack app from the integrations page.

About the Author

Amit is the CEO of Tallyfy. He is a workflow expert and specializes in process automation and the next generation of business process management in the post-flowchart age. He has decades of consulting experience in task and workflow automation, continuous improvement (all the flavors) and AI-driven workflows for small and large companies. Amit did a Computer Science degree at the University of Bath and moved from the UK to St. Louis, MO in 2014. He loves watching American robins and their nesting behaviors!

Follow Amit on his website, LinkedIn, Facebook, Reddit, X (Twitter) or YouTube.

Automate your workflows with Tallyfy

Stop chasing status updates. Track and automate your processes in one place.